KAOS/code/internal/server/task_detail_test.go
djuka 098ed13705 T22: Reorganizacija testova + logs handler + konzola fix
- Razbijen monolitni server_test.go na fokusirane test fajlove:
  api_test.go, dashboard_test.go, docs_test.go, search_test.go,
  submit_test.go, task_detail_test.go, console_test.go, sse_test.go,
  timestamp_test.go, ui_test.go, test_helpers_test.go
- Dodat logs.go handler (handleLogsTail) koji je nedostajao
- Dodat LogFile u config
- Fix konzola: prompt se šalje preko fajla umesto direktno u PTY
- 192 testova prolazi

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 04:45:50 +00:00

273 lines
7.1 KiB
Go

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, "<h1>") {
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)
}
}