test(memory): 修播种日志用例的偶发假红 + 补 TempTTL 接线缝断言

两件都是**测试面**的补件,不碰被测逻辑。

① 既有 flake(internal/config/runtimeconfig/memorytidy_seed_test.go):
TestSeedMemoryTidy_LogsPathAndReasonWithoutValues 原先用**裸数字**扫**整行**日志找
4242/10800/1800,而同一行合法地打了 `path=<临时目录>`,`t.TempDir()` 的目录名带
随机数十进制 ⇒ 偶发命中(约个位数/10000),表现为假红。
修法取「断言带字段名的形态」:哨兵改成 `=4242` / `=10800` / `=1800` / `=259200`
(slog 的 `key=value` 字段形态),路径里的随机数前面是路径分隔片段而不是 `=`,
因此不受影响。
**为什么不用另一个备选「只扫 keys= 之后的内容」**:那会把断言缩成「keys 列表里没
有值」,任何以别的字段名打出来的值(`interval_sec=4242`、将来新加的字段)都会溜
过去 —— 等于把这条防护改成空壳,而不是修掉它的假红。
顺手把 tempTtlSec 的哨兵值 259200 一并纳入(上一切片给 completeSeedTuning 加了这
个键,漏掉它会让「不打配置值」在这一个键上失去覆盖)。

② 缝断言(cmd/cyrene/wire_memory_review_test.go):
TestMemoryReviewIsWired 的 seams 表补上 `a.memoryTempTTL()` 与 `TempTTL: tempTTL`
—— 上一切片把面板 `tempTtlSec` 接进 ReviewConfig 时,这条缝没有被覆盖,「接线被
摘掉」不会红。

自证:
- ① 前后对比(确定性,把临时目录名强制含 1800):旧形态 FAIL(「日志里出现了配置
  值 1800」)、新形态 PASS;另跑 `-count=20` 全绿。
- ② 变异:把 helper 与调用点一起改名 ⇒ `wire_memory_review_test.go💯 找不到
  "a.memoryTempTTL()"` FAIL。
This commit is contained in:
AskaEth committed 2026-10-13 20:26:46 +08:00
1 parent 21b906ffb0
commit d4ed043d5c
2 files changed
+29 -4

No files matched your search

+6
View File
@@ -75,6 +75,12 @@ func TestMemoryReviewIsWired(t *testing.T) {
"a.memoryT1RateLimit()",
"T1PerWindow: t1PerWindow",
"T1Window: time.Duration(t1WindowSec) * time.Second",
// 临时记忆 TTL(design/03 §9.6):第五态 `defer` 写进 `expires_at` 的
// 存活秒数,同样来自那个文件(`tempTtlSec`),缺位/坏值回落代码默认
// 7 天。**它没有 `.env` 档**(`.env` 已冻结,本键在那里从来没有出处),
// 因此这一行被摘掉 = 面板配了也不生效,正是本缝断言要拦的回归。
"a.memoryTempTTL()",
"TempTTL: tempTTL",
},
"main.go": {
"a.memoryReview.Start(ctx)",
@@ -248,6 +248,25 @@ func TestSeedMemoryTidy_RefusesIncompleteSeed(t *testing.T) {
}
}
// tidySeedLeakedValues 是「不许出现在日志里的配置值」的哨兵,**刻意带 `=` 前缀**
// (断言的形态是「字段值」而不是裸数字)。
//
// 为什么必须带 `=`:这条用例的日志里**合法地**带 `path=<临时目录>`,而
// `t.TempDir()` 的目录名带随机数十进制 —— 裸数字扫整行会**偶发假红**(实测命中过
// `…WithoutValues4091800659\001\config\…` 里的 `1800`,约个位数/10000 的窗口)。
// 带 `=` 的形态只可能来自 slog 的 `key=value` 字段;路径里的随机数前面是路径分隔
// 片段而不是 `=`,因此不受影响(值的**位置**语义也没变松)。
//
// 为什么不用另一个备选「只扫 `keys=` 之后的内容」:那会把断言缩成「keys 列表里
// 没有值」,于是任何以**别的**字段名打出来的值(`interval_sec=4242`、将来新加的
// 字段)都会溜过去 —— 那等于把这条防护改成空壳,而不是修掉它的假红。
//
// 259200 是 tempTtlSec(completeSeedTuning 里显式给的值),与其余三个同为「一眼可
// 辨、且与代码默认值不同」的哨兵。
func tidySeedLeakedValues() []string {
return []string{"=4242", "=10800", "=1800", "=259200"}
}
// TestSeedMemoryTidy_LogsPathAndReasonWithoutValues 钉住「可观测」:写盘与跳过都有
// 日志(带路径与原因、带 source),且**不打任何配置值**(本仓配置日志的既有口径)。
func TestSeedMemoryTidy_LogsPathAndReasonWithoutValues(t *testing.T) {
@@ -277,9 +296,9 @@ func TestSeedMemoryTidy_LogsPathAndReasonWithoutValues(t *testing.T) {
if !strings.Contains(out, "已按当时生效值播种落盘") {
t.Fatalf("播种日志没说明做了什么:%s", out)
}
for _, value := range []string{"4242", "10800", "1800"} {
for _, value := range tidySeedLeakedValues() {
if strings.Contains(out, value) {
t.Fatalf("日志里出现了配置值 %s(配置日志只许打键名):%s", value, out)
t.Fatalf("日志里出现了配置值 %s(配置日志只许打键名):%s", strings.TrimPrefix(value, "="), out)
}
}
@@ -299,9 +318,9 @@ func TestSeedMemoryTidy_LogsPathAndReasonWithoutValues(t *testing.T) {
if !strings.Contains(skipped, memoryTidySeedReasonExists) {
t.Fatalf("跳过播种的日志没带 reason 字段:%s", skipped)
}
for _, value := range []string{"4242", "10800", "1800"} {
for _, value := range tidySeedLeakedValues() {
if strings.Contains(skipped, value) {
t.Fatalf("跳过日志里出现了配置值 %s:%s", value, skipped)
t.Fatalf("跳过日志里出现了配置值 %s:%s", strings.TrimPrefix(value, "="), skipped)
}
}