Semantic-preserving canonicalization, same-byte write paths, structured content errors, and a dry-run-default migration_repair stop 404s caused by trimming project-143 hard-breaks and no-op lease hydrations. Co-authored-by: Cursor <cursoragent@cursor.com>
106 lines
3.2 KiB
Go
106 lines
3.2 KiB
Go
package docgov_test
|
|
|
|
import (
|
|
"errors"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"chat-agent-system/internal/docgov"
|
|
)
|
|
|
|
func TestReadDocumentContent_TypedErrors(t *testing.T) {
|
|
ctx := t.Context()
|
|
eng := docgov.NewGovernanceEngine(nil)
|
|
|
|
_, err := eng.ReadDocumentContent(ctx, "doc_missing", t.TempDir())
|
|
if !errors.Is(err, docgov.ErrDocumentNotFound) {
|
|
t.Fatalf("not-found: %v", err)
|
|
}
|
|
var nf *docgov.ContentError
|
|
if !errors.As(err, &nf) || nf.Code != docgov.ErrCodeDocumentNotFound {
|
|
t.Fatalf("not-found code: %v", err)
|
|
}
|
|
|
|
dir := t.TempDir()
|
|
if _, err := eng.CreateOrGetDocument(ctx, "doc_nofile", 1, "intake", "t", "V0.1", filepath.Join(dir, "missing-ws"), 1, "a", 2, "r", 3, "p"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
_, err = eng.ReadDocumentContent(ctx, "doc_nofile", dir)
|
|
var miss *docgov.ContentError
|
|
if !errors.As(err, &miss) || miss.Code != docgov.ErrCodeProjectionMissing {
|
|
t.Fatalf("missing: %v", err)
|
|
}
|
|
if !miss.Retryable || !miss.NeedsAdmin {
|
|
t.Fatalf("missing flags %+v", miss)
|
|
}
|
|
|
|
emptyDir := filepath.Join(dir, "empty-ws")
|
|
if err := os.MkdirAll(emptyDir, 0o755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := os.WriteFile(filepath.Join(emptyDir, "output.md"), nil, 0o644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := eng.CreateOrGetDocument(ctx, "doc_empty", 1, "intake", "t", "V0.1", emptyDir, 1, "a", 2, "r", 3, "p"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
_, err = eng.ReadDocumentContent(ctx, "doc_empty", dir)
|
|
var empty *docgov.ContentError
|
|
if !errors.As(err, &empty) || empty.Code != docgov.ErrCodeProjectionEmpty {
|
|
t.Fatalf("empty: %v", err)
|
|
}
|
|
|
|
ws := filepath.Join(dir, "mismatch-ws")
|
|
if err := os.MkdirAll(ws, 0o755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := os.WriteFile(filepath.Join(ws, "output.md"), []byte("file-bytes\n"), 0o644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := eng.CreateOrGetDocument(ctx, "doc_mismatch", 1, "intake", "t", "V0.1", ws, 1, "a", 2, "r", 3, "p"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := eng.MarkDocumentContentUpdated(ctx, "doc_mismatch", "deadbeef", false); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
_, err = eng.ReadDocumentContent(ctx, "doc_mismatch", dir)
|
|
var mm *docgov.ContentError
|
|
if !errors.As(err, &mm) || mm.Code != docgov.ErrCodeIntegrityMismatch {
|
|
t.Fatalf("mismatch: %v", err)
|
|
}
|
|
if mm.Retryable || !mm.NeedsAdmin {
|
|
t.Fatalf("mismatch flags %+v", mm)
|
|
}
|
|
}
|
|
|
|
func TestSaveDocumentContent_CanonicalBytesMatchHash(t *testing.T) {
|
|
ctx := t.Context()
|
|
dir := t.TempDir()
|
|
ws := filepath.Join(dir, "ws")
|
|
if err := os.MkdirAll(ws, 0o755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
eng := docgov.NewGovernanceEngine(nil)
|
|
if _, err := eng.CreateOrGetDocument(ctx, "doc_save", 1, "intake", "t", "V0.1", ws, 9, "a", 2, "r", 3, "p"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
res, err := eng.SaveDocumentContent(ctx, docgov.ContentSaveRequest{
|
|
DocID: "doc_save", ActorID: 9, ActorRole: docgov.RoleAuthor, ActorName: "a",
|
|
MarkdownBody: "标题 \r\n下一行\r\n",
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
got, err := os.ReadFile(filepath.Join(ws, "output.md"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if string(got) != "标题 \n下一行\n" {
|
|
t.Fatalf("saved bytes %q", got)
|
|
}
|
|
if res.ContentSHA256 == "" || res.ContentSHA256 != res.Doc.ContentSHA256 {
|
|
t.Fatalf("sha not synced: %+v", res)
|
|
}
|
|
}
|