Add split pipeline configuration example bundle

This commit is contained in:
2026-08-30 15:17:46 +00:00
parent 4c57ace2f6
commit fcb5f825e1
16 changed files with 294 additions and 10 deletions

View File

@@ -242,6 +242,32 @@ func TestConfigDiffReportsEqualityAndRejectsInvalidInput(t *testing.T) {
}
}
func TestConfigCommandsInspectMaintainedSplitBundleWithoutRuntimeState(t *testing.T) {
examplesDir := filepath.Join("..", "..", "examples")
pipelinePath := filepath.Join(examplesDir, "production-testing", "pipeline.yml")
campaignPath := filepath.Join(examplesDir, "campaigns", "sample-campaign", "campaign.yml")
workspacePath := filepath.Join(examplesDir, "production-testing", "workspace")
if _, err := os.Stat(workspacePath); !os.IsNotExist(err) {
t.Fatalf("example workspace stat = %v, want absent", err)
}
for _, command := range []func(context.Context, []string, io.Writer) error{ConfigValidate, ConfigShow, ConfigSources} {
if err := command(context.Background(), []string{"--config", pipelinePath, "--campaign-file", campaignPath, "--profile", "production"}, io.Discard); err != nil {
t.Fatalf("inspection command %T error = %v", command, err)
}
}
var diff bytes.Buffer
if err := ConfigDiff(context.Background(), []string{"production", "testing", "--config", pipelinePath, "--campaign-file", campaignPath}, &diff); err != nil {
t.Fatalf("ConfigDiff() error = %v", err)
}
if !strings.Contains(diff.String(), "audita.model") {
t.Fatalf("split bundle profile diff = %q, want model change", diff.String())
}
if _, err := os.Stat(workspacePath); !os.IsNotExist(err) {
t.Fatalf("inspection created example workspace: %v", err)
}
}
func writeInspectionProfiles(t *testing.T, pipelinePath, production, testing string) {
t.Helper()
data, err := os.ReadFile(pipelinePath)

View File

@@ -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) {