diff options
| author | Peter Stone <thepeterstone@gmail.com> | 2026-07-16 06:46:53 +0000 |
|---|---|---|
| committer | Peter Stone <thepeterstone@gmail.com> | 2026-07-16 06:46:53 +0000 |
| commit | 2413413b01925c7fab07768d7e405ac13c735000 (patch) | |
| tree | cc168393e8a2b9bb795e3003366be14e8d8303d6 /internal/handlers/widget_test.go | |
| parent | cef79fc49fd8d6e607c71432755812adca847ce2 (diff) | |
Surface budget_status on GET /api/widget when tracked tasks exist
Wires ComputeBudgetPeriod together with availability blocks, tracked
project/label sets, calendar events, and native tasks into a new
computeBudgetStatus helper on *Handler. HandleWidgetGet now populates
WidgetResponse.BudgetStatus with today/week periods, but only when at
least one incomplete budget-tracked task exists in the rolling week
window -- otherwise the field is omitted entirely so unconfigured users
see no new UI. Errors are logged and swallowed, matching the existing
resilience pattern for this widget endpoint.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PQaPGQVSfmKUiHXB87qRTC
Diffstat (limited to 'internal/handlers/widget_test.go')
| -rw-r--r-- | internal/handlers/widget_test.go | 54 |
1 files changed, 54 insertions, 0 deletions
diff --git a/internal/handlers/widget_test.go b/internal/handlers/widget_test.go index f934dba..a638eca 100644 --- a/internal/handlers/widget_test.go +++ b/internal/handlers/widget_test.go @@ -1214,3 +1214,57 @@ func TestTimelineItemToWidgetItem_NoProject_NilProjectColor(t *testing.T) { } } +func TestHandleWidgetGet_NoBudgetTrackedTasks_OmitsBudgetStatus(t *testing.T) { + db, cleanup := setupTestDB(t) + defer cleanup() + h := &Handler{store: db} + + req := httptest.NewRequest("GET", "/api/widget", nil) + w := httptest.NewRecorder() + h.HandleWidgetGet(w, req) + + var resp models.WidgetResponse + if err := json.NewDecoder(w.Body).Decode(&resp); err != nil { + t.Fatal(err) + } + if resp.BudgetStatus != nil { + t.Errorf("expected nil BudgetStatus with no tracked tasks, got %+v", resp.BudgetStatus) + } +} + +func TestHandleWidgetGet_BudgetTrackedTaskDueToday_IncludesBudgetStatus(t *testing.T) { + db, cleanup := setupTestDB(t) + defer cleanup() + h := &Handler{store: db} + + project, err := h.store.CreateProject("Tracked", "#111111") + if err != nil { + t.Fatal(err) + } + if err := h.store.SetProjectBudgetTracked(project.ID, true); err != nil { + t.Fatal(err) + } + due := time.Now() + if err := h.store.CreateNativeTask(models.Task{ID: "t-tracked", Content: "Tracked task", ProjectID: project.ID, DueDate: &due, EstimatedMinutes: 45}); err != nil { + t.Fatal(err) + } + + req := httptest.NewRequest("GET", "/api/widget", nil) + w := httptest.NewRecorder() + h.HandleWidgetGet(w, req) + + var resp models.WidgetResponse + if err := json.NewDecoder(w.Body).Decode(&resp); err != nil { + t.Fatal(err) + } + if resp.BudgetStatus == nil { + t.Fatal("expected non-nil BudgetStatus") + } + if resp.BudgetStatus.Today.ScheduledMinutes != 45 { + t.Errorf("Today.ScheduledMinutes = %d, want 45", resp.BudgetStatus.Today.ScheduledMinutes) + } + if resp.BudgetStatus.Week.ScheduledMinutes != 45 { + t.Errorf("Week.ScheduledMinutes = %d, want 45", resp.BudgetStatus.Week.ScheduledMinutes) + } +} + |
