diff options
| author | Peter Stone <thepeterstone@gmail.com> | 2026-07-15 18:24:13 +0000 |
|---|---|---|
| committer | Peter Stone <thepeterstone@gmail.com> | 2026-07-16 02:54:17 +0000 |
| commit | b55c01eb9c48d2af754ff22c55deab6f0832e358 (patch) | |
| tree | 5f51b2ab79eea1b94988402cee60f1ff0c5fa518 | |
| parent | 7e2386c0ebbc2d1cafc670ad4fd05b884f8b8fa7 (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.go | 161 |
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) + } +} + |
