package server import ( "encoding/json" "net/http" "net/http/httptest" "os" "path/filepath" "strings" "testing" ) func TestConsolePage(t *testing.T) { srv := setupTestServer(t) req := httptest.NewRequest(http.MethodGet, "/console", 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, "KAOS") { t.Error("expected 'KAOS' in console page") } if !containsStr(body, "Konzola") { t.Error("expected 'Konzola' in console page") } } func TestConsoleSessions_Empty(t *testing.T) { srv := setupTestServer(t) req := httptest.NewRequest(http.MethodGet, "/console/sessions", nil) w := httptest.NewRecorder() srv.Router.ServeHTTP(w, req) if w.Code != http.StatusOK { t.Fatalf("expected 200, got %d", w.Code) } var sessions []taskSessionResponse if err := json.Unmarshal(w.Body.Bytes(), &sessions); err != nil { t.Fatalf("invalid JSON: %v", err) } if len(sessions) != 0 { t.Fatalf("expected 0 sessions, got %d", len(sessions)) } } func TestConsoleKill_NotFound(t *testing.T) { srv := setupTestServer(t) req := httptest.NewRequest(http.MethodPost, "/console/kill/T99", nil) w := httptest.NewRecorder() srv.Router.ServeHTTP(w, req) if w.Code != http.StatusNotFound { t.Fatalf("expected 404, got %d", w.Code) } } func TestSessionKey(t *testing.T) { if got := sessionKey("T01", "work"); got != "T01" { t.Errorf("expected T01, got %s", got) } if got := sessionKey("T01", "review"); got != "T01-review" { t.Errorf("expected T01-review, got %s", got) } } func TestTaskSessionManager_ListEmpty(t *testing.T) { sm := newTaskSessionManager() sessions := sm.listSessions() if len(sessions) != 0 { t.Errorf("expected 0 sessions, got %d", len(sessions)) } } func TestTaskSessionManager_KillNotFound(t *testing.T) { sm := newTaskSessionManager() if sm.killSession("T99", "work") { t.Error("expected false for non-existent session") } } func TestTaskSessionManager_GetNotFound(t *testing.T) { sm := newTaskSessionManager() if sm.getSessionByKey("T99") != nil { t.Error("expected nil for non-existent session") } } func TestBuildWorkPrompt(t *testing.T) { prompt := buildWorkPrompt("T08", []byte("# T08: Test task\n\nOpis.")) if !containsStr(prompt, "T08") { t.Error("expected task ID in prompt") } if !containsStr(prompt, "Test task") { t.Error("expected task content in prompt") } if !containsStr(prompt, "agents/coder/CLAUDE.md") { t.Error("expected coder CLAUDE.md reference") } if !containsStr(prompt, "go test") { t.Error("expected test instruction") } if !containsStr(prompt, "report") { t.Error("expected report instruction") } } func TestBuildReviewPrompt(t *testing.T) { prompt := buildReviewPrompt("T08", []byte("# T08: Test\n"), []byte("# Report\nSve ok.")) if !containsStr(prompt, "T08") { t.Error("expected task ID in review prompt") } if !containsStr(prompt, "Sve ok") { t.Error("expected report content in review prompt") } if !containsStr(prompt, "agents/checker/CLAUDE.md") { t.Error("expected checker CLAUDE.md reference") } } func TestBuildReviewPrompt_NoReport(t *testing.T) { prompt := buildReviewPrompt("T08", []byte("# T08: Test\n"), nil) if !containsStr(prompt, "T08") { t.Error("expected task ID") } if containsStr(prompt, "Izveštaj agenta") { t.Error("should not include report section when no report") } } func TestReviewTask_NotFound(t *testing.T) { srv := setupTestServer(t) req := httptest.NewRequest(http.MethodPost, "/task/T99/review", nil) w := httptest.NewRecorder() srv.Router.ServeHTTP(w, req) if w.Code != http.StatusNotFound { t.Fatalf("expected 404, got %d", w.Code) } } func TestConsolePage_HasKillButton(t *testing.T) { srv := setupTestServer(t) req := httptest.NewRequest(http.MethodGet, "/console", nil) w := httptest.NewRecorder() srv.Router.ServeHTTP(w, req) body := w.Body.String() if !containsStr(body, "killSession") { t.Error("expected killSession function in console page") } } func TestTaskDetail_HasProveriButton(t *testing.T) { srv := setupTestServer(t) // Put T08 in review os.Rename( filepath.Join(srv.Config.TasksDir, "backlog", "T08.md"), filepath.Join(srv.Config.TasksDir, "review", "T08.md"), ) req := httptest.NewRequest(http.MethodGet, "/task/T08", nil) w := httptest.NewRecorder() srv.Router.ServeHTTP(w, req) body := w.Body.String() if !containsStr(body, "Proveri") { t.Error("expected 'Proveri' button for review task") } if !containsStr(body, "/review") { t.Error("expected /review endpoint in Proveri button") } } func TestConsolePage_ToolbarAbovePanels(t *testing.T) { srv := setupTestServer(t) req := httptest.NewRequest(http.MethodGet, "/console", nil) w := httptest.NewRecorder() srv.Router.ServeHTTP(w, req) body := w.Body.String() // Toolbar should appear before console-panels in the HTML toolbarIdx := strings.Index(body, "console-toolbar") panelsIdx := strings.Index(body, "console-panels") if toolbarIdx == -1 { t.Fatal("expected console-toolbar in console page") } if panelsIdx == -1 { t.Fatal("expected console-panels in console page") } if toolbarIdx > panelsIdx { t.Error("expected toolbar before panels in console HTML") } } func TestConsolePage_HasDynamicSessions(t *testing.T) { srv := setupTestServer(t) req := httptest.NewRequest(http.MethodGet, "/console", nil) w := httptest.NewRecorder() srv.Router.ServeHTTP(w, req) body := w.Body.String() if !containsStr(body, "refreshSessions") { t.Error("expected refreshSessions function in console page") } if !containsStr(body, "/console/sessions") { t.Error("expected /console/sessions API call") } } func TestConsolePage_HasXtermJS(t *testing.T) { srv := setupTestServer(t) req := httptest.NewRequest(http.MethodGet, "/console", nil) w := httptest.NewRecorder() srv.Router.ServeHTTP(w, req) body := w.Body.String() if !containsStr(body, "xterm.min.js") { t.Error("expected xterm.min.js CDN link in console page") } if !containsStr(body, "addon-fit") { t.Error("expected addon-fit CDN link in console page") } if !containsStr(body, "xterm.css") { t.Error("expected xterm.css CDN link in console page") } } func TestConsolePage_HasWebSocket(t *testing.T) { srv := setupTestServer(t) req := httptest.NewRequest(http.MethodGet, "/console", nil) w := httptest.NewRecorder() srv.Router.ServeHTTP(w, req) body := w.Body.String() if !containsStr(body, "console/ws/") { t.Error("expected WebSocket URL console/ws/ in console page") } if !containsStr(body, "new WebSocket") { t.Error("expected WebSocket constructor in console page") } } func TestConsolePage_HasEmptyState(t *testing.T) { srv := setupTestServer(t) req := httptest.NewRequest(http.MethodGet, "/console", nil) w := httptest.NewRecorder() srv.Router.ServeHTTP(w, req) body := w.Body.String() if !containsStr(body, "empty-state") { t.Error("expected empty-state element") } if !containsStr(body, "Pusti") { t.Error("expected 'Pusti' instruction in empty state") } if !containsStr(body, "console-terminal") { t.Error("expected console-terminal class in JS code") } } func TestConsolePage_HasBinaryMessageSupport(t *testing.T) { srv := setupTestServer(t) req := httptest.NewRequest(http.MethodGet, "/console", nil) w := httptest.NewRecorder() srv.Router.ServeHTTP(w, req) body := w.Body.String() if !containsStr(body, "arraybuffer") { t.Error("expected arraybuffer binary type for WebSocket") } if !containsStr(body, "Uint8Array") { t.Error("expected Uint8Array handling for binary messages") } } func TestConsolePage_HasResizeHandler(t *testing.T) { srv := setupTestServer(t) req := httptest.NewRequest(http.MethodGet, "/console", nil) w := httptest.NewRecorder() srv.Router.ServeHTTP(w, req) body := w.Body.String() if !containsStr(body, "fitAddon.fit()") { t.Error("expected fitAddon.fit() for resize handling") } if !containsStr(body, `'resize'`) { t.Error("expected resize message type in WebSocket handler") } }