Add stage planning and placeholder runner
This commit is contained in:
148
internal/app/runner_test.go
Normal file
148
internal/app/runner_test.go
Normal file
@@ -0,0 +1,148 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"gitea.maximumdirect.net/eric/narratio/internal/config"
|
||||
"gitea.maximumdirect.net/eric/narratio/internal/manifest"
|
||||
"gitea.maximumdirect.net/eric/narratio/internal/stage"
|
||||
)
|
||||
|
||||
type failingStage struct {
|
||||
name string
|
||||
err error
|
||||
}
|
||||
|
||||
func (s failingStage) Name() string { return s.name }
|
||||
func (s failingStage) Declares() stage.IODecl { return stage.IODecl{} }
|
||||
func (s failingStage) Run(_ context.Context, _ *stage.Env, _ *manifest.Manifest) (*stage.StageResult, error) {
|
||||
return nil, s.err
|
||||
}
|
||||
|
||||
func TestExecuteStagesPlaceholderSuccessUpdatesManifest(t *testing.T) {
|
||||
cfg := testConfig(t)
|
||||
|
||||
summary, err := executeStages(context.Background(), cfg, BuildFullPlan(), RunOptions{})
|
||||
if err != nil {
|
||||
t.Fatalf("executeStages() error = %v", err)
|
||||
}
|
||||
if len(summary.StageNames) != 8 {
|
||||
t.Fatalf("stage count = %d, want 8", len(summary.StageNames))
|
||||
}
|
||||
|
||||
store := &manifest.LocalStore{}
|
||||
m, err := store.Load(context.Background(), summary.ManifestPath)
|
||||
if err != nil {
|
||||
t.Fatalf("Load manifest error = %v", err)
|
||||
}
|
||||
|
||||
for _, name := range []string{"prepare", "transcribe", "normalize", "merge", "polish", "analyze", "archive", "notify"} {
|
||||
sr := m.Stages[name]
|
||||
if sr == nil {
|
||||
t.Fatalf("missing stage record %q", name)
|
||||
}
|
||||
if sr.Status != manifest.StatusSucceeded {
|
||||
t.Fatalf("stage %q status = %q, want %q", name, sr.Status, manifest.StatusSucceeded)
|
||||
}
|
||||
if sr.Metadata == nil || sr.Metadata["placeholder"] != true {
|
||||
t.Fatalf("stage %q missing placeholder metadata", name)
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := os.Stat(summary.ManifestPath); err != nil {
|
||||
t.Fatalf("manifest file missing at %q: %v", summary.ManifestPath, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExecuteStagesFailureUpdatesManifest(t *testing.T) {
|
||||
cfg := testConfig(t)
|
||||
|
||||
stages := []stage.Stage{
|
||||
BuildFullPlan()[0],
|
||||
failingStage{name: "transcribe", err: errors.New("boom")},
|
||||
BuildFullPlan()[2],
|
||||
}
|
||||
|
||||
summary, err := executeStages(context.Background(), cfg, stages, RunOptions{})
|
||||
if err == nil {
|
||||
t.Fatal("expected error, got nil")
|
||||
}
|
||||
if summary != nil {
|
||||
t.Fatalf("summary = %#v, want nil on failure", summary)
|
||||
}
|
||||
if !strings.Contains(err.Error(), "stage \"transcribe\" failed") {
|
||||
t.Fatalf("error = %q, want stage failure", err.Error())
|
||||
}
|
||||
|
||||
manifestPath := manifestPathFor(cfg)
|
||||
store := &manifest.LocalStore{}
|
||||
m, loadErr := store.Load(context.Background(), manifestPath)
|
||||
if loadErr != nil {
|
||||
t.Fatalf("Load manifest error = %v", loadErr)
|
||||
}
|
||||
|
||||
if got := m.Stages["prepare"]; got == nil || got.Status != manifest.StatusSucceeded {
|
||||
t.Fatalf("prepare status = %#v, want succeeded", got)
|
||||
}
|
||||
if got := m.Stages["transcribe"]; got == nil || got.Status != manifest.StatusFailed {
|
||||
t.Fatalf("transcribe status = %#v, want failed", got)
|
||||
}
|
||||
if got := m.Stages["normalize"]; got != nil {
|
||||
t.Fatalf("normalize should not run, got %#v", got)
|
||||
}
|
||||
}
|
||||
|
||||
func testConfig(t *testing.T) *config.Config {
|
||||
t.Helper()
|
||||
|
||||
workspace := t.TempDir()
|
||||
return &config.Config{
|
||||
Pipeline: &config.PipelineConfig{Workspace: config.WorkspaceConfig{Root: workspace}},
|
||||
Session: &config.SessionConfig{
|
||||
SessionID: "2026-05-03",
|
||||
Inputs: config.SessionInputsConfig{
|
||||
AudioDir: "./audio",
|
||||
SpeakersFile: "./speakers.yml",
|
||||
AutocorrectFile: "./autocorrect.yml",
|
||||
GlossaryFile: "./glossary.yml",
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func TestExecuteStagesLoadsExistingManifest(t *testing.T) {
|
||||
cfg := testConfig(t)
|
||||
manifestPath := manifestPathFor(cfg)
|
||||
store := &manifest.LocalStore{}
|
||||
|
||||
existing := manifest.New(cfg.Session.SessionID, time.Date(2026, 5, 3, 1, 0, 0, 0, time.UTC))
|
||||
existing.MarkStageSucceeded("prepare", time.Date(2026, 5, 3, 1, 1, 0, 0, time.UTC), nil)
|
||||
if err := os.MkdirAll(filepath.Dir(manifestPath), 0o755); err != nil {
|
||||
t.Fatalf("MkdirAll() error = %v", err)
|
||||
}
|
||||
if err := store.Save(context.Background(), manifestPath, existing); err != nil {
|
||||
t.Fatalf("Save manifest error = %v", err)
|
||||
}
|
||||
|
||||
_, err := executeStages(context.Background(), cfg, []stage.Stage{BuildFullPlan()[1]}, RunOptions{})
|
||||
if err != nil {
|
||||
t.Fatalf("executeStages() error = %v", err)
|
||||
}
|
||||
|
||||
loaded, err := store.Load(context.Background(), manifestPath)
|
||||
if err != nil {
|
||||
t.Fatalf("Load manifest error = %v", err)
|
||||
}
|
||||
if loaded.Stages["prepare"] == nil || loaded.Stages["prepare"].Status != manifest.StatusSucceeded {
|
||||
t.Fatalf("existing stage prepare should remain succeeded")
|
||||
}
|
||||
if loaded.Stages["transcribe"] == nil || loaded.Stages["transcribe"].Status != manifest.StatusSucceeded {
|
||||
t.Fatalf("transcribe should be succeeded after run")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user