Complete downstream semantic resume coverage
This commit is contained in:
@@ -61,7 +61,7 @@ func TestRunNoRemainingStagesRecordsSkippedStages(t *testing.T) {
|
||||
m.MarkStageSucceeded(name, time.Date(2026, 5, 3, 10, 1, 0, 0, time.UTC), nil)
|
||||
}
|
||||
m.MarkStageSkipped("extract", time.Date(2026, 5, 3, 10, 1, 0, 0, time.UTC), "notarius_disabled")
|
||||
seedCurrentSemanticEvidence(t, loadConfigForSemanticEvidence(t, pipelinePath, campaignPath, sessionPath), m, "prepare", "transcribe", "merge", "polish", "normalize", "trim", "render")
|
||||
seedCurrentSemanticEvidence(t, loadConfigForSemanticEvidence(t, pipelinePath, campaignPath, sessionPath), m, "prepare", "transcribe", "merge", "polish", "normalize", "trim", "render", "publish", "notify")
|
||||
if err := store.Save(context.Background(), manifestPath, m); err != nil {
|
||||
t.Fatalf("save manifest: %v", err)
|
||||
}
|
||||
|
||||
@@ -435,6 +435,122 @@ func TestRefinementSemanticChangesRespectDependencyBranches(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeliverySemanticChangesInvalidateOnlyTheirFixedDependents(t *testing.T) {
|
||||
t.Run("extract contract", func(t *testing.T) {
|
||||
cfg := testConfig(t)
|
||||
configureDeliverySemanticConfig(cfg)
|
||||
provider := canonicalSemanticProvider(t, "extract")
|
||||
seed := manifest.New(cfg.Session.SessionID, time.Now().UTC())
|
||||
for _, name := range []string{"render", "extract", "analyze", "publish", "notify"} {
|
||||
seed.MarkStageSucceeded(name, time.Now().UTC(), nil)
|
||||
}
|
||||
fingerprint, err := provider.SemanticConfigFingerprint(&stage.Env{Config: cfg})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
seed.Stages["extract"].SemanticConfig = &fingerprint
|
||||
store := &manifest.LocalStore{}
|
||||
if err := store.Save(context.Background(), manifestPathFor(cfg), seed); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
cfg.Pipeline.Notarius.PipelineID = "session-v2"
|
||||
runs := 0
|
||||
selected := semanticContractRunStub{name: "extract", provider: provider, runs: &runs}
|
||||
if _, err := executeStages(context.Background(), cfg, []stage.Stage{selected}, RunOptions{}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
loaded, err := store.Load(context.Background(), manifestPathFor(cfg))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if runs != 1 || loaded.Stages["render"].Status != manifest.StatusSucceeded {
|
||||
t.Fatalf("runs=%d render=%q", runs, loaded.Stages["render"].Status)
|
||||
}
|
||||
for _, name := range []string{"analyze", "publish", "notify"} {
|
||||
if loaded.Stages[name].Status != manifest.StatusStale {
|
||||
t.Fatalf("%s status = %q, want stale", name, loaded.Stages[name].Status)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
for _, test := range []struct {
|
||||
name string
|
||||
mutate func(*config.Config)
|
||||
wantRuns [2]int
|
||||
}{
|
||||
{name: "publish destination", mutate: func(cfg *config.Config) {
|
||||
cfg.Pipeline.Publish.Outputs[0].Dest = "published/alternate.json"
|
||||
}, wantRuns: [2]int{1, 1}},
|
||||
{name: "publish credentials", mutate: func(cfg *config.Config) {
|
||||
cfg.Pipeline.Storage.S3.AccessKeyIDEnv = "OTHER_ACCESS_KEY"
|
||||
cfg.Pipeline.Storage.S3.SecretKeyEnv = "OTHER_SECRET_KEY"
|
||||
}, wantRuns: [2]int{0, 0}},
|
||||
{name: "notify mode", mutate: func(cfg *config.Config) {
|
||||
cfg.Pipeline.Notification.Mode = "webhook"
|
||||
}, wantRuns: [2]int{0, 1}},
|
||||
} {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
cfg := testConfig(t)
|
||||
configureDeliverySemanticConfig(cfg)
|
||||
names := []string{"publish", "notify"}
|
||||
providers := []stage.SemanticConfigFingerprinter{
|
||||
canonicalSemanticProvider(t, names[0]), canonicalSemanticProvider(t, names[1]),
|
||||
}
|
||||
seed := manifest.New(cfg.Session.SessionID, time.Now().UTC())
|
||||
for index, name := range names {
|
||||
seed.MarkStageSucceeded(name, time.Now().UTC(), nil)
|
||||
fingerprint, err := providers[index].SemanticConfigFingerprint(&stage.Env{Config: cfg})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
seed.Stages[name].SemanticConfig = &fingerprint
|
||||
}
|
||||
store := &manifest.LocalStore{}
|
||||
if err := store.Save(context.Background(), manifestPathFor(cfg), seed); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
test.mutate(cfg)
|
||||
runs := [2]int{}
|
||||
selected := []stage.Stage{
|
||||
semanticContractRunStub{name: names[0], provider: providers[0], runs: &runs[0]},
|
||||
semanticContractRunStub{name: names[1], provider: providers[1], runs: &runs[1]},
|
||||
}
|
||||
if _, err := executeStages(context.Background(), cfg, selected, RunOptions{}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if runs != test.wantRuns {
|
||||
t.Fatalf("runs = %v, want %v", runs, test.wantRuns)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func configureDeliverySemanticConfig(cfg *config.Config) {
|
||||
enabled := true
|
||||
disabled := false
|
||||
cfg.Pipeline.Notarius = &config.NotariusConfig{
|
||||
Enabled: true, PipelineID: "session",
|
||||
References: map[string]string{"party": "narratio.input.party"},
|
||||
Outputs: map[string]config.NotariusOutputConfig{
|
||||
"encounters": {LaneID: "encounters", MediaType: "application/json", SchemaID: "encounters", SchemaVersion: "1"},
|
||||
},
|
||||
}
|
||||
cfg.Pipeline.Publish = &config.PublishConfig{
|
||||
Enabled: &disabled, UploadRun: &enabled,
|
||||
Outputs: []config.PublishOutputRule{{
|
||||
Source: "narratio.transcript.final_trimmed", Dest: "transcripts/final.trimmed.json", Required: &enabled,
|
||||
}},
|
||||
}
|
||||
cfg.Pipeline.Storage = config.StorageConfig{
|
||||
Backend: config.StorageBackendLocal,
|
||||
S3: &config.StorageS3Config{
|
||||
Bucket: "campaign", RootPrefix: "narratio", Region: "us-east-1",
|
||||
Endpoint: "https://objects.example", AccessKeyIDEnv: "ACCESS_KEY", SecretKeyEnv: "SECRET_KEY",
|
||||
},
|
||||
}
|
||||
cfg.Pipeline.Notification.Mode = config.DefaultNotificationMode
|
||||
}
|
||||
|
||||
func canonicalSemanticProvider(t *testing.T, name string) stage.SemanticConfigFingerprinter {
|
||||
t.Helper()
|
||||
for _, candidate := range stage.All() {
|
||||
|
||||
Reference in New Issue
Block a user