141 lines
4.6 KiB
Go
141 lines
4.6 KiB
Go
package app
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"gitea.maximumdirect.net/eric/narratio/internal/manifest"
|
|
)
|
|
|
|
func TestExecuteRunStageArtifactsNonAnalyzeFails(t *testing.T) {
|
|
workspaceRoot := t.TempDir()
|
|
pipelinePath, campaignPath, sessionPath := writeValidConfigFilesWithScriptoriumArtifacts(t, workspaceRoot)
|
|
|
|
var stdout bytes.Buffer
|
|
var stderr bytes.Buffer
|
|
code := Execute(
|
|
[]string{"run-stage", "--config", pipelinePath, "--campaign", campaignPath, "--session", sessionPath, "--artifacts", "session_recap", "polish"},
|
|
&stdout,
|
|
&stderr,
|
|
)
|
|
if code == 0 {
|
|
t.Fatal("exit code = 0, want non-zero")
|
|
}
|
|
if !strings.Contains(stderr.String(), `run-stage: --artifacts is only supported for stage "analyze"`) {
|
|
t.Fatalf("stderr = %q, want stage-gating error", stderr.String())
|
|
}
|
|
}
|
|
|
|
func TestExecuteUnknownArtifactsFailValidation(t *testing.T) {
|
|
workspaceRoot := t.TempDir()
|
|
pipelinePath, campaignPath, sessionPath := writeValidConfigFilesWithScriptoriumArtifacts(t, workspaceRoot)
|
|
|
|
var stdout bytes.Buffer
|
|
var stderr bytes.Buffer
|
|
code := Execute(
|
|
[]string{"run", "--config", pipelinePath, "--campaign", campaignPath, "--session", sessionPath, "--artifacts", "unknown_artifact"},
|
|
&stdout,
|
|
&stderr,
|
|
)
|
|
if code == 0 {
|
|
t.Fatal("exit code = 0, want non-zero")
|
|
}
|
|
if !strings.Contains(stderr.String(), `run: --artifacts includes unknown artifact "unknown_artifact"`) {
|
|
t.Fatalf("stderr = %q, want unknown-artifact validation error", stderr.String())
|
|
}
|
|
}
|
|
|
|
func TestRunStageArtifactsDoesNotImplyForce(t *testing.T) {
|
|
workspaceRoot := t.TempDir()
|
|
pipelinePath, campaignPath, sessionPath := writeValidConfigFilesWithScriptoriumArtifacts(t, workspaceRoot)
|
|
manifestPath := filepath.Join(workspaceRoot, "work", "sample-campaign", "2026-05-03", "manifest.json")
|
|
|
|
store := &manifest.LocalStore{}
|
|
seed := manifest.New("2026-05-03", time.Date(2026, 5, 3, 10, 0, 0, 0, time.UTC))
|
|
seed.MarkStageSucceeded("analyze", time.Date(2026, 5, 3, 10, 1, 0, 0, time.UTC), nil)
|
|
if err := store.Save(context.Background(), manifestPath, seed); err != nil {
|
|
t.Fatalf("save manifest: %v", err)
|
|
}
|
|
|
|
var out bytes.Buffer
|
|
err := RunStage(
|
|
context.Background(),
|
|
[]string{"--config", pipelinePath, "--campaign", campaignPath, "--session", sessionPath, "--artifacts", "session_recap,session_recap", "analyze"},
|
|
&out,
|
|
)
|
|
if err != nil {
|
|
t.Fatalf("RunStage() error = %v", err)
|
|
}
|
|
if !strings.Contains(out.String(), "stage=analyze executed=0 skipped=1 force=false") {
|
|
t.Fatalf("output = %q, want analyze skip without force", out.String())
|
|
}
|
|
}
|
|
|
|
func TestResumeArtifactsWithSucceededAnalyzeSkipsUnlessForced(t *testing.T) {
|
|
workspaceRoot := t.TempDir()
|
|
pipelinePath, campaignPath, sessionPath := writeValidConfigFilesWithScriptoriumArtifacts(t, workspaceRoot)
|
|
manifestPath := filepath.Join(workspaceRoot, "work", "sample-campaign", "2026-05-03", "manifest.json")
|
|
|
|
store := &manifest.LocalStore{}
|
|
seed := manifest.New("2026-05-03", time.Date(2026, 5, 3, 10, 0, 0, 0, time.UTC))
|
|
for _, stageName := range []string{"prepare", "transcribe", "merge", "polish", "normalize", "trim", "analyze", "archive", "notify"} {
|
|
seed.MarkStageSucceeded(stageName, time.Date(2026, 5, 3, 10, 1, 0, 0, time.UTC), nil)
|
|
}
|
|
if err := store.Save(context.Background(), manifestPath, seed); err != nil {
|
|
t.Fatalf("save manifest: %v", err)
|
|
}
|
|
|
|
var out bytes.Buffer
|
|
err := Resume(
|
|
context.Background(),
|
|
[]string{"--config", pipelinePath, "--campaign", campaignPath, "--session", sessionPath, "--artifacts", "session_recap"},
|
|
&out,
|
|
)
|
|
if err != nil {
|
|
t.Fatalf("Resume() error = %v", err)
|
|
}
|
|
if !strings.Contains(out.String(), "has no remaining stages") {
|
|
t.Fatalf("output = %q, want no remaining stages", out.String())
|
|
}
|
|
}
|
|
|
|
func writeValidConfigFilesWithScriptoriumArtifacts(t *testing.T, workspaceRoot string) (string, string, string) {
|
|
t.Helper()
|
|
|
|
pipelinePath, campaignPath, sessionPath := writeValidConfigFiles(t, workspaceRoot)
|
|
f, err := os.OpenFile(pipelinePath, os.O_APPEND|os.O_WRONLY, 0)
|
|
if err != nil {
|
|
t.Fatalf("open pipeline config for append: %v", err)
|
|
}
|
|
defer f.Close()
|
|
|
|
extra := `
|
|
scriptorium:
|
|
binary: scriptorium
|
|
artifacts:
|
|
session_recap:
|
|
enabled: true
|
|
prompt_id: dnd.session_recap
|
|
output_path: artifacts/session_recap.md
|
|
player_handout:
|
|
enabled: true
|
|
prompt_id: dnd.player_handout
|
|
output_path: artifacts/player_handout.md
|
|
depends_on:
|
|
- session_recap
|
|
inputs:
|
|
recap:
|
|
source: narratio.artifact.session_recap
|
|
required: true
|
|
`
|
|
if _, err := f.WriteString(extra); err != nil {
|
|
t.Fatalf("append scriptorium config: %v", err)
|
|
}
|
|
return pipelinePath, campaignPath, sessionPath
|
|
}
|