1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
|
package handlers
import (
"net/http"
"net/http/httptest"
"strings"
"testing"
"time"
"task-dashboard/internal/models"
)
func TestWidgetAuthMiddleware_NoToken(t *testing.T) {
called := false
inner := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { called = true })
h := WidgetAuthMiddleware("secret", inner)
req := httptest.NewRequest("GET", "/api/widget", nil)
w := httptest.NewRecorder()
h.ServeHTTP(w, req)
if w.Code != http.StatusUnauthorized {
t.Fatalf("expected 401, got %d", w.Code)
}
if called {
t.Fatal("inner handler should not have been called")
}
}
func TestWidgetAuthMiddleware_WrongToken(t *testing.T) {
inner := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})
h := WidgetAuthMiddleware("secret", inner)
req := httptest.NewRequest("GET", "/api/widget", nil)
req.Header.Set("Authorization", "Bearer wrong")
w := httptest.NewRecorder()
h.ServeHTTP(w, req)
if w.Code != http.StatusUnauthorized {
t.Fatalf("expected 401, got %d", w.Code)
}
}
func TestWidgetAuthMiddleware_CorrectToken(t *testing.T) {
called := false
inner := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { called = true })
h := WidgetAuthMiddleware("secret", inner)
req := httptest.NewRequest("GET", "/api/widget", nil)
req.Header.Set("Authorization", "Bearer secret")
w := httptest.NewRecorder()
h.ServeHTTP(w, req)
if !called {
t.Fatal("inner handler should have been called")
}
}
func TestTimelineItemToWidgetItem_Task(t *testing.T) {
now := time.Now()
item := models.TimelineItem{
ID: "abc",
Title: "Write notes",
Source: "doot",
Type: models.TimelineItemTypeTask,
Time: now,
IsAllDay: true,
URL: "https://example.com/task/abc",
}
wi := TimelineItemToWidgetItem(item)
if wi.ID != "abc" {
t.Errorf("ID: got %q, want %q", wi.ID, "abc")
}
if wi.Type != "task" {
t.Errorf("Type: got %q, want %q", wi.Type, "task")
}
if !wi.Completable {
t.Error("doot task should be completable")
}
if wi.Start != nil {
t.Error("all-day item should have nil Start")
}
}
func TestTimelineItemToWidgetItem_Event(t *testing.T) {
start := time.Now()
end := start.Add(time.Hour)
item := models.TimelineItem{
ID: "cal1",
Title: "Team sync",
Source: "calendar",
Type: models.TimelineItemTypeEvent,
Time: start,
EndTime: &end,
}
wi := TimelineItemToWidgetItem(item)
if wi.Type != "event" {
t.Errorf("Type: got %q, want %q", wi.Type, "event")
}
if wi.Completable {
t.Error("calendar event should not be completable")
}
if wi.Start == nil {
t.Error("timed event should have non-nil Start")
}
}
func TestHandleWidgetComplete_NonCompletable(t *testing.T) {
h := &Handler{}
body := `{"id":"x","source":"calendar"}`
req := httptest.NewRequest("POST", "/api/widget/complete", strings.NewReader(body))
w := httptest.NewRecorder()
http.HandlerFunc(h.HandleWidgetComplete).ServeHTTP(w, req)
if w.Code != http.StatusBadRequest {
t.Fatalf("expected 400, got %d", w.Code)
}
}
// 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)
req := httptest.NewRequest("GET", "/api/widget", nil)
req.Header.Set("Authorization", "Bearer ")
w := httptest.NewRecorder()
h.ServeHTTP(w, req)
if w.Code != http.StatusUnauthorized {
t.Fatalf("expected 401 with empty token, got %d", w.Code)
}
}
|