Add canonical source unit provenance

This commit is contained in:
2026-07-17 05:19:57 +00:00
parent 15c369c509
commit 40709e4ad8
26 changed files with 254 additions and 34 deletions

View File

@@ -142,6 +142,86 @@ func TestValidateDocumentDuplicateUnitIDs(t *testing.T) {
}
}
func TestValidateDocumentUnitReferences(t *testing.T) {
tests := []struct {
name string
mutate func(*SourceDocument)
wantErr string
}{
{
name: "missing",
mutate: func(doc *SourceDocument) { doc.Units[0].Ref = SourceRef{} },
wantErr: "source unit[0].ref: source ref source_id must not be empty",
},
{
name: "foreign source",
mutate: func(doc *SourceDocument) { doc.Units[0].Ref.SourceID = "source-2" },
wantErr: "source unit[0].ref: source ref source_id \"source-2\" does not match document id \"source-1\"",
},
{
name: "non-self range",
mutate: func(doc *SourceDocument) {
doc.Units[0].Ref.StartUnitID = 2
doc.Units[0].Ref.EndUnitID = 2
},
wantErr: "source unit[0].ref must identify source unit id 1",
},
{
name: "reversed range",
mutate: func(doc *SourceDocument) {
doc.Units[0].Ref.StartUnitID = 2
doc.Units[0].Ref.EndUnitID = 1
},
wantErr: "source unit[0].ref: source ref start_unit_id 2 appears after end_unit_id 1",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
doc := validDocument()
tt.mutate(doc)
err := ValidateDocument(doc)
if err == nil {
t.Fatal("ValidateDocument() error = nil, want unit reference error")
}
if err.Error() != tt.wantErr {
t.Fatalf("ValidateDocument() error = %q, want %q", err.Error(), tt.wantErr)
}
})
}
}
func TestDigestDocumentIsDeterministicAndIncludesUnitReference(t *testing.T) {
doc := validDocument()
doc.Metadata = map[string]any{"second": "value", "first": true}
first, err := DigestDocument(doc)
if err != nil {
t.Fatalf("DigestDocument() error = %v, want nil", err)
}
reordered := validDocument()
reordered.Metadata = map[string]any{"first": true, "second": "value"}
second, err := DigestDocument(reordered)
if err != nil {
t.Fatalf("DigestDocument(reordered) error = %v, want nil", err)
}
if first != second {
t.Fatalf("digests = %q and %q, want deterministic map ordering", first, second)
}
changed := validDocument()
changed.Metadata = map[string]any{"first": true, "second": "value"}
changed.Units[0].Ref.SourceID = "different-source"
changedDigest, err := DigestDocument(changed)
if err != nil {
t.Fatalf("DigestDocument(changed) error = %v, want nil", err)
}
if first == changedDigest {
t.Fatalf("digest = %q after reference change, want different digest", changedDigest)
}
}
func TestValidateRefValid(t *testing.T) {
doc := validDocument()
ref := SourceRef{
@@ -274,11 +354,13 @@ func validDocument() *SourceDocument {
ID: 1,
Kind: "paragraph",
Text: "First unit.",
Ref: SourceRef{SourceID: "source-1", StartUnitID: 1, EndUnitID: 1},
},
{
ID: 2,
Kind: "paragraph",
Text: "Second unit.",
Ref: SourceRef{SourceID: "source-1", StartUnitID: 2, EndUnitID: 2},
},
},
}