package main import ( "os" "path/filepath" "strings" "testing" "github.com/dal/kaos/internal/config" "github.com/dal/kaos/internal/supervisor" ) func setupTestTasks(t *testing.T) (string, *config.Config) { t.Helper() dir := t.TempDir() tasksDir := filepath.Join(dir, "TASKS") for _, folder := range []string{"backlog", "ready", "active", "review", "done", "reports"} { os.MkdirAll(filepath.Join(tasksDir, folder), 0755) } // Create test tasks t1 := `# T01: Prvi task **Agent:** coder **Model:** Sonnet **Zavisi od:** — --- ## Opis Opis prvog taska. --- ` t2 := `# T02: Drugi task **Agent:** coder **Model:** Sonnet **Zavisi od:** T01 --- ## Opis Opis drugog taska. --- ` os.WriteFile(filepath.Join(tasksDir, "done", "T01.md"), []byte(t1), 0644) os.WriteFile(filepath.Join(tasksDir, "ready", "T02.md"), []byte(t2), 0644) cfg := &config.Config{ TasksDir: tasksDir, ProjectPath: dir, } return dir, cfg } func TestCmdStatus_ShowsTasks(t *testing.T) { _, cfg := setupTestTasks(t) // Capture output by redirecting stdout old := os.Stdout r, w, _ := os.Pipe() os.Stdout = w exitCode := cmdStatus(cfg) w.Close() os.Stdout = old var buf [4096]byte n, _ := r.Read(buf[:]) output := string(buf[:n]) if exitCode != 0 { t.Errorf("expected exit code 0, got %d", exitCode) } if !strings.Contains(output, "T01") { t.Errorf("expected T01 in output, got:\n%s", output) } if !strings.Contains(output, "T02") { t.Errorf("expected T02 in output, got:\n%s", output) } if !strings.Contains(output, "done") { t.Errorf("expected 'done' status in output, got:\n%s", output) } if !strings.Contains(output, "ready") { t.Errorf("expected 'ready' status in output, got:\n%s", output) } } func TestCmdNext_FindsReadyTask(t *testing.T) { _, cfg := setupTestTasks(t) old := os.Stdout r, w, _ := os.Pipe() os.Stdout = w exitCode := cmdNext(cfg) w.Close() os.Stdout = old var buf [4096]byte n, _ := r.Read(buf[:]) output := string(buf[:n]) if exitCode != 0 { t.Errorf("expected exit code 0, got %d", exitCode) } if !strings.Contains(output, "T02") { t.Errorf("expected T02 as next task, got:\n%s", output) } } func TestCmdVerify_OnTestProject(t *testing.T) { dir := t.TempDir() // Create a minimal passing Go project os.WriteFile(filepath.Join(dir, "go.mod"), []byte("module testproj\n\ngo 1.22\n"), 0644) os.WriteFile(filepath.Join(dir, "main.go"), []byte("package main\n\nfunc main() {}\n"), 0644) cfg := &config.Config{ProjectPath: dir} // Set os.Args so cmdVerify doesn't pick up test flags oldArgs := os.Args os.Args = []string{"kaos-supervisor", "verify"} defer func() { os.Args = oldArgs }() old := os.Stdout r, w, _ := os.Pipe() os.Stdout = w exitCode := cmdVerify(cfg) w.Close() os.Stdout = old var buf [4096]byte n, _ := r.Read(buf[:]) output := string(buf[:n]) if exitCode != 0 { t.Errorf("expected exit code 0, got %d\noutput: %s", exitCode, output) } if !strings.Contains(output, "All checks passed") { t.Errorf("expected 'All checks passed', got:\n%s", output) } } func TestCmdHistory_ShowsReports(t *testing.T) { _, cfg := setupTestTasks(t) // Create a fake report reportsDir := filepath.Join(cfg.TasksDir, "reports") os.WriteFile(filepath.Join(reportsDir, "T01-report.md"), []byte("# T01 report\n"), 0644) old := os.Stdout r, w, _ := os.Pipe() os.Stdout = w exitCode := cmdHistory(cfg) w.Close() os.Stdout = old var buf [4096]byte n, _ := r.Read(buf[:]) output := string(buf[:n]) if exitCode != 0 { t.Errorf("expected exit code 0, got %d", exitCode) } if !strings.Contains(output, "T01") { t.Errorf("expected T01 in history, got:\n%s", output) } } func TestUnknownCommand(t *testing.T) { // Test printHelp doesn't panic old := os.Stdout _, w, _ := os.Pipe() os.Stdout = w printHelp() w.Close() os.Stdout = old } func TestCmdRun_NoReadyTasks(t *testing.T) { dir := t.TempDir() tasksDir := filepath.Join(dir, "TASKS") for _, folder := range []string{"backlog", "ready", "active", "review", "done", "reports"} { os.MkdirAll(filepath.Join(tasksDir, folder), 0755) } cfg := &config.Config{ TasksDir: tasksDir, ProjectPath: dir, } old := os.Stdout r, w, _ := os.Pipe() os.Stdout = w // Save and restore os.Args oldArgs := os.Args os.Args = []string{"kaos-supervisor", "run"} defer func() { os.Args = oldArgs }() exitCode := cmdRun(cfg) w.Close() os.Stdout = old var buf [4096]byte n, _ := r.Read(buf[:]) output := string(buf[:n]) if exitCode != 0 { t.Errorf("expected exit code 0, got %d", exitCode) } if !strings.Contains(output, "Nema taskova") { t.Errorf("expected 'Nema taskova', got:\n%s", output) } } func TestStatusIcon(t *testing.T) { tests := []struct { status string want string }{ {"done", "✅"}, {"active", "🔄"}, {"ready", "📋"}, {"review", "👀"}, {"backlog", "📦"}, {"unknown", "❓"}, } for _, tt := range tests { got := statusIcon(tt.status) if got != tt.want { t.Errorf("statusIcon(%q) = %q, want %q", tt.status, got, tt.want) } } } // Verify that ScanTasks and NextTask integration works func TestNextTask_Integration(t *testing.T) { _, cfg := setupTestTasks(t) tasks, err := supervisor.ScanTasks(cfg.TasksDir) if err != nil { t.Fatalf("ScanTasks error: %v", err) } next := supervisor.NextTask(tasks) if next == nil { t.Fatal("expected a next task") } if next.ID != "T02" { t.Errorf("expected T02, got %s", next.ID) } }