Harden item occurrence grounding

This commit is contained in:
2026-08-06 13:37:21 +00:00
parent 8cf03a2a44
commit 4192aa8584
16 changed files with 152 additions and 121 deletions

View File

@@ -2,6 +2,7 @@ package itemoccurrences
import (
"context"
"strings"
"testing"
"gitea.maximumdirect.net/eric/notarius/internal/framework/contracts"
@@ -12,8 +13,6 @@ func TestExtractGroundsOccurrencesInRequiredRegistry(t *testing.T) {
id := itemidentity.DeriveID("Torch")
client := &fakeItemOccurrencesLLMClient{response: extractionResponse{Occurrences: []itemOccurrenceResponse{
{ItemID: id, Name: "Torch", Kind: "lost", From: "party", SourceRefs: responseRefs(1, 1)},
{ItemID: "unknown", Name: "Unknown", Kind: "lost", From: "party", SourceRefs: responseRefs(2, 2)},
{ItemID: "torch", Name: "Lantern", Kind: "lost", From: "party", SourceRefs: responseRefs(3, 3)},
}}}
req := extractionRequest()
req.References = itemRegistryReferences(t)
@@ -30,6 +29,56 @@ func TestExtractGroundsOccurrencesInRequiredRegistry(t *testing.T) {
}
}
func TestExtractRejectsInvalidRegistryPairs(t *testing.T) {
id := itemidentity.DeriveID("Torch")
for _, test := range []struct {
name string
occurrence itemOccurrenceResponse
wantError string
}{
{
name: "unknown item ID",
occurrence: itemOccurrenceResponse{ItemID: "unknown", Name: "Torch", Kind: "lost", From: "party", SourceRefs: responseRefs(1, 1)},
wantError: "occurrences[0].item_id is not in the item registry",
},
{
name: "mismatched item name",
occurrence: itemOccurrenceResponse{ItemID: id, Name: "Lantern", Kind: "lost", From: "party", SourceRefs: responseRefs(1, 1)},
wantError: "occurrences[0].name does not match item_id",
},
} {
t.Run(test.name, func(t *testing.T) {
client := &fakeItemOccurrencesLLMClient{response: extractionResponse{Occurrences: []itemOccurrenceResponse{test.occurrence}}}
req := extractionRequest()
req.References = itemRegistryReferences(t)
result, err := newExtractor(t, client, req.References).Extract(context.Background(), req)
if err == nil || !strings.Contains(err.Error(), test.wantError) {
t.Fatalf("Extract() error = %v, want %q", err, test.wantError)
}
if len(result.Value.Occurrences) != 0 {
t.Fatalf("Extract() returned partial occurrences: %#v", result.Value.Occurrences)
}
})
}
}
func TestExtractRejectsResponseWithInvalidRegistryPairAfterValidOccurrence(t *testing.T) {
id := itemidentity.DeriveID("Torch")
client := &fakeItemOccurrencesLLMClient{response: extractionResponse{Occurrences: []itemOccurrenceResponse{
{ItemID: id, Name: "Torch", Kind: "lost", From: "party", SourceRefs: responseRefs(1, 1)},
{ItemID: "unknown", Name: "Unknown", Kind: "lost", From: "party", SourceRefs: responseRefs(2, 2)},
}}}
req := extractionRequest()
req.References = itemRegistryReferences(t)
result, err := newExtractor(t, client, req.References).Extract(context.Background(), req)
if err == nil || !strings.Contains(err.Error(), "occurrences[1].item_id") {
t.Fatalf("Extract() error = %v, want occurrence index and item ID", err)
}
if len(result.Value.Occurrences) != 0 {
t.Fatalf("Extract() returned partial occurrences: %#v", result.Value.Occurrences)
}
}
func TestExtractRequiresItemRegistry(t *testing.T) {
_, err := newExtractor(t, &fakeItemOccurrencesLLMClient{}).Extract(context.Background(), extractionRequest())
if err == nil {