Complete cleanup stage workstream
This commit is contained in:
@@ -1,11 +1,13 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -162,6 +164,76 @@ func TestCleanupFailurePreservesPublishAndRetriesWithoutRepublishing(t *testing.
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunStageCleanupUsesPublishEvidenceAndFailsClosedWithoutIt(t *testing.T) {
|
||||
workspaceRoot := t.TempDir()
|
||||
pipelinePath, campaignPath, sessionPath := writeValidConfigFiles(t, workspaceRoot)
|
||||
enableCleanupForCommandTest(t, pipelinePath)
|
||||
cfg, err := config.Load(pipelinePath, campaignPath, sessionPath)
|
||||
if err != nil {
|
||||
t.Fatalf("load cleanup config: %v", err)
|
||||
}
|
||||
|
||||
publishedRunID := "20260904T010203Z-a1b2c3d4"
|
||||
m := manifest.New(cfg.Session.SessionID, time.Date(2026, 9, 4, 1, 2, 3, 0, time.UTC))
|
||||
m.Campaign = cfg.Session.Campaign
|
||||
m.RunID = publishedRunID
|
||||
m.S3SessionPrefix = artifacts.S3SessionPrefix(cfg.Pipeline.Storage.S3.RootPrefix, m.Campaign, m.SessionID)
|
||||
m.MarkStageSucceeded("publish", time.Date(2026, 9, 4, 1, 3, 0, 0, time.UTC), nil)
|
||||
m.Stages["publish"].Metadata = cleanupPublishMetadata(m)
|
||||
manifestPath := artifacts.SessionManifestPathForCampaign(cfg.Pipeline.Workspace.Root, m.Campaign, m.SessionID)
|
||||
if err := (&manifest.LocalStore{}).Save(context.Background(), manifestPath, m); err != nil {
|
||||
t.Fatalf("save publication evidence: %v", err)
|
||||
}
|
||||
workspaceTarget := artifacts.SessionRunRootForCampaign(cfg.Pipeline.Workspace.Root, m.Campaign, m.SessionID, publishedRunID)
|
||||
spoolTarget := artifacts.SessionSpoolAudioDir(cfg.Pipeline.Spool.Root, m.Campaign, m.SessionID, publishedRunID)
|
||||
for _, target := range []string{workspaceTarget, spoolTarget} {
|
||||
if err := os.MkdirAll(target, 0o755); err != nil {
|
||||
t.Fatalf("create cleanup target: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
var output bytes.Buffer
|
||||
if err := RunStage(context.Background(), []string{
|
||||
"cleanup", cfg.Session.SessionID, "--config", pipelinePath,
|
||||
"--campaign-file", campaignPath, "--session", sessionPath,
|
||||
}, &output); err != nil {
|
||||
t.Fatalf("RunStage(cleanup): %v", err)
|
||||
}
|
||||
if !strings.Contains(output.String(), "stage=cleanup executed=1 skipped=0") {
|
||||
t.Fatalf("output = %q, want direct cleanup execution", output.String())
|
||||
}
|
||||
for _, target := range []string{workspaceTarget, spoolTarget} {
|
||||
if _, statErr := os.Lstat(target); !errors.Is(statErr, os.ErrNotExist) {
|
||||
t.Fatalf("cleanup target %q stat error = %v, want absent", target, statErr)
|
||||
}
|
||||
}
|
||||
|
||||
missingRoot := t.TempDir()
|
||||
missingPipeline, missingCampaign, missingSession := writeValidConfigFiles(t, missingRoot)
|
||||
enableCleanupForCommandTest(t, missingPipeline)
|
||||
err = RunStage(context.Background(), []string{
|
||||
"cleanup", "2026-05-03", "--config", missingPipeline,
|
||||
"--campaign-file", missingCampaign, "--session", missingSession,
|
||||
}, &bytes.Buffer{})
|
||||
if err == nil || !strings.Contains(err.Error(), "cleanup requires a recorded publish stage") {
|
||||
t.Fatalf("RunStage(cleanup) without evidence error = %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func enableCleanupForCommandTest(t *testing.T, pipelinePath string) {
|
||||
t.Helper()
|
||||
data, err := os.ReadFile(pipelinePath)
|
||||
if err != nil {
|
||||
t.Fatalf("read pipeline config: %v", err)
|
||||
}
|
||||
content := strings.Replace(string(data), "workspace:\n root: ", "workspace:\n cleanup_after_publish: true\n root: ", 1)
|
||||
content = strings.Replace(content, "spool:\n root: ", "spool:\n delete_audio_after_publish: true\n root: ", 1)
|
||||
content = strings.Replace(content, " upload_run: false", " upload_run: true", 1)
|
||||
if err := os.WriteFile(pipelinePath, []byte(content), 0o644); err != nil {
|
||||
t.Fatalf("write cleanup config: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func cleanupOrchestrationConfig(t *testing.T) (*config.Config, *storage.FakeBackend) {
|
||||
t.Helper()
|
||||
cfg := testConfig(t)
|
||||
|
||||
@@ -91,10 +91,10 @@ func validateCleanupPublishPolicy(cfg *PipelineConfig) error {
|
||||
return nil
|
||||
}
|
||||
if cfg.Publish == nil || (cfg.Publish.Enabled != nil && !*cfg.Publish.Enabled) {
|
||||
return fmt.Errorf("post-publish cleanup policies require pipeline.publish.enabled=true")
|
||||
return fmt.Errorf("cleanup stage policies require pipeline.publish.enabled=true")
|
||||
}
|
||||
if cfg.Publish.UploadRun != nil && !*cfg.Publish.UploadRun {
|
||||
return fmt.Errorf("post-publish cleanup policies require pipeline.publish.upload_run=true")
|
||||
return fmt.Errorf("cleanup stage policies require pipeline.publish.upload_run=true")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user