2026-07-18 12:42:10 +00:00
package cli
import (
"bytes"
"os"
"path/filepath"
"strings"
"testing"
2026-07-18 14:21:26 +00:00
"time"
2026-07-18 12:42:10 +00:00
)
2026-07-18 14:21:26 +00:00
const stateSurfaceRunID = "run-1000000000-55555555555555555555555555555555"
func stateSurfaceRunIDGenerator ( time . Time ) ( string , error ) { return stateSurfaceRunID , nil }
2026-07-18 12:42:10 +00:00
func TestRunRejectsDebugDirectoryWithoutDebug ( t * testing . T ) {
var stdout , stderr bytes . Buffer
code := RunWithOptions ( [ ] string { "run" , "example" , "--input" , "source.json" , "--debug-dir" , t . TempDir ( ) } , & stdout , & stderr , Options { } )
if code != 2 || ! strings . Contains ( stderr . String ( ) , "--debug-dir requires --debug" ) {
t . Fatalf ( "code=%d stderr=%q" , code , stderr . String ( ) )
}
}
func TestRunDebugAllocatesBeforePipelineResolution ( t * testing . T ) {
root := t . TempDir ( )
configPath := writeV3Config ( t , "" )
var stdout , stderr bytes . Buffer
2026-07-18 14:21:26 +00:00
code := RunWithOptions ( [ ] string { "run" , "missing" , "--config" , configPath , "--input" , "source.json" , "--debug" , "--debug-dir" , root , "--chunk_cache" , "bypass" } , & stdout , & stderr , Options { LookupEnv : emptyLookup , RunIDGenerator : stateSurfaceRunIDGenerator } )
2026-07-18 12:42:10 +00:00
if code != 1 {
t . Fatalf ( "code=%d stderr=%q" , code , stderr . String ( ) )
}
entries , err := os . ReadDir ( root )
if err != nil || len ( entries ) != 1 {
t . Fatalf ( "debug bundles: %v, %v" , entries , err )
}
2026-07-18 14:21:26 +00:00
if entries [ 0 ] . Name ( ) != stateSurfaceRunID {
t . Fatalf ( "debug bundle name = %q, want %q" , entries [ 0 ] . Name ( ) , stateSurfaceRunID )
}
bundle := filepath . Join ( root , stateSurfaceRunID )
2026-07-18 12:42:10 +00:00
for _ , name := range [ ] string { "summary" , "trace" } {
if info , err := os . Stat ( filepath . Join ( bundle , name ) ) ; err != nil || ! info . IsDir ( ) {
t . Fatalf ( "%s: %v" , name , err )
}
}
if ! strings . Contains ( stderr . String ( ) , "debug=" ) {
t . Fatalf ( "stderr does not include bundle path: %q" , stderr . String ( ) )
}
}
func TestRunWithoutDebugDoesNotAllocateDebugRoot ( t * testing . T ) {
root := filepath . Join ( t . TempDir ( ) , "not-created" )
configPath := writeV3Config ( t , "" )
var stdout , stderr bytes . Buffer
lookup := func ( name string ) ( string , bool ) {
if name == "NOTARIUS_DEBUG_DIR" {
return root , true
}
return "" , false
}
2026-07-18 14:21:26 +00:00
code := RunWithOptions ( [ ] string { "run" , "missing" , "--config" , configPath , "--input" , "source.json" , "--chunk_cache" , "bypass" } , & stdout , & stderr , Options { LookupEnv : lookup , RunIDGenerator : stateSurfaceRunIDGenerator } )
2026-07-18 12:42:10 +00:00
if code != 1 {
t . Fatalf ( "code=%d stderr=%q" , code , stderr . String ( ) )
}
if _ , err := os . Stat ( root ) ; ! os . IsNotExist ( err ) {
t . Fatalf ( "debug root exists or unexpected error: %v" , err )
}
}
func TestConfigValidateUsesVersion3AndRemovedFieldsFail ( t * testing . T ) {
configPath := writeV3Config ( t , "" )
var stdout , stderr bytes . Buffer
if code := RunWithOptions ( [ ] string { "config" , "validate" , "--config" , configPath } , & stdout , & stderr , Options { LookupEnv : emptyLookup } ) ; code != 0 {
t . Fatalf ( "code=%d stderr=%q" , code , stderr . String ( ) )
}
legacy := filepath . Join ( t . TempDir ( ) , "legacy.yml" )
if err := os . WriteFile ( legacy , [ ] byte ( "version: 3\nworkspace:\n directory: /tmp/old\n" ) , 0 o600 ) ; err != nil {
t . Fatal ( err )
}
stdout . Reset ( )
stderr . Reset ( )
if code := RunWithOptions ( [ ] string { "config" , "validate" , "--config" , legacy } , & stdout , & stderr , Options { LookupEnv : emptyLookup } ) ; code != 1 || ! strings . Contains ( stderr . String ( ) , "field workspace not found" ) {
t . Fatalf ( "code=%d stderr=%q" , code , stderr . String ( ) )
}
}
func writeV3Config ( t * testing . T , extra string ) string {
t . Helper ( )
path := filepath . Join ( t . TempDir ( ) , "config.yml" )
data := "version: 3\noutput:\n directory: ./out\ncache:\n chunk_plans:\n mode: bypass\n checkpoints: {}\ndebug:\n directory: ./debug\n" + extra + "pipelines: {}\n"
if err := os . WriteFile ( path , [ ] byte ( data ) , 0 o600 ) ; err != nil {
t . Fatal ( err )
}
return path
}
func emptyLookup ( string ) ( string , bool ) { return "" , false }