149 lines
4.8 KiB
Go
149 lines
4.8 KiB
Go
package app
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"gitea.maximumdirect.net/eric/narratio/internal/artifacts"
|
|
"gitea.maximumdirect.net/eric/narratio/internal/manifest"
|
|
)
|
|
|
|
func TestPlanCreatesAndReusesWorkdir(t *testing.T) {
|
|
workspaceRoot := t.TempDir()
|
|
pipelinePath, campaignPath, sessionPath := writeValidConfigFiles(t, workspaceRoot)
|
|
|
|
var out bytes.Buffer
|
|
args := []string{"2026-05-03", "--config", pipelinePath, "--campaign-file", campaignPath, "--session", sessionPath}
|
|
|
|
if err := Plan(context.Background(), args, &out); err != nil {
|
|
t.Fatalf("first Plan() error = %v", err)
|
|
}
|
|
got := out.String()
|
|
if !strings.Contains(got, "narratio session plan: workdir prepared at") {
|
|
t.Fatalf("first output = %q, want workdir prepared", got)
|
|
}
|
|
for _, name := range []string{"prepare", "transcribe", "merge", "polish", "normalize", "trim", "render", "analyze", "publish", "notify"} {
|
|
if !strings.Contains(got, name+": run") {
|
|
t.Fatalf("first output = %q, missing stage %q", got, name)
|
|
}
|
|
}
|
|
if !strings.Contains(got, "totals: run=10 skip=0") {
|
|
t.Fatalf("first output = %q, want totals", got)
|
|
}
|
|
|
|
sessionWorkdir := artifacts.SessionWorkDirForCampaign(workspaceRoot, "sample-campaign", "2026-05-03")
|
|
expectedDirs := []string{
|
|
sessionWorkdir,
|
|
filepath.Join(sessionWorkdir, "inputs"),
|
|
filepath.Join(sessionWorkdir, "audio"),
|
|
filepath.Join(sessionWorkdir, "transcripts", "raw"),
|
|
filepath.Join(sessionWorkdir, "transcripts", "trimmed"),
|
|
filepath.Join(sessionWorkdir, "artifacts"),
|
|
filepath.Join(sessionWorkdir, "config"),
|
|
filepath.Join(sessionWorkdir, "logs"),
|
|
}
|
|
for _, dir := range expectedDirs {
|
|
assertDir(t, dir)
|
|
}
|
|
|
|
out.Reset()
|
|
if err := Plan(context.Background(), args, &out); err != nil {
|
|
t.Fatalf("second Plan() error = %v", err)
|
|
}
|
|
if !strings.Contains(out.String(), "narratio session plan: workdir prepared at") {
|
|
t.Fatalf("second output = %q, want workdir prepared", out.String())
|
|
}
|
|
}
|
|
|
|
func TestPlanShowsRunAndSkipFromManifest(t *testing.T) {
|
|
workspaceRoot := t.TempDir()
|
|
pipelinePath, campaignPath, sessionPath := writeValidConfigFiles(t, workspaceRoot)
|
|
manifestPath := filepath.Join(workspaceRoot, "work", "sample-campaign", "2026-05-03", "manifest.json")
|
|
|
|
store := &manifest.LocalStore{}
|
|
m := manifest.New("2026-05-03", time.Date(2026, 5, 3, 10, 0, 0, 0, time.UTC))
|
|
m.MarkStageSucceeded("prepare", time.Date(2026, 5, 3, 10, 1, 0, 0, time.UTC), nil)
|
|
m.MarkStageSucceeded("transcribe", time.Date(2026, 5, 3, 10, 2, 0, 0, time.UTC), nil)
|
|
if err := store.Save(context.Background(), manifestPath, m); err != nil {
|
|
t.Fatalf("save manifest: %v", err)
|
|
}
|
|
|
|
var out bytes.Buffer
|
|
if err := Plan(context.Background(), []string{"2026-05-03", "--config", pipelinePath, "--campaign-file", campaignPath, "--session", sessionPath}, &out); err != nil {
|
|
t.Fatalf("Plan() error = %v", err)
|
|
}
|
|
got := out.String()
|
|
if !strings.Contains(got, "prepare: skip") || !strings.Contains(got, "transcribe: skip") {
|
|
t.Fatalf("output = %q, want prepare/transcribe skipped", got)
|
|
}
|
|
if !strings.Contains(got, "trim: run") {
|
|
t.Fatalf("output = %q, want trim run", got)
|
|
}
|
|
if !strings.Contains(got, "totals: run=8 skip=2") {
|
|
t.Fatalf("output = %q, want totals run=8 skip=2", got)
|
|
}
|
|
}
|
|
|
|
func TestPlanFailsWhenConfiguredSecretsDirMissing(t *testing.T) {
|
|
workspaceRoot := t.TempDir()
|
|
configDir := t.TempDir()
|
|
pipelinePath := filepath.Join(configDir, "pipeline.yml")
|
|
campaignPath := writeAppTestCampaignConfig(t, configDir)
|
|
sessionPath := filepath.Join(configDir, "session.yml")
|
|
|
|
pipelineYAML := `workspace:
|
|
root: ` + workspaceRoot + `
|
|
storage:
|
|
backend: local
|
|
secrets:
|
|
env_dir: ./missing-secrets
|
|
whisperx:
|
|
transcribe_url: https://example.com/transcribe
|
|
seriatim:
|
|
binary: seriatim
|
|
audita:
|
|
binary: audita
|
|
notification:
|
|
timeout: 10s
|
|
`
|
|
sessionYAML := `session_id: 2026-05-03
|
|
campaign: sample-campaign
|
|
inputs:
|
|
audio_dir: ./audio
|
|
speakers_file: ./speakers.yml
|
|
autocorrect_file: ./autocorrect.yml
|
|
glossary_file: ./glossary.yml
|
|
`
|
|
if err := os.WriteFile(pipelinePath, []byte(pipelineYAML), 0o644); err != nil {
|
|
t.Fatalf("write pipeline.yml: %v", err)
|
|
}
|
|
if err := os.WriteFile(sessionPath, []byte(sessionYAML), 0o644); err != nil {
|
|
t.Fatalf("write session.yml: %v", err)
|
|
}
|
|
|
|
var out bytes.Buffer
|
|
err := Plan(context.Background(), []string{"2026-05-03", "--config", pipelinePath, "--campaign-file", campaignPath, "--session", sessionPath}, &out)
|
|
if err == nil {
|
|
t.Fatal("expected error, got nil")
|
|
}
|
|
if !strings.Contains(err.Error(), "read secrets env_dir") {
|
|
t.Fatalf("error = %q, want secrets read error context", err.Error())
|
|
}
|
|
}
|
|
|
|
func assertDir(t *testing.T, path string) {
|
|
t.Helper()
|
|
info, err := os.Stat(path)
|
|
if err != nil {
|
|
t.Fatalf("Stat(%q) error = %v", path, err)
|
|
}
|
|
if !info.IsDir() {
|
|
t.Fatalf("%q is not a directory", path)
|
|
}
|
|
}
|