summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Stone <thepeterstone@gmail.com>2026-07-15 18:24:13 +0000
committerPeter Stone <thepeterstone@gmail.com>2026-07-16 02:54:17 +0000
commitb55c01eb9c48d2af754ff22c55deab6f0832e358 (patch)
tree5f51b2ab79eea1b94988402cee60f1ff0c5fa518
parent7e2386c0ebbc2d1cafc670ad4fd05b884f8b8fa7 (diff)
test(tasks): add test cases for project and label endpoints
Tests for HandleWidgetProjectsGet, HandleWidgetProjectsCreate, HandleWidgetTaskProject, HandleWidgetTaskLabels, HandleWidgetLabelsGet, HandleWidgetLabelsColorSet, and the extended HandleWidgetTaskDetail response with project and labels fields. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VTUSAEKfsPc6WGDq45yPHD
-rw-r--r--internal/handlers/widget_test.go161
1 files changed, 161 insertions, 0 deletions
diff --git a/internal/handlers/widget_test.go b/internal/handlers/widget_test.go
index 95f5264..a6ff095 100644
--- a/internal/handlers/widget_test.go
+++ b/internal/handlers/widget_test.go
@@ -1028,3 +1028,164 @@ func TestHandleWidgetTaskNextDate_NonRecurringTask_Returns400(t *testing.T) {
}
}
+func TestHandleWidgetProjectsGet_ReturnsNonArchivedProjects(t *testing.T) {
+ s, cleanup := setupTestDB(t)
+ defer cleanup()
+ h := &Handler{store: s}
+ if _, err := s.CreateProject("Sailing prep", "#3B82F6"); err != nil {
+ t.Fatal(err)
+ }
+
+ req := httptest.NewRequest("GET", "/api/widget/projects", nil)
+ w := httptest.NewRecorder()
+ http.HandlerFunc(h.HandleWidgetProjectsGet).ServeHTTP(w, req)
+
+ if w.Code != http.StatusOK {
+ t.Fatalf("expected 200, got %d: %s", w.Code, w.Body.String())
+ }
+ var projects []projectResponse
+ if err := json.NewDecoder(w.Body).Decode(&projects); err != nil {
+ t.Fatalf("failed to decode response: %v", err)
+ }
+ if len(projects) != 1 || projects[0].Name != "Sailing prep" {
+ t.Fatalf("projects = %+v, want 1 entry named 'Sailing prep'", projects)
+ }
+}
+
+func TestHandleWidgetProjectsCreate_CreatesProject(t *testing.T) {
+ s, cleanup := setupTestDB(t)
+ defer cleanup()
+ h := &Handler{store: s}
+
+ body := `{"name":"Sailing prep","color":"#3B82F6"}`
+ req := httptest.NewRequest("POST", "/api/widget/projects", strings.NewReader(body))
+ w := httptest.NewRecorder()
+ http.HandlerFunc(h.HandleWidgetProjectsCreate).ServeHTTP(w, req)
+
+ if w.Code != http.StatusOK {
+ t.Fatalf("expected 200, got %d: %s", w.Code, w.Body.String())
+ }
+ var resp projectResponse
+ if err := json.NewDecoder(w.Body).Decode(&resp); err != nil {
+ t.Fatalf("failed to decode response: %v", err)
+ }
+ if resp.Name != "Sailing prep" || resp.Color != "#3B82F6" || resp.ID == "" {
+ t.Errorf("resp = %+v, want a created project with name/color/id set", resp)
+ }
+}
+
+func TestHandleWidgetTaskProject_SetsProjectID(t *testing.T) {
+ s, cleanup := setupTestDB(t)
+ defer cleanup()
+ h := &Handler{store: s}
+ project, err := s.CreateProject("Sailing prep", "#3B82F6")
+ if err != nil {
+ t.Fatal(err)
+ }
+ if _, err := s.DB().Exec(`INSERT INTO native_tasks (id, content) VALUES ('plain-1', 'Buy line')`); err != nil {
+ t.Fatal(err)
+ }
+
+ body := `{"id":"plain-1","project_id":"` + project.ID + `"}`
+ req := httptest.NewRequest("POST", "/api/widget/task/project", strings.NewReader(body))
+ w := httptest.NewRecorder()
+ http.HandlerFunc(h.HandleWidgetTaskProject).ServeHTTP(w, req)
+
+ if w.Code != http.StatusOK {
+ t.Fatalf("expected 200, got %d: %s", w.Code, w.Body.String())
+ }
+ task, err := s.GetNativeTaskByID("plain-1")
+ if err != nil {
+ t.Fatal(err)
+ }
+ if task.ProjectID != project.ID {
+ t.Errorf("ProjectID = %q, want %q", task.ProjectID, project.ID)
+ }
+}
+
+func TestHandleWidgetTaskLabels_SetsLabels(t *testing.T) {
+ s, cleanup := setupTestDB(t)
+ defer cleanup()
+ h := &Handler{store: s}
+ if _, err := s.DB().Exec(`INSERT INTO native_tasks (id, content) VALUES ('plain-1', 'Buy line')`); err != nil {
+ t.Fatal(err)
+ }
+
+ body := `{"id":"plain-1","labels":["chore","urgent"]}`
+ req := httptest.NewRequest("POST", "/api/widget/task/labels", strings.NewReader(body))
+ w := httptest.NewRecorder()
+ http.HandlerFunc(h.HandleWidgetTaskLabels).ServeHTTP(w, req)
+
+ if w.Code != http.StatusOK {
+ t.Fatalf("expected 200, got %d: %s", w.Code, w.Body.String())
+ }
+ task, err := s.GetNativeTaskByID("plain-1")
+ if err != nil {
+ t.Fatal(err)
+ }
+ if len(task.Labels) != 2 || task.Labels[0] != "chore" || task.Labels[1] != "urgent" {
+ t.Errorf("Labels = %v, want [chore urgent]", task.Labels)
+ }
+}
+
+func TestHandleWidgetLabelsColorSet_ThenGet_ReturnsIt(t *testing.T) {
+ s, cleanup := setupTestDB(t)
+ defer cleanup()
+ h := &Handler{store: s}
+
+ body := `{"name":"chore","color":"#F59E0B"}`
+ setReq := httptest.NewRequest("POST", "/api/widget/labels/color", strings.NewReader(body))
+ setW := httptest.NewRecorder()
+ http.HandlerFunc(h.HandleWidgetLabelsColorSet).ServeHTTP(setW, setReq)
+ if setW.Code != http.StatusOK {
+ t.Fatalf("expected 200, got %d: %s", setW.Code, setW.Body.String())
+ }
+
+ getReq := httptest.NewRequest("GET", "/api/widget/labels", nil)
+ getW := httptest.NewRecorder()
+ http.HandlerFunc(h.HandleWidgetLabelsGet).ServeHTTP(getW, getReq)
+ if getW.Code != http.StatusOK {
+ t.Fatalf("expected 200, got %d: %s", getW.Code, getW.Body.String())
+ }
+ var colors []labelColorResponse
+ if err := json.NewDecoder(getW.Body).Decode(&colors); err != nil {
+ t.Fatalf("failed to decode response: %v", err)
+ }
+ if len(colors) != 1 || colors[0].Name != "chore" || colors[0].Color != "#F59E0B" {
+ t.Fatalf("colors = %+v, want [{chore #F59E0B}]", colors)
+ }
+}
+
+func TestHandleWidgetTaskDetail_IncludesProjectAndLabels(t *testing.T) {
+ s, cleanup := setupTestDB(t)
+ defer cleanup()
+ h := &Handler{store: s}
+ project, err := s.CreateProject("Sailing prep", "#3B82F6")
+ if err != nil {
+ t.Fatal(err)
+ }
+ if _, err := s.DB().Exec(`
+ INSERT INTO native_tasks (id, content, project_id, labels) VALUES ('rec-1', 'Water plants', ?, '["chore"]')
+ `, project.ID); err != nil {
+ t.Fatal(err)
+ }
+
+ req := httptest.NewRequest("GET", "/api/widget/task?id=rec-1&source=doot", nil)
+ w := httptest.NewRecorder()
+ http.HandlerFunc(h.HandleWidgetTaskDetail).ServeHTTP(w, req)
+
+ if w.Code != http.StatusOK {
+ t.Fatalf("expected 200, got %d: %s", w.Code, w.Body.String())
+ }
+ var resp taskDetailResponse
+ if err := json.NewDecoder(w.Body).Decode(&resp); err != nil {
+ t.Fatalf("failed to decode response: %v", err)
+ }
+ if resp.Project == nil || resp.Project.ID != project.ID || resp.Project.Name != "Sailing prep" {
+ t.Fatalf("Project = %+v, want the created project", resp.Project)
+ }
+ if len(resp.Labels) != 1 || resp.Labels[0] != "chore" {
+ t.Errorf("Labels = %v, want [chore]", resp.Labels)
+ }
+}
+