Hydrate/mutate against schema 00028, ensure first-access bootstrap, and build deterministic change reports instead of static fiction. Co-authored-by: Cursor <cursoragent@cursor.com>
95 lines
3.1 KiB
Go
95 lines
3.1 KiB
Go
package docrevision_test
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
|
|
"chat-agent-system/internal/docrevision"
|
|
)
|
|
|
|
func TestEnsureBootstrapIdempotentAndReadable(t *testing.T) {
|
|
svc := docrevision.NewService(nil)
|
|
ctx := context.Background()
|
|
st, err := svc.EnsureBootstrap(ctx, docrevision.BootstrapRequest{
|
|
DocID: "doc_proj143_srs", Kind: "controlled", Markdown: "# SRS\n\nbody\n",
|
|
ProjectID: 143, WorkDir: "workspaces/p143/srs", LifecycleState: "drafting",
|
|
Actor: docrevision.ActorSnapshot{DisplayName: "bootstrap"},
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if st.CurrentVersionNo != 1 || st.HeadRevisionNo != 0 {
|
|
t.Fatalf("unexpected state %+v", st)
|
|
}
|
|
st2, err := svc.EnsureBootstrap(ctx, docrevision.BootstrapRequest{
|
|
DocID: "doc_proj143_srs", Markdown: "# other\n", ProjectID: 143,
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if st2.HeadSHA256 != st.HeadSHA256 {
|
|
t.Fatalf("bootstrap should be idempotent")
|
|
}
|
|
got, err := svc.GetRevisionState(ctx, "doc_proj143_srs")
|
|
if err != nil || got.ETag == "" {
|
|
t.Fatalf("get after bootstrap: %v %+v", err, got)
|
|
}
|
|
}
|
|
|
|
func TestEnsureBootstrapNeedsContent(t *testing.T) {
|
|
svc := docrevision.NewService(nil)
|
|
_, err := svc.EnsureBootstrap(context.Background(), docrevision.BootstrapRequest{DocID: "doc-upload-x"})
|
|
if err != docrevision.ErrNeedsContent {
|
|
t.Fatalf("want ErrNeedsContent got %v", err)
|
|
}
|
|
}
|
|
|
|
func TestDeterministicChangeReportNoStaticFiction(t *testing.T) {
|
|
before := docrevision.CanonicalizeMarkdown("# A\n\n旧文\n")
|
|
after := docrevision.CanonicalizeMarkdown("# A\n\n新文 REQ-1\n")
|
|
fb := docrevision.BuildBlockMap(before, nil)
|
|
ab := docrevision.BuildBlockMap(after, fb)
|
|
diff := docrevision.ComputeDiff(before, after, fb, ab)
|
|
from := docrevision.SHA256Hex(before)
|
|
to := docrevision.SHA256Hex(after)
|
|
raw := docrevision.BuildDeterministicChangeReportJSON(from, to, docrevision.RiskHigh, nil, diff)
|
|
if raw == "" {
|
|
t.Fatal("empty report")
|
|
}
|
|
rep, err := docrevision.ValidateChangeReportJSON(raw, from, to, docrevision.RiskHigh, nil, diff.Hunks)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(rep.Summary) == 0 {
|
|
t.Fatal("expected deterministic summary")
|
|
}
|
|
for _, s := range rep.Summary {
|
|
if s == "智能体交付物已生成" || s == "系统需求规格已更新" {
|
|
t.Fatalf("static fiction summary leaked: %q", s)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestHumanSaveAfterBootstrap(t *testing.T) {
|
|
svc := docrevision.NewService(nil)
|
|
ctx := context.Background()
|
|
st, _ := svc.EnsureBootstrap(ctx, docrevision.BootstrapRequest{
|
|
DocID: "doc_edit", Markdown: "# A\n", ProjectID: 1, LifecycleState: "drafting",
|
|
})
|
|
r, err := svc.CommitHumanRevision(ctx, docrevision.HumanRevisionRequest{
|
|
DocID: "doc_edit", Markdown: "# A\n\nedit\n",
|
|
ExpectedHeadSnapshot: &st.HeadSnapshotID, ExpectedRevisionNo: &st.HeadRevisionNo,
|
|
IdempotencyKey: "e1", ActorUserID: 9, LifecycleState: "drafting",
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if r.State.HeadRevisionNo != 1 {
|
|
t.Fatalf("want r1 got %d", r.State.HeadRevisionNo)
|
|
}
|
|
ov, err := svc.ManualOverlay(ctx, "doc_edit")
|
|
if err != nil || ov == nil || (ov.Summary.Modifies+ov.Summary.Inserts) == 0 {
|
|
t.Fatalf("manual overlay empty: %+v %v", ov, err)
|
|
}
|
|
}
|