headless_audit_test.go 1.6 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667
  1. package main
  2. // 无头审计: 复用 analyzer 全套审计逻辑 (结构 + 索引内容一致性), 不走 HTTP。
  3. // 用法: AUDIT_DB=<库文件> AUDIT_OUT=<JSON 输出> go test -run TestHeadlessAudit -v ./analyzer
  4. // 若被分析库旁边有 <db>.analyzer 缓存且指纹匹配, 会直接命中缓存秒回。
  5. import (
  6. "encoding/json"
  7. "fmt"
  8. "os"
  9. "testing"
  10. "time"
  11. )
  12. func TestHeadlessAudit(t *testing.T) {
  13. p := os.Getenv("AUDIT_DB")
  14. if p == "" {
  15. t.Skip("AUDIT_DB 未设置")
  16. }
  17. out := os.Getenv("AUDIT_OUT")
  18. if out == "" {
  19. out = "audit_out.json"
  20. }
  21. if err := useDB(p); err != nil {
  22. t.Fatal("打开库:", err)
  23. }
  24. invalidateAudit()
  25. mu.Lock()
  26. fp = p
  27. rHdr()
  28. tabs = pTabs()
  29. mu.Unlock()
  30. setCacheFP(fingerprint())
  31. deadline := time.Now().Add(15 * time.Minute)
  32. for {
  33. a, _, _ := computeAudit()
  34. if a == nil {
  35. cacheMu.Lock()
  36. msg := auditErrMsg
  37. cacheMu.Unlock()
  38. t.Fatal("审计失败:", msg)
  39. }
  40. if !a.IdxPending {
  41. b, err := json.MarshalIndent(a, "", " ")
  42. if err != nil {
  43. t.Fatal("序列化:", err)
  44. }
  45. if err := os.WriteFile(out, b, 0o644); err != nil {
  46. t.Fatal("写结果:", err)
  47. }
  48. fmt.Printf("审计完成: verdict=%s pages=%d 孤儿=%d 交叉引用=%d 非法指针=%d "+
  49. "索引序乱=%d 悬空PK=%d 缺主键条目=%d 缺二级条目=%d 空闲活页=%d\n",
  50. a.Verdict, a.TotalPages,
  51. a.Reach.Unaccounted,
  52. a.MultiRef, a.BadRefCount,
  53. a.IdxBadOrder, a.IdxOrphanPK, a.IdxMissingRow, a.IdxMissingEntry,
  54. len(a.FreeLive))
  55. fmt.Println("结果 JSON:", out)
  56. return
  57. }
  58. if time.Now().After(deadline) {
  59. t.Fatal("索引内容补全超时")
  60. }
  61. time.Sleep(500 * time.Millisecond)
  62. }
  63. }