2026-07-23 13:35:15 +00:00
package shape
import (
"context"
"reflect"
"strings"
"testing"
"unicode/utf8"
"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 TestValidatorOwnsRequiredNameKindAndEvidence ( t * testing . T ) {
valid := validList ( )
2026-08-05 18:55:58 +00:00
result , err := New ( Options { } ) . Validate ( context . Background ( ) , contracts . TypedValidationRequest [ dnd . NPCOccurrenceList ] { Value : valid } )
2026-07-23 13:35:15 +00:00
if err != nil || ! result . Approved {
t . Fatalf ( "Validate() = %#v, %v" , result , err )
}
for _ , test := range [ ] struct {
name string
2026-08-05 18:55:58 +00:00
value dnd . NPCOccurrenceList
2026-07-23 13:35:15 +00:00
want string
} {
2026-08-05 18:55:58 +00:00
{ "missing occurrences" , dnd . NPCOccurrenceList { } , "occurrences must be present" } ,
{ "blank name" , dnd . NPCOccurrenceList { Occurrences : [ ] dnd . NPCOccurrence { { NPCID : "npc:test" , Name : " " , Kind : dnd . NPCOccurrenceKindDialogue , SourceRefs : valid . Occurrences [ 0 ] . SourceRefs } } } , "name must not be empty" } ,
{ "unsupported kind" , dnd . NPCOccurrenceList { Occurrences : [ ] dnd . NPCOccurrence { { NPCID : "npc:test" , Name : "Mira Thorn" , Kind : "unsupported" , SourceRefs : valid . Occurrences [ 0 ] . SourceRefs } } } , "kind is unsupported" } ,
{ "missing evidence" , dnd . NPCOccurrenceList { Occurrences : [ ] dnd . NPCOccurrence { { NPCID : "npc:test" , Name : "Mira Thorn" , Kind : dnd . NPCOccurrenceKindDialogue } } } , "source_refs must contain" } ,
2026-07-23 13:35:15 +00:00
} {
t . Run ( test . name , func ( t * testing . T ) {
2026-08-05 18:55:58 +00:00
result , err := New ( Options { } ) . Validate ( context . Background ( ) , contracts . TypedValidationRequest [ dnd . NPCOccurrenceList ] { Value : test . value } )
2026-07-23 13:35:15 +00:00
if err != nil || result . Approved || result . ReasonCode != ReasonCode || ! strings . Contains ( result . Message , test . want ) {
t . Fatalf ( "Validate() = %#v, %v; want %q" , result , err , test . want )
}
} )
}
}
func TestValidatorBoundsDiagnosticsPreservesValueAndRegistersTypedContract ( t * testing . T ) {
2026-08-05 18:55:58 +00:00
value := dnd . NPCOccurrenceList { Occurrences : make ( [ ] dnd . NPCOccurrence , 24 ) }
for index := range value . Occurrences {
value . Occurrences [ index ] = dnd . NPCOccurrence { NPCID : "npc:test" , Name : strings . Repeat ( "火" , 220 ) + "\n" , Kind : "unsupported" }
2026-07-23 13:35:15 +00:00
}
2026-08-05 18:55:58 +00:00
result , err := New ( Options { } ) . Validate ( context . Background ( ) , contracts . TypedValidationRequest [ dnd . NPCOccurrenceList ] { Value : value } )
2026-07-23 13:35:15 +00:00
if err != nil || result . Approved || len ( [ ] byte ( result . Message ) ) > 4096 || ! utf8 . ValidString ( result . Message ) || ! strings . Contains ( result . Message , "additional issue(s) omitted" ) {
t . Fatalf ( "Validate() = %#v, %v" , result , err )
}
2026-08-05 18:55:58 +00:00
if value . Occurrences [ 0 ] . Name != strings . Repeat ( "火" , 220 ) + "\n" {
2026-07-23 13:35:15 +00:00
t . Fatal ( "Validate() mutated input" )
}
if got := New ( Options { } ) . CheckpointFingerprints ( ) ; ! reflect . DeepEqual ( got , [ ] pipeline . CheckpointFingerprint { { Name : "policy" , Value : policy } } ) {
t . Fatalf ( "CheckpointFingerprints() = %#v" , got )
}
registry := pipeline . NewValidatorRegistry ( )
if err := Register ( registry ) ; err != nil {
t . Fatal ( err )
}
if _ , err := DecodeOptions ( map [ string ] any { "unexpected" : true } ) ; err == nil {
t . Fatal ( "DecodeOptions() accepted unknown option" )
}
}
2026-08-05 18:55:58 +00:00
func validList ( ) dnd . NPCOccurrenceList {
return dnd . NPCOccurrenceList { Occurrences : [ ] dnd . NPCOccurrence { {
NPCID : "npc:test" , Name : "Mira Thorn" , Kind : dnd . NPCOccurrenceKindDialogue ,
2026-07-23 13:35:15 +00:00
SourceRefs : [ ] source . SourceRef { { SourceID : "session" , StartUnitID : 1 , EndUnitID : 1 } } ,
} } }
}