@@ -380,3 +380,105 @@ func Test_bindMySqlArgs(t *testing.T) {
380380 }
381381 })
382382}
383+
384+ func Test_twoGenCache_hit_in_hot (t * testing.T ) {
385+ c := newTwoGenCache (10 )
386+ v := mysqlNamedParsedResult {Sql : "SELECT 1" , Names : []string {"id" }}
387+ c .Store ("key1" , v )
388+
389+ got , ok := c .Load ("key1" )
390+ if ! ok {
391+ t .Fatal ("expected cache hit in hot, got miss" )
392+ }
393+ if got .Sql != v .Sql {
394+ t .Errorf ("expected sql=%s, got=%s" , v .Sql , got .Sql )
395+ }
396+ }
397+
398+ func Test_twoGenCache_hit_in_cold (t * testing.T ) {
399+ c := newTwoGenCache (2 )
400+ v := mysqlNamedParsedResult {Sql : "SELECT cold" , Names : []string {"x" }}
401+ c .Store ("cold_key" , v )
402+
403+ // 填满 hot,触发轮转,cold_key 进入 cold 代。
404+ c .Store ("fill1" , mysqlNamedParsedResult {Sql : "s1" , Names : []string {"a" }})
405+ c .Store ("fill2" , mysqlNamedParsedResult {Sql : "s2" , Names : []string {"b" }})
406+
407+ got , ok := c .Load ("cold_key" )
408+ if ! ok {
409+ t .Fatal ("expected cache hit in cold, got miss" )
410+ }
411+ if got .Sql != v .Sql {
412+ t .Errorf ("expected sql=%s, got=%s" , v .Sql , got .Sql )
413+ }
414+
415+ // 命中 cold 后,条目应被提升到 hot。
416+ c .mu .RLock ()
417+ _ , promotedToHot := c .hot ["cold_key" ]
418+ c .mu .RUnlock ()
419+ if ! promotedToHot {
420+ t .Error ("expected cold_key to be promoted to hot after cold hit" )
421+ }
422+ }
423+
424+ func Test_twoGenCache_eviction_on_capacity (t * testing.T ) {
425+ c := newTwoGenCache (2 )
426+
427+ c .Store ("k1" , mysqlNamedParsedResult {Sql : "s1" , Names : []string {"a" }})
428+ c .Store ("k2" , mysqlNamedParsedResult {Sql : "s2" , Names : []string {"b" }})
429+
430+ // hot 已满(2 条),再写入触发轮转,k1/k2 进入 cold,hot 重置。
431+ c .Store ("k3" , mysqlNamedParsedResult {Sql : "s3" , Names : []string {"c" }})
432+
433+ c .mu .RLock ()
434+ hotLen := len (c .hot )
435+ coldLen := len (c .cold )
436+ c .mu .RUnlock ()
437+
438+ if hotLen != 1 {
439+ t .Errorf ("expected hot len=1 after eviction, got=%d" , hotLen )
440+ }
441+ if coldLen != 2 {
442+ t .Errorf ("expected cold len=2 after eviction, got=%d" , coldLen )
443+ }
444+ }
445+
446+ func Test_twoGenCache_miss (t * testing.T ) {
447+ c := newTwoGenCache (10 )
448+ _ , ok := c .Load ("nonexistent" )
449+ if ok {
450+ t .Error ("expected cache miss, got hit" )
451+ }
452+ }
453+
454+ func Test_parseMySqlNamedSql_no_params_not_cached (t * testing.T ) {
455+ // 使用一个独立的 twoGenCache 实例验证语义,避免污染全局缓存。
456+ c := newTwoGenCache (10 )
457+
458+ noParamSql := "SELECT * FROM t WHERE id = 1"
459+ result := mysqlNamedParsedResult {Sql : noParamSql , Names : []string {}}
460+
461+ // 模拟 parseMySqlNamedSql 中的判断:无命名参数时不写缓存。
462+ if len (result .Names ) > 0 {
463+ c .Store (noParamSql , result )
464+ }
465+
466+ _ , ok := c .Load (noParamSql )
467+ if ok {
468+ t .Error ("expected no-param SQL to not be cached, but it was" )
469+ }
470+
471+ // 有参数的 SQL 应正常缓存。
472+ paramSql := "SELECT * FROM t WHERE id=@id"
473+ parsedResult := parseMySqlNamedSql (paramSql )
474+ if len (parsedResult .Names ) == 0 {
475+ t .Fatal ("expected paramSql to have named params" )
476+ }
477+ cached , ok := mysqlNamedSqlParsedResult .Load (paramSql )
478+ if ! ok {
479+ t .Error ("expected parameterized SQL to be cached, but it was not" )
480+ }
481+ if cached .Sql != parsedResult .Sql {
482+ t .Errorf ("cached sql mismatch: expected=%s, got=%s" , parsedResult .Sql , cached .Sql )
483+ }
484+ }
0 commit comments