1// SPDX-FileCopyrightText: Amolith <amolith@secluded.site>
2//
3// SPDX-License-Identifier: AGPL-3.0-or-later
4
5package lunatask_test
6
7import (
8 "errors"
9 "testing"
10
11 lunatask "git.secluded.site/go-lunatask"
12)
13
14// --- Eisenhower String Method ---
15
16func TestEisenhower_String(t *testing.T) {
17 t.Parallel()
18
19 tests := []struct {
20 name string
21 value lunatask.Eisenhower
22 want string
23 }{
24 {"uncategorized", lunatask.EisenhowerUncategorized, "uncategorized"},
25 {"do_now", lunatask.EisenhowerDoNow, "do now (important + urgent)"},
26 {"delegate", lunatask.EisenhowerDelegate, "delegate (urgent)"},
27 {"do_later", lunatask.EisenhowerDoLater, "do later (important)"},
28 {"eliminate", lunatask.EisenhowerEliminate, "eliminate"},
29 }
30
31 for _, tc := range tests {
32 t.Run(tc.name, func(t *testing.T) {
33 t.Parallel()
34
35 if got := tc.value.String(); got != tc.want {
36 t.Errorf("Eisenhower(%d).String() = %q, want %q", tc.value, got, tc.want)
37 }
38 })
39 }
40}
41
42func TestEisenhower_String_Unknown(t *testing.T) {
43 t.Parallel()
44
45 unknown := lunatask.Eisenhower(99)
46 want := "Eisenhower(99)"
47
48 if got := unknown.String(); got != want {
49 t.Errorf("Eisenhower(99).String() = %q, want %q", got, want)
50 }
51}
52
53// --- Eisenhower Type Constants ---
54
55func TestEisenhower_Constants(t *testing.T) {
56 t.Parallel()
57
58 tests := []struct {
59 name string
60 value lunatask.Eisenhower
61 want int
62 }{
63 {"uncategorized", lunatask.EisenhowerUncategorized, 0},
64 {"do_now", lunatask.EisenhowerDoNow, 1},
65 {"delegate", lunatask.EisenhowerDelegate, 2},
66 {"do_later", lunatask.EisenhowerDoLater, 3},
67 {"eliminate", lunatask.EisenhowerEliminate, 4},
68 }
69
70 for _, tc := range tests {
71 t.Run(tc.name, func(t *testing.T) {
72 t.Parallel()
73
74 if int(tc.value) != tc.want {
75 t.Errorf("Eisenhower constant = %d, want %d", tc.value, tc.want)
76 }
77 })
78 }
79}
80
81// --- Eisenhower Helper Methods ---
82
83func TestEisenhower_IsUrgent(t *testing.T) {
84 t.Parallel()
85
86 tests := []struct {
87 name string
88 value lunatask.Eisenhower
89 want bool
90 }{
91 {"uncategorized", lunatask.EisenhowerUncategorized, false},
92 {"do_now", lunatask.EisenhowerDoNow, true}, // urgent + important
93 {"delegate", lunatask.EisenhowerDelegate, true}, // urgent only
94 {"do_later", lunatask.EisenhowerDoLater, false}, // important only
95 {"eliminate", lunatask.EisenhowerEliminate, false}, // neither
96 }
97
98 for _, tc := range tests {
99 t.Run(tc.name, func(t *testing.T) {
100 t.Parallel()
101
102 if got := tc.value.IsUrgent(); got != tc.want {
103 t.Errorf("Eisenhower(%d).IsUrgent() = %v, want %v", tc.value, got, tc.want)
104 }
105 })
106 }
107}
108
109func TestEisenhower_IsImportant(t *testing.T) {
110 t.Parallel()
111
112 tests := []struct {
113 name string
114 value lunatask.Eisenhower
115 want bool
116 }{
117 {"uncategorized", lunatask.EisenhowerUncategorized, false},
118 {"do_now", lunatask.EisenhowerDoNow, true}, // urgent + important
119 {"delegate", lunatask.EisenhowerDelegate, false}, // urgent only
120 {"do_later", lunatask.EisenhowerDoLater, true}, // important only
121 {"eliminate", lunatask.EisenhowerEliminate, false}, // neither
122 }
123
124 for _, tc := range tests {
125 t.Run(tc.name, func(t *testing.T) {
126 t.Parallel()
127
128 if got := tc.value.IsImportant(); got != tc.want {
129 t.Errorf("Eisenhower(%d).IsImportant() = %v, want %v", tc.value, got, tc.want)
130 }
131 })
132 }
133}
134
135// --- Eisenhower Helper Function ---
136
137func TestNewEisenhower(t *testing.T) {
138 t.Parallel()
139
140 tests := []struct {
141 name string
142 important bool
143 urgent bool
144 want lunatask.Eisenhower
145 }{
146 {"both", true, true, lunatask.EisenhowerDoNow},
147 {"urgent_only", false, true, lunatask.EisenhowerDelegate},
148 {"important_only", true, false, lunatask.EisenhowerDoLater},
149 {"neither", false, false, lunatask.EisenhowerEliminate},
150 }
151
152 for _, testCase := range tests {
153 t.Run(testCase.name, func(t *testing.T) {
154 t.Parallel()
155
156 got := lunatask.NewEisenhower(testCase.important, testCase.urgent)
157 if got != testCase.want {
158 t.Errorf("NewEisenhower(%v, %v) = %d, want %d", testCase.important, testCase.urgent, got, testCase.want)
159 }
160 })
161 }
162}
163
164// --- TaskBuilder Semantic Methods ---
165
166func TestTaskBuilder_Important(t *testing.T) {
167 t.Parallel()
168
169 server, capture := newPOSTServer(t, "/tasks", singleTaskResponseBody)
170 defer server.Close()
171
172 client := lunatask.NewClient(testToken, lunatask.BaseURL(server.URL))
173
174 _, err := client.NewTask("Important task").
175 Important().
176 Create(ctx())
177 if err != nil {
178 t.Fatalf("error = %v", err)
179 }
180
181 // Important only = 3 (DoLater)
182 assertBodyFieldFloat(t, capture.Body, "eisenhower", 3)
183}
184
185func TestTaskBuilder_Urgent(t *testing.T) {
186 t.Parallel()
187
188 server, capture := newPOSTServer(t, "/tasks", singleTaskResponseBody)
189 defer server.Close()
190
191 client := lunatask.NewClient(testToken, lunatask.BaseURL(server.URL))
192
193 _, err := client.NewTask("Urgent task").
194 Urgent().
195 Create(ctx())
196 if err != nil {
197 t.Fatalf("error = %v", err)
198 }
199
200 // Urgent only = 2 (Delegate)
201 assertBodyFieldFloat(t, capture.Body, "eisenhower", 2)
202}
203
204func TestTaskBuilder_ImportantAndUrgent(t *testing.T) {
205 t.Parallel()
206
207 server, capture := newPOSTServer(t, "/tasks", singleTaskResponseBody)
208 defer server.Close()
209
210 client := lunatask.NewClient(testToken, lunatask.BaseURL(server.URL))
211
212 _, err := client.NewTask("Do now task").
213 Important().
214 Urgent().
215 Create(ctx())
216 if err != nil {
217 t.Fatalf("error = %v", err)
218 }
219
220 // Both = 1 (DoNow)
221 assertBodyFieldFloat(t, capture.Body, "eisenhower", 1)
222}
223
224func TestTaskBuilder_UrgentAndImportant_ReverseOrder(t *testing.T) {
225 t.Parallel()
226
227 server, capture := newPOSTServer(t, "/tasks", singleTaskResponseBody)
228 defer server.Close()
229
230 client := lunatask.NewClient(testToken, lunatask.BaseURL(server.URL))
231
232 _, err := client.NewTask("Do now task").
233 Urgent().
234 Important().
235 Create(ctx())
236 if err != nil {
237 t.Fatalf("error = %v", err)
238 }
239
240 // Both = 1 (DoNow), order shouldn't matter
241 assertBodyFieldFloat(t, capture.Body, "eisenhower", 1)
242}
243
244func TestTaskBuilder_NeitherImportantNorUrgent(t *testing.T) {
245 t.Parallel()
246
247 server, capture := newPOSTServer(t, "/tasks", singleTaskResponseBody)
248 defer server.Close()
249
250 client := lunatask.NewClient(testToken, lunatask.BaseURL(server.URL))
251
252 // Explicitly set neither - should result in Eliminate (4)
253 _, err := client.NewTask("Eliminate task").
254 NotImportant().
255 NotUrgent().
256 Create(ctx())
257 if err != nil {
258 t.Fatalf("error = %v", err)
259 }
260
261 // Neither = 4 (Eliminate)
262 assertBodyFieldFloat(t, capture.Body, "eisenhower", 4)
263}
264
265// --- TaskUpdateBuilder Semantic Methods ---
266
267func TestTaskUpdateBuilder_Important(t *testing.T) {
268 t.Parallel()
269
270 server, capture := newPUTServer(t, "/tasks/"+taskID, singleTaskResponseBody)
271 defer server.Close()
272
273 client := lunatask.NewClient(testToken, lunatask.BaseURL(server.URL))
274
275 _, err := client.NewTaskUpdate(taskID).
276 Important().
277 Update(ctx())
278 if err != nil {
279 t.Fatalf("error = %v", err)
280 }
281
282 // Important only = 3 (DoLater)
283 assertBodyFieldFloat(t, capture.Body, "eisenhower", 3)
284}
285
286func TestTaskUpdateBuilder_Urgent(t *testing.T) {
287 t.Parallel()
288
289 server, capture := newPUTServer(t, "/tasks/"+taskID, singleTaskResponseBody)
290 defer server.Close()
291
292 client := lunatask.NewClient(testToken, lunatask.BaseURL(server.URL))
293
294 _, err := client.NewTaskUpdate(taskID).
295 Urgent().
296 Update(ctx())
297 if err != nil {
298 t.Fatalf("error = %v", err)
299 }
300
301 // Urgent only = 2 (Delegate)
302 assertBodyFieldFloat(t, capture.Body, "eisenhower", 2)
303}
304
305func TestTaskUpdateBuilder_ImportantAndUrgent(t *testing.T) {
306 t.Parallel()
307
308 server, capture := newPUTServer(t, "/tasks/"+taskID, singleTaskResponseBody)
309 defer server.Close()
310
311 client := lunatask.NewClient(testToken, lunatask.BaseURL(server.URL))
312
313 _, err := client.NewTaskUpdate(taskID).
314 Important().
315 Urgent().
316 Update(ctx())
317 if err != nil {
318 t.Fatalf("error = %v", err)
319 }
320
321 // Both = 1 (DoNow)
322 assertBodyFieldFloat(t, capture.Body, "eisenhower", 1)
323}
324
325func TestTaskUpdateBuilder_NeitherImportantNorUrgent(t *testing.T) {
326 t.Parallel()
327
328 server, capture := newPUTServer(t, "/tasks/"+taskID, singleTaskResponseBody)
329 defer server.Close()
330
331 client := lunatask.NewClient(testToken, lunatask.BaseURL(server.URL))
332
333 _, err := client.NewTaskUpdate(taskID).
334 NotImportant().
335 NotUrgent().
336 Update(ctx())
337 if err != nil {
338 t.Fatalf("error = %v", err)
339 }
340
341 // Neither = 4 (Eliminate)
342 assertBodyFieldFloat(t, capture.Body, "eisenhower", 4)
343}
344
345// --- WithEisenhower still works with typed constant ---
346
347func TestTaskBuilder_WithEisenhowerTyped(t *testing.T) {
348 t.Parallel()
349
350 server, capture := newPOSTServer(t, "/tasks", singleTaskResponseBody)
351 defer server.Close()
352
353 client := lunatask.NewClient(testToken, lunatask.BaseURL(server.URL))
354
355 _, err := client.NewTask("Typed eisenhower").
356 WithEisenhower(lunatask.EisenhowerDoNow).
357 Create(ctx())
358 if err != nil {
359 t.Fatalf("error = %v", err)
360 }
361
362 assertBodyFieldFloat(t, capture.Body, "eisenhower", 1)
363}
364
365func TestTaskUpdateBuilder_WithEisenhowerTyped(t *testing.T) {
366 t.Parallel()
367
368 server, capture := newPUTServer(t, "/tasks/"+taskID, singleTaskResponseBody)
369 defer server.Close()
370
371 client := lunatask.NewClient(testToken, lunatask.BaseURL(server.URL))
372
373 _, err := client.NewTaskUpdate(taskID).
374 WithEisenhower(lunatask.EisenhowerDelegate).
375 Update(ctx())
376 if err != nil {
377 t.Fatalf("error = %v", err)
378 }
379
380 assertBodyFieldFloat(t, capture.Body, "eisenhower", 2)
381}
382
383// --- ParseEisenhower ---
384
385func TestParseEisenhower(t *testing.T) {
386 t.Parallel()
387
388 tests := []struct {
389 name string
390 input string
391 want lunatask.Eisenhower
392 wantErr bool
393 }{
394 // Text values
395 {"uncategorized_lower", "uncategorized", lunatask.EisenhowerUncategorized, false},
396 {"uncategorized_upper", "UNCATEGORIZED", lunatask.EisenhowerUncategorized, false},
397 {"do_now_lower", "do-now", lunatask.EisenhowerDoNow, false},
398 {"do_now_upper", "DO-NOW", lunatask.EisenhowerDoNow, false},
399 {"donow_lower", "donow", lunatask.EisenhowerDoNow, false},
400 {"donow_upper", "DONOW", lunatask.EisenhowerDoNow, false},
401 {"delegate_lower", "delegate", lunatask.EisenhowerDelegate, false},
402 {"delegate_upper", "DELEGATE", lunatask.EisenhowerDelegate, false},
403 {"do_later_lower", "do-later", lunatask.EisenhowerDoLater, false},
404 {"do_later_upper", "DO-LATER", lunatask.EisenhowerDoLater, false},
405 {"dolater_lower", "dolater", lunatask.EisenhowerDoLater, false},
406 {"dolater_upper", "DOLATER", lunatask.EisenhowerDoLater, false},
407 {"eliminate_lower", "eliminate", lunatask.EisenhowerEliminate, false},
408 {"eliminate_upper", "ELIMINATE", lunatask.EisenhowerEliminate, false},
409
410 // Numeric values
411 {"zero", "0", lunatask.EisenhowerUncategorized, false},
412 {"one", "1", lunatask.EisenhowerDoNow, false},
413 {"two", "2", lunatask.EisenhowerDelegate, false},
414 {"three", "3", lunatask.EisenhowerDoLater, false},
415 {"four", "4", lunatask.EisenhowerEliminate, false},
416
417 // Invalid values
418 {"invalid", "invalid", 0, true},
419 {"empty", "", 0, true},
420 {"five", "5", 0, true},
421 {"negative", "-1", 0, true},
422 {"typo", "elimate", 0, true},
423 }
424
425 for _, testCase := range tests {
426 t.Run(testCase.name, func(t *testing.T) {
427 t.Parallel()
428
429 got, err := lunatask.ParseEisenhower(testCase.input)
430 if (err != nil) != testCase.wantErr {
431 t.Errorf("ParseEisenhower(%q) error = %v, wantErr %v", testCase.input, err, testCase.wantErr)
432
433 return
434 }
435
436 if testCase.wantErr {
437 if !errors.Is(err, lunatask.ErrInvalidEisenhower) {
438 t.Errorf("ParseEisenhower(%q) error = %v, want ErrInvalidEisenhower", testCase.input, err)
439 }
440
441 return
442 }
443
444 if got != testCase.want {
445 t.Errorf("ParseEisenhower(%q) = %v, want %v", testCase.input, got, testCase.want)
446 }
447 })
448 }
449}