Add typed spell validation strategies

This commit is contained in:
2026-07-17 07:02:30 +00:00
parent 142ba36695
commit 52e6b31408
25 changed files with 708 additions and 410 deletions

View File

@@ -4,12 +4,14 @@ import (
"context"
"testing"
"gitea.maximumdirect.net/eric/notarius/internal/core/source"
"gitea.maximumdirect.net/eric/notarius/internal/framework/contracts"
"gitea.maximumdirect.net/eric/notarius/internal/framework/pipeline"
"gitea.maximumdirect.net/eric/notarius/internal/modules/dnd"
)
func TestValidatorApprovesWellFormedSpellPayload(t *testing.T) {
result, err := New().Validate(context.Background(), requestWithPayload(`{"spell_casts":[{"caster":"Aria","spell":"Cure Wounds","effect":"heals","narrative_description":"Aria heals Borin.","source_refs":[{"source_id":"session","start_unit_id":1,"end_unit_id":1}]}]}`))
result, err := New(Options{}).Validate(context.Background(), requestWithValue(validSpellList()))
if err != nil {
t.Fatalf("Validate() error = %v, want nil", err)
}
@@ -18,8 +20,8 @@ func TestValidatorApprovesWellFormedSpellPayload(t *testing.T) {
}
}
func TestValidatorRejectsMalformedPayload(t *testing.T) {
result, err := New().Validate(context.Background(), requestWithPayload(`{"spell_casts":`))
func TestValidatorRejectsMissingSpellList(t *testing.T) {
result, err := New(Options{}).Validate(context.Background(), requestWithValue(dnd.SpellList{}))
if err != nil {
t.Fatalf("Validate() error = %v, want nil", err)
}
@@ -32,7 +34,9 @@ func TestValidatorRejectsMalformedPayload(t *testing.T) {
}
func TestValidatorRejectsMissingRequiredSpellFields(t *testing.T) {
result, err := New().Validate(context.Background(), requestWithPayload(`{"spell_casts":[{"caster":"Aria","effect":"heals","narrative_description":"Aria heals Borin.","source_refs":[{"source_id":"session","start_unit_id":1,"end_unit_id":1}]}]}`))
value := validSpellList()
value.SpellCasts[0].Spell = ""
result, err := New(Options{}).Validate(context.Background(), requestWithValue(value))
if err != nil {
t.Fatalf("Validate() error = %v, want nil", err)
}
@@ -49,20 +53,15 @@ func TestSpecAndRegister(t *testing.T) {
if err := Register(registry); err != nil {
t.Fatalf("Register() error = %v, want nil", err)
}
validator, err := registry.BuildLegacyRaw(Key)
if err != nil {
t.Fatalf("Build(%q) error = %v, want nil", Key, err)
}
if validator.Name() != Key || validator.ExecutionClass() != contracts.ExecutionClassDeterministic {
t.Fatalf("validator = %q/%q, want key and deterministic execution", validator.Name(), validator.ExecutionClass())
if _, err := DecodeOptions(map[string]any{"unexpected": true}); err == nil {
t.Fatal("DecodeOptions() error = nil, want unknown option error")
}
}
func requestWithPayload(payload string) contracts.ValidationRequest {
return contracts.ValidationRequest{
Payload: contracts.RawPayload{
Content: []byte(payload),
MediaType: "application/json",
},
}
func requestWithValue(value dnd.SpellList) contracts.TypedValidationRequest[dnd.SpellList] {
return contracts.TypedValidationRequest[dnd.SpellList]{Value: value}
}
func validSpellList() dnd.SpellList {
return dnd.SpellList{SpellCasts: []dnd.SpellCast{{Caster: "Aria", Spell: "Cure Wounds", Effect: "heals", NarrativeDescription: "Aria heals Borin.", SourceRefs: []source.SourceRef{{SourceID: "session", StartUnitID: 1, EndUnitID: 1}}}}}
}