Add assembled pipeline configuration regression coverage
This commit is contained in:
@@ -1185,7 +1185,7 @@ describe the completed feature once, at the correct canonical owners.
|
||||
|
||||
## Stage 22 — Assembled Workflow Regression And Final Validation
|
||||
|
||||
**Status: Pending**
|
||||
**Status: Completed**
|
||||
|
||||
### Goal
|
||||
|
||||
|
||||
@@ -3,8 +3,11 @@ package app
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -271,6 +274,188 @@ func TestAssembledLegacyAnalyzeTransitionPublishesOnlyCurrentRecords(t *testing.
|
||||
}
|
||||
}
|
||||
|
||||
func TestAssembledSplitBundleCommandsUseOneEffectiveFamilyConfiguration(t *testing.T) {
|
||||
pipelinePath, campaignPath, sessionPath := assembledSplitBundlePaths()
|
||||
workspacePath := filepath.Join(filepath.Dir(pipelinePath), "workspace")
|
||||
if _, err := os.Stat(workspacePath); !os.IsNotExist(err) {
|
||||
t.Fatalf("example workspace stat = %v, want absent", err)
|
||||
}
|
||||
|
||||
var validated bytes.Buffer
|
||||
if err := ConfigValidate(context.Background(), []string{"--config", pipelinePath, "--campaign-file", campaignPath}, &validated); err != nil {
|
||||
t.Fatalf("validate production default: %v", err)
|
||||
}
|
||||
if !strings.Contains(validated.String(), "profile=production") {
|
||||
t.Fatalf("validate output = %q, want production profile", validated.String())
|
||||
}
|
||||
for _, command := range []func(context.Context, []string, io.Writer) error{ConfigShow, ConfigSources} {
|
||||
if err := command(context.Background(), []string{
|
||||
"--config", pipelinePath, "--campaign-file", campaignPath, "--profile", "testing",
|
||||
}, io.Discard); err != nil {
|
||||
t.Fatalf("testing inspection command %T: %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("compare profiles: %v", err)
|
||||
}
|
||||
if !strings.Contains(diff.String(), "audita.model") || !strings.Contains(diff.String(), "character_items_arannis.profile_id") {
|
||||
t.Fatalf("profile diff = %q, want model and expanded family changes", diff.String())
|
||||
}
|
||||
|
||||
common := []string{
|
||||
"2026-05-03", "--config", pipelinePath, "--campaign-file", campaignPath,
|
||||
"--session", sessionPath, "--profile", "testing",
|
||||
}
|
||||
var planned bytes.Buffer
|
||||
if err := Plan(context.Background(), common, &planned); err != nil {
|
||||
t.Fatalf("plan testing bundle: %v", err)
|
||||
}
|
||||
if !strings.Contains(planned.String(), "profile=testing") {
|
||||
t.Fatalf("plan output = %q, want testing provenance", planned.String())
|
||||
}
|
||||
|
||||
original := executeStagesFn
|
||||
t.Cleanup(func() { executeStagesFn = original })
|
||||
var capturedCfg *config.Config
|
||||
var capturedPlan BoundedPlan
|
||||
var capturedOptions RunOptions
|
||||
executeStagesFn = func(_ context.Context, cfg *config.Config, plan BoundedPlan, options RunOptions) (*RunSummary, error) {
|
||||
capturedCfg = cfg
|
||||
capturedPlan = plan
|
||||
capturedOptions = options
|
||||
return &RunSummary{SessionID: cfg.Session.SessionID, ManifestPath: manifestPathForConfig(cfg.Pipeline.Workspace.Root)}, nil
|
||||
}
|
||||
runArgs := append(append([]string(nil), common...), "--from", "analyze", "--through", "analyze", "--artifacts", "character_items_arannis")
|
||||
var runOut bytes.Buffer
|
||||
if err := Run(context.Background(), runArgs, &runOut); err != nil {
|
||||
t.Fatalf("bounded family run: %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(capturedPlan.Names(), []string{"analyze"}) {
|
||||
t.Fatalf("bounded plan = %#v, want analyze only", capturedPlan.Names())
|
||||
}
|
||||
if profile, ok := config.SelectedPipelineProfile(capturedCfg.Pipeline); !ok || profile.Name != "testing" {
|
||||
t.Fatalf("captured profile = %#v, selected=%t", profile, ok)
|
||||
}
|
||||
if digest := config.EffectivePipelineDigest(capturedCfg.Pipeline); digest == "" || !strings.Contains(runOut.String(), "digest="+digest) {
|
||||
t.Fatalf("run output = %q, want effective digest %q", runOut.String(), digest)
|
||||
}
|
||||
if got := capturedOptions.EffectiveArtifacts.Keys(); !reflect.DeepEqual(got, []string{"character_items_arannis"}) {
|
||||
t.Fatalf("effective artifact keys = %#v", got)
|
||||
}
|
||||
if origin, ok := capturedOptions.EffectiveArtifacts.Origin("character_items_arannis"); !ok || origin.Family != "character_items" || origin.CharacterID != "arannis" {
|
||||
t.Fatalf("family origin = %#v, present=%t", origin, ok)
|
||||
}
|
||||
fullFamily, err := resolveEffectiveArtifacts(capturedCfg, []string{"character_items"})
|
||||
if err != nil {
|
||||
t.Fatalf("resolve complete family: %v", err)
|
||||
}
|
||||
if got := fullFamily.Keys(); !reflect.DeepEqual(got, []string{"character_items_arannis", "character_items_brenna"}) {
|
||||
t.Fatalf("full family keys = %#v", got)
|
||||
}
|
||||
if _, err := os.Stat(workspacePath); !os.IsNotExist(err) {
|
||||
t.Fatalf("inspection or bounded run created example workspace: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAssembledSplitBundleProfileSwitchRecordsProvenanceAndLimitsReuse(t *testing.T) {
|
||||
production := loadAssembledSplitBundleConfig(t, "")
|
||||
testingCfg := loadAssembledSplitBundleConfig(t, "testing")
|
||||
workspace := t.TempDir()
|
||||
production.Pipeline.Workspace.Root = workspace
|
||||
testingCfg.Pipeline.Workspace.Root = workspace
|
||||
|
||||
names := []string{"prepare", "transcribe", "merge", "polish", "normalize", "trim", "render"}
|
||||
providers := make([]stage.SemanticConfigFingerprinter, len(names))
|
||||
seed := manifest.New(production.Session.SessionID, time.Now().UTC())
|
||||
seed.Campaign = production.Session.Campaign
|
||||
for _, candidate := range stage.All() {
|
||||
seed.MarkStageSucceeded(candidate.Name(), time.Now().UTC(), nil)
|
||||
}
|
||||
for index, name := range names {
|
||||
providers[index] = canonicalSemanticProvider(t, name)
|
||||
fingerprint, err := providers[index].SemanticConfigFingerprint(&stage.Env{Config: production})
|
||||
if err != nil {
|
||||
t.Fatalf("production %s fingerprint: %v", name, err)
|
||||
}
|
||||
seed.Stages[name].SemanticConfig = &fingerprint
|
||||
}
|
||||
store := &manifest.LocalStore{}
|
||||
if err := store.Save(context.Background(), manifestPathForConfig(workspace), seed); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
productionRuns := make([]int, len(names))
|
||||
productionStages := assembledSemanticStages(names, providers, productionRuns)
|
||||
productionSummary, err := executeStages(context.Background(), production, productionStages, RunOptions{})
|
||||
if err != nil {
|
||||
t.Fatalf("record production provenance: %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(productionRuns, make([]int, len(names))) {
|
||||
t.Fatalf("production runs = %v, want complete reuse", productionRuns)
|
||||
}
|
||||
assertAssembledProvenance(t, store, productionSummary, "production", config.EffectivePipelineDigest(production.Pipeline))
|
||||
|
||||
testingRuns := make([]int, len(names))
|
||||
testingStages := assembledSemanticStages(names, providers, testingRuns)
|
||||
testingSummary, err := executeStages(context.Background(), testingCfg, testingStages, RunOptions{})
|
||||
if err != nil {
|
||||
t.Fatalf("switch to testing profile: %v", err)
|
||||
}
|
||||
if got, want := testingRuns, []int{0, 0, 0, 1, 1, 1, 1}; !reflect.DeepEqual(got, want) {
|
||||
t.Fatalf("testing profile runs = %v, want %v", got, want)
|
||||
}
|
||||
assertAssembledProvenance(t, store, testingSummary, "testing", config.EffectivePipelineDigest(testingCfg.Pipeline))
|
||||
}
|
||||
|
||||
func assembledSplitBundlePaths() (pipelinePath, campaignPath, sessionPath string) {
|
||||
examplesDir := filepath.Join("..", "..", "examples")
|
||||
return filepath.Join(examplesDir, "production-testing", "pipeline.yml"),
|
||||
filepath.Join(examplesDir, "campaigns", "sample-campaign", "campaign.yml"),
|
||||
filepath.Join(examplesDir, "session.local-audio.yml")
|
||||
}
|
||||
|
||||
func loadAssembledSplitBundleConfig(t *testing.T, profile string) *config.Config {
|
||||
t.Helper()
|
||||
pipelinePath, campaignPath, sessionPath := assembledSplitBundlePaths()
|
||||
options := config.SessionLoadOptions{}
|
||||
if profile != "" {
|
||||
options.Profile = &profile
|
||||
}
|
||||
cfg, err := config.LoadWithSessionOptions(pipelinePath, campaignPath, sessionPath, options)
|
||||
if err != nil {
|
||||
t.Fatalf("load split bundle profile %q: %v", profile, err)
|
||||
}
|
||||
return cfg
|
||||
}
|
||||
|
||||
func assembledSemanticStages(names []string, providers []stage.SemanticConfigFingerprinter, runs []int) []stage.Stage {
|
||||
stages := make([]stage.Stage, 0, len(names))
|
||||
for index, name := range names {
|
||||
stages = append(stages, semanticContractRunStub{name: name, provider: providers[index], runs: &runs[index]})
|
||||
}
|
||||
return stages
|
||||
}
|
||||
|
||||
func assertAssembledProvenance(t *testing.T, store *manifest.LocalStore, summary *RunSummary, profile, digest string) {
|
||||
t.Helper()
|
||||
session, err := store.Load(context.Background(), summary.ManifestPath)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
run, err := store.LoadRun(context.Background(), summary.RunManifestPath)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, value := range []*manifest.EffectiveConfigProvenance{session.EffectiveConfig, run.EffectiveConfig} {
|
||||
if value == nil || value.SelectedProfile == nil || value.SelectedProfile.Name != profile || value.EffectiveConfigDigest != digest {
|
||||
t.Fatalf("effective configuration provenance = %#v, want profile=%q digest=%q", value, profile, digest)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func seedAllStagesSucceeded(t *testing.T, cfg *config.Config) {
|
||||
t.Helper()
|
||||
m := manifest.New(cfg.Session.SessionID, time.Now().UTC())
|
||||
|
||||
Reference in New Issue
Block a user