Files
MailUI4Agents/server/internal/repo/debt_registry_test.go
JianFeeeee d25770ea2f fix(欠账): Skip 进余额且条件必须是测量;豁免按文件+次数;三笔欠账合成一处可读余额
pi 2026-09-14 三条(他接受了我对"恒红=相位错"的反驳,但指出 Skip 带来的两处漏洞)。

1. **Skip 必须进余额、条件必须是测量**:
   - **条件**:跳过与否由 `measureMailStatusDebt` **实测**(详情路径的 status 是否真的
     等于按读者派生),不是常量、不是"我们还没迁完"这种没人会更新的事实;
   - **余额**:`docs/DEBTS.json` 是**唯一登记**,Go 侧判据 `TestDebtLedgerMatchesMeasurement`
     **自己测量**后与登记比对(第一版我让余额由另一条判据写入 ⇒ **排序依赖**,
     Go 同包内按源文件顺序跑,登记那条先跑就读到 0 —— 排序依赖是隐蔽的假绿,已抽成自足函数);
   - **可见性**:`go test` 跑通时**不打印包的输出**,我第一版把余额打在 TestMain 里,
     常态运行一个字都看不见 —— 正是 pi 说的"不显形"。所以常态可见的那份打在
     electron 套件的 RESULT 行:`RESULT phase=install static=5 debts=7
     (static-criteria:5,mails-status-derived:1,gesture-semantics:1) probe=ok`。

2. **豁免从"按文件"改成"按文件 + 次数"**:`migrate.go` 这类比较**上限 2 处**(附理由),
   多一处即红。我在读侧清册上自己修过这个洞,豁免那格却退了一格 —— pi 指出得对。

3. **三笔欠账合成一处**:原先各自表达(`RESULT static=5` / `t.Skip` 无余额 /
   文档里的到期前提无余额),**没有一处能一眼看全**。现在统一登记在 `docs/DEBTS.json`
   (id / 余额 / 到期前提 / 判据位置),两端读同一份:Go 侧比对实测,electron 侧打进 RESULT 行。
   还清那天:登记要跟着清 —— 不清则由 `TestDebtLedgerMatchesMeasurement` 报
   "**欠账已还清**,但登记还记着 N"(还清是可测事件,这正是那条判据存在的意义)。
2026-09-14 17:17:37 +08:00

153 lines
5.1 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package repo
import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"sort"
"strings"
"testing"
)
/*
欠账的**单一余额**(pi 2026-09-14 裁定 §3)。
背景:到这轮为止已经有三笔不同类型、各自表达方式的欠账 ——
鸿蒙静态判据(`RESULT static=5`,有余额、有到期探针)、
`mails.status` 行级派生化(`t.Skip`,**原先无余额**)、
手势语义契约(登记的到期前提,**原先无余额**)。
三笔都"可判",但**没有一处能一眼看全**;而"欠账不显形就等于没有"。
所以:**登记在一处、可打印**。Go 这一侧打印在 TestMain 收尾,
跨端的净值同时登记在 `docs/DEBTS.json`(两端都能读,见该文件)。
*/
type debt struct {
ID string
Count int
Due string // 到期前提(什么时候该还清)
Where string // 判据在哪
}
var debts = map[string]debt{
"mails-status-derived": {
ID: "mails-status-derived",
Count: 0, // 由 mail_status_derived_test.go **实测**后标 1(不许手写:常量余额 = 永久 Skip 的死法)
Due: "详情/线程改为按读者派生(readStateFor)之后 —— 那时 mail_status_derived_test.go 从 Skip 转实跑",
Where: "server/internal/repo/mail_status_derived_test.go",
},
"static-criteria": {
ID: "static-criteria",
Count: 5,
Due: "本工作区能装、能点设备(探针三值转 true 时自动变红)",
Where: "client/electron/test/run-all.mjs(STATIC_ONLY)",
},
"gesture-semantics": {
ID: "gesture-semantics",
Count: 1,
Due: "P6 第 3 步:鸿蒙侧出现滑动手势代码时立即建(此前建 = 只有一端存在的假判据)",
Where: "docs/HARMONY-ALIGN-PLAN.md P6 段",
},
}
// debtMark 由各条欠账的判据在"确认未清"时调用(余额来自**测量**,不是常量)。
func debtMark(id string) {
d := debts[id]
d.Count = 1
debts[id] = d
}
type debtLedger struct {
Debts []struct {
ID string `json:"id"`
Count int `json:"count"`
Due string `json:"due"`
Where string `json:"where"`
} `json:"debts"`
}
// loadDebts 读**那一份**登记(docs/DEBTS.json):两端的余额必须来自同一处。
func loadDebts(t *testing.T) debtLedger {
t.Helper()
b, err := os.ReadFile(filepath.Join("..", "..", "..", "docs", "DEBTS.json"))
if err != nil {
t.Fatalf("读欠账登记 docs/DEBTS.json 失败:%v(登记丢了 = 欠账不显形)", err)
}
var l debtLedger
if err := json.Unmarshal(b, &l); err != nil {
t.Fatalf("欠账登记不是合法 JSON:%v", err)
}
return l
}
/*
★ 登记与**实测**必须一致(pi 2026-09-14 裁定 §1:Skip 的条件要是测量结果,"跳过"要进余额)。
这条判据挡的是两种死法:
① 条件被写死:余额不是从测量来的,而是某人抄的数字 ⇒ 这里用 debtMark 的实测值比对;
② 跳过不可数:`go test` 对 Skip 是退出码 0、`--- SKIP` 只是一行输出
(而且**跑得通时 go test 根本不打印包的输出** —— 我第一次就把余额打在 TestMain 里,
结果常态运行时一个字都看不见,正是"不显形")。所以可见的那份在
electron 套件的 RESULT 行(它读同一个文件),这里保证两处**同源**。
*/
func TestDebtLedgerMatchesMeasurement(t *testing.T) {
l := loadDebts(t)
seen := map[string]bool{}
for _, d := range l.Debts {
seen[d.ID] = true
if d.Due == "" || d.Where == "" {
t.Fatalf("欠账 %s 必须写明到期前提与判据位置(否则它只是「存在」,不是「欠账」)", d.ID)
}
}
for _, id := range []string{"static-criteria", "mails-status-derived", "gesture-semantics"} {
if !seen[id] {
t.Fatalf("欠账登记里缺 %s —— 三笔必须同处登记,否则审计只会找到一处就当全部", id)
}
}
// 本包能实测的那一笔:**自己测**(不依赖别的测试先跑过 —— 排序依赖是隐蔽的假绿)
var want int
for _, d := range l.Debts {
if d.ID == "mails-status-derived" {
want = d.Count
}
}
outstanding, detail, derived := measureMailStatusDebt(t)
got := 0
if outstanding {
got = 1
}
if got != want {
if got == 0 {
t.Fatalf("**欠账已还清**(详情 %q 已等于按读者派生 %q),但 docs/DEBTS.json 还记着 %d —— "+
"还清是可测事件,登记要跟着清(这正是这条判据存在的意义)", detail, derived, want)
}
t.Fatalf("mails-status-derived 的余额:登记说 %d,实测说 %d —— 登记与测量分叉了", want, got)
}
}
// debtSummary 供 TestMain 打印:一处能看全的余额 + 到期前提。
func debtSummary() string {
ids := make([]string, 0, len(debts))
total := 0
for id, d := range debts {
if d.Count > 0 {
ids = append(ids, id)
total += d.Count
}
}
if len(ids) == 0 {
return " debts=0(全部还清)"
}
sort.Strings(ids)
var b strings.Builder
fmt.Fprintf(&b, "\n======= 欠账余额 debts=%d =======", total)
for _, id := range ids {
d := debts[id]
fmt.Fprintf(&b, "\n · %s ×%d\n 判据:%s\n 到期:%s", d.ID, d.Count, d.Where, d.Due)
}
fmt.Fprintf(&b, "\n(三笔合在一处看全:分散登记时,审计只会找到一处就当全部)")
return b.String()
}