package api_test import ( "bytes" "encoding/json" "net/http" "net/http/httptest" "path/filepath" "testing" "task/internal/command" "task/internal/daemon/api" "task/internal/project" "task/internal/ticket" ) func setup(t *testing.T) (*api.Server, string) { t.Helper() dir := t.TempDir() store := ticket.NewJSONStore(dir) timeStore := ticket.NewJSONTimeStore(dir) projects := project.NewDirStore(dir) _ = projects.Ensure("inbox") hist, err := command.OpenHistory(filepath.Join(dir, "history.json"), store, timeStore) if err != nil { t.Fatal(err) } return api.New(store, timeStore, projects, hist), dir } func TestHealthAndCRUD(t *testing.T) { srv, _ := setup(t) h := srv.Handler() req := httptest.NewRequest(http.MethodGet, "/v1/health", nil) rec := httptest.NewRecorder() h.ServeHTTP(rec, req) if rec.Code != 200 { t.Fatalf("health: %d", rec.Code) } body, _ := json.Marshal(ticket.Ticket{Title: "Hi", ProjectID: "inbox", Status: ticket.StatusTodo}) req = httptest.NewRequest(http.MethodPost, "/v1/tickets", bytes.NewReader(body)) req.Header.Set(api.HeaderClient, api.ClientTUI) req.Header.Set("Content-Type", "application/json") rec = httptest.NewRecorder() h.ServeHTTP(rec, req) if rec.Code != http.StatusCreated { t.Fatalf("create: %d %s", rec.Code, rec.Body.String()) } var created ticket.Ticket _ = json.Unmarshal(rec.Body.Bytes(), &created) if created.ID == "" { t.Fatal("missing id") } req = httptest.NewRequest(http.MethodGet, "/v1/tickets?projectId=inbox", nil) req.Header.Set(api.HeaderClient, api.ClientTUI) rec = httptest.NewRecorder() h.ServeHTTP(rec, req) if rec.Code != 200 { t.Fatalf("list: %d", rec.Code) } } func TestMCPAuthRequired(t *testing.T) { srv, _ := setup(t) h := srv.Handler() req := httptest.NewRequest(http.MethodGet, "/v1/tickets?projectId=inbox", nil) req.Header.Set(api.HeaderClient, api.ClientMCP) req.Header.Set("X-TM-Project", "inbox") rec := httptest.NewRecorder() h.ServeHTTP(rec, req) if rec.Code != http.StatusUnauthorized && rec.Code != http.StatusForbidden { t.Fatalf("expected auth failure, got %d %s", rec.Code, rec.Body.String()) } // Create token via TUI client req = httptest.NewRequest(http.MethodPost, "/v1/projects/inbox/mcp-token", bytes.NewReader([]byte("{}"))) req.Header.Set(api.HeaderClient, api.ClientTUI) req.Header.Set("Content-Type", "application/json") rec = httptest.NewRecorder() h.ServeHTTP(rec, req) if rec.Code != 200 { t.Fatalf("token create: %d %s", rec.Code, rec.Body.String()) } var tok struct { Token string `json:"token"` } _ = json.Unmarshal(rec.Body.Bytes(), &tok) if tok.Token == "" { t.Fatal("empty token") } req = httptest.NewRequest(http.MethodGet, "/v1/tickets?projectId=inbox", nil) req.Header.Set(api.HeaderClient, api.ClientMCP) req.Header.Set("Authorization", "Bearer "+tok.Token) req.Header.Set("X-TM-Project", "inbox") rec = httptest.NewRecorder() h.ServeHTTP(rec, req) if rec.Code != 200 { t.Fatalf("mcp list: %d %s", rec.Code, rec.Body.String()) } req = httptest.NewRequest(http.MethodGet, "/v1/tickets?projectId=inbox", nil) req.Header.Set(api.HeaderClient, api.ClientMCP) req.Header.Set("Authorization", "Bearer wrong") req.Header.Set("X-TM-Project", "inbox") rec = httptest.NewRecorder() h.ServeHTTP(rec, req) if rec.Code == 200 { t.Fatal("wrong token should fail") } }