Files
knowledge-governance/internal/librarydiff/wiki_metadata_test.go
T
姚章浩 98294b9583
build / build (push) Failing after 24s
fix(library): 统一执行身份、终态收尾、准入与数据身份
why:审计 19 项(12 P1 / 7 P2)来自同一批根因,逐点试错会把协议撕成十几份:
  · 意图与执行混为一谈(F01/F15):`library.sync:<scope>:<快照>` 是终身幂等键,
    A→B→A 的第三次发布拿到第一次的历史成功作业,什么都不做;
  · 版本行既是实体又是事件(F02/F09):主键只由 (kind,scope,快照) 决定、写入是
    upsert,第二个作业覆盖 `source_job_id`/`evidence`/`created_at`;
  · 接管没有代次(F13):两个 worker 都能认领同一 pending 作业,失租的旧执行还能
    写业务状态、释放别人的维护锁;
  · 早退与终态没有统一政策(F03/F04/F05/F06):半写窗口的前置检查失败会重新开门、
    服务起不来也照样开入口、journal 终态后收尾不可重建、恢复成功不回写 current;
  · 数据身份与准入各写一套(F07/F08/F14/F17):接管丢了复用语义、全库搬运不改
    location_hash、通用入口可造 library.* 作业、目标模型从没被问过;
  · 存储/容量/UI/CI 判据各自为政(F10/F11/F12/F16/F18/F19)。

what:按批准的一套协议集中实施(详见 handoffs/release-consolidated-implementation/
current-contract.md):
  · operation(一次明确意图)/ attempt(epoch 代次)/ 不可变版本事件 / current 指针;
    治理库迁移 76,只动治理库,不碰 WeKnora Schema/源码/镜像;
  · 所有业务状态、阶段、版本事件、维护锁写入带 epoch 守卫(失租代次写不进去);
    心跳失租立刻取消 handler;
  · 半写窗口早退保持关闭;服务未全部健康绝不开入口;journal 终态可从持久事实重建
    收尾;恢复成功登记 recovery 事件并推进 current;
  · worker 在动维护前核对业务行/kind/scope/队列关联;通用入口拒绝 library.*;
  · 全库搬运重算 location_hash 并与文档级导入共用实现;接管消费写集合的复用决定;
    发布前核目标实时模型契约、容量门禁前置;
  · 存储保护闭包改为全量依赖、类型只认显式登记、保护未知则不出可删候选;
  · 页面核 HTTP 状态与结构、恢复纳入轮询、Wiki 差异覆盖标题/摘要/父级。

how:`make verify` rc=0(16 项必需检查全绿,含 go test 全量与前端 485 例);
`go vet ./...` rc=0;`npx tsc -p tsconfig.json --noEmit` rc=0;逐项负例见各包测试
(F03/F04/F08/F12/F13/F14/F16/F18/F19 都有先红后绿记录)。
未执行:治理库迁移到任何已有库、真实 PG/ES/S3/WeKnora 集成、故障注入、部署。
2026-10-30 16:14:54 +08:00

38 lines
1.5 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 librarydiff
import "testing"
// F18:只改 title / summary / parent_slug 也必须在发布前差异里出现。
// 旧比较结构根本没读 summary/parent_slug,title 读了却不比较——三种改动都被漏报,
// 于是"预览无变化"可以对应一次真实的元数据变更。
func TestWikiMetadataChangeMustAppear(t *testing.T) {
for _, field := range []string{"title", "summary", "parent_slug"} {
t.Run(field, func(t *testing.T) {
before := map[string]any{"slug": "page-a", "title": "old", "summary": "old",
"parent_slug": "old", "content": "unchanged"}
after := map[string]any{"slug": "page-a", "title": "old", "summary": "old",
"parent_slug": "old", "content": "unchanged"}
after[field] = "new"
result, err := CompareWiki([]map[string]any{before}, []map[string]any{after})
if err != nil {
t.Fatal(err)
}
if result.Updated != 1 {
t.Fatalf("只改 %s 必须记成 updated=1:%+v", field, result)
}
if len(result.Pages) != 1 || len(result.Pages[0].Changed) == 0 {
t.Fatalf("差异项必须列出改了哪些字段:%+v", result.Pages)
}
})
}
// 反例:完全没有变化时不报差异(避免把"无变化"也渲染成更新)。
same := map[string]any{"slug": "page-b", "title": "t", "summary": "s", "parent_slug": "p", "content": "c"}
result, err := CompareWiki([]map[string]any{same}, []map[string]any{same})
if err != nil {
t.Fatal(err)
}
if result.Updated != 0 || len(result.Pages) != 0 {
t.Fatalf("无变化不应产生差异:%+v", result)
}
}