package server import ( "encoding/json" "net/http" "net/http/httptest" "os" "path/filepath" "testing" "github.com/dal/kaos/internal/supervisor" ) func TestTaskDetailHTML(t *testing.T) { srv := setupTestServer(t) req := httptest.NewRequest(http.MethodGet, "/task/T01", nil) w := httptest.NewRecorder() srv.Router.ServeHTTP(w, req) if w.Code != http.StatusOK { t.Fatalf("expected 200, got %d", w.Code) } body := w.Body.String() if !containsStr(body, "T01") { t.Error("expected T01 in detail HTML") } if !containsStr(body, "Prvi task") { t.Error("expected task title in detail HTML") } } func TestTaskDetailHTML_NotFound(t *testing.T) { srv := setupTestServer(t) req := httptest.NewRequest(http.MethodGet, "/task/T99", nil) w := httptest.NewRecorder() srv.Router.ServeHTTP(w, req) if w.Code != http.StatusNotFound { t.Fatalf("expected 404, got %d", w.Code) } } func TestReport_Exists(t *testing.T) { srv := setupTestServer(t) // Create a report reportsDir := filepath.Join(srv.Config.TasksDir, "reports") os.MkdirAll(reportsDir, 0755) os.WriteFile(filepath.Join(reportsDir, "T01-report.md"), []byte("# T01 Report\nSve ok."), 0644) req := httptest.NewRequest(http.MethodGet, "/report/T01", nil) w := httptest.NewRecorder() srv.Router.ServeHTTP(w, req) if w.Code != http.StatusOK { t.Fatalf("expected 200, got %d", w.Code) } if !containsStr(w.Body.String(), "T01 Report") { t.Error("expected report content") } } func TestReport_NotFound(t *testing.T) { srv := setupTestServer(t) req := httptest.NewRequest(http.MethodGet, "/report/T99", nil) w := httptest.NewRecorder() srv.Router.ServeHTTP(w, req) if w.Code != http.StatusNotFound { t.Fatalf("expected 404, got %d", w.Code) } } func TestRunTask_Ready(t *testing.T) { srv := setupTestServer(t) // Move T08 to ready first os.Rename( filepath.Join(srv.Config.TasksDir, "backlog", "T08.md"), filepath.Join(srv.Config.TasksDir, "ready", "T08.md"), ) req := httptest.NewRequest(http.MethodPost, "/task/T08/run", nil) w := httptest.NewRecorder() srv.Router.ServeHTTP(w, req) if w.Code != http.StatusOK { t.Fatalf("expected 200, got %d: %s", w.Code, w.Body.String()) } var resp map[string]interface{} json.Unmarshal(w.Body.Bytes(), &resp) if resp["status"] != "started" { t.Errorf("expected status started, got %v", resp["status"]) } if resp["task"] != "T08" { t.Errorf("expected task T08, got %v", resp["task"]) } // Verify task moved to active/ if _, err := os.Stat(filepath.Join(srv.Config.TasksDir, "active", "T08.md")); err != nil { t.Error("expected T08.md in active/") } } func TestRunTask_BacklogWithDeps(t *testing.T) { srv := setupTestServer(t) // T08 depends on T07, T01 is in done // T08 depends on T07 which is NOT in done → should fail req := httptest.NewRequest(http.MethodPost, "/task/T08/run", nil) w := httptest.NewRecorder() srv.Router.ServeHTTP(w, req) if w.Code != http.StatusBadRequest { t.Fatalf("expected 400 for unmet deps, got %d: %s", w.Code, w.Body.String()) } } func TestRunTask_AlreadyDone(t *testing.T) { srv := setupTestServer(t) // T01 is in done req := httptest.NewRequest(http.MethodPost, "/task/T01/run", nil) w := httptest.NewRecorder() srv.Router.ServeHTTP(w, req) if w.Code != http.StatusBadRequest { t.Fatalf("expected 400 for done task, got %d", w.Code) } } func TestRunTask_NotFound(t *testing.T) { srv := setupTestServer(t) req := httptest.NewRequest(http.MethodPost, "/task/T99/run", nil) w := httptest.NewRecorder() srv.Router.ServeHTTP(w, req) if w.Code != http.StatusNotFound { t.Fatalf("expected 404, got %d", w.Code) } } func TestRunTask_MovesToActive(t *testing.T) { srv := setupTestServer(t) // Move T08 to ready os.Rename( filepath.Join(srv.Config.TasksDir, "backlog", "T08.md"), filepath.Join(srv.Config.TasksDir, "ready", "T08.md"), ) req := httptest.NewRequest(http.MethodPost, "/task/T08/run", nil) w := httptest.NewRecorder() srv.Router.ServeHTTP(w, req) if w.Code != http.StatusOK { t.Fatalf("expected 200, got %d: %s", w.Code, w.Body.String()) } // Verify task moved to active/ if _, err := os.Stat(filepath.Join(srv.Config.TasksDir, "active", "T08.md")); err != nil { t.Error("expected T08.md in active/ after run") } } func TestResolveTaskAction_Blocked(t *testing.T) { doneSet := map[string]bool{"T01": true} task := supervisor.Task{ID: "T08", Status: "backlog", DependsOn: []string{"T07"}} if got := resolveTaskAction(task, doneSet); got != "blocked" { t.Errorf("expected blocked, got %s", got) } } func TestResolveTaskAction_Review(t *testing.T) { doneSet := map[string]bool{"T07": true} task := supervisor.Task{ID: "T08", Status: "backlog", DependsOn: []string{"T07"}} if got := resolveTaskAction(task, doneSet); got != "review" { t.Errorf("expected review, got %s", got) } } func TestResolveTaskAction_Run(t *testing.T) { task := supervisor.Task{ID: "T08", Status: "ready"} if got := resolveTaskAction(task, nil); got != "run" { t.Errorf("expected run, got %s", got) } } func TestResolveTaskAction_Running(t *testing.T) { task := supervisor.Task{ID: "T08", Status: "active"} if got := resolveTaskAction(task, nil); got != "running" { t.Errorf("expected running, got %s", got) } } func TestResolveTaskAction_Approve(t *testing.T) { task := supervisor.Task{ID: "T08", Status: "review"} if got := resolveTaskAction(task, nil); got != "approve" { t.Errorf("expected approve, got %s", got) } } func TestResolveTaskAction_Done(t *testing.T) { task := supervisor.Task{ID: "T01", Status: "done"} if got := resolveTaskAction(task, nil); got != "done" { t.Errorf("expected done, got %s", got) } } func TestReport_RendersMarkdownInModal(t *testing.T) { srv := setupTestServer(t) req := httptest.NewRequest(http.MethodGet, "/report/T01", nil) w := httptest.NewRecorder() srv.Router.ServeHTTP(w, req) if w.Code != http.StatusOK { t.Fatalf("expected 200, got %d", w.Code) } body := w.Body.String() if !containsStr(body, "detail-inner") { t.Error("expected detail-inner wrapper for modal") } if !containsStr(body, "docs-content") { t.Error("expected docs-content class for markdown rendering") } if !containsStr(body, "

") { t.Error("expected rendered markdown heading") } if !containsStr(body, "Izveštaj") { t.Error("expected 'Izveštaj' in title") } } func TestReport_NoReport_ShowsTask(t *testing.T) { srv := setupTestServer(t) // T08 has no report — should show task content req := httptest.NewRequest(http.MethodGet, "/report/T08", nil) w := httptest.NewRecorder() srv.Router.ServeHTTP(w, req) if w.Code != http.StatusOK { t.Fatalf("expected 200, got %d: %s", w.Code, w.Body.String()) } body := w.Body.String() if !containsStr(body, "detail-inner") { t.Error("expected detail-inner wrapper") } if !containsStr(body, "HTTP server") { t.Error("expected task title in fallback content") } } func TestReport_NotFoundTask(t *testing.T) { srv := setupTestServer(t) req := httptest.NewRequest(http.MethodGet, "/report/T99", nil) w := httptest.NewRecorder() srv.Router.ServeHTTP(w, req) if w.Code != http.StatusNotFound { t.Fatalf("expected 404, got %d", w.Code) } }