- 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>
269 lines
5.4 KiB
Go
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)
|
|
}
|
|
}
|