Make Notarius validation transactional

This commit is contained in:
2026-09-05 00:26:54 +00:00
parent 3136c26d4e
commit fddf78dc80
3 changed files with 187 additions and 67 deletions

View File

@@ -4,6 +4,7 @@ import (
"fmt"
"os"
"path/filepath"
"reflect"
"strings"
"testing"
)
@@ -252,6 +253,99 @@ func TestNotariusNilAndEmptyReferencesAreValid(t *testing.T) {
}
}
func TestNotariusValidationFailureDoesNotMutateConfig(t *testing.T) {
tests := []struct {
name string
mutate func(*NotariusConfig)
want string
}{
{
name: "reference",
mutate: func(cfg *NotariusConfig) {
cfg.Binary = " notarius "
cfg.References = map[string]string{" party ": "unsupported"}
},
want: "pipeline.notarius.references.party",
},
{
name: "output",
mutate: func(cfg *NotariusConfig) {
cfg.Binary = " notarius "
cfg.References = map[string]string{" party ": " narratio.input.party "}
cfg.Outputs["npc_registry"] = NotariusOutputConfig{LaneID: " missing-schema "}
},
want: "pipeline.notarius.outputs.npc_registry.media_type",
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
cfg := validNotariusConfig()
test.mutate(cfg)
before := cloneNotariusConfigForTest(cfg)
err := validateNotarius(cfg, nil)
if err == nil || !strings.Contains(err.Error(), test.want) {
t.Fatalf("validateNotarius() error = %v, want field containing %q", err, test.want)
}
if !reflect.DeepEqual(cfg, before) {
t.Fatalf("config mutated after validation failure:\n got: %#v\nwant: %#v", cfg, before)
}
})
}
}
func TestNotariusNormalizationIsIndependentOfMapInsertionOrder(t *testing.T) {
first := validNotariusConfig()
first.References = make(map[string]string)
first.References[" party "] = " narratio.input.party "
first.References["chunk . players"] = " narratio.input.players "
first.Outputs = make(map[string]NotariusOutputConfig)
first.Outputs[" npc_registry "] = NotariusOutputConfig{
LaneID: " npc-registry ", MediaType: " application/json ",
SchemaID: " notarius.dnd.npc_registry ", SchemaVersion: " v1 ",
}
first.Outputs["spells"] = NotariusOutputConfig{
LaneID: " spells ", MediaType: " application/json ",
SchemaID: " notarius.dnd.spells ", SchemaVersion: " v1 ",
}
second := validNotariusConfig()
second.References = make(map[string]string)
second.References["chunk . players"] = " narratio.input.players "
second.References[" party "] = " narratio.input.party "
second.Outputs = make(map[string]NotariusOutputConfig)
second.Outputs["spells"] = first.Outputs["spells"]
second.Outputs[" npc_registry "] = first.Outputs[" npc_registry "]
if err := validateNotarius(first, nil); err != nil {
t.Fatalf("validateNotarius(first) error = %v", err)
}
if err := validateNotarius(second, nil); err != nil {
t.Fatalf("validateNotarius(second) error = %v", err)
}
if !reflect.DeepEqual(first, second) {
t.Fatalf("normalized configs differ:\nfirst: %#v\nsecond: %#v", first, second)
}
}
func cloneNotariusConfigForTest(cfg *NotariusConfig) *NotariusConfig {
clone := *cfg
if cfg.References != nil {
clone.References = make(map[string]string, len(cfg.References))
for key, value := range cfg.References {
clone.References[key] = value
}
}
if cfg.Outputs != nil {
clone.Outputs = make(map[string]NotariusOutputConfig, len(cfg.Outputs))
for key, value := range cfg.Outputs {
clone.Outputs[key] = value
}
}
return &clone
}
func TestNotariusSpellCatalogReferenceRequiresEffectiveInput(t *testing.T) {
cfg := loadedValidConfig(t)
cfg.Pipeline.Notarius = validNotariusConfig()