- 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>
246 lines
6.6 KiB
Go
246 lines
6.6 KiB
Go
package server
|
|
|
|
import (
|
|
"encoding/json"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/dal/kaos/internal/supervisor"
|
|
)
|
|
|
|
func TestSubmitPage(t *testing.T) {
|
|
srv := setupTestServer(t)
|
|
|
|
req := httptest.NewRequest(http.MethodGet, "/submit", 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, "Klijent") {
|
|
t.Error("expected 'Klijent' mode button")
|
|
}
|
|
if !containsStr(body, "Operater") {
|
|
t.Error("expected 'Operater' mode button")
|
|
}
|
|
if !containsStr(body, "mode-client") {
|
|
t.Error("expected client mode section")
|
|
}
|
|
}
|
|
|
|
func TestSubmitPage_ClientModeIsDefault(t *testing.T) {
|
|
srv := setupTestServer(t)
|
|
|
|
req := httptest.NewRequest(http.MethodGet, "/submit", nil)
|
|
w := httptest.NewRecorder()
|
|
srv.Router.ServeHTTP(w, req)
|
|
|
|
body := w.Body.String()
|
|
// Operator mode should be hidden by default
|
|
if !containsStr(body, `id="mode-operator" class="submit-mode" style="display:none"`) {
|
|
t.Error("expected operator mode to be hidden by default")
|
|
}
|
|
}
|
|
|
|
func TestSimpleSubmit_CreatesTask(t *testing.T) {
|
|
srv := setupTestServer(t)
|
|
|
|
form := strings.NewReader("title=Test+prijava&description=Opis+testa&priority=Visok")
|
|
req := httptest.NewRequest(http.MethodPost, "/submit/simple", form)
|
|
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
|
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"] != "ok" {
|
|
t.Errorf("expected status ok, got %v", resp["status"])
|
|
}
|
|
|
|
taskID, ok := resp["task_id"].(string)
|
|
if !ok || taskID == "" {
|
|
t.Fatal("expected non-empty task_id")
|
|
}
|
|
|
|
// Verify file was created in backlog
|
|
path := filepath.Join(srv.Config.TasksDir, "backlog", taskID+".md")
|
|
content, err := os.ReadFile(path)
|
|
if err != nil {
|
|
t.Fatalf("expected task file in backlog: %v", err)
|
|
}
|
|
|
|
if !containsStr(string(content), "Test prijava") {
|
|
t.Error("expected title in task file")
|
|
}
|
|
if !containsStr(string(content), "Visok") {
|
|
t.Error("expected priority in task file")
|
|
}
|
|
if !containsStr(string(content), "klijent (prijava)") {
|
|
t.Error("expected 'klijent (prijava)' as creator")
|
|
}
|
|
}
|
|
|
|
func TestSimpleSubmit_MissingTitle(t *testing.T) {
|
|
srv := setupTestServer(t)
|
|
|
|
form := strings.NewReader("description=Samo+opis")
|
|
req := httptest.NewRequest(http.MethodPost, "/submit/simple", form)
|
|
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
|
w := httptest.NewRecorder()
|
|
srv.Router.ServeHTTP(w, req)
|
|
|
|
if w.Code != http.StatusBadRequest {
|
|
t.Fatalf("expected 400 for missing title, got %d", w.Code)
|
|
}
|
|
}
|
|
|
|
func TestSimpleSubmit_AutoNumbering(t *testing.T) {
|
|
srv := setupTestServer(t)
|
|
|
|
// Existing tasks: T01 (done), T08 (backlog)
|
|
// Next should be T09
|
|
|
|
form := strings.NewReader("title=Novi+task&priority=Srednji")
|
|
req := httptest.NewRequest(http.MethodPost, "/submit/simple", form)
|
|
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
|
w := httptest.NewRecorder()
|
|
srv.Router.ServeHTTP(w, req)
|
|
|
|
var resp map[string]interface{}
|
|
json.Unmarshal(w.Body.Bytes(), &resp)
|
|
|
|
taskID, _ := resp["task_id"].(string)
|
|
if taskID != "T09" {
|
|
t.Errorf("expected T09 (next after T08), got %s", taskID)
|
|
}
|
|
}
|
|
|
|
func TestSimpleSubmit_DefaultPriority(t *testing.T) {
|
|
srv := setupTestServer(t)
|
|
|
|
form := strings.NewReader("title=Bez+prioriteta")
|
|
req := httptest.NewRequest(http.MethodPost, "/submit/simple", form)
|
|
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
|
w := httptest.NewRecorder()
|
|
srv.Router.ServeHTTP(w, req)
|
|
|
|
var resp map[string]interface{}
|
|
json.Unmarshal(w.Body.Bytes(), &resp)
|
|
|
|
taskID, _ := resp["task_id"].(string)
|
|
path := filepath.Join(srv.Config.TasksDir, "backlog", taskID+".md")
|
|
content, _ := os.ReadFile(path)
|
|
|
|
if !containsStr(string(content), "Srednji") {
|
|
t.Error("expected default priority 'Srednji'")
|
|
}
|
|
}
|
|
|
|
func TestChatSubmit_ReturnsChatID(t *testing.T) {
|
|
srv := setupTestServer(t)
|
|
|
|
body := `{"message":"test poruka"}`
|
|
req := httptest.NewRequest(http.MethodPost, "/submit/chat", strings.NewReader(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
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["chat_id"] == nil || resp["chat_id"] == "" {
|
|
t.Error("expected non-empty chat_id in response")
|
|
}
|
|
}
|
|
|
|
func TestChatSubmit_EmptyMessage(t *testing.T) {
|
|
srv := setupTestServer(t)
|
|
|
|
body := `{"message":""}`
|
|
req := httptest.NewRequest(http.MethodPost, "/submit/chat", strings.NewReader(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
w := httptest.NewRecorder()
|
|
srv.Router.ServeHTTP(w, req)
|
|
|
|
if w.Code != http.StatusBadRequest {
|
|
t.Fatalf("expected 400 for empty message, got %d", w.Code)
|
|
}
|
|
}
|
|
|
|
func TestChatStream_NotFound(t *testing.T) {
|
|
srv := setupTestServer(t)
|
|
|
|
req := httptest.NewRequest(http.MethodGet, "/submit/chat/stream/nonexistent", nil)
|
|
w := httptest.NewRecorder()
|
|
srv.Router.ServeHTTP(w, req)
|
|
|
|
if w.Code != http.StatusNotFound {
|
|
t.Fatalf("expected 404, got %d", w.Code)
|
|
}
|
|
}
|
|
|
|
func TestNextTaskNumber(t *testing.T) {
|
|
dir := t.TempDir()
|
|
tasksDir := filepath.Join(dir, "TASKS")
|
|
for _, f := range []string{"backlog", "ready", "active", "review", "done"} {
|
|
os.MkdirAll(filepath.Join(tasksDir, f), 0755)
|
|
}
|
|
|
|
os.WriteFile(filepath.Join(tasksDir, "done", "T01.md"), []byte(testTask1), 0644)
|
|
os.WriteFile(filepath.Join(tasksDir, "backlog", "T08.md"), []byte(testTask2), 0644)
|
|
|
|
num, err := nextTaskNumber(tasksDir)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if num != "T09" {
|
|
t.Errorf("expected T09, got %s", num)
|
|
}
|
|
}
|
|
|
|
func TestBuildTaskContext(t *testing.T) {
|
|
tasks := []supervisor.Task{
|
|
{ID: "T01", Title: "Init", Status: "done", Agent: "coder", Model: "Sonnet"},
|
|
{ID: "T02", Title: "Server", Status: "active", Agent: "coder", Model: "Opus"},
|
|
}
|
|
|
|
ctx := buildTaskContext(tasks)
|
|
if !containsStr(ctx, "T01: Init") {
|
|
t.Error("expected T01 in context")
|
|
}
|
|
if !containsStr(ctx, "T02: Server") {
|
|
t.Error("expected T02 in context")
|
|
}
|
|
if !containsStr(ctx, "DONE") {
|
|
t.Error("expected DONE section")
|
|
}
|
|
}
|
|
|
|
func TestSubmitPage_HasPrijavaNav(t *testing.T) {
|
|
srv := setupTestServer(t)
|
|
|
|
req := httptest.NewRequest(http.MethodGet, "/submit", nil)
|
|
w := httptest.NewRecorder()
|
|
srv.Router.ServeHTTP(w, req)
|
|
|
|
body := w.Body.String()
|
|
if !containsStr(body, `href="/submit"`) {
|
|
t.Error("expected Prijava nav link")
|
|
}
|
|
}
|