summaryrefslogtreecommitdiff
path: root/internal/handlers
diff options
context:
space:
mode:
Diffstat (limited to 'internal/handlers')
-rw-r--r--internal/handlers/timeline_logic_test.go7
-rw-r--r--internal/handlers/widget.go22
-rw-r--r--internal/handlers/widget_test.go49
3 files changed, 78 insertions, 0 deletions
diff --git a/internal/handlers/timeline_logic_test.go b/internal/handlers/timeline_logic_test.go
index 9ddaeda..5e7cb27 100644
--- a/internal/handlers/timeline_logic_test.go
+++ b/internal/handlers/timeline_logic_test.go
@@ -23,6 +23,9 @@ type MockCalendarClient struct {
// with, for tests that need to assert on how the caller resolved its
// calendar ID list (e.g. fetchCalendarEvents' comma-split fallback).
SetCalendarIDsCalls [][]string
+ // RecurrenceRule is returned by GetRecurrenceRule for any id when RecurrenceErr is nil.
+ RecurrenceRule string
+ RecurrenceErr error
}
func (m *MockCalendarClient) GetUpcomingEvents(ctx context.Context, maxResults int) ([]models.CalendarEvent, error) {
@@ -41,6 +44,10 @@ func (m *MockCalendarClient) SetCalendarIDs(ids []string) {
m.SetCalendarIDsCalls = append(m.SetCalendarIDsCalls, ids)
}
+func (m *MockCalendarClient) GetRecurrenceRule(ctx context.Context, recurringEventID string) (string, error) {
+ return m.RecurrenceRule, m.RecurrenceErr
+}
+
func setupTestStore(t *testing.T) *store.Store {
t.Helper()
tempDir := t.TempDir()
diff --git a/internal/handlers/widget.go b/internal/handlers/widget.go
index 29124fb..6b1e774 100644
--- a/internal/handlers/widget.go
+++ b/internal/handlers/widget.go
@@ -195,6 +195,28 @@ func (h *Handler) HandleWidgetComplete(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
}
+// HandleWidgetRecurrence looks up and formats a recurring calendar event's schedule.
+func (h *Handler) HandleWidgetRecurrence(w http.ResponseWriter, r *http.Request) {
+ recurringEventID := r.URL.Query().Get("recurring_event_id")
+ if recurringEventID == "" {
+ http.Error(w, "recurring_event_id is required", http.StatusBadRequest)
+ return
+ }
+
+ recurrence, err := h.googleCalendarClient.GetRecurrenceRule(r.Context(), recurringEventID)
+ if err != nil {
+ http.Error(w, "recurring event not found", http.StatusNotFound)
+ return
+ }
+
+ resp := struct {
+ Recurrence string `json:"recurrence"`
+ }{Recurrence: recurrence}
+
+ w.Header().Set("Content-Type", "application/json")
+ _ = json.NewEncoder(w).Encode(resp)
+}
+
// HandleWidgetAdd creates a new undated native task from the widget's quick-add sheet.
func (h *Handler) HandleWidgetAdd(w http.ResponseWriter, r *http.Request) {
var req widgetAddRequest
diff --git a/internal/handlers/widget_test.go b/internal/handlers/widget_test.go
index efe6858..610a55c 100644
--- a/internal/handlers/widget_test.go
+++ b/internal/handlers/widget_test.go
@@ -1,6 +1,8 @@
package handlers
import (
+ "encoding/json"
+ "fmt"
"net/http"
"net/http/httptest"
"strings"
@@ -424,3 +426,50 @@ func TestTimelineItemToWidgetItem_NonRecurring_EmptyRecurringEventID(t *testing.
t.Errorf("expected empty RecurringEventID for a non-recurring event, got %q", wi.RecurringEventID)
}
}
+
+// TestHandleWidgetRecurrence_ReturnsFormattedSchedule proves the recurrence
+// lookup endpoint: given a recurring_event_id query param, it calls the
+// calendar client's GetRecurrenceRule and returns the formatted text.
+func TestHandleWidgetRecurrence_ReturnsFormattedSchedule(t *testing.T) {
+ mock := &MockCalendarClient{RecurrenceRule: "Repeats weekly on Monday"}
+ h := &Handler{googleCalendarClient: mock}
+ req := httptest.NewRequest("GET", "/api/widget/recurrence?recurring_event_id=master-1", nil)
+ w := httptest.NewRecorder()
+ http.HandlerFunc(h.HandleWidgetRecurrence).ServeHTTP(w, req)
+
+ if w.Code != http.StatusOK {
+ t.Fatalf("expected 200, got %d", w.Code)
+ }
+ var resp struct {
+ Recurrence string `json:"recurrence"`
+ }
+ if err := json.NewDecoder(w.Body).Decode(&resp); err != nil {
+ t.Fatalf("failed to decode response: %v", err)
+ }
+ if resp.Recurrence != "Repeats weekly on Monday" {
+ t.Errorf("recurrence = %q, want %q", resp.Recurrence, "Repeats weekly on Monday")
+ }
+}
+
+func TestHandleWidgetRecurrence_NotFound_Returns404(t *testing.T) {
+ mock := &MockCalendarClient{RecurrenceErr: fmt.Errorf("not found")}
+ h := &Handler{googleCalendarClient: mock}
+ req := httptest.NewRequest("GET", "/api/widget/recurrence?recurring_event_id=missing", nil)
+ w := httptest.NewRecorder()
+ http.HandlerFunc(h.HandleWidgetRecurrence).ServeHTTP(w, req)
+
+ if w.Code != http.StatusNotFound {
+ t.Fatalf("expected 404, got %d", w.Code)
+ }
+}
+
+func TestHandleWidgetRecurrence_MissingParam_Returns400(t *testing.T) {
+ h := &Handler{}
+ req := httptest.NewRequest("GET", "/api/widget/recurrence", nil)
+ w := httptest.NewRecorder()
+ http.HandlerFunc(h.HandleWidgetRecurrence).ServeHTTP(w, req)
+
+ if w.Code != http.StatusBadRequest {
+ t.Fatalf("expected 400, got %d", w.Code)
+ }
+}