package supervisor import ( "context" "os/exec" "strings" "testing" "time" ) func mockCommandBuilder(name string, args ...string) CommandBuilder { return func(ctx context.Context, task Task, projectPath string) *exec.Cmd { cmd := exec.CommandContext(ctx, name, args...) cmd.Dir = projectPath return cmd } } func TestRunTask_MockEcho(t *testing.T) { task := Task{ID: "T99", Title: "Test task", Description: "Do stuff"} result := RunTask(task, "/tmp", 10*time.Second, mockCommandBuilder("echo", "done")) if result.TaskID != "T99" { t.Errorf("expected TaskID T99, got %s", result.TaskID) } if result.ExitCode != 0 { t.Errorf("expected exit code 0, got %d", result.ExitCode) } if !strings.Contains(result.Output, "done") { t.Errorf("expected output to contain 'done', got %q", result.Output) } if result.Duration <= 0 { t.Error("expected duration > 0") } if result.StartedAt.IsZero() { t.Error("expected StartedAt to be set") } if result.FinishedAt.IsZero() { t.Error("expected FinishedAt to be set") } } func TestRunTask_Timeout(t *testing.T) { task := Task{ID: "T99", Title: "Slow task", Description: "Takes forever"} result := RunTask(task, "/tmp", 1*time.Second, mockCommandBuilder("sleep", "999")) if result.ExitCode == 0 { t.Error("expected non-zero exit code for timed-out process") } if result.Duration < 500*time.Millisecond { t.Errorf("expected duration >= 500ms, got %s", result.Duration) } } func TestRunTask_OutputCapture(t *testing.T) { task := Task{ID: "T99", Title: "Output task", Description: "Writes output"} // Use sh -c to write to both stdout and stderr result := RunTask(task, "/tmp", 10*time.Second, mockCommandBuilder("sh", "-c", "echo stdout_data; echo stderr_data >&2")) if !strings.Contains(result.Output, "stdout_data") { t.Errorf("expected stdout to contain 'stdout_data', got %q", result.Output) } if !strings.Contains(result.Error, "stderr_data") { t.Errorf("expected stderr to contain 'stderr_data', got %q", result.Error) } } func TestRunTask_FailingCommand(t *testing.T) { task := Task{ID: "T99", Title: "Fail task", Description: "Will fail"} result := RunTask(task, "/tmp", 10*time.Second, mockCommandBuilder("sh", "-c", "exit 42")) if result.ExitCode != 42 { t.Errorf("expected exit code 42, got %d", result.ExitCode) } } func TestRunTask_EmptyTask(t *testing.T) { task := Task{} result := RunTask(task, "/tmp", 10*time.Second, nil) if result.ExitCode == 0 { t.Error("expected non-zero exit code for empty task") } if result.Error == "" { t.Error("expected error message for empty task") } } func TestBuildPrompt_ContainsFields(t *testing.T) { task := Task{ ID: "T05", Title: "Deploy aplikacije", Description: "Deployuj na server sa testovima.", } prompt := buildPrompt(task) if !strings.Contains(prompt, "T05") { t.Error("expected prompt to contain task ID") } if !strings.Contains(prompt, "Deploy aplikacije") { t.Error("expected prompt to contain task title") } if !strings.Contains(prompt, "Deployuj na server sa testovima.") { t.Error("expected prompt to contain description") } if !strings.Contains(prompt, "CLAUDE.md") { t.Error("expected prompt to mention CLAUDE.md") } } func TestBuildPrompt_CommitFormat(t *testing.T) { task := Task{ID: "T12", Title: "Nešto", Description: "Opis."} prompt := buildPrompt(task) if !strings.Contains(prompt, `"T12: opis"`) { t.Errorf("expected commit format with T12, got:\n%s", prompt) } }