diff options
Diffstat (limited to 'internal/handlers')
| -rw-r--r-- | internal/handlers/widget.go | 14 | ||||
| -rw-r--r-- | internal/handlers/widget_test.go | 20 |
2 files changed, 34 insertions, 0 deletions
diff --git a/internal/handlers/widget.go b/internal/handlers/widget.go index 876aaad..6c3b455 100644 --- a/internal/handlers/widget.go +++ b/internal/handlers/widget.go @@ -2,12 +2,14 @@ package handlers import ( "encoding/json" + "errors" "net/http" "strings" "time" "task-dashboard/internal/config" "task-dashboard/internal/models" + "task-dashboard/internal/store" ) // WidgetAuthMiddleware validates the static bearer token for widget API endpoints. @@ -126,6 +128,10 @@ func (h *Handler) HandleWidgetReschedule(w http.ResponseWriter, r *http.Request) tz := config.GetDisplayTimezone() dueDate := time.Date(parsed.Year(), parsed.Month(), parsed.Day(), 0, 0, 0, 0, tz) if err := h.store.RescheduleNativeTask(req.ID, dueDate); err != nil { + if errors.Is(err, store.ErrNativeTaskNotFound) { + http.Error(w, "task not found", http.StatusNotFound) + return + } http.Error(w, "failed to reschedule", http.StatusInternalServerError) return } @@ -143,6 +149,14 @@ func (h *Handler) HandleWidgetComplete(w http.ResponseWriter, r *http.Request) { switch req.Source { case "doot": if err := h.store.CompleteNativeTask(req.ID); err != nil { + if errors.Is(err, store.ErrNativeTaskNotFound) { + // Surfaced as 404 (not a silent 200) so the caller -- the + // Android widget -- can tell "nothing changed" apart from + // "it worked", instead of the previous behavior where a + // stale/wrong id looked identical to a real completion. + http.Error(w, "task not found", http.StatusNotFound) + return + } http.Error(w, "failed to complete task", http.StatusInternalServerError) return } diff --git a/internal/handlers/widget_test.go b/internal/handlers/widget_test.go index f56fe07..850dc07 100644 --- a/internal/handlers/widget_test.go +++ b/internal/handlers/widget_test.go @@ -121,6 +121,26 @@ func TestHandleWidgetComplete_NonCompletable(t *testing.T) { } } +// TestHandleWidgetComplete_UnknownID_Returns404 proves the 2026-07-12 fix at +// the handler layer: a "doot" completion for an id that doesn't exist must +// surface as 404, not the previous silent 200 (see +// store.ErrNativeTaskNotFound's doc comment for the underlying bug this +// closes -- a real production incident where the widget's completeTask tap +// intermittently looked like it worked but changed nothing). +func TestHandleWidgetComplete_UnknownID_Returns404(t *testing.T) { + s, cleanup := setupTestDB(t) + defer cleanup() + h := &Handler{store: s} + body := `{"id":"does-not-exist","source":"doot"}` + req := httptest.NewRequest("POST", "/api/widget/complete", strings.NewReader(body)) + w := httptest.NewRecorder() + http.HandlerFunc(h.HandleWidgetComplete).ServeHTTP(w, req) + + if w.Code != http.StatusNotFound { + t.Fatalf("expected 404, got %d", w.Code) + } +} + func TestWidgetAuthMiddleware_EmptyToken(t *testing.T) { inner := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}) h := WidgetAuthMiddleware("", inner) |
