Add split pipeline configuration example bundle
This commit is contained in:
@@ -1,9 +1,11 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
@@ -101,6 +103,15 @@ func TestValidateMissingAudioSource(t *testing.T) {
|
||||
|
||||
func TestExamplesLoadAndValidate(t *testing.T) {
|
||||
examplesDir := filepath.Join("..", "..", "examples")
|
||||
if got, want := maintainedPipelineRoots(t, examplesDir), []string{
|
||||
"pipeline.extraction-subset.yml",
|
||||
"pipeline.full.annotated.yml",
|
||||
"pipeline.minimal.yml",
|
||||
"pipeline.production.yml",
|
||||
filepath.Join("production-testing", "pipeline.yml"),
|
||||
}; !reflect.DeepEqual(got, want) {
|
||||
t.Fatalf("maintained pipeline roots = %#v, want %#v", got, want)
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
pipelineFile string
|
||||
@@ -143,6 +154,108 @@ func TestExamplesLoadAndValidate(t *testing.T) {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
t.Run("split production and testing bundle", func(t *testing.T) {
|
||||
pipelinePath := filepath.Join(examplesDir, "production-testing", "pipeline.yml")
|
||||
campaignPath := filepath.Join(examplesDir, "campaigns", "sample-campaign", "campaign.yml")
|
||||
sessionPath := filepath.Join(examplesDir, "session.local-audio.yml")
|
||||
campaign, err := LoadCampaign(campaignPath)
|
||||
if err != nil {
|
||||
t.Fatalf("load split bundle campaign: %v", err)
|
||||
}
|
||||
for _, profile := range []struct {
|
||||
name string
|
||||
model string
|
||||
}{
|
||||
{name: "production", model: "narratio-production-model-placeholder"},
|
||||
{name: "testing", model: "narratio-testing-model-placeholder"},
|
||||
} {
|
||||
t.Run(profile.name, func(t *testing.T) {
|
||||
selected := profile.name
|
||||
pipeline, err := LoadPipelineWithOptions(pipelinePath, PipelineLoadOptions{Profile: &selected})
|
||||
if err != nil {
|
||||
t.Fatalf("load split bundle pipeline: %v", err)
|
||||
}
|
||||
loaded, err := LoadPipelineCampaign(pipelinePath, pipeline, campaignPath, campaign)
|
||||
if err != nil {
|
||||
t.Fatalf("resolve split bundle campaign: %v", err)
|
||||
}
|
||||
cfg, err := LoadSessionWithPipelineCampaignOptions(loaded, sessionPath, SessionLoadOptions{})
|
||||
if err != nil {
|
||||
t.Fatalf("load split bundle session: %v", err)
|
||||
}
|
||||
if err := Validate(cfg); err != nil {
|
||||
t.Fatalf("validate split bundle: %v", err)
|
||||
}
|
||||
if cfg.Pipeline.Audita.Model != profile.model {
|
||||
t.Fatalf("audita model = %q, want %q", cfg.Pipeline.Audita.Model, profile.model)
|
||||
}
|
||||
if cfg.Pipeline.Secrets != nil || cfg.Pipeline.Audita.LLMAPIKeyEnv != "" || cfg.Pipeline.Storage.Backend != StorageBackendLocal || cfg.Pipeline.Storage.S3 != nil {
|
||||
t.Fatalf("split bundle must remain offline and secret-free: secrets=%#v audita=%#v storage=%#v", cfg.Pipeline.Secrets, cfg.Pipeline.Audita, cfg.Pipeline.Storage)
|
||||
}
|
||||
if selectedProfile, ok := SelectedPipelineProfile(cfg.Pipeline); !ok || selectedProfile.Name != profile.name {
|
||||
t.Fatalf("selected profile = %#v, want %q", selectedProfile, profile.name)
|
||||
}
|
||||
for _, key := range []string{
|
||||
"character_meta_arannis",
|
||||
"character_meta_brenna",
|
||||
"character_items_arannis",
|
||||
"character_items_brenna",
|
||||
} {
|
||||
if _, exists := cfg.Pipeline.Scriptorium.Artifacts[key]; !exists {
|
||||
t.Fatalf("expanded artifact %q is absent", key)
|
||||
}
|
||||
}
|
||||
if got := cfg.Pipeline.Scriptorium.Artifacts["character_items_arannis"].Inputs["character_meta"].Source; got != "narratio.artifact.character_meta_arannis" {
|
||||
t.Fatalf("same-member input source = %q", got)
|
||||
}
|
||||
if cfg.Party.Mode != PartyModeCanonical || cfg.Party.Canonical == nil || len(cfg.Party.Canonical.Characters) != 2 {
|
||||
t.Fatalf("canonical party = %#v", cfg.Party)
|
||||
}
|
||||
characters := cfg.Party.Canonical.Characters
|
||||
if characters[0].Player.Name != characters[1].Player.Name || len(characters[0].Character.Aliases) != 2 || len(characters[1].Character.Classes) != 2 {
|
||||
t.Fatalf("canonical party does not preserve repeated player, aliases, and multiclass data: %#v", characters)
|
||||
}
|
||||
if len(cfg.Pipeline.Publish.Outputs) != 3 {
|
||||
t.Fatalf("publish outputs = %#v, want transcript plus two family outputs", cfg.Pipeline.Publish.Outputs)
|
||||
}
|
||||
if profile.name == "testing" {
|
||||
artifact, exists := cfg.Pipeline.Scriptorium.Artifacts["testing_notes"]
|
||||
if !exists || artifact.Enabled {
|
||||
t.Fatalf("testing-only disabled artifact = %#v", artifact)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func maintainedPipelineRoots(t *testing.T, examplesDir string) []string {
|
||||
t.Helper()
|
||||
var roots []string
|
||||
err := filepath.WalkDir(examplesDir, func(path string, entry fs.DirEntry, walkErr error) error {
|
||||
if walkErr != nil {
|
||||
return walkErr
|
||||
}
|
||||
if entry.IsDir() {
|
||||
return nil
|
||||
}
|
||||
name := entry.Name()
|
||||
if name != "pipeline.yml" && !(strings.HasPrefix(name, "pipeline.") && (strings.HasSuffix(name, ".yml") || strings.HasSuffix(name, ".yaml"))) {
|
||||
return nil
|
||||
}
|
||||
relative, err := filepath.Rel(examplesDir, path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
roots = append(roots, relative)
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("discover maintained pipeline roots: %v", err)
|
||||
}
|
||||
sort.Strings(roots)
|
||||
return roots
|
||||
}
|
||||
|
||||
func TestMaintainedExtractionExamplesPreservePublishedContracts(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user