eisenhower_test.go

  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}