package scriptorium import ( "context" "reflect" "strings" "testing" "time" ) func TestRenderConstructsCommand(t *testing.T) { commands := &fakeCommands{result: CommandResult{Stdout: []byte(`{"ok":true}`)}} runner := Runner{ Binary: "/usr/local/bin/scriptorium", ConfigPath: "/etc/scriptorium.yml", Profile: "weather", Timeout: time.Minute, Commands: commands, } result, err := runner.Render(context.Background(), RenderRequest{ PromptID: "weather.daily_report", DataPackagePath: "/tmp/data_package.yaml", }) if err != nil { t.Fatalf("Render() error = %v", err) } wantArgs := []string{ "render", "--config", "/etc/scriptorium.yml", "--profile", "weather", "--prompt", "weather.daily_report", "--input", "data_package=/tmp/data_package.yaml", "--format", "json", } if commands.name != "/usr/local/bin/scriptorium" { t.Fatalf("command name = %q, want custom binary", commands.name) } if !reflect.DeepEqual(commands.args, wantArgs) { t.Fatalf("args = %#v, want %#v", commands.args, wantArgs) } if !reflect.DeepEqual(result.Command, append([]string{"/usr/local/bin/scriptorium"}, wantArgs...)) { t.Fatalf("result command = %#v, want full argv", result.Command) } } func TestRenderReturnsResultForNonzeroExit(t *testing.T) { runner := Runner{ Commands: &fakeCommands{ result: CommandResult{ Stderr: []byte("missing input"), ExitCode: 1, }, }, } result, err := runner.Render(context.Background(), RenderRequest{ PromptID: "weather.daily_report", DataPackagePath: "/tmp/data_package.yaml", }) if err == nil { t.Fatal("Render() error = nil, want nonzero exit error") } if result == nil { t.Fatal("Render() result = nil, want captured result") } if result.ExitCode != 1 { t.Fatalf("ExitCode = %d, want 1", result.ExitCode) } if !strings.Contains(err.Error(), "missing input") { t.Fatalf("error = %q, want stderr context", err.Error()) } } func TestRunConstructsCommand(t *testing.T) { commands := &fakeCommands{result: CommandResult{Stderr: []byte("wrote report")}} runner := Runner{ Binary: "/usr/local/bin/scriptorium", ConfigPath: "/etc/scriptorium.yml", Profile: "weather", Timeout: 45 * time.Second, Commands: commands, } result, err := runner.Run(context.Background(), RunRequest{ PromptID: "weather.daily_report", DataPackagePath: "/tmp/data_package.yaml", OutputPath: "/tmp/daily.md", }) if err != nil { t.Fatalf("Run() error = %v", err) } wantArgs := []string{ "run", "--config", "/etc/scriptorium.yml", "--profile", "weather", "--prompt", "weather.daily_report", "--input", "data_package=/tmp/data_package.yaml", "--out", "/tmp/daily.md", } if commands.name != "/usr/local/bin/scriptorium" { t.Fatalf("command name = %q, want custom binary", commands.name) } if !reflect.DeepEqual(commands.args, wantArgs) { t.Fatalf("args = %#v, want %#v", commands.args, wantArgs) } if commands.timeout != 45*time.Second { t.Fatalf("timeout = %s, want 45s", commands.timeout) } if !reflect.DeepEqual(result.Command, append([]string{"/usr/local/bin/scriptorium"}, wantArgs...)) { t.Fatalf("result command = %#v, want full argv", result.Command) } if result.OutputPath != "/tmp/daily.md" { t.Fatalf("OutputPath = %q, want /tmp/daily.md", result.OutputPath) } } func TestRunReturnsResultForValidationExit(t *testing.T) { runner := Runner{ Commands: &fakeCommands{ result: CommandResult{ Stdout: []byte("# Daily Report\n"), Stderr: []byte("validation failed"), ExitCode: 2, }, }, } result, err := runner.Run(context.Background(), RunRequest{ PromptID: "weather.daily_report", DataPackagePath: "/tmp/data_package.yaml", OutputPath: "/tmp/daily.md", }) if err == nil { t.Fatal("Run() error = nil, want nonzero exit error") } if result == nil { t.Fatal("Run() result = nil, want captured result") } if result.ExitCode != 2 { t.Fatalf("ExitCode = %d, want 2", result.ExitCode) } if !strings.Contains(err.Error(), "validation failed") { t.Fatalf("error = %q, want stderr context", err.Error()) } } type fakeCommands struct { name string args []string timeout time.Duration result CommandResult err error } func (f *fakeCommands) Run(_ context.Context, name string, args []string, timeout time.Duration) (CommandResult, error) { f.name = name f.args = append([]string{}, args...) f.timeout = timeout return f.result, f.err }