784 lines
41 KiB
Go
784 lines
41 KiB
Go
package register
|
|
|
|
import (
|
|
"io/fs"
|
|
"reflect"
|
|
"sort"
|
|
"strings"
|
|
"testing"
|
|
|
|
"gitea.maximumdirect.net/eric/notarius/internal/core/source"
|
|
"gitea.maximumdirect.net/eric/notarius/internal/framework/contracts"
|
|
"gitea.maximumdirect.net/eric/notarius/internal/framework/llm"
|
|
"gitea.maximumdirect.net/eric/notarius/internal/framework/pipeline"
|
|
"gitea.maximumdirect.net/eric/notarius/internal/modules/dnd"
|
|
combatextract "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/extract/combatturns"
|
|
enemyeventextract "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/extract/enemyevents"
|
|
itemeventextract "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/extract/itemevents"
|
|
interactionextract "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/extract/npcinteractions"
|
|
npcextract "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/extract/npcs"
|
|
scenedescriptionextract "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/extract/scenedescriptions"
|
|
"gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/extract/spells"
|
|
combatnormalize "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/normalize/combatturns"
|
|
enemyeventnormalize "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/normalize/enemyevents"
|
|
itemeventnormalize "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/normalize/itemevents"
|
|
interactionnormalize "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/normalize/npcinteractions"
|
|
npcnormalize "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/normalize/npcs"
|
|
scenedescriptionnormalize "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/normalize/scenedescriptions"
|
|
spellnormalize "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/normalize/spells"
|
|
)
|
|
|
|
func TestRegisterAddsDNDFamily(t *testing.T) {
|
|
registries := completeRegistries()
|
|
assets := llm.NewAssetRegistry()
|
|
if err := Register(registries, assets); err != nil {
|
|
t.Fatalf("Register() error = %v, want nil", err)
|
|
}
|
|
promptFS, err := assets.PromptFS()
|
|
if err != nil {
|
|
t.Fatalf("PromptFS() error = %v", err)
|
|
}
|
|
if _, err := fs.ReadFile(promptFS, "dnd.npcs.normalize/dnd.npcs.normalize.yaml"); err != nil {
|
|
t.Fatalf("normalization prompt asset = %v, want registered private prompt", err)
|
|
}
|
|
for _, name := range []string{
|
|
"dnd.scenes/dnd.scenes.yaml",
|
|
"dnd.spells/dnd.spells.yaml",
|
|
"dnd.npcs/dnd.npcs.yaml",
|
|
"dnd.combat_turns/dnd.combat_turns.yaml",
|
|
"dnd.enemy_events/dnd.enemy_events.yaml",
|
|
"dnd.item_events/dnd.item_events.yaml",
|
|
"dnd.npc_interactions/dnd.npc_interactions.yaml",
|
|
"dnd.scene_descriptions/dnd.scene_descriptions.yaml",
|
|
"dnd.npcs.normalize/dnd.npcs.normalize.yaml",
|
|
} {
|
|
content, err := fs.ReadFile(promptFS, name)
|
|
if err != nil {
|
|
t.Fatalf("read prompt asset %q: %v", name, err)
|
|
}
|
|
if !strings.Contains(string(content), "default_profile: dnd-extraction") {
|
|
t.Fatalf("prompt asset %q does not select dnd-extraction", name)
|
|
}
|
|
}
|
|
fallbackFS, err := assets.FallbackProfileFS()
|
|
if err != nil {
|
|
t.Fatalf("FallbackProfileFS() error = %v", err)
|
|
}
|
|
if _, err := fs.ReadFile(fallbackFS, "dnd-extraction.yaml"); err != nil {
|
|
t.Fatalf("fallback profile asset = %v, want registered D&D profile", err)
|
|
}
|
|
schemaFS, err := assets.SchemaFS()
|
|
if err != nil {
|
|
t.Fatalf("SchemaFS() error = %v", err)
|
|
}
|
|
if _, err := fs.ReadFile(schemaFS, "dnd_npcs_normalize_llm.v1.json"); err != nil {
|
|
t.Fatalf("normalization schema asset = %v, want registered private schema", err)
|
|
}
|
|
assertContainsKeys(t, "chunkers", registries.Chunkers.RegisteredKeys(), []string{"dnd/scenes"})
|
|
assertContainsKeys(t, "extractors", registries.Extractors.RegisteredKeys(), []string{"dnd/spells", npcextract.Key, combatextract.Key, enemyeventextract.Key, itemeventextract.Key, interactionextract.Key, scenedescriptionextract.Key})
|
|
assertContainsKeys(t, "normalizers", registries.Normalizers.RegisteredKeys(), []string{spellnormalize.Key, npcnormalize.Key, combatnormalize.Key, enemyeventnormalize.Key, itemeventnormalize.Key, interactionnormalize.Key, scenedescriptionnormalize.Key, pipeline.DefaultNormalizeModule})
|
|
assertContainsArtifactKinds(t, registries.ArtifactCodecs.RegisteredKinds(), []contracts.ArtifactKind{dnd.SpellListKind, dnd.NPCListKind, dnd.CombatTurnListKind, dnd.EnemyEventListKind, dnd.ItemEventListKind, dnd.NPCInteractionListKind, dnd.SceneDescriptionListKind})
|
|
assertContainsArtifactKinds(t, registries.ArtifactEvidence.RegisteredKinds(), []contracts.ArtifactKind{dnd.SpellListKind, dnd.NPCListKind, dnd.CombatTurnListKind, dnd.EnemyEventListKind, dnd.ItemEventListKind, dnd.NPCInteractionListKind, dnd.SceneDescriptionListKind})
|
|
assertContainsArtifactKinds(t, registries.Mergers.RegisteredArtifactKinds(pipeline.DefaultMergeModule), []contracts.ArtifactKind{dnd.SpellListKind, dnd.NPCListKind, dnd.CombatTurnListKind, dnd.EnemyEventListKind, dnd.ItemEventListKind, dnd.NPCInteractionListKind, dnd.SceneDescriptionListKind})
|
|
assertContainsArtifactKinds(t, registries.Normalizers.RegisteredArtifactKinds(pipeline.DefaultNormalizeModule), []contracts.ArtifactKind{dnd.SpellListKind, dnd.NPCListKind, dnd.CombatTurnListKind, dnd.EnemyEventListKind, dnd.ItemEventListKind, dnd.NPCInteractionListKind, dnd.SceneDescriptionListKind})
|
|
assertContainsArtifactKinds(t, registries.Normalizers.RegisteredArtifactKinds(npcnormalize.Key), []contracts.ArtifactKind{dnd.NPCListKind})
|
|
assertContainsArtifactKinds(t, registries.Normalizers.RegisteredArtifactKinds(combatnormalize.Key), []contracts.ArtifactKind{dnd.CombatTurnListKind})
|
|
assertContainsArtifactKinds(t, registries.Normalizers.RegisteredArtifactKinds(enemyeventnormalize.Key), []contracts.ArtifactKind{dnd.EnemyEventListKind})
|
|
assertContainsArtifactKinds(t, registries.Normalizers.RegisteredArtifactKinds(itemeventnormalize.Key), []contracts.ArtifactKind{dnd.ItemEventListKind})
|
|
assertContainsArtifactKinds(t, registries.Normalizers.RegisteredArtifactKinds(interactionnormalize.Key), []contracts.ArtifactKind{dnd.NPCInteractionListKind})
|
|
assertContainsArtifactKinds(t, registries.Normalizers.RegisteredArtifactKinds(scenedescriptionnormalize.Key), []contracts.ArtifactKind{dnd.SceneDescriptionListKind})
|
|
assertContainsKeys(t, "validators", registries.Validators.RegisteredKeys(), []string{
|
|
"extract/dnd/npcs/shape",
|
|
"extract/dnd/npcs/source_refs",
|
|
"extract/dnd/npcs/source_relatedness",
|
|
"normalize/dnd/npcs/identity",
|
|
"extract/dnd/spells/catalog",
|
|
"extract/dnd/spells/shape",
|
|
"extract/dnd/spells/source_refs",
|
|
"extract/dnd/spells/source_relatedness",
|
|
"extract/dnd/combat-turns/shape",
|
|
"extract/dnd/combat-turns/source_refs",
|
|
"extract/dnd/combat-turns/source_relatedness",
|
|
"normalize/dnd/combat-turns/invariants",
|
|
"extract/dnd/enemy-events/shape",
|
|
"extract/dnd/enemy-events/engagements",
|
|
"extract/dnd/enemy-events/source_refs",
|
|
"extract/dnd/enemy-events/source_relatedness",
|
|
"normalize/dnd/enemy-events/invariants",
|
|
"extract/dnd/item-events/shape",
|
|
"extract/dnd/item-events/source_refs",
|
|
"extract/dnd/item-events/source_relatedness",
|
|
"normalize/dnd/item-events/invariants",
|
|
"extract/dnd/npc-interactions/shape",
|
|
"extract/dnd/npc-interactions/registry",
|
|
"extract/dnd/npc-interactions/source_refs",
|
|
"extract/dnd/npc-interactions/source_relatedness",
|
|
"normalize/dnd/npc-interactions/invariants",
|
|
"extract/dnd/scene-descriptions/shape",
|
|
"extract/dnd/scene-descriptions/source_refs",
|
|
"extract/dnd/scene-descriptions/source_relatedness",
|
|
"normalize/dnd/scene-descriptions/invariants",
|
|
"generic/always_accept",
|
|
"generic/always_reject",
|
|
})
|
|
wantChain := []pipeline.ModuleBinding{
|
|
pipeline.Binding("generic/valid_json"),
|
|
pipeline.Binding("extract/dnd/spells/shape"),
|
|
pipeline.Binding("extract/dnd/spells/catalog"),
|
|
pipeline.Binding("extract/dnd/spells/source_refs"),
|
|
pipeline.Binding("generic/valid_json_schema"),
|
|
pipeline.Binding("extract/dnd/spells/source_relatedness"),
|
|
}
|
|
if got := registries.ValidatorChains.Validators(pipeline.StageExtract, spells.Key); !reflect.DeepEqual(got, wantChain) {
|
|
t.Fatalf("spell validator chain = %#v, want %#v", got, wantChain)
|
|
}
|
|
if got := registries.ValidatorChains.Validators(pipeline.StageNormalize, spellnormalize.Key); !reflect.DeepEqual(got, wantChain) {
|
|
t.Fatalf("spell normalize validator chain = %#v, want %#v", got, wantChain)
|
|
}
|
|
npcExtractChain := []pipeline.ModuleBinding{
|
|
pipeline.Binding("generic/valid_json"),
|
|
pipeline.Binding("extract/dnd/npcs/shape"),
|
|
pipeline.Binding("extract/dnd/npcs/source_refs"),
|
|
pipeline.Binding("generic/valid_json_schema"),
|
|
pipeline.Binding("extract/dnd/npcs/source_relatedness"),
|
|
}
|
|
if got := registries.ValidatorChains.Validators(pipeline.StageExtract, npcextract.Key); !reflect.DeepEqual(got, npcExtractChain) {
|
|
t.Fatalf("NPC extract validator chain = %#v, want %#v", got, npcExtractChain)
|
|
}
|
|
npcNormalizeChain := []pipeline.ModuleBinding{
|
|
pipeline.Binding("generic/valid_json"),
|
|
pipeline.Binding("extract/dnd/npcs/shape"),
|
|
pipeline.Binding("normalize/dnd/npcs/identity"),
|
|
pipeline.Binding("extract/dnd/npcs/source_refs"),
|
|
pipeline.Binding("generic/valid_json_schema"),
|
|
pipeline.Binding("extract/dnd/npcs/source_relatedness"),
|
|
}
|
|
if got := registries.ValidatorChains.Validators(pipeline.StageNormalize, npcnormalize.Key); !reflect.DeepEqual(got, npcNormalizeChain) {
|
|
t.Fatalf("NPC normalize validator chain = %#v, want %#v", got, npcNormalizeChain)
|
|
}
|
|
combatExtractChain := []pipeline.ModuleBinding{
|
|
pipeline.Binding("generic/valid_json"),
|
|
pipeline.Binding("extract/dnd/combat-turns/shape"),
|
|
pipeline.Binding("extract/dnd/combat-turns/source_refs"),
|
|
pipeline.Binding("generic/valid_json_schema"),
|
|
pipeline.Binding("extract/dnd/combat-turns/source_relatedness"),
|
|
}
|
|
if got := registries.ValidatorChains.Validators(pipeline.StageExtract, combatextract.Key); !reflect.DeepEqual(got, combatExtractChain) {
|
|
t.Fatalf("combat extract validator chain = %#v, want %#v", got, combatExtractChain)
|
|
}
|
|
combatNormalizeChain := []pipeline.ModuleBinding{
|
|
pipeline.Binding("generic/valid_json"),
|
|
pipeline.Binding("extract/dnd/combat-turns/shape"),
|
|
pipeline.Binding("normalize/dnd/combat-turns/invariants"),
|
|
pipeline.Binding("extract/dnd/combat-turns/source_refs"),
|
|
pipeline.Binding("generic/valid_json_schema"),
|
|
pipeline.Binding("extract/dnd/combat-turns/source_relatedness"),
|
|
}
|
|
if got := registries.ValidatorChains.Validators(pipeline.StageNormalize, combatnormalize.Key); !reflect.DeepEqual(got, combatNormalizeChain) {
|
|
t.Fatalf("combat normalize validator chain = %#v, want %#v", got, combatNormalizeChain)
|
|
}
|
|
enemyEventExtractChain := []pipeline.ModuleBinding{
|
|
pipeline.Binding("generic/valid_json"),
|
|
pipeline.Binding("extract/dnd/enemy-events/shape"),
|
|
pipeline.Binding("extract/dnd/enemy-events/engagements"),
|
|
pipeline.Binding("extract/dnd/enemy-events/source_refs"),
|
|
pipeline.Binding("generic/valid_json_schema"),
|
|
pipeline.Binding("extract/dnd/enemy-events/source_relatedness"),
|
|
}
|
|
enemyEventNormalizeChain := []pipeline.ModuleBinding{
|
|
pipeline.Binding("generic/valid_json"),
|
|
pipeline.Binding("extract/dnd/enemy-events/shape"),
|
|
pipeline.Binding("normalize/dnd/enemy-events/invariants"),
|
|
pipeline.Binding("extract/dnd/enemy-events/source_refs"),
|
|
pipeline.Binding("generic/valid_json_schema"),
|
|
pipeline.Binding("extract/dnd/enemy-events/source_relatedness"),
|
|
}
|
|
if got := registries.ValidatorChains.Validators(pipeline.StageExtract, enemyeventextract.Key); !reflect.DeepEqual(got, enemyEventExtractChain) {
|
|
t.Fatalf("enemy event extract validator chain = %#v, want %#v", got, enemyEventExtractChain)
|
|
}
|
|
if got := registries.ValidatorChains.Validators(pipeline.StageNormalize, enemyeventnormalize.Key); !reflect.DeepEqual(got, enemyEventNormalizeChain) {
|
|
t.Fatalf("enemy event normalize validator chain = %#v, want %#v", got, enemyEventNormalizeChain)
|
|
}
|
|
itemEventExtractChain := []pipeline.ModuleBinding{
|
|
pipeline.Binding("generic/valid_json"),
|
|
pipeline.Binding("extract/dnd/item-events/shape"),
|
|
pipeline.Binding("extract/dnd/item-events/source_refs"),
|
|
pipeline.Binding("generic/valid_json_schema"),
|
|
pipeline.Binding("extract/dnd/item-events/source_relatedness"),
|
|
}
|
|
itemEventNormalizeChain := []pipeline.ModuleBinding{
|
|
pipeline.Binding("generic/valid_json"),
|
|
pipeline.Binding("extract/dnd/item-events/shape"),
|
|
pipeline.Binding("normalize/dnd/item-events/invariants"),
|
|
pipeline.Binding("extract/dnd/item-events/source_refs"),
|
|
pipeline.Binding("generic/valid_json_schema"),
|
|
pipeline.Binding("extract/dnd/item-events/source_relatedness"),
|
|
}
|
|
if got := registries.ValidatorChains.Validators(pipeline.StageExtract, itemeventextract.Key); !reflect.DeepEqual(got, itemEventExtractChain) {
|
|
t.Fatalf("item event extract validator chain = %#v, want %#v", got, itemEventExtractChain)
|
|
}
|
|
if got := registries.ValidatorChains.Validators(pipeline.StageNormalize, itemeventnormalize.Key); !reflect.DeepEqual(got, itemEventNormalizeChain) {
|
|
t.Fatalf("item event normalize validator chain = %#v, want %#v", got, itemEventNormalizeChain)
|
|
}
|
|
interactionExtractChain := []pipeline.ModuleBinding{
|
|
pipeline.Binding("generic/valid_json"),
|
|
pipeline.Binding("extract/dnd/npc-interactions/shape"),
|
|
pipeline.Binding("extract/dnd/npc-interactions/registry"),
|
|
pipeline.Binding("extract/dnd/npc-interactions/source_refs"),
|
|
pipeline.Binding("generic/valid_json_schema"),
|
|
pipeline.Binding("extract/dnd/npc-interactions/source_relatedness"),
|
|
}
|
|
interactionNormalizeChain := []pipeline.ModuleBinding{
|
|
pipeline.Binding("generic/valid_json"),
|
|
pipeline.Binding("extract/dnd/npc-interactions/shape"),
|
|
pipeline.Binding("extract/dnd/npc-interactions/registry"),
|
|
pipeline.Binding("normalize/dnd/npc-interactions/invariants"),
|
|
pipeline.Binding("extract/dnd/npc-interactions/source_refs"),
|
|
pipeline.Binding("generic/valid_json_schema"),
|
|
pipeline.Binding("extract/dnd/npc-interactions/source_relatedness"),
|
|
}
|
|
if got := registries.ValidatorChains.Validators(pipeline.StageExtract, interactionextract.Key); !reflect.DeepEqual(got, interactionExtractChain) {
|
|
t.Fatalf("NPC interaction extract validator chain = %#v, want %#v", got, interactionExtractChain)
|
|
}
|
|
if got := registries.ValidatorChains.Validators(pipeline.StageNormalize, interactionnormalize.Key); !reflect.DeepEqual(got, interactionNormalizeChain) {
|
|
t.Fatalf("NPC interaction normalize validator chain = %#v, want %#v", got, interactionNormalizeChain)
|
|
}
|
|
sceneExtractChain := []pipeline.ModuleBinding{
|
|
pipeline.Binding("generic/valid_json"),
|
|
pipeline.Binding("extract/dnd/scene-descriptions/shape"),
|
|
pipeline.Binding("extract/dnd/scene-descriptions/source_refs"),
|
|
pipeline.Binding("generic/valid_json_schema"),
|
|
pipeline.Binding("extract/dnd/scene-descriptions/source_relatedness"),
|
|
}
|
|
if got := registries.ValidatorChains.Validators(pipeline.StageExtract, scenedescriptionextract.Key); !reflect.DeepEqual(got, sceneExtractChain) {
|
|
t.Fatalf("scene description extract validator chain = %#v, want %#v", got, sceneExtractChain)
|
|
}
|
|
sceneNormalizeChain := []pipeline.ModuleBinding{
|
|
pipeline.Binding("generic/valid_json"),
|
|
pipeline.Binding("extract/dnd/scene-descriptions/shape"),
|
|
pipeline.Binding("normalize/dnd/scene-descriptions/invariants"),
|
|
pipeline.Binding("extract/dnd/scene-descriptions/source_refs"),
|
|
pipeline.Binding("generic/valid_json_schema"),
|
|
pipeline.Binding("extract/dnd/scene-descriptions/source_relatedness"),
|
|
}
|
|
if got := registries.ValidatorChains.Validators(pipeline.StageNormalize, scenedescriptionnormalize.Key); !reflect.DeepEqual(got, sceneNormalizeChain) {
|
|
t.Fatalf("scene description normalize validator chain = %#v, want %#v", got, sceneNormalizeChain)
|
|
}
|
|
if got := registries.ValidatorChains.Validators(pipeline.StageMerge, npcextract.Key); got != nil {
|
|
t.Fatalf("NPC merge validator chain = %#v, want absent", got)
|
|
}
|
|
if got := registries.ValidatorChains.Validators(pipeline.StageMerge, interactionextract.Key); got != nil {
|
|
t.Fatalf("NPC interaction merge validator chain = %#v, want absent", got)
|
|
}
|
|
assertAssetNamesContain(t, assets.PromptFS, []string{
|
|
"dnd.scenes/dnd.scenes.yaml",
|
|
"dnd.scenes/instructions.md",
|
|
"dnd.scenes/sharedassets/common-dnd-references.md",
|
|
"dnd.scenes/sharedassets/common-dnd-system.md",
|
|
"dnd.scenes/sharedassets/common-dnd-transcript.md",
|
|
"dnd.scenes/task.md",
|
|
"dnd.spells/dnd.spells.yaml",
|
|
"dnd.spells/instructions.md",
|
|
"dnd.spells/sharedassets/common-dnd-references.md",
|
|
"dnd.spells/sharedassets/common-dnd-system.md",
|
|
"dnd.spells/sharedassets/common-dnd-transcript.md",
|
|
"dnd.spells/task.md",
|
|
"dnd.npcs/dnd.npcs.yaml",
|
|
"dnd.npcs/instructions.md",
|
|
"dnd.npcs/sharedassets/common-dnd-references.md",
|
|
"dnd.npcs/sharedassets/common-dnd-system.md",
|
|
"dnd.npcs/sharedassets/common-dnd-transcript.md",
|
|
"dnd.npcs/task.md",
|
|
"dnd.combat_turns/dnd.combat_turns.yaml",
|
|
"dnd.combat_turns/instructions.md",
|
|
"dnd.combat_turns/sharedassets/common-dnd-references.md",
|
|
"dnd.combat_turns/sharedassets/common-dnd-system.md",
|
|
"dnd.combat_turns/sharedassets/common-dnd-transcript.md",
|
|
"dnd.combat_turns/task.md",
|
|
"dnd.enemy_events/dnd.enemy_events.yaml",
|
|
"dnd.enemy_events/grounding.md",
|
|
"dnd.enemy_events/instructions.md",
|
|
"dnd.enemy_events/task.md",
|
|
"dnd.item_events/dnd.item_events.yaml",
|
|
"dnd.item_events/instructions.md",
|
|
"dnd.item_events/sharedassets/common-dnd-extraction-evidence.md",
|
|
"dnd.item_events/sharedassets/common-dnd-identity.md",
|
|
"dnd.item_events/sharedassets/common-dnd-references.md",
|
|
"dnd.item_events/sharedassets/common-dnd-system.md",
|
|
"dnd.item_events/sharedassets/common-dnd-transcript.md",
|
|
"dnd.item_events/task.md",
|
|
"dnd.npc_interactions/dnd.npc_interactions.yaml",
|
|
"dnd.npc_interactions/instructions.md",
|
|
"dnd.npc_interactions/sharedassets/common-dnd-npcs.md",
|
|
"dnd.npc_interactions/task.md",
|
|
"dnd.scene_descriptions/dnd.scene_descriptions.yaml",
|
|
"dnd.scene_descriptions/instructions.md",
|
|
"dnd.scene_descriptions/sharedassets/common-dnd-identity.md",
|
|
"dnd.scene_descriptions/sharedassets/common-dnd-references.md",
|
|
"dnd.scene_descriptions/sharedassets/common-dnd-system.md",
|
|
"dnd.scene_descriptions/sharedassets/common-dnd-transcript.md",
|
|
"dnd.scene_descriptions/task.md",
|
|
})
|
|
assertAssetNamesContain(t, assets.SchemaFS, []string{
|
|
"dnd_scenes.v1.json",
|
|
"dnd_spells_llm.v1.json",
|
|
"dnd_npcs_llm.v1.json",
|
|
"dnd_combat_turns_llm.v1.json",
|
|
"dnd_enemy_events_llm.v1.json",
|
|
"dnd_item_events_llm.v1.json",
|
|
"dnd_npc_interactions_llm.v1.json",
|
|
"dnd_scene_descriptions_llm.v1.json",
|
|
})
|
|
if spec, ok := registries.Chunkers.Spec("dnd/scenes"); !ok || spec.Key != "dnd/scenes" {
|
|
t.Fatalf("scene chunker spec = %#v, present = %t; want family-owned spec", spec, ok)
|
|
}
|
|
if spec, ok := registries.Extractors.Spec(spells.Key); !ok || spec.ArtifactKind != dnd.SpellListKind {
|
|
t.Fatalf("spell extractor spec = %#v, present = %t; want dnd spell-list artifact", spec, ok)
|
|
}
|
|
if spec, ok := registries.Normalizers.Spec(spellnormalize.Key); !ok || spec.ArtifactKind != dnd.SpellListKind || spec.Stage != pipeline.StageNormalize {
|
|
t.Fatalf("spell normalizer spec = %#v, present = %t; want dnd spell-list artifact", spec, ok)
|
|
}
|
|
if spec, ok := registries.Extractors.Spec(npcextract.Key); !ok || spec.ArtifactKind != dnd.NPCListKind {
|
|
t.Fatalf("NPC extractor spec = %#v, present = %t; want dnd NPC-list artifact", spec, ok)
|
|
}
|
|
if spec, ok := registries.Normalizers.Spec(npcnormalize.Key); !ok || spec.ArtifactKind != dnd.NPCListKind || spec.Stage != pipeline.StageNormalize {
|
|
t.Fatalf("NPC normalizer spec = %#v, present = %t; want dnd NPC-list artifact", spec, ok)
|
|
}
|
|
if spec, ok := registries.Extractors.Spec(combatextract.Key); !ok || spec.ArtifactKind != dnd.CombatTurnListKind {
|
|
t.Fatalf("combat extractor spec = %#v, present = %t; want dnd combat-turn-list artifact", spec, ok)
|
|
}
|
|
if spec, ok := registries.Normalizers.Spec(combatnormalize.Key); !ok || spec.ArtifactKind != dnd.CombatTurnListKind || spec.Stage != pipeline.StageNormalize {
|
|
t.Fatalf("combat normalizer spec = %#v, present = %t; want dnd combat-turn-list artifact", spec, ok)
|
|
}
|
|
enemyEventExtractSpec, enemyEventExtractOK := registries.Extractors.Spec(enemyeventextract.Key)
|
|
enemyEventNormalizeSpec, enemyEventNormalizeOK := registries.Normalizers.Spec(enemyeventnormalize.Key)
|
|
if !enemyEventExtractOK || enemyEventExtractSpec.ArtifactKind != dnd.EnemyEventListKind || !enemyEventNormalizeOK || enemyEventNormalizeSpec.ArtifactKind != dnd.EnemyEventListKind || enemyEventNormalizeSpec.Stage != pipeline.StageNormalize {
|
|
t.Fatalf("enemy event specs = %#v / %#v, present = %t / %t", enemyEventExtractSpec, enemyEventNormalizeSpec, enemyEventExtractOK, enemyEventNormalizeOK)
|
|
}
|
|
if len(enemyEventExtractSpec.ReferenceSlots) != 8 || len(enemyEventNormalizeSpec.ReferenceSlots) != 1 {
|
|
t.Fatalf("enemy event reference slots = %#v / %#v", enemyEventExtractSpec.ReferenceSlots, enemyEventNormalizeSpec.ReferenceSlots)
|
|
}
|
|
npcRegistrySlot := referenceSlot(enemyEventExtractSpec.ReferenceSlots, "npcs")
|
|
enemyNormalizeRegistrySlot := referenceSlot(enemyEventNormalizeSpec.ReferenceSlots, "npcs")
|
|
if !npcRegistrySlot.Required || !enemyNormalizeRegistrySlot.Required || npcRegistrySlot.MaxBytes != enemyNormalizeRegistrySlot.MaxBytes || !reflect.DeepEqual(npcRegistrySlot.AcceptedMediaTypes, enemyNormalizeRegistrySlot.AcceptedMediaTypes) || !reflect.DeepEqual(npcRegistrySlot.AcceptedArtifactKinds, enemyNormalizeRegistrySlot.AcceptedArtifactKinds) {
|
|
t.Fatalf("enemy-event NPC registry slots disagree: %#v / %#v", enemyEventExtractSpec.ReferenceSlots, enemyEventNormalizeSpec.ReferenceSlots)
|
|
}
|
|
itemEventExtractSpec, itemEventExtractOK := registries.Extractors.Spec(itemeventextract.Key)
|
|
itemEventNormalizeSpec, itemEventNormalizeOK := registries.Normalizers.Spec(itemeventnormalize.Key)
|
|
if !itemEventExtractOK || itemEventExtractSpec.ArtifactKind != dnd.ItemEventListKind || !itemEventNormalizeOK || itemEventNormalizeSpec.ArtifactKind != dnd.ItemEventListKind || itemEventNormalizeSpec.Stage != pipeline.StageNormalize || len(itemEventNormalizeSpec.ReferenceSlots) != 0 {
|
|
t.Fatalf("item event specs = %#v / %#v, present = %t / %t", itemEventExtractSpec, itemEventNormalizeSpec, itemEventExtractOK, itemEventNormalizeOK)
|
|
}
|
|
for _, slot := range itemEventExtractSpec.ReferenceSlots {
|
|
if slot.Required || len(slot.AcceptedArtifactKinds) != 0 {
|
|
t.Fatalf("item event extractor has a generated-reference dependency: %#v", slot)
|
|
}
|
|
}
|
|
interactionExtractSpec, extractOK := registries.Extractors.Spec(interactionextract.Key)
|
|
interactionNormalizeSpec, normalizeOK := registries.Normalizers.Spec(interactionnormalize.Key)
|
|
if !extractOK || interactionExtractSpec.ArtifactKind != dnd.NPCInteractionListKind || !normalizeOK || interactionNormalizeSpec.ArtifactKind != dnd.NPCInteractionListKind || interactionNormalizeSpec.Stage != pipeline.StageNormalize {
|
|
t.Fatalf("NPC interaction specs = %#v / %#v, present = %t / %t", interactionExtractSpec, interactionNormalizeSpec, extractOK, normalizeOK)
|
|
}
|
|
sceneExtractSpec, sceneExtractOK := registries.Extractors.Spec(scenedescriptionextract.Key)
|
|
sceneNormalizeSpec, sceneNormalizeOK := registries.Normalizers.Spec(scenedescriptionnormalize.Key)
|
|
if !sceneExtractOK || sceneExtractSpec.ArtifactKind != dnd.SceneDescriptionListKind || !sceneNormalizeOK || sceneNormalizeSpec.ArtifactKind != dnd.SceneDescriptionListKind || sceneNormalizeSpec.Stage != pipeline.StageNormalize {
|
|
t.Fatalf("scene description specs = %#v / %#v, present = %t / %t", sceneExtractSpec, sceneNormalizeSpec, sceneExtractOK, sceneNormalizeOK)
|
|
}
|
|
if len(sceneExtractSpec.ReferenceSlots) != 3 || len(sceneNormalizeSpec.ReferenceSlots) != 0 {
|
|
t.Fatalf("scene description reference slots = %#v / %#v, want extractor campaign slots only", sceneExtractSpec.ReferenceSlots, sceneNormalizeSpec.ReferenceSlots)
|
|
}
|
|
extractRegistrySlot := referenceSlot(interactionExtractSpec.ReferenceSlots, "npcs")
|
|
normalizeRegistrySlot := referenceSlot(interactionNormalizeSpec.ReferenceSlots, "npcs")
|
|
if !extractRegistrySlot.Required || !reflect.DeepEqual(extractRegistrySlot.AcceptedArtifactKinds, []contracts.ArtifactKind{dnd.NPCListKind}) || !reflect.DeepEqual(extractRegistrySlot, normalizeRegistrySlot) {
|
|
t.Fatalf("NPC interaction registry slots disagree: %#v / %#v", interactionExtractSpec.ReferenceSlots, interactionNormalizeSpec.ReferenceSlots)
|
|
}
|
|
}
|
|
|
|
func TestEvidenceProjectorsPreserveDirectReferencesWithIndependentStorage(t *testing.T) {
|
|
first := source.SourceRef{SourceID: "session", StartUnitID: 1, EndUnitID: 1}
|
|
second := source.SourceRef{SourceID: "session", StartUnitID: 2, EndUnitID: 2}
|
|
for _, test := range []struct {
|
|
name string
|
|
project func() []source.SourceRef
|
|
want []source.SourceRef
|
|
}{
|
|
{name: "spells", project: func() []source.SourceRef {
|
|
return spellEvidence(dnd.SpellList{SpellCasts: []dnd.SpellCast{{SourceRefs: []source.SourceRef{first, second}}}})
|
|
}, want: []source.SourceRef{first, second}},
|
|
{name: "npcs", project: func() []source.SourceRef {
|
|
return npcEvidence(dnd.NPCList{NPCs: []dnd.NPC{{SourceRefs: []source.SourceRef{first, second}}}})
|
|
}, want: []source.SourceRef{first, second}},
|
|
{name: "combat turns", project: func() []source.SourceRef {
|
|
return combatTurnEvidence(dnd.CombatTurnList{CombatTurns: []dnd.CombatTurn{{SourceRefs: []source.SourceRef{first, second}}}})
|
|
}, want: []source.SourceRef{first, second}},
|
|
{name: "enemy events", project: func() []source.SourceRef {
|
|
return enemyEventEvidence(dnd.EnemyEventList{Events: []dnd.EnemyEvent{{SourceRefs: []source.SourceRef{first, second}}}})
|
|
}, want: []source.SourceRef{first, second}},
|
|
{name: "item events", project: func() []source.SourceRef {
|
|
return itemEventEvidence(dnd.ItemEventList{Events: []dnd.ItemEvent{{SourceRefs: []source.SourceRef{first, second}}}})
|
|
}, want: []source.SourceRef{first, second}},
|
|
{name: "npc interactions", project: func() []source.SourceRef {
|
|
return npcInteractionEvidence(dnd.NPCInteractionList{Interactions: []dnd.NPCInteraction{{SourceRefs: []source.SourceRef{first, second}}}})
|
|
}, want: []source.SourceRef{first, second}},
|
|
{name: "scene descriptions", project: func() []source.SourceRef {
|
|
return sceneDescriptionEvidence(dnd.SceneDescriptionList{Scenes: []dnd.SceneDescription{{SourceRef: first}, {SourceRef: second}}})
|
|
}, want: []source.SourceRef{first, second}},
|
|
} {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
got := test.project()
|
|
if !reflect.DeepEqual(got, test.want) {
|
|
t.Fatalf("projected references = %#v, want %#v", got, test.want)
|
|
}
|
|
got[0].StartUnitID = 99
|
|
if first.StartUnitID != 1 {
|
|
t.Fatal("projector returned aliased reference storage")
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func referenceSlot(slots []contracts.ReferenceSlot, name string) contracts.ReferenceSlot {
|
|
for _, slot := range slots {
|
|
if slot.Name == name {
|
|
return slot
|
|
}
|
|
}
|
|
return contracts.ReferenceSlot{}
|
|
}
|
|
|
|
func TestAppendNPCListsPreservesOrderAndArrayPresence(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
in []dnd.NPCList
|
|
want dnd.NPCList
|
|
}{
|
|
{name: "no values", in: nil, want: dnd.NPCList{}},
|
|
{name: "nil values", in: []dnd.NPCList{{}, {}}, want: dnd.NPCList{}},
|
|
{name: "present empty", in: []dnd.NPCList{{NPCs: []dnd.NPC{}}}, want: dnd.NPCList{NPCs: []dnd.NPC{}}},
|
|
{name: "ordered values", in: []dnd.NPCList{{NPCs: []dnd.NPC{{Name: "first"}}}, {NPCs: []dnd.NPC{{Name: "second"}}}}, want: dnd.NPCList{NPCs: []dnd.NPC{{Name: "first"}, {Name: "second"}}}},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
got, err := appendNPCLists(tt.in)
|
|
if err != nil || !reflect.DeepEqual(got, tt.want) {
|
|
t.Fatalf("appendNPCLists() = %#v, error = %v, want %#v", got, err, tt.want)
|
|
}
|
|
})
|
|
}
|
|
|
|
input := []dnd.NPCList{{NPCs: []dnd.NPC{{Name: "first", SourceRefs: []source.SourceRef{{SourceID: "session", StartUnitID: 10, EndUnitID: 10}}}}}}
|
|
merged, err := appendNPCLists(input)
|
|
if err != nil {
|
|
t.Fatalf("appendNPCLists() error = %v", err)
|
|
}
|
|
merged.NPCs[0].SourceRefs[0].StartUnitID = 999
|
|
if input[0].NPCs[0].SourceRefs[0].StartUnitID == 999 {
|
|
t.Fatal("merged NPCs share source reference storage")
|
|
}
|
|
}
|
|
|
|
func TestAppendSpellListsPreservesOrderPresenceAndOwnership(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
in []dnd.SpellList
|
|
want dnd.SpellList
|
|
}{
|
|
{name: "no values", in: nil, want: dnd.SpellList{}},
|
|
{name: "nil values", in: []dnd.SpellList{{}, {}}, want: dnd.SpellList{}},
|
|
{name: "present empty", in: []dnd.SpellList{{SpellCasts: []dnd.SpellCast{}}}, want: dnd.SpellList{SpellCasts: []dnd.SpellCast{}}},
|
|
{name: "ordered values", in: []dnd.SpellList{{SpellCasts: []dnd.SpellCast{{Spell: "first"}}}, {SpellCasts: []dnd.SpellCast{{Spell: "second"}}}}, want: dnd.SpellList{SpellCasts: []dnd.SpellCast{{Spell: "first"}, {Spell: "second"}}}},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
got, err := appendSpellLists(tt.in)
|
|
if err != nil || !reflect.DeepEqual(got, tt.want) {
|
|
t.Fatalf("appendSpellLists() = %#v, error = %v, want %#v", got, err, tt.want)
|
|
}
|
|
})
|
|
}
|
|
|
|
input := []dnd.SpellList{{SpellCasts: []dnd.SpellCast{{Spell: "Shield", SourceRefs: []source.SourceRef{{SourceID: "session", StartUnitID: 10, EndUnitID: 10}}}}}}
|
|
merged, err := appendSpellLists(input)
|
|
if err != nil {
|
|
t.Fatalf("appendSpellLists() error = %v", err)
|
|
}
|
|
merged.SpellCasts[0].SourceRefs[0].StartUnitID = 999
|
|
if input[0].SpellCasts[0].SourceRefs[0].StartUnitID == 999 {
|
|
t.Fatal("merged spell casts share source reference storage")
|
|
}
|
|
}
|
|
|
|
func TestAppendNPCInteractionListsPreservesOrderPresenceAndOwnership(t *testing.T) {
|
|
refs := []source.SourceRef{{SourceID: "session", StartUnitID: 10, EndUnitID: 10}}
|
|
input := []dnd.NPCInteractionList{
|
|
{},
|
|
{Interactions: []dnd.NPCInteraction{}},
|
|
{Interactions: []dnd.NPCInteraction{{Name: "Aria", Kind: dnd.NPCInteractionKindDialogue, SourceRefs: refs}}},
|
|
{Interactions: []dnd.NPCInteraction{{Name: "Borin", Kind: dnd.NPCInteractionKindMentioned, SourceRefs: []source.SourceRef{{SourceID: "session", StartUnitID: 20, EndUnitID: 20}}}}},
|
|
}
|
|
got, err := appendNPCInteractionLists(input)
|
|
if err != nil {
|
|
t.Fatalf("appendNPCInteractionLists() error = %v", err)
|
|
}
|
|
if got.Interactions == nil || !reflect.DeepEqual([]string{got.Interactions[0].Name, got.Interactions[1].Name}, []string{"Aria", "Borin"}) {
|
|
t.Fatalf("combined interactions = %#v", got)
|
|
}
|
|
got.Interactions[0].SourceRefs[0].StartUnitID = 999
|
|
if input[2].Interactions[0].SourceRefs[0].StartUnitID == 999 {
|
|
t.Fatal("merged interactions share source reference storage")
|
|
}
|
|
for _, values := range [][]dnd.NPCInteractionList{nil, []dnd.NPCInteractionList{{}, {}}} {
|
|
result, err := appendNPCInteractionLists(values)
|
|
if err != nil || result.Interactions != nil {
|
|
t.Fatalf("nil-only merge = %#v, %v; want nil interactions", result, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestAppendSceneDescriptionListsPreservesOrderAndArrayPresence(t *testing.T) {
|
|
input := []dnd.SceneDescriptionList{
|
|
{},
|
|
{Scenes: []dnd.SceneDescription{}},
|
|
{Scenes: []dnd.SceneDescription{{ID: "first"}}},
|
|
{Scenes: []dnd.SceneDescription{{ID: "second"}}},
|
|
}
|
|
got, err := appendSceneDescriptionLists(input)
|
|
if err != nil || !reflect.DeepEqual(got.Scenes, []dnd.SceneDescription{{ID: "first"}, {ID: "second"}}) {
|
|
t.Fatalf("appendSceneDescriptionLists() = %#v, error = %v", got, err)
|
|
}
|
|
for _, values := range [][]dnd.SceneDescriptionList{nil, []dnd.SceneDescriptionList{{}, {}}} {
|
|
result, err := appendSceneDescriptionLists(values)
|
|
if err != nil || result.Scenes != nil {
|
|
t.Fatalf("nil-only merge = %#v, %v; want nil scenes", result, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestAppendCombatTurnListsPreservesOrderPresenceAndOwnership(t *testing.T) {
|
|
refs := []source.SourceRef{{SourceID: "session", StartUnitID: 1, EndUnitID: 1}}
|
|
input := []dnd.CombatTurnList{
|
|
{CombatTurns: []dnd.CombatTurn{{Actor: "first", SourceRefs: refs}}},
|
|
{CombatTurns: []dnd.CombatTurn{{Actor: "second"}}},
|
|
}
|
|
got, err := appendCombatTurnLists(input)
|
|
if err != nil {
|
|
t.Fatalf("appendCombatTurnLists() error = %v, want nil", err)
|
|
}
|
|
if len(got.CombatTurns) != 2 || got.CombatTurns[0].Actor != "first" || got.CombatTurns[1].Actor != "second" {
|
|
t.Fatalf("combat turns = %#v, want chunk order", got.CombatTurns)
|
|
}
|
|
if &got.CombatTurns[0].SourceRefs[0] == &refs[0] {
|
|
t.Fatal("appendCombatTurnLists() retained nested input aliases")
|
|
}
|
|
tests := []struct {
|
|
name string
|
|
in []dnd.CombatTurnList
|
|
want dnd.CombatTurnList
|
|
}{
|
|
{name: "no values", in: nil, want: dnd.CombatTurnList{}},
|
|
{name: "nil values", in: []dnd.CombatTurnList{{}, {}}, want: dnd.CombatTurnList{}},
|
|
{name: "present empty", in: []dnd.CombatTurnList{{CombatTurns: []dnd.CombatTurn{}}}, want: dnd.CombatTurnList{CombatTurns: []dnd.CombatTurn{}}},
|
|
}
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
got, err := appendCombatTurnLists(test.in)
|
|
if err != nil || !reflect.DeepEqual(got, test.want) {
|
|
t.Fatalf("appendCombatTurnLists() = %#v, error = %v, want %#v", got, err, test.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestAppendEnemyEventListsPreservesOrderPresenceAndOwnership(t *testing.T) {
|
|
refs := []source.SourceRef{{SourceID: "session", StartUnitID: 1, EndUnitID: 1}}
|
|
input := []dnd.EnemyEventList{
|
|
{},
|
|
{Events: []dnd.EnemyEvent{}},
|
|
{Events: []dnd.EnemyEvent{{Name: "first", Kind: dnd.EnemyEventKindEngaged, SourceRefs: refs}}},
|
|
{Events: []dnd.EnemyEvent{{Name: "second", Kind: dnd.EnemyEventKindFled, SourceRefs: []source.SourceRef{{SourceID: "session", StartUnitID: 2, EndUnitID: 2}}}}},
|
|
}
|
|
got, err := appendEnemyEventLists(input)
|
|
if err != nil || got.Events == nil || !reflect.DeepEqual([]string{got.Events[0].Name, got.Events[1].Name}, []string{"first", "second"}) {
|
|
t.Fatalf("appendEnemyEventLists() = %#v, error = %v", got, err)
|
|
}
|
|
got.Events[0].SourceRefs[0].StartUnitID = 999
|
|
if input[2].Events[0].SourceRefs[0].StartUnitID != 1 {
|
|
t.Fatal("merged enemy events share source-reference storage")
|
|
}
|
|
for _, values := range [][]dnd.EnemyEventList{nil, []dnd.EnemyEventList{{}, {}}} {
|
|
result, err := appendEnemyEventLists(values)
|
|
if err != nil || result.Events != nil {
|
|
t.Fatalf("nil-only merge = %#v, %v; want nil events", result, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestAppendItemEventListsPreservesOrderPresenceAndOwnership(t *testing.T) {
|
|
quantity := 3
|
|
refs := []source.SourceRef{{SourceID: "session", StartUnitID: 1, EndUnitID: 1}}
|
|
input := []dnd.ItemEventList{
|
|
{},
|
|
{Events: []dnd.ItemEvent{}},
|
|
{Events: []dnd.ItemEvent{{Name: "first", Kind: dnd.ItemEventKindDiscovered, Quantity: &quantity, SourceRefs: refs}}},
|
|
{Events: []dnd.ItemEvent{{Name: "second", Kind: dnd.ItemEventKindAcquired, To: "party", SourceRefs: []source.SourceRef{{SourceID: "session", StartUnitID: 2, EndUnitID: 2}}}}},
|
|
}
|
|
got, err := appendItemEventLists(input)
|
|
if err != nil || got.Events == nil || !reflect.DeepEqual([]string{got.Events[0].Name, got.Events[1].Name}, []string{"first", "second"}) {
|
|
t.Fatalf("appendItemEventLists() = %#v, error = %v", got, err)
|
|
}
|
|
*got.Events[0].Quantity = 99
|
|
got.Events[0].SourceRefs[0].StartUnitID = 999
|
|
if quantity != 3 || input[2].Events[0].SourceRefs[0].StartUnitID != 1 {
|
|
t.Fatal("merged item events share quantity or source-reference storage")
|
|
}
|
|
for _, values := range [][]dnd.ItemEventList{nil, []dnd.ItemEventList{{}, {}}} {
|
|
result, err := appendItemEventLists(values)
|
|
if err != nil || result.Events != nil {
|
|
t.Fatalf("nil-only merge = %#v, %v; want nil events", result, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestAppendListsPreserveNestedSourceReferencePresence(t *testing.T) {
|
|
spells, err := appendSpellLists([]dnd.SpellList{{SpellCasts: []dnd.SpellCast{{SourceRefs: []source.SourceRef{}}}}})
|
|
if err != nil || spells.SpellCasts[0].SourceRefs == nil {
|
|
t.Fatalf("appendSpellLists() = %#v, %v; want present-empty source refs", spells, err)
|
|
}
|
|
|
|
npcs, err := appendNPCLists([]dnd.NPCList{{NPCs: []dnd.NPC{{SourceRefs: []source.SourceRef{}}}}})
|
|
if err != nil || npcs.NPCs[0].SourceRefs == nil {
|
|
t.Fatalf("appendNPCLists() = %#v, %v; want present-empty source refs", npcs, err)
|
|
}
|
|
|
|
turns, err := appendCombatTurnLists([]dnd.CombatTurnList{{CombatTurns: []dnd.CombatTurn{{SourceRefs: []source.SourceRef{}}}}})
|
|
if err != nil || turns.CombatTurns[0].SourceRefs == nil {
|
|
t.Fatalf("appendCombatTurnLists() = %#v, %v; want present-empty source refs", turns, err)
|
|
}
|
|
|
|
enemyEvents, err := appendEnemyEventLists([]dnd.EnemyEventList{{Events: []dnd.EnemyEvent{{SourceRefs: []source.SourceRef{}}}}})
|
|
if err != nil || enemyEvents.Events[0].SourceRefs == nil {
|
|
t.Fatalf("appendEnemyEventLists() = %#v, %v; want present-empty source refs", enemyEvents, err)
|
|
}
|
|
|
|
interactions, err := appendNPCInteractionLists([]dnd.NPCInteractionList{{Interactions: []dnd.NPCInteraction{{SourceRefs: []source.SourceRef{}}}}})
|
|
if err != nil || interactions.Interactions[0].SourceRefs == nil {
|
|
t.Fatalf("appendNPCInteractionLists() = %#v, %v; want present-empty source refs", interactions, err)
|
|
}
|
|
|
|
events, err := appendItemEventLists([]dnd.ItemEventList{{Events: []dnd.ItemEvent{{SourceRefs: []source.SourceRef{}}}}})
|
|
if err != nil || events.Events[0].SourceRefs == nil {
|
|
t.Fatalf("appendItemEventLists() = %#v, %v; want present-empty source refs", events, err)
|
|
}
|
|
}
|
|
|
|
func TestRegisterRejectsMissingDNDDependenciesBeforeMutation(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
remove func(*pipeline.Registries, **llm.AssetRegistry)
|
|
wantErr string
|
|
}{
|
|
{name: "chunkers", remove: func(r *pipeline.Registries, _ **llm.AssetRegistry) { r.Chunkers = nil }, wantErr: "chunker registry"},
|
|
{name: "artifact codecs", remove: func(r *pipeline.Registries, _ **llm.AssetRegistry) { r.ArtifactCodecs = nil }, wantErr: "artifact codec registry"},
|
|
{name: "artifact evidence", remove: func(r *pipeline.Registries, _ **llm.AssetRegistry) { r.ArtifactEvidence = nil }, wantErr: "artifact evidence registry"},
|
|
{name: "extractors", remove: func(r *pipeline.Registries, _ **llm.AssetRegistry) { r.Extractors = nil }, wantErr: "extractor registry"},
|
|
{name: "mergers", remove: func(r *pipeline.Registries, _ **llm.AssetRegistry) { r.Mergers = nil }, wantErr: "merger registry"},
|
|
{name: "normalizers", remove: func(r *pipeline.Registries, _ **llm.AssetRegistry) { r.Normalizers = nil }, wantErr: "normalizer registry"},
|
|
{name: "validators", remove: func(r *pipeline.Registries, _ **llm.AssetRegistry) { r.Validators = nil }, wantErr: "validator registry"},
|
|
{name: "validator chains", remove: func(r *pipeline.Registries, _ **llm.AssetRegistry) { r.ValidatorChains = nil }, wantErr: "validator chain registry"},
|
|
{name: "assets", remove: func(_ *pipeline.Registries, assets **llm.AssetRegistry) { *assets = nil }, wantErr: "asset registry"},
|
|
}
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
registries := completeRegistries()
|
|
assets := llm.NewAssetRegistry()
|
|
test.remove(®istries, &assets)
|
|
err := Register(registries, assets)
|
|
if err == nil || !strings.Contains(err.Error(), test.wantErr) {
|
|
t.Fatalf("Register() error = %v, want %q", err, test.wantErr)
|
|
}
|
|
if got := registries.Chunkers; got != nil && len(got.RegisteredKeys()) != 0 {
|
|
t.Fatalf("chunker keys = %#v, want validation before mutation", got.RegisteredKeys())
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestRegisterReportsDuplicateDNDRegistration(t *testing.T) {
|
|
registries := completeRegistries()
|
|
assets := llm.NewAssetRegistry()
|
|
if err := Register(registries, assets); err != nil {
|
|
t.Fatalf("first Register() error = %v, want nil", err)
|
|
}
|
|
err := Register(registries, assets)
|
|
if err == nil || !strings.Contains(err.Error(), "register dnd spells codec") || !strings.Contains(err.Error(), "already registered") {
|
|
t.Fatalf("second Register() error = %v, want contextual duplicate error", err)
|
|
}
|
|
}
|
|
|
|
func completeRegistries() pipeline.Registries {
|
|
return pipeline.Registries{
|
|
Inputs: pipeline.NewInputAdapterRegistry(),
|
|
Chunkers: pipeline.NewChunkerRegistry(),
|
|
ArtifactCodecs: pipeline.NewArtifactCodecRegistry(),
|
|
ArtifactEvidence: pipeline.NewArtifactEvidenceRegistry(),
|
|
Extractors: pipeline.NewExtractorRegistry(),
|
|
Mergers: pipeline.NewMergerRegistry(),
|
|
Normalizers: pipeline.NewNormalizerRegistry(),
|
|
Validators: pipeline.NewValidatorRegistry(),
|
|
ValidatorChains: pipeline.NewValidatorChainRegistry(),
|
|
Outputs: pipeline.NewOutputEncoderRegistry(),
|
|
}
|
|
}
|
|
|
|
func assertContainsKeys(t *testing.T, name string, got, want []string) {
|
|
t.Helper()
|
|
seen := make(map[string]struct{}, len(got))
|
|
for _, key := range got {
|
|
seen[key] = struct{}{}
|
|
}
|
|
for _, key := range want {
|
|
if _, ok := seen[key]; !ok {
|
|
t.Fatalf("%s keys = %#v, want required key %q", name, got, key)
|
|
}
|
|
}
|
|
}
|
|
|
|
func assertContainsArtifactKinds(t *testing.T, got, want []contracts.ArtifactKind) {
|
|
t.Helper()
|
|
seen := make(map[contracts.ArtifactKind]struct{}, len(got))
|
|
for _, kind := range got {
|
|
seen[kind] = struct{}{}
|
|
}
|
|
for _, kind := range want {
|
|
if _, ok := seen[kind]; !ok {
|
|
t.Fatalf("artifact codec kinds = %#v, want required kind %q", got, kind)
|
|
}
|
|
}
|
|
}
|
|
|
|
func assertAssetNamesContain(t *testing.T, getFS func() (fs.FS, error), want []string) {
|
|
t.Helper()
|
|
fSys, err := getFS()
|
|
if err != nil {
|
|
t.Fatalf("asset filesystem error = %v, want nil", err)
|
|
}
|
|
var got []string
|
|
if err := fs.WalkDir(fSys, ".", func(path string, entry fs.DirEntry, err error) error {
|
|
if err == nil && !entry.IsDir() {
|
|
got = append(got, path)
|
|
}
|
|
return err
|
|
}); err != nil {
|
|
t.Fatalf("walk assets: %v", err)
|
|
}
|
|
sort.Strings(got)
|
|
seen := make(map[string]struct{}, len(got))
|
|
for _, name := range got {
|
|
seen[name] = struct{}{}
|
|
}
|
|
for _, name := range want {
|
|
if _, ok := seen[name]; !ok {
|
|
t.Fatalf("asset names = %#v, want required asset %q", got, name)
|
|
}
|
|
}
|
|
}
|