Add local JSON daemon and route TUI/CLI through it.
A per-DataDir background process owns ticket JSON, history, and sync so multiple TUI instances stay consistent; MCP token APIs are ready for phase 2. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,115 @@
|
||||
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")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user