package main // 无头审计: 复用 analyzer 全套审计逻辑 (结构 + 索引内容一致性), 不走 HTTP。 // 用法: AUDIT_DB=<库文件> AUDIT_OUT= go test -run TestHeadlessAudit -v ./analyzer // 若被分析库旁边有 .analyzer 缓存且指纹匹配, 会直接命中缓存秒回。 import ( "encoding/json" "fmt" "os" "testing" "time" ) func TestHeadlessAudit(t *testing.T) { p := os.Getenv("AUDIT_DB") if p == "" { t.Skip("AUDIT_DB 未设置") } out := os.Getenv("AUDIT_OUT") if out == "" { out = "audit_out.json" } if err := useDB(p); err != nil { t.Fatal("打开库:", err) } invalidateAudit() mu.Lock() fp = p rHdr() tabs = pTabs() mu.Unlock() setCacheFP(fingerprint()) deadline := time.Now().Add(15 * time.Minute) for { a, _, _ := computeAudit() if a == nil { cacheMu.Lock() msg := auditErrMsg cacheMu.Unlock() t.Fatal("审计失败:", msg) } if !a.IdxPending { b, err := json.MarshalIndent(a, "", " ") if err != nil { t.Fatal("序列化:", err) } if err := os.WriteFile(out, b, 0o644); err != nil { t.Fatal("写结果:", err) } fmt.Printf("审计完成: verdict=%s pages=%d 孤儿=%d 交叉引用=%d 非法指针=%d "+ "索引序乱=%d 悬空PK=%d 缺主键条目=%d 缺二级条目=%d 空闲活页=%d\n", a.Verdict, a.TotalPages, a.Reach.Unaccounted, a.MultiRef, a.BadRefCount, a.IdxBadOrder, a.IdxOrphanPK, a.IdxMissingRow, a.IdxMissingEntry, len(a.FreeLive)) fmt.Println("结果 JSON:", out) return } if time.Now().After(deadline) { t.Fatal("索引内容补全超时") } time.Sleep(500 * time.Millisecond) } }