workflow_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	"slices"
  9	"testing"
 10
 11	lunatask "git.secluded.site/go-lunatask"
 12)
 13
 14func TestParseWorkflow(t *testing.T) {
 15	t.Parallel()
 16
 17	tests := []struct {
 18		name    string
 19		input   string
 20		want    lunatask.Workflow
 21		wantErr bool
 22	}{
 23		{"priority_list_snake", "priority_list", lunatask.WorkflowPriorityList, false},
 24		{"priority_list_kebab", "priority-list", lunatask.WorkflowPriorityList, false},
 25		{"priority_list_upper", "PRIORITY_LIST", lunatask.WorkflowPriorityList, false},
 26		{"priority_list_mixed", "Priority-List", lunatask.WorkflowPriorityList, false},
 27		{"now_later_snake", "now_later", lunatask.WorkflowNowLater, false},
 28		{"now_later_kebab", "now-later", lunatask.WorkflowNowLater, false},
 29		{"now_later_upper", "NOW_LATER", lunatask.WorkflowNowLater, false},
 30		{"kanban_lower", "kanban", lunatask.WorkflowKanban, false},
 31		{"kanban_upper", "KANBAN", lunatask.WorkflowKanban, false},
 32		{"plan_your_days_snake", "plan_your_days", lunatask.WorkflowPlanYourDays, false},
 33		{"plan_your_days_kebab", "plan-your-days", lunatask.WorkflowPlanYourDays, false},
 34		{"must_should_want_snake", "must_should_want", lunatask.WorkflowMustShouldWant, false},
 35		{"must_should_want_kebab", "must-should-want", lunatask.WorkflowMustShouldWant, false},
 36		{"eisenhower_lower", "eisenhower", lunatask.WorkflowEisenhower, false},
 37		{"eisenhower_upper", "EISENHOWER", lunatask.WorkflowEisenhower, false},
 38		{"invalid", "invalid", "", true},
 39		{"empty", "", "", true},
 40		{"numeric", "1", "", true},
 41		{"typo", "kanbann", "", true},
 42	}
 43
 44	for _, testCase := range tests {
 45		t.Run(testCase.name, func(t *testing.T) {
 46			t.Parallel()
 47
 48			got, err := lunatask.ParseWorkflow(testCase.input)
 49			if (err != nil) != testCase.wantErr {
 50				t.Errorf("ParseWorkflow(%q) error = %v, wantErr %v", testCase.input, err, testCase.wantErr)
 51
 52				return
 53			}
 54
 55			if !testCase.wantErr && got != testCase.want {
 56				t.Errorf("ParseWorkflow(%q) = %q, want %q", testCase.input, got, testCase.want)
 57			}
 58		})
 59	}
 60}
 61
 62func TestWorkflow_String(t *testing.T) {
 63	t.Parallel()
 64
 65	tests := []struct {
 66		name  string
 67		value lunatask.Workflow
 68		want  string
 69	}{
 70		{"priority_list", lunatask.WorkflowPriorityList, "priority_list"},
 71		{"now_later", lunatask.WorkflowNowLater, "now_later"},
 72		{"kanban", lunatask.WorkflowKanban, "kanban"},
 73		{"plan_your_days", lunatask.WorkflowPlanYourDays, "plan_your_days"},
 74		{"must_should_want", lunatask.WorkflowMustShouldWant, "must_should_want"},
 75		{"eisenhower", lunatask.WorkflowEisenhower, "eisenhower"},
 76	}
 77
 78	for _, tc := range tests {
 79		t.Run(tc.name, func(t *testing.T) {
 80			t.Parallel()
 81
 82			if got := tc.value.String(); got != tc.want {
 83				t.Errorf("Workflow.String() = %q, want %q", got, tc.want)
 84			}
 85		})
 86	}
 87}
 88
 89func TestWorkflow_ValidStatuses(t *testing.T) {
 90	t.Parallel()
 91
 92	tests := []struct {
 93		name     string
 94		workflow lunatask.Workflow
 95		want     []lunatask.TaskStatus
 96	}{
 97		{
 98			"priority_list",
 99			lunatask.WorkflowPriorityList,
100			nil,
101		},
102		{
103			"now_later",
104			lunatask.WorkflowNowLater,
105			[]lunatask.TaskStatus{lunatask.StatusLater, lunatask.StatusInProgress, lunatask.StatusCompleted},
106		},
107		{
108			"kanban",
109			lunatask.WorkflowKanban,
110			[]lunatask.TaskStatus{
111				lunatask.StatusLater, lunatask.StatusNext, lunatask.StatusInProgress,
112				lunatask.StatusWaiting, lunatask.StatusCompleted,
113			},
114		},
115		{
116			"plan_your_days",
117			lunatask.WorkflowPlanYourDays,
118			[]lunatask.TaskStatus{lunatask.StatusCompleted},
119		},
120		{
121			"must_should_want",
122			lunatask.WorkflowMustShouldWant,
123			[]lunatask.TaskStatus{lunatask.StatusCompleted},
124		},
125		{
126			"eisenhower",
127			lunatask.WorkflowEisenhower,
128			[]lunatask.TaskStatus{lunatask.StatusCompleted},
129		},
130		{
131			"unknown",
132			lunatask.Workflow("unknown"),
133			nil,
134		},
135	}
136
137	for _, testCase := range tests {
138		t.Run(testCase.name, func(t *testing.T) {
139			t.Parallel()
140
141			got := testCase.workflow.ValidStatuses()
142			if !slices.Equal(got, testCase.want) {
143				t.Errorf("Workflow.ValidStatuses() = %v, want %v", got, testCase.want)
144			}
145		})
146	}
147}
148
149func TestWorkflow_UsesMotivation(t *testing.T) {
150	t.Parallel()
151
152	tests := []struct {
153		name     string
154		workflow lunatask.Workflow
155		want     bool
156	}{
157		{"priority_list", lunatask.WorkflowPriorityList, false},
158		{"now_later", lunatask.WorkflowNowLater, false},
159		{"kanban", lunatask.WorkflowKanban, false},
160		{"plan_your_days", lunatask.WorkflowPlanYourDays, false},
161		{"must_should_want", lunatask.WorkflowMustShouldWant, true},
162		{"eisenhower", lunatask.WorkflowEisenhower, false},
163	}
164
165	for _, tc := range tests {
166		t.Run(tc.name, func(t *testing.T) {
167			t.Parallel()
168
169			if got := tc.workflow.UsesMotivation(); got != tc.want {
170				t.Errorf("Workflow.UsesMotivation() = %v, want %v", got, tc.want)
171			}
172		})
173	}
174}
175
176func TestWorkflow_UsesEisenhower(t *testing.T) {
177	t.Parallel()
178
179	tests := []struct {
180		name     string
181		workflow lunatask.Workflow
182		want     bool
183	}{
184		{"priority_list", lunatask.WorkflowPriorityList, false},
185		{"now_later", lunatask.WorkflowNowLater, false},
186		{"kanban", lunatask.WorkflowKanban, false},
187		{"plan_your_days", lunatask.WorkflowPlanYourDays, false},
188		{"must_should_want", lunatask.WorkflowMustShouldWant, false},
189		{"eisenhower", lunatask.WorkflowEisenhower, true},
190	}
191
192	for _, tc := range tests {
193		t.Run(tc.name, func(t *testing.T) {
194			t.Parallel()
195
196			if got := tc.workflow.UsesEisenhower(); got != tc.want {
197				t.Errorf("Workflow.UsesEisenhower() = %v, want %v", got, tc.want)
198			}
199		})
200	}
201}
202
203func TestWorkflow_UsesScheduling(t *testing.T) {
204	t.Parallel()
205
206	tests := []struct {
207		name     string
208		workflow lunatask.Workflow
209		want     bool
210	}{
211		{"priority_list", lunatask.WorkflowPriorityList, false},
212		{"now_later", lunatask.WorkflowNowLater, false},
213		{"kanban", lunatask.WorkflowKanban, false},
214		{"plan_your_days", lunatask.WorkflowPlanYourDays, true},
215		{"must_should_want", lunatask.WorkflowMustShouldWant, false},
216		{"eisenhower", lunatask.WorkflowEisenhower, false},
217	}
218
219	for _, tc := range tests {
220		t.Run(tc.name, func(t *testing.T) {
221			t.Parallel()
222
223			if got := tc.workflow.UsesScheduling(); got != tc.want {
224				t.Errorf("Workflow.UsesScheduling() = %v, want %v", got, tc.want)
225			}
226		})
227	}
228}
229
230func TestWorkflow_UsesPriority(t *testing.T) {
231	t.Parallel()
232
233	tests := []struct {
234		name     string
235		workflow lunatask.Workflow
236		want     bool
237	}{
238		{"priority_list", lunatask.WorkflowPriorityList, true},
239		{"now_later", lunatask.WorkflowNowLater, false},
240		{"kanban", lunatask.WorkflowKanban, false},
241		{"plan_your_days", lunatask.WorkflowPlanYourDays, false},
242		{"must_should_want", lunatask.WorkflowMustShouldWant, false},
243		{"eisenhower", lunatask.WorkflowEisenhower, false},
244	}
245
246	for _, tc := range tests {
247		t.Run(tc.name, func(t *testing.T) {
248			t.Parallel()
249
250			if got := tc.workflow.UsesPriority(); got != tc.want {
251				t.Errorf("Workflow.UsesPriority() = %v, want %v", got, tc.want)
252			}
253		})
254	}
255}
256
257func TestWorkflow_Description(t *testing.T) {
258	t.Parallel()
259
260	tests := []struct {
261		name     string
262		workflow lunatask.Workflow
263		contains string
264	}{
265		{"priority_list", lunatask.WorkflowPriorityList, "priority"},
266		{"now_later", lunatask.WorkflowNowLater, "in-progress"},
267		{"kanban", lunatask.WorkflowKanban, "kanban"},
268		{"plan_your_days", lunatask.WorkflowPlanYourDays, "scheduled_on"},
269		{"must_should_want", lunatask.WorkflowMustShouldWant, "must"},
270		{"eisenhower", lunatask.WorkflowEisenhower, "Eisenhower"},
271		{"unknown", lunatask.Workflow("unknown"), "Unknown"},
272	}
273
274	for _, testCase := range tests {
275		t.Run(testCase.name, func(t *testing.T) {
276			t.Parallel()
277
278			got := testCase.workflow.Description()
279			if got == "" {
280				t.Error("Workflow.Description() returned empty string")
281			}
282
283			if !contains(got, testCase.contains) {
284				t.Errorf("Workflow.Description() = %q, want to contain %q", got, testCase.contains)
285			}
286		})
287	}
288}
289
290func TestWorkflows(t *testing.T) {
291	t.Parallel()
292
293	workflows := lunatask.Workflows()
294
295	if len(workflows) != 6 {
296		t.Errorf("Workflows() returned %d workflows, want 6", len(workflows))
297	}
298
299	expected := []lunatask.Workflow{
300		lunatask.WorkflowPriorityList,
301		lunatask.WorkflowNowLater,
302		lunatask.WorkflowKanban,
303		lunatask.WorkflowPlanYourDays,
304		lunatask.WorkflowMustShouldWant,
305		lunatask.WorkflowEisenhower,
306	}
307
308	if !slices.Equal(workflows, expected) {
309		t.Errorf("Workflows() = %v, want %v", workflows, expected)
310	}
311}
312
313func contains(s, substr string) bool {
314	return len(s) >= len(substr) && (s == substr || len(substr) == 0 ||
315		(len(s) > 0 && len(substr) > 0 && findSubstring(s, substr)))
316}
317
318func findSubstring(s, substr string) bool {
319	for i := 0; i <= len(s)-len(substr); i++ {
320		if s[i:i+len(substr)] == substr {
321			return true
322		}
323	}
324
325	return false
326}