2026-05-19 21:39:13 -05:00
package app
import (
"bytes"
2026-05-19 21:49:19 -05:00
"context"
"fmt"
2026-05-19 21:39:13 -05:00
"os"
"path/filepath"
"strings"
"testing"
2026-05-19 21:49:19 -05:00
"gitea.maximumdirect.net/eric/narratio/internal/adapters/storage"
2026-05-19 21:39:13 -05:00
"gitea.maximumdirect.net/eric/narratio/internal/artifacts"
2026-05-19 21:49:19 -05:00
"gitea.maximumdirect.net/eric/narratio/internal/config"
2026-05-19 21:39:13 -05:00
)
func TestExecuteRestoreHelp ( t * testing . T ) {
var stdout bytes . Buffer
var stderr bytes . Buffer
2026-05-22 22:09:17 -05:00
code := Execute ( [ ] string { "session" , "restore" , "--help" } , & stdout , & stderr )
2026-05-19 21:39:13 -05:00
if code != 0 {
t . Fatalf ( "exit code = %d, want 0" , code )
}
if stderr . Len ( ) != 0 {
t . Fatalf ( "stderr = %q, want empty" , stderr . String ( ) )
}
out := stdout . String ( )
2026-05-22 22:09:17 -05:00
if ! strings . Contains ( out , "Usage: narratio session restore <session_id>" ) {
2026-05-19 21:39:13 -05:00
t . Fatalf ( "stdout = %q, want restore usage" , out )
}
if ! strings . Contains ( out , "--include-audio" ) {
t . Fatalf ( "stdout = %q, want --include-audio flag" , out )
}
2026-05-20 20:41:28 -05:00
if ! strings . Contains ( out , "--campaign" ) {
t . Fatalf ( "stdout = %q, want --campaign flag" , out )
}
2026-05-19 21:39:13 -05:00
}
func TestExecuteRestoreRecognizedAndReturnsNYI ( t * testing . T ) {
2026-05-19 21:49:19 -05:00
origStoreFn := newObjectStoreFromConfigFn
origDiscoverFn := discoverRemoteCurrentStateFn
2026-05-19 21:58:38 -05:00
origPlanFn := buildRestorePlanFn
2026-05-19 22:06:48 -05:00
origExecuteFn := executeRestorePlanFn
2026-05-19 21:49:19 -05:00
t . Cleanup ( func ( ) {
newObjectStoreFromConfigFn = origStoreFn
discoverRemoteCurrentStateFn = origDiscoverFn
2026-05-19 21:58:38 -05:00
buildRestorePlanFn = origPlanFn
2026-05-19 22:06:48 -05:00
executeRestorePlanFn = origExecuteFn
2026-05-19 21:49:19 -05:00
} )
newObjectStoreFromConfigFn = func ( context . Context , * config . Config ) ( storage . ObjectStore , error ) {
return & storage . FakeBackend { } , nil
}
discoverRemoteCurrentStateFn = func ( context . Context , * config . Config , storage . ObjectStore ) ( * RemoteCurrentState , error ) {
return & RemoteCurrentState {
SessionID : "2026-05-03" ,
Campaign : "sample-campaign" ,
RunID : "20260519T010203Z-a1b2c3d4" ,
} , nil
}
2026-05-19 21:58:38 -05:00
buildRestorePlanFn = func ( context . Context , * config . Config , * RemoteCurrentState , storage . ObjectStore , RestorePlanOptions ) ( * RestorePlan , error ) {
return & RestorePlan {
Actions : [ ] RestoreAction {
{
Kind : RestoreActionDownload ,
LocalRelativePath : "manifest.json" ,
2026-09-02 14:18:51 +00:00
RemoteKey : "dnd/campaigns/sample-campaign/sessions/2026-05-03/runs/20260519T010203Z-a1b2c3d4/session-manifest.json" ,
2026-05-19 21:58:38 -05:00
Reason : "local file missing" ,
} ,
} ,
DownloadCount : 1 ,
} , nil
}
2026-05-19 21:49:19 -05:00
2026-05-19 21:39:13 -05:00
workspaceRoot := t . TempDir ( )
2026-05-20 20:41:28 -05:00
pipelinePath , campaignPath , sessionPath := writeValidConfigFiles ( t , workspaceRoot )
2026-05-19 21:39:13 -05:00
var stdout bytes . Buffer
var stderr bytes . Buffer
code := Execute (
[ ] string {
2026-05-22 22:09:17 -05:00
"session" , "restore" , "2026-05-03" ,
2026-05-19 21:39:13 -05:00
"--config" , pipelinePath ,
2026-05-22 23:01:27 -05:00
"--campaign-file" , campaignPath ,
2026-05-19 21:39:13 -05:00
"--session" , sessionPath ,
"--dry-run" ,
"--force" ,
"--include-audio" ,
} ,
& stdout ,
& stderr ,
)
2026-05-19 21:58:38 -05:00
if code != 0 {
t . Fatalf ( "exit code = %d, want 0 for --dry-run restore planning; stderr=%q" , code , stderr . String ( ) )
2026-05-19 21:39:13 -05:00
}
2026-05-19 21:58:38 -05:00
if stderr . Len ( ) != 0 {
t . Fatalf ( "stderr = %q, want empty" , stderr . String ( ) )
2026-05-19 21:49:19 -05:00
}
2026-05-19 21:58:38 -05:00
outText := stdout . String ( )
2026-05-19 22:15:40 -05:00
if ! strings . Contains ( outText , "Restore plan for sample-campaign/2026-05-03" ) {
2026-05-19 21:58:38 -05:00
t . Fatalf ( "stdout = %q, want restore plan summary" , outText )
2026-05-19 21:39:13 -05:00
}
2026-05-19 22:15:40 -05:00
if ! strings . Contains ( outText , "Would download: 1" ) {
t . Fatalf ( "stdout = %q, want plan count output" , outText )
}
if ! strings . Contains ( outText , "Would download: manifest.json" ) {
2026-05-19 21:58:38 -05:00
t . Fatalf ( "stdout = %q, want action output" , outText )
2026-05-19 21:39:13 -05:00
}
manifestPath := artifacts . SessionManifestPathForCampaign ( workspaceRoot , "sample-campaign" , "2026-05-03" )
if _ , err := os . Stat ( manifestPath ) ; ! os . IsNotExist ( err ) {
2026-05-19 21:58:38 -05:00
t . Fatalf ( "manifest should not be created during phase-4 restore planning; stat err=%v" , err )
2026-05-19 21:39:13 -05:00
}
2026-05-19 22:15:40 -05:00
reportPath := filepath . Join ( workspaceRoot , "work" , "sample-campaign" , "2026-05-03" , "reports" , "restore-latest.json" )
if _ , err := os . Stat ( reportPath ) ; ! os . IsNotExist ( err ) {
t . Fatalf ( "restore report should not be written during dry-run; stat err=%v" , err )
}
2026-05-19 21:39:13 -05:00
}
func TestExecuteRestoreRejectsUnexpectedPositionalArguments ( t * testing . T ) {
workspaceRoot := t . TempDir ( )
2026-05-20 20:41:28 -05:00
pipelinePath , campaignPath , sessionPath := writeValidConfigFiles ( t , workspaceRoot )
2026-05-19 21:39:13 -05:00
var stdout bytes . Buffer
var stderr bytes . Buffer
2026-05-22 23:01:27 -05:00
code := Execute ( [ ] string { "session" , "restore" , "2026-05-03" , "--config" , pipelinePath , "--campaign-file" , campaignPath , "--session" , sessionPath , "extra" } , & stdout , & stderr )
2026-05-19 21:39:13 -05:00
if code == 0 {
t . Fatal ( "exit code = 0, want non-zero" )
}
if ! strings . Contains ( stderr . String ( ) , "restore: unexpected positional arguments" ) {
t . Fatalf ( "stderr = %q, want positional-args failure" , stderr . String ( ) )
}
}
func TestExecuteRestoreFailsWhenStorageBackendNotConfigured ( t * testing . T ) {
2026-05-19 21:49:19 -05:00
origStoreFn := newObjectStoreFromConfigFn
origDiscoverFn := discoverRemoteCurrentStateFn
t . Cleanup ( func ( ) {
newObjectStoreFromConfigFn = origStoreFn
discoverRemoteCurrentStateFn = origDiscoverFn
} )
2026-05-19 21:39:13 -05:00
workspaceRoot := t . TempDir ( )
2026-05-20 20:41:28 -05:00
pipelinePath , campaignPath , sessionPath := writeRestoreConfigWithoutStorage ( t , workspaceRoot )
2026-05-19 21:39:13 -05:00
var stdout bytes . Buffer
var stderr bytes . Buffer
2026-05-22 23:01:27 -05:00
code := Execute ( [ ] string { "session" , "restore" , "2026-05-03" , "--config" , pipelinePath , "--campaign-file" , campaignPath , "--session" , sessionPath } , & stdout , & stderr )
2026-05-19 21:39:13 -05:00
if code == 0 {
t . Fatal ( "exit code = 0, want non-zero" )
}
if ! strings . Contains ( stderr . String ( ) , "no remote object store backend is configured" ) {
t . Fatalf ( "stderr = %q, want storage backend preflight failure" , stderr . String ( ) )
}
}
2026-05-19 21:49:19 -05:00
func TestExecuteRestoreDiscoveryErrorSurfaced ( t * testing . T ) {
origStoreFn := newObjectStoreFromConfigFn
origDiscoverFn := discoverRemoteCurrentStateFn
t . Cleanup ( func ( ) {
newObjectStoreFromConfigFn = origStoreFn
discoverRemoteCurrentStateFn = origDiscoverFn
} )
newObjectStoreFromConfigFn = func ( context . Context , * config . Config ) ( storage . ObjectStore , error ) {
return & storage . FakeBackend { } , nil
}
discoverRemoteCurrentStateFn = func ( context . Context , * config . Config , storage . ObjectStore ) ( * RemoteCurrentState , error ) {
2026-09-02 14:18:51 +00:00
return nil , fmt . Errorf ( "remote current commit pointer missing: %q" , "dnd/campaigns/sample-campaign/sessions/2026-05-03/current/commit-pointer.json" )
2026-05-19 21:49:19 -05:00
}
workspaceRoot := t . TempDir ( )
2026-05-20 20:41:28 -05:00
pipelinePath , campaignPath , sessionPath := writeValidConfigFiles ( t , workspaceRoot )
2026-05-19 21:49:19 -05:00
var stdout bytes . Buffer
var stderr bytes . Buffer
2026-05-22 23:01:27 -05:00
code := Execute ( [ ] string { "session" , "restore" , "2026-05-03" , "--config" , pipelinePath , "--campaign-file" , campaignPath , "--session" , sessionPath } , & stdout , & stderr )
2026-05-19 21:49:19 -05:00
if code == 0 {
t . Fatal ( "exit code = 0, want non-zero" )
}
2026-09-02 14:18:51 +00:00
if ! strings . Contains ( stderr . String ( ) , "remote current commit pointer missing" ) {
2026-05-19 21:49:19 -05:00
t . Fatalf ( "stderr = %q, want discovery error context" , stderr . String ( ) )
}
}
2026-05-19 22:51:26 -05:00
func TestExecuteRestoreLoadsSecretsBeforeObjectStoreInit ( t * testing . T ) {
origStoreFn := newObjectStoreFromConfigFn
origDiscoverFn := discoverRemoteCurrentStateFn
origPlanFn := buildRestorePlanFn
origExecuteFn := executeRestorePlanFn
t . Cleanup ( func ( ) {
newObjectStoreFromConfigFn = origStoreFn
discoverRemoteCurrentStateFn = origDiscoverFn
buildRestorePlanFn = origPlanFn
executeRestorePlanFn = origExecuteFn
} )
const accessKeyEnv = "OBJECT_STORAGE_KEY_ID"
const secretKeyEnv = "OBJECT_STORAGE_KEY"
restoreEnv := func ( name string ) {
value , exists := os . LookupEnv ( name )
_ = os . Unsetenv ( name )
t . Cleanup ( func ( ) {
if exists {
_ = os . Setenv ( name , value )
return
}
_ = os . Unsetenv ( name )
} )
}
restoreEnv ( accessKeyEnv )
restoreEnv ( secretKeyEnv )
workspaceRoot := t . TempDir ( )
2026-05-20 20:41:28 -05:00
pipelinePath , campaignPath , sessionPath := writeValidConfigFiles ( t , workspaceRoot )
2026-05-19 22:51:26 -05:00
secretsDir := filepath . Join ( t . TempDir ( ) , "secrets" )
2026-08-10 18:24:29 +00:00
mustWriteSecretFile ( t , filepath . Join ( secretsDir , accessKeyEnv ) , "test-access-key-id\n" )
mustWriteSecretFile ( t , filepath . Join ( secretsDir , secretKeyEnv ) , "test-secret-key\n" )
2026-05-19 22:51:26 -05:00
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 ( )
if _ , err := f . WriteString ( "\nsecrets:\n env_dir: " + secretsDir + "\n" ) ; err != nil {
t . Fatalf ( "append secrets config: %v" , err )
}
storeInitCalled := false
newObjectStoreFromConfigFn = func ( context . Context , * config . Config ) ( storage . ObjectStore , error ) {
storeInitCalled = true
gotID , okID := os . LookupEnv ( accessKeyEnv )
if ! okID || gotID != "test-access-key-id" {
return nil , fmt . Errorf ( "missing or unexpected %s: %q (set=%t)" , accessKeyEnv , gotID , okID )
}
gotSecret , okSecret := os . LookupEnv ( secretKeyEnv )
if ! okSecret || gotSecret != "test-secret-key" {
return nil , fmt . Errorf ( "missing or unexpected %s: %q (set=%t)" , secretKeyEnv , gotSecret , okSecret )
}
return & storage . FakeBackend { } , nil
}
discoverRemoteCurrentStateFn = func ( context . Context , * config . Config , storage . ObjectStore ) ( * RemoteCurrentState , error ) {
return & RemoteCurrentState {
SessionID : "2026-05-03" ,
Campaign : "sample-campaign" ,
RunID : "20260519T010203Z-a1b2c3d4" ,
} , nil
}
buildRestorePlanFn = func ( context . Context , * config . Config , * RemoteCurrentState , storage . ObjectStore , RestorePlanOptions ) ( * RestorePlan , error ) {
return & RestorePlan {
Actions : [ ] RestoreAction {
{
Kind : RestoreActionDownload ,
LocalRelativePath : "manifest.json" ,
2026-09-02 14:18:51 +00:00
RemoteKey : "dnd/campaigns/sample-campaign/sessions/2026-05-03/runs/20260519T010203Z-a1b2c3d4/session-manifest.json" ,
2026-05-19 22:51:26 -05:00
Reason : "local file missing" ,
} ,
} ,
DownloadCount : 1 ,
} , nil
}
var stdout bytes . Buffer
var stderr bytes . Buffer
code := Execute (
[ ] string {
2026-05-22 22:09:17 -05:00
"session" , "restore" , "2026-05-03" ,
2026-05-19 22:51:26 -05:00
"--config" , pipelinePath ,
2026-05-22 23:01:27 -05:00
"--campaign-file" , campaignPath ,
2026-05-19 22:51:26 -05:00
"--session" , sessionPath ,
"--dry-run" ,
} ,
& stdout ,
& stderr ,
)
if code != 0 {
t . Fatalf ( "exit code = %d, want 0; stderr=%q" , code , stderr . String ( ) )
}
if ! storeInitCalled {
t . Fatal ( "expected object store initialization to be called" )
}
}
2026-05-19 21:58:38 -05:00
func TestExecuteRestoreNonDryRunConflictFailsBeforeNYI ( t * testing . T ) {
origStoreFn := newObjectStoreFromConfigFn
origDiscoverFn := discoverRemoteCurrentStateFn
origPlanFn := buildRestorePlanFn
2026-05-19 22:06:48 -05:00
origExecuteFn := executeRestorePlanFn
2026-05-19 21:58:38 -05:00
t . Cleanup ( func ( ) {
newObjectStoreFromConfigFn = origStoreFn
discoverRemoteCurrentStateFn = origDiscoverFn
buildRestorePlanFn = origPlanFn
2026-05-19 22:06:48 -05:00
executeRestorePlanFn = origExecuteFn
2026-05-19 21:58:38 -05:00
} )
newObjectStoreFromConfigFn = func ( context . Context , * config . Config ) ( storage . ObjectStore , error ) {
return & storage . FakeBackend { } , nil
}
discoverRemoteCurrentStateFn = func ( context . Context , * config . Config , storage . ObjectStore ) ( * RemoteCurrentState , error ) {
return & RemoteCurrentState {
SessionID : "2026-05-03" ,
Campaign : "sample-campaign" ,
RunID : "20260519T010203Z-a1b2c3d4" ,
} , nil
}
buildRestorePlanFn = func ( context . Context , * config . Config , * RemoteCurrentState , storage . ObjectStore , RestorePlanOptions ) ( * RestorePlan , error ) {
return & RestorePlan {
Actions : [ ] RestoreAction {
{ Kind : RestoreActionConflict , LocalRelativePath : "transcripts/full.json" , RemoteKey : "k" , Reason : "local file differs" } ,
} ,
ConflictCount : 1 ,
} , nil
}
workspaceRoot := t . TempDir ( )
2026-05-20 20:41:28 -05:00
pipelinePath , campaignPath , sessionPath := writeValidConfigFiles ( t , workspaceRoot )
2026-05-19 21:58:38 -05:00
var stdout bytes . Buffer
var stderr bytes . Buffer
2026-05-22 23:01:27 -05:00
code := Execute ( [ ] string { "session" , "restore" , "2026-05-03" , "--config" , pipelinePath , "--campaign-file" , campaignPath , "--session" , sessionPath } , & stdout , & stderr )
2026-05-19 21:58:38 -05:00
if code == 0 {
t . Fatal ( "exit code = 0, want non-zero" )
}
2026-05-19 22:15:40 -05:00
if stdout . Len ( ) != 0 {
t . Fatalf ( "stdout = %q, want empty on conflict failure" , stdout . String ( ) )
2026-05-19 21:58:38 -05:00
}
2026-08-10 20:42:43 +00:00
if ! strings . Contains ( stderr . String ( ) , "restore conflict: 1 conflicting path(s)" ) {
2026-05-19 21:58:38 -05:00
t . Fatalf ( "stderr = %q, want conflict failure" , stderr . String ( ) )
}
if strings . Contains ( stderr . String ( ) , "phase 4: restore execution" ) {
t . Fatalf ( "stderr = %q, should fail before phase-4 NYI boundary" , stderr . String ( ) )
}
}
2026-08-10 20:42:43 +00:00
func TestExecuteRestoreForceStillBlocksUnresolvedConflict ( t * testing . T ) {
origStoreFn := newObjectStoreFromConfigFn
origDiscoverFn := discoverRemoteCurrentStateFn
origPlanFn := buildRestorePlanFn
origExecuteFn := executeRestorePlanFn
t . Cleanup ( func ( ) {
newObjectStoreFromConfigFn = origStoreFn
discoverRemoteCurrentStateFn = origDiscoverFn
buildRestorePlanFn = origPlanFn
executeRestorePlanFn = origExecuteFn
} )
newObjectStoreFromConfigFn = func ( context . Context , * config . Config ) ( storage . ObjectStore , error ) {
return & storage . FakeBackend { } , nil
}
discoverRemoteCurrentStateFn = func ( context . Context , * config . Config , storage . ObjectStore ) ( * RemoteCurrentState , error ) {
return & RemoteCurrentState { SessionID : "2026-05-03" , Campaign : "sample-campaign" , RunID : "20260519T010203Z-a1b2c3d4" } , nil
}
buildRestorePlanFn = func ( context . Context , * config . Config , * RemoteCurrentState , storage . ObjectStore , RestorePlanOptions ) ( * RestorePlan , error ) {
return & RestorePlan { Actions : [ ] RestoreAction { { Kind : RestoreActionConflict , LocalRelativePath : "artifacts/session_recap.md" , Reason : "local path is a directory" } } , ConflictCount : 1 } , nil
}
executed := false
executeRestorePlanFn = func ( context . Context , * config . Config , * RemoteCurrentState , * RestorePlan , * RestoreReport , storage . ObjectStore ) ( * RestoreExecutionResult , error ) {
executed = true
return & RestoreExecutionResult { } , nil
}
workspaceRoot := t . TempDir ( )
pipelinePath , campaignPath , sessionPath := writeValidConfigFiles ( t , workspaceRoot )
var stdout bytes . Buffer
var stderr bytes . Buffer
code := Execute ( [ ] string { "session" , "restore" , "2026-05-03" , "--config" , pipelinePath , "--campaign-file" , campaignPath , "--session" , sessionPath , "--force" } , & stdout , & stderr )
if code == 0 {
t . Fatal ( "exit code = 0, want conflict failure" )
}
if executed {
t . Fatal ( "restore execution ran despite unresolved conflict" )
}
}
2026-05-19 22:06:48 -05:00
func TestExecuteRestoreNonDryRunForceExecutesPlan ( t * testing . T ) {
2026-05-19 21:58:38 -05:00
origStoreFn := newObjectStoreFromConfigFn
origDiscoverFn := discoverRemoteCurrentStateFn
origPlanFn := buildRestorePlanFn
2026-05-19 22:06:48 -05:00
origExecuteFn := executeRestorePlanFn
2026-05-19 21:58:38 -05:00
t . Cleanup ( func ( ) {
newObjectStoreFromConfigFn = origStoreFn
discoverRemoteCurrentStateFn = origDiscoverFn
buildRestorePlanFn = origPlanFn
2026-05-19 22:06:48 -05:00
executeRestorePlanFn = origExecuteFn
2026-05-19 21:58:38 -05:00
} )
newObjectStoreFromConfigFn = func ( context . Context , * config . Config ) ( storage . ObjectStore , error ) {
return & storage . FakeBackend { } , nil
}
discoverRemoteCurrentStateFn = func ( context . Context , * config . Config , storage . ObjectStore ) ( * RemoteCurrentState , error ) {
return & RemoteCurrentState {
SessionID : "2026-05-03" ,
Campaign : "sample-campaign" ,
RunID : "20260519T010203Z-a1b2c3d4" ,
} , nil
}
buildRestorePlanFn = func ( context . Context , * config . Config , * RemoteCurrentState , storage . ObjectStore , RestorePlanOptions ) ( * RestorePlan , error ) {
return & RestorePlan {
Actions : [ ] RestoreAction {
{ Kind : RestoreActionDownload , LocalRelativePath : "transcripts/full.json" , RemoteKey : "k" , Reason : "local file differs; overwrite with --force" } ,
} ,
DownloadCount : 1 ,
} , nil
}
2026-05-19 22:15:40 -05:00
executeRestorePlanFn = func ( context . Context , * config . Config , * RemoteCurrentState , * RestorePlan , * RestoreReport , storage . ObjectStore ) ( * RestoreExecutionResult , error ) {
2026-05-19 22:06:48 -05:00
return & RestoreExecutionResult { DownloadedCount : 1 } , nil
}
2026-05-19 21:58:38 -05:00
workspaceRoot := t . TempDir ( )
2026-05-20 20:41:28 -05:00
pipelinePath , campaignPath , sessionPath := writeValidConfigFiles ( t , workspaceRoot )
2026-05-19 21:58:38 -05:00
var stdout bytes . Buffer
var stderr bytes . Buffer
2026-05-22 23:01:27 -05:00
code := Execute ( [ ] string { "session" , "restore" , "2026-05-03" , "--config" , pipelinePath , "--campaign-file" , campaignPath , "--session" , sessionPath , "--force" } , & stdout , & stderr )
2026-05-19 22:06:48 -05:00
if code != 0 {
t . Fatalf ( "exit code = %d, want 0; stderr=%q" , code , stderr . String ( ) )
2026-05-19 21:58:38 -05:00
}
2026-05-23 10:05:14 -05:00
if ! strings . Contains ( stdout . String ( ) , "Restored session state for sample-campaign/2026-05-03" ) {
2026-05-19 22:06:48 -05:00
t . Fatalf ( "stdout = %q, want completion summary" , stdout . String ( ) )
}
if stderr . Len ( ) != 0 {
t . Fatalf ( "stderr = %q, want empty" , stderr . String ( ) )
2026-05-19 21:58:38 -05:00
}
}
2026-05-20 20:41:28 -05:00
func writeRestoreConfigWithoutStorage ( t * testing . T , workspaceRoot string ) ( string , string , string ) {
2026-05-19 21:39:13 -05:00
t . Helper ( )
dir := t . TempDir ( )
pipelinePath := filepath . Join ( dir , "pipeline.yml" )
2026-05-20 20:41:28 -05:00
campaignPath := writeAppTestCampaignConfig ( t , dir )
2026-05-19 21:39:13 -05:00
sessionPath := filepath . Join ( dir , "session.yml" )
pipelineYAML := ` workspace :
root : ` + workspaceRoot + `
whisperx :
transcribe_url : https : //example.com/transcribe
`
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 ) , 0 o644 ) ; err != nil {
t . Fatalf ( "write pipeline config: %v" , err )
}
if err := os . WriteFile ( sessionPath , [ ] byte ( sessionYAML ) , 0 o644 ) ; err != nil {
t . Fatalf ( "write session config: %v" , err )
}
mustWriteTestFile ( t , filepath . Join ( dir , "speakers.yml" ) , "alice: alice.flac\n" )
mustWriteTestFile ( t , filepath . Join ( dir , "autocorrect.yml" ) , "[]\n" )
mustWriteTestFile ( t , filepath . Join ( dir , "glossary.yml" ) , "[]\n" )
2026-09-02 11:57:51 +00:00
mustWriteTestFile ( t , filepath . Join ( dir , "party.yml" ) , "schema_version: narratio.party.v1\ncharacters:\n arannis:\n player: {name: Eric}\n character:\n name: Arannis\n classes: [{name: wizard}]\n" )
2026-05-19 21:39:13 -05:00
mustWriteTestFile ( t , filepath . Join ( dir , "audio" , "alice.flac" ) , "audio-bytes" )
2026-05-20 20:41:28 -05:00
return pipelinePath , campaignPath , sessionPath
2026-05-19 21:39:13 -05:00
}