207 lines
6.1 KiB
Go
207 lines
6.1 KiB
Go
package app
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"reflect"
|
|
"strings"
|
|
"testing"
|
|
|
|
"gitea.maximumdirect.net/eric/narratio/internal/config"
|
|
)
|
|
|
|
func TestResolveEffectiveArtifactsExpandsFamilySelections(t *testing.T) {
|
|
dir := t.TempDir()
|
|
write := func(name, body string) string {
|
|
path := filepath.Join(dir, name)
|
|
if err := os.WriteFile(path, []byte(body), 0o644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return path
|
|
}
|
|
pipeline := write("pipeline.yml", `workspace: {root: /tmp/narratio-work}
|
|
whisperx: {transcribe_url: https://example.test/transcribe}
|
|
notification: {mode: noop}
|
|
scriptorium:
|
|
artifact_families:
|
|
character_meta:
|
|
enabled: false
|
|
for_each: party.characters
|
|
prompt_id: dnd.character_meta
|
|
output_path_pattern: artifacts/characters/{character_id}/meta.md
|
|
`)
|
|
campaign := write("campaign.yml", `campaign_id: campaign
|
|
inputs: {speakers_file: speakers.yml, autocorrect_file: autocorrect.yml, glossary_file: glossary.yml, party_file: party.yml}
|
|
`)
|
|
session := write("session.yml", `session_id: session
|
|
campaign: campaign
|
|
inputs: {audio_dir: audio}
|
|
`)
|
|
write("party.yml", `schema_version: narratio.party.v1
|
|
characters:
|
|
zeta: {player: {name: Z}, character: {name: Zeta, classes: [{name: wizard}]}}
|
|
alpha: {player: {name: A}, character: {name: Alpha, classes: [{name: ranger}]}}
|
|
`)
|
|
cfg, err := config.LoadWithSessionOptions(pipeline, campaign, session, config.SessionLoadOptions{})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
effective, err := resolveEffectiveArtifacts(cfg, []string{"character_meta", "character_meta_alpha"})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if got, want := effective.Keys(), []string{"character_meta_alpha", "character_meta_zeta"}; !reflect.DeepEqual(got, want) {
|
|
t.Fatalf("keys = %#v, want %#v", got, want)
|
|
}
|
|
if origin, ok := effective.Origin("character_meta_alpha"); !ok || origin.Family != "character_meta" || origin.CharacterID != "alpha" {
|
|
t.Fatalf("origin = %#v, %t", origin, ok)
|
|
}
|
|
if _, err := resolveEffectiveArtifacts(cfg, []string{"unknown"}); err == nil || !strings.Contains(err.Error(), "unknown artifact") {
|
|
t.Fatalf("unknown selection error = %v", err)
|
|
}
|
|
if defaultEffective, err := resolveEffectiveArtifacts(cfg, nil); err != nil {
|
|
t.Fatal(err)
|
|
} else if len(defaultEffective.Keys()) != 0 {
|
|
t.Fatal("default selection should omit disabled family members")
|
|
}
|
|
}
|
|
|
|
func TestArtifactSelectionFlagNormalize(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
inputs []string
|
|
want []string
|
|
wantErr string
|
|
}{
|
|
{
|
|
name: "single value",
|
|
inputs: []string{"session_recap"},
|
|
want: []string{"session_recap"},
|
|
},
|
|
{
|
|
name: "repeatable and comma separated values are deduped and sorted",
|
|
inputs: []string{"session_recap,player_handout", "session_recap"},
|
|
want: []string{"player_handout", "session_recap"},
|
|
},
|
|
{
|
|
name: "empty token fails",
|
|
inputs: []string{"session_recap,"},
|
|
wantErr: "artifact names must be non-empty",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
var flag artifactSelectionFlag
|
|
for _, in := range tt.inputs {
|
|
if err := flag.Set(in); err != nil {
|
|
t.Fatalf("Set(%q) error = %v", in, err)
|
|
}
|
|
}
|
|
|
|
got, err := flag.Normalize()
|
|
if tt.wantErr != "" {
|
|
if err == nil {
|
|
t.Fatalf("Normalize() error = nil, want %q", tt.wantErr)
|
|
}
|
|
if err.Error() != tt.wantErr {
|
|
t.Fatalf("Normalize() error = %q, want %q", err.Error(), tt.wantErr)
|
|
}
|
|
return
|
|
}
|
|
if err != nil {
|
|
t.Fatalf("Normalize() error = %v", err)
|
|
}
|
|
if len(got) != len(tt.want) {
|
|
t.Fatalf("Normalize() len = %d, want %d; got=%v", len(got), len(tt.want), got)
|
|
}
|
|
for i := range got {
|
|
if got[i] != tt.want[i] {
|
|
t.Fatalf("Normalize()[%d] = %q, want %q", i, got[i], tt.want[i])
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestValidateSelectedArtifacts(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
cfg *config.Config
|
|
selected []string
|
|
wantErr string
|
|
}{
|
|
{
|
|
name: "empty selection is accepted",
|
|
cfg: &config.Config{},
|
|
selected: nil,
|
|
},
|
|
{
|
|
name: "scriptorium required when selected artifacts present",
|
|
cfg: &config.Config{Pipeline: &config.PipelineConfig{}},
|
|
selected: []string{"session_recap"},
|
|
wantErr: "--artifacts requires pipeline.scriptorium.artifacts to be configured",
|
|
},
|
|
{
|
|
name: "unknown selected artifact fails",
|
|
cfg: &config.Config{
|
|
Pipeline: &config.PipelineConfig{
|
|
Scriptorium: &config.ScriptoriumConfig{
|
|
Artifacts: map[string]config.ScriptoriumArtifactConfig{
|
|
"session_recap": {Enabled: true, PromptID: "dnd.session_recap", OutputPath: "artifacts/session_recap.md"},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
selected: []string{"player_handout"},
|
|
wantErr: `--artifacts includes unknown artifact "player_handout"`,
|
|
},
|
|
{
|
|
name: "known selected artifacts are accepted",
|
|
cfg: &config.Config{
|
|
Pipeline: &config.PipelineConfig{
|
|
Scriptorium: &config.ScriptoriumConfig{
|
|
Artifacts: map[string]config.ScriptoriumArtifactConfig{
|
|
"session_recap": {Enabled: true, PromptID: "dnd.session_recap", OutputPath: "artifacts/session_recap.md"},
|
|
"player_handout": {Enabled: true, PromptID: "dnd.player_handout", OutputPath: "artifacts/player_handout.md"},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
selected: []string{"player_handout", "session_recap"},
|
|
},
|
|
{
|
|
name: "selected disabled artifact must be executable",
|
|
cfg: &config.Config{
|
|
Pipeline: &config.PipelineConfig{
|
|
Scriptorium: &config.ScriptoriumConfig{
|
|
Artifacts: map[string]config.ScriptoriumArtifactConfig{
|
|
"player_handout": {Enabled: false, OutputPath: "artifacts/player_handout.md"},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
selected: []string{"player_handout"},
|
|
wantErr: "pipeline.scriptorium.artifacts.player_handout.prompt_id is required when selected",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
err := validateSelectedArtifacts(tt.cfg, tt.selected)
|
|
if tt.wantErr != "" {
|
|
if err == nil {
|
|
t.Fatalf("error = nil, want %q", tt.wantErr)
|
|
}
|
|
if err.Error() != tt.wantErr {
|
|
t.Fatalf("error = %q, want %q", err.Error(), tt.wantErr)
|
|
}
|
|
return
|
|
}
|
|
if err != nil {
|
|
t.Fatalf("error = %v, want nil", err)
|
|
}
|
|
})
|
|
}
|
|
}
|