KAOS/code/cmd/kaos-supervisor/main_test.go
djuka 38e1e1029c T06: CLI — komandni interfejs
- 5 komandi: run, status, next, verify, history
- Config proširen sa KAOS_TASKS_DIR
- Tabelarni status sa emoji ikonama
- run: scan → find → move → run → verify → report → review
- 8 CLI testova, 59 ukupno — svi prolaze
- T05 premešten u done/

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 11:56:06 +00:00

269 lines
5.4 KiB
Go

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)
}
}