package exec_test import ( "context" "os" "path/filepath" "testing" "time" iexec "github.com/mathiasbq/supervisor/internal/exec" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) // fakeClaudePath writes a shell script that prints fixed output and returns its path. func fakeClaudePath(t *testing.T, output string, exitCode int) string { t.Helper() dir := t.TempDir() script := filepath.Join(dir, "claude") var content string if exitCode != 0 { content = "#!/bin/sh\necho 'error' >&2\nexit 1\n" } else { content = "#!/bin/sh\necho '" + output + "'\n" } require.NoError(t, os.WriteFile(script, []byte(content), 0755)) return script } func TestExecutorParsesValidResult(t *testing.T) { validJSON := `{"status":"pass","phase":"red","skill":"tdd","file_path":"/tmp/x_test.go","runner_output":"FAIL","verified":true,"model_used":"self","message":"ok"}` claude := fakeClaudePath(t, validJSON, 0) ex := iexec.New(iexec.Config{ ClaudeBinary: claude, SystemPrompt: "you are a supervisor", Timeout: 5 * time.Second, }) result, err := ex.Run(context.Background(), iexec.Request{ SkillPrompt: "tdd rules", TaskPrompt: "run red phase", }) require.NoError(t, err) assert.Equal(t, "pass", result.Status) assert.True(t, result.Verified) } func TestExecutorReturnsErrorOnNonZeroExit(t *testing.T) { claude := fakeClaudePath(t, "", 1) ex := iexec.New(iexec.Config{ ClaudeBinary: claude, SystemPrompt: "you are a supervisor", Timeout: 5 * time.Second, }) _, err := ex.Run(context.Background(), iexec.Request{TaskPrompt: "fail"}) assert.Error(t, err) } func TestExecutorTimesOut(t *testing.T) { dir := t.TempDir() script := filepath.Join(dir, "claude") require.NoError(t, os.WriteFile(script, []byte("#!/bin/sh\nsleep 60\n"), 0755)) ex := iexec.New(iexec.Config{ ClaudeBinary: script, SystemPrompt: "you are a supervisor", Timeout: 100 * time.Millisecond, }) _, err := ex.Run(context.Background(), iexec.Request{TaskPrompt: "slow"}) assert.ErrorContains(t, err, "timeout") }