Fix D&D extraction issues and retire the completed audit
This commit is contained in:
@@ -249,6 +249,9 @@ func TestValidateRefValid(t *testing.T) {
|
||||
if err := ValidateRef(doc, ref); err != nil {
|
||||
t.Fatalf("ValidateRef() error = %v, want nil", err)
|
||||
}
|
||||
if err := NewDocumentIndex(doc).ValidateRef(ref); err != nil {
|
||||
t.Fatalf("DocumentIndex.ValidateRef() error = %v, want nil", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateRefRejectsMalformedReferences(t *testing.T) {
|
||||
@@ -301,13 +304,56 @@ func TestValidateRefRejectsMalformedReferences(t *testing.T) {
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
err := ValidateRef(validDocument(), tt.ref)
|
||||
|
||||
requireErrorFragments(t, err, tt.fragments...)
|
||||
doc := validDocument()
|
||||
validators := []struct {
|
||||
name string
|
||||
validate func(SourceRef) error
|
||||
}{
|
||||
{name: "document", validate: func(ref SourceRef) error { return ValidateRef(doc, ref) }},
|
||||
{name: "index", validate: NewDocumentIndex(doc).ValidateRef},
|
||||
}
|
||||
for _, validator := range validators {
|
||||
t.Run(validator.name, func(t *testing.T) {
|
||||
requireErrorFragments(t, validator.validate(tt.ref), tt.fragments...)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDocumentIndexSnapshotsIdentityAndUnitPositions(t *testing.T) {
|
||||
doc := &SourceDocument{
|
||||
ID: "source-1",
|
||||
Units: []SourceUnit{
|
||||
{ID: 30},
|
||||
{ID: 10},
|
||||
{ID: 30},
|
||||
},
|
||||
}
|
||||
index := NewDocumentIndex(doc)
|
||||
doc.ID = "changed"
|
||||
doc.Units[0].ID = 99
|
||||
|
||||
if position, ok := index.Position(30); !ok || position != 0 {
|
||||
t.Fatalf("Position(30) = %d, %t, want 0, true", position, ok)
|
||||
}
|
||||
if position, ok := index.Position(10); !ok || position != 1 {
|
||||
t.Fatalf("Position(10) = %d, %t, want 1, true", position, ok)
|
||||
}
|
||||
ref := SourceRef{SourceID: "source-1", StartUnitID: 30, EndUnitID: 10}
|
||||
if err := index.ValidateRef(ref); err != nil {
|
||||
t.Fatalf("ValidateRef() error = %v, want nil", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestZeroDocumentIndexIsSafe(t *testing.T) {
|
||||
var index DocumentIndex
|
||||
if position, ok := index.Position(1); ok || position != 0 {
|
||||
t.Fatalf("Position(1) = %d, %t, want 0, false", position, ok)
|
||||
}
|
||||
requireErrorFragments(t, index.ValidateRef(SourceRef{}), "source document must not be nil")
|
||||
}
|
||||
|
||||
func TestUnitIndex(t *testing.T) {
|
||||
doc := validDocument()
|
||||
|
||||
|
||||
@@ -5,6 +5,46 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// DocumentIndex is an immutable snapshot of a source document's identity and
|
||||
// unit positions for repeated source-reference operations.
|
||||
type DocumentIndex struct {
|
||||
documentID string
|
||||
positions map[int]int
|
||||
hasDocument bool
|
||||
}
|
||||
|
||||
// NewDocumentIndex snapshots doc without retaining or mutating it.
|
||||
func NewDocumentIndex(doc *SourceDocument) DocumentIndex {
|
||||
if doc == nil {
|
||||
return DocumentIndex{}
|
||||
}
|
||||
positions := make(map[int]int, len(doc.Units))
|
||||
for position, unit := range doc.Units {
|
||||
if _, exists := positions[unit.ID]; !exists {
|
||||
positions[unit.ID] = position
|
||||
}
|
||||
}
|
||||
return DocumentIndex{
|
||||
documentID: doc.ID,
|
||||
positions: positions,
|
||||
hasDocument: true,
|
||||
}
|
||||
}
|
||||
|
||||
// Position returns the indexed document position for unitID.
|
||||
func (i DocumentIndex) Position(unitID int) (int, bool) {
|
||||
position, ok := i.positions[unitID]
|
||||
return position, ok
|
||||
}
|
||||
|
||||
// ValidateRef validates ref against the indexed document snapshot.
|
||||
func (i DocumentIndex) ValidateRef(ref SourceRef) error {
|
||||
if !i.hasDocument {
|
||||
return fmt.Errorf("source document must not be nil")
|
||||
}
|
||||
return validateRef(i.documentID, i.Position, ref)
|
||||
}
|
||||
|
||||
func ValidateDocument(doc *SourceDocument) error {
|
||||
if doc == nil {
|
||||
return fmt.Errorf("source document must not be nil")
|
||||
@@ -60,6 +100,12 @@ func ValidateRef(doc *SourceDocument, ref SourceRef) error {
|
||||
if doc == nil {
|
||||
return fmt.Errorf("source document must not be nil")
|
||||
}
|
||||
return validateRef(doc.ID, func(unitID int) (int, bool) {
|
||||
return UnitIndex(doc, unitID)
|
||||
}, ref)
|
||||
}
|
||||
|
||||
func validateRef(documentID string, position func(int) (int, bool), ref SourceRef) error {
|
||||
if isBlank(ref.SourceID) {
|
||||
return fmt.Errorf("source ref source_id must not be empty")
|
||||
}
|
||||
@@ -72,15 +118,15 @@ func ValidateRef(doc *SourceDocument, ref SourceRef) error {
|
||||
if ref.EndUnitID <= 0 {
|
||||
return fmt.Errorf("source ref end_unit_id must be positive")
|
||||
}
|
||||
if ref.SourceID != doc.ID {
|
||||
return fmt.Errorf("source ref source_id %q does not match document id %q", ref.SourceID, doc.ID)
|
||||
if ref.SourceID != documentID {
|
||||
return fmt.Errorf("source ref source_id %q does not match document id %q", ref.SourceID, documentID)
|
||||
}
|
||||
|
||||
startIndex, ok := UnitIndex(doc, ref.StartUnitID)
|
||||
startIndex, ok := position(ref.StartUnitID)
|
||||
if !ok {
|
||||
return fmt.Errorf("source ref start_unit_id %d was not found", ref.StartUnitID)
|
||||
}
|
||||
endIndex, ok := UnitIndex(doc, ref.EndUnitID)
|
||||
endIndex, ok := position(ref.EndUnitID)
|
||||
if !ok {
|
||||
return fmt.Errorf("source ref end_unit_id %d was not found", ref.EndUnitID)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user