From d4ed043d5c6b91364ca347fe4fbfe52a705ee2cb Mon Sep 17 00:00:00 2001 From: AskaEth Date: Tue, 13 Oct 2026 20:26:46 +0800 Subject: [PATCH] =?UTF-8?q?test(memory):=20=E4=BF=AE=E6=92=AD=E7=A7=8D?= =?UTF-8?q?=E6=97=A5=E5=BF=97=E7=94=A8=E4=BE=8B=E7=9A=84=E5=81=B6=E5=8F=91?= =?UTF-8?q?=E5=81=87=E7=BA=A2=20+=20=E8=A1=A5=20TempTTL=20=E6=8E=A5?= =?UTF-8?q?=E7=BA=BF=E7=BC=9D=E6=96=AD=E8=A8=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 两件都是**测试面**的补件,不碰被测逻辑。 ① 既有 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:100: 找不到 "a.memoryTempTTL()"` FAIL。 --- cmd/cyrene/wire_memory_review_test.go | 6 +++++ .../runtimeconfig/memorytidy_seed_test.go | 27 ++++++++++++++++--- 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/cmd/cyrene/wire_memory_review_test.go b/cmd/cyrene/wire_memory_review_test.go index 9b4459a..31b5d47 100644 --- a/cmd/cyrene/wire_memory_review_test.go +++ b/cmd/cyrene/wire_memory_review_test.go @@ -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)", diff --git a/internal/config/runtimeconfig/memorytidy_seed_test.go b/internal/config/runtimeconfig/memorytidy_seed_test.go index 0e8242b..c2a90b1 100644 --- a/internal/config/runtimeconfig/memorytidy_seed_test.go +++ b/internal/config/runtimeconfig/memorytidy_seed_test.go @@ -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) } }