Minimize D&D NPC extraction contracts

This commit is contained in:
2026-07-22 18:51:26 +00:00
parent 14991cf58b
commit a263a0840c
43 changed files with 486 additions and 1395 deletions

View File

@@ -61,31 +61,6 @@ func issuesFor(value dnd.NPCList) []string {
if strings.TrimSpace(npc.Name) == "" {
issues = append(issues, prefix+".name must not be empty")
}
if npc.Aliases == nil {
issues = append(issues, prefix+".aliases must be present")
} else {
for aliasIndex, alias := range npc.Aliases {
if strings.TrimSpace(alias) == "" {
issues = append(issues, fmt.Sprintf("%s.aliases[%d] must not be empty: %s", prefix, aliasIndex, diagnostics.Quote(alias)))
}
}
}
if strings.TrimSpace(npc.Description) == "" {
issues = append(issues, prefix+".description must not be empty")
}
if npc.Relationships == nil {
issues = append(issues, prefix+".relationships must be present")
} else {
for relationshipIndex, relationship := range npc.Relationships {
relationshipPrefix := fmt.Sprintf("%s.relationships[%d]", prefix, relationshipIndex)
if strings.TrimSpace(relationship.Target) == "" {
issues = append(issues, relationshipPrefix+".target must not be empty: "+diagnostics.Quote(relationship.Target))
}
if strings.TrimSpace(relationship.Relationship) == "" {
issues = append(issues, relationshipPrefix+".relationship must not be empty: "+diagnostics.Quote(relationship.Relationship))
}
}
}
if len(npc.SourceRefs) == 0 {
issues = append(issues, prefix+".source_refs must not be empty")
}

View File

@@ -21,9 +21,9 @@ func TestValidatorApprovesWellFormedNPCPayload(t *testing.T) {
func TestValidatorRejectsRequiredShapeValues(t *testing.T) {
value := validNPCList()
value.NPCs[0].Aliases = nil
value.NPCs[0].Name = ""
result, err := New(Options{}).Validate(context.Background(), requestWithValue(value))
if err != nil || result.Approved || result.ReasonCode != ReasonCode || !strings.Contains(result.Message, "aliases must be present") {
if err != nil || result.Approved || result.ReasonCode != ReasonCode || !strings.Contains(result.Message, "name must not be empty") {
t.Fatalf("Validate() = %#v, %v; want bounded shape rejection", result, err)
}
@@ -38,13 +38,13 @@ func TestValidatorBoundsDiagnosticsAndQuotesUnicode(t *testing.T) {
value := dnd.NPCList{NPCs: make([]dnd.NPC, 24)}
long := strings.Repeat("火", 220) + "\n\t"
for index := range value.NPCs {
value.NPCs[index] = dnd.NPC{ID: "candidate", Name: long, Aliases: []string{"\n\t"}, Description: "", Relationships: []dnd.NPCRelationship{{Target: " ", Relationship: " "}}, SourceRefs: []source.SourceRef{}}
value.NPCs[index] = dnd.NPC{ID: "", Name: long, SourceRefs: []source.SourceRef{}}
}
result, err := New(Options{}).Validate(context.Background(), requestWithValue(value))
if err != nil || result.Approved || len([]byte(result.Message)) > diagnosticsMaxMessageBytes || !utf8.ValidString(result.Message) {
t.Fatalf("Validate() = %#v, %v; want bounded valid UTF-8 rejection", result, err)
}
if strings.Count(result.Message, "npcs[") > diagnosticsMaxIssues || !strings.Contains(result.Message, "additional issue(s) omitted") || !strings.Contains(result.Message, `\n\t`) {
if strings.Count(result.Message, "npcs[") > diagnosticsMaxIssues || !strings.Contains(result.Message, "additional issue(s) omitted") {
t.Fatalf("message = %q, want bounded quoted diagnostics", result.Message)
}
}
@@ -69,7 +69,7 @@ func TestValidatorDoesNotMutateValue(t *testing.T) {
value := validNPCList()
before := value
_, err := New(Options{}).Validate(context.Background(), requestWithValue(value))
if err != nil || value.NPCs[0].Aliases[0] != before.NPCs[0].Aliases[0] {
if err != nil || value.NPCs[0].Name != before.NPCs[0].Name {
t.Fatalf("Validate() mutated value: %#v", value)
}
}
@@ -80,9 +80,8 @@ func requestWithValue(value dnd.NPCList) contracts.TypedValidationRequest[dnd.NP
func validNPCList() dnd.NPCList {
return dnd.NPCList{NPCs: []dnd.NPC{{
ID: "candidate", Name: "Mira Thorn", Aliases: []string{"The Greencloak"}, Description: "A guarded ranger.",
Relationships: []dnd.NPCRelationship{{Target: "Captain Vale", Relationship: "reports to"}},
SourceRefs: []source.SourceRef{{SourceID: "session", StartUnitID: 1, EndUnitID: 1}},
ID: "candidate", Name: "Mira Thorn",
SourceRefs: []source.SourceRef{{SourceID: "session", StartUnitID: 1, EndUnitID: 1}},
}}}
}