@@ -0,0 +1,326 @@
+// SPDX-FileCopyrightText: Amolith <amolith@secluded.site>
+//
+// SPDX-License-Identifier: AGPL-3.0-or-later
+
+package lunatask_test
+
+import (
+ "slices"
+ "testing"
+
+ lunatask "git.secluded.site/go-lunatask"
+)
+
+func TestParseWorkflow(t *testing.T) {
+ t.Parallel()
+
+ tests := []struct {
+ name string
+ input string
+ want lunatask.Workflow
+ wantErr bool
+ }{
+ {"priority_list_snake", "priority_list", lunatask.WorkflowPriorityList, false},
+ {"priority_list_kebab", "priority-list", lunatask.WorkflowPriorityList, false},
+ {"priority_list_upper", "PRIORITY_LIST", lunatask.WorkflowPriorityList, false},
+ {"priority_list_mixed", "Priority-List", lunatask.WorkflowPriorityList, false},
+ {"now_later_snake", "now_later", lunatask.WorkflowNowLater, false},
+ {"now_later_kebab", "now-later", lunatask.WorkflowNowLater, false},
+ {"now_later_upper", "NOW_LATER", lunatask.WorkflowNowLater, false},
+ {"kanban_lower", "kanban", lunatask.WorkflowKanban, false},
+ {"kanban_upper", "KANBAN", lunatask.WorkflowKanban, false},
+ {"plan_your_days_snake", "plan_your_days", lunatask.WorkflowPlanYourDays, false},
+ {"plan_your_days_kebab", "plan-your-days", lunatask.WorkflowPlanYourDays, false},
+ {"must_should_want_snake", "must_should_want", lunatask.WorkflowMustShouldWant, false},
+ {"must_should_want_kebab", "must-should-want", lunatask.WorkflowMustShouldWant, false},
+ {"eisenhower_lower", "eisenhower", lunatask.WorkflowEisenhower, false},
+ {"eisenhower_upper", "EISENHOWER", lunatask.WorkflowEisenhower, false},
+ {"invalid", "invalid", "", true},
+ {"empty", "", "", true},
+ {"numeric", "1", "", true},
+ {"typo", "kanbann", "", true},
+ }
+
+ for _, testCase := range tests {
+ t.Run(testCase.name, func(t *testing.T) {
+ t.Parallel()
+
+ got, err := lunatask.ParseWorkflow(testCase.input)
+ if (err != nil) != testCase.wantErr {
+ t.Errorf("ParseWorkflow(%q) error = %v, wantErr %v", testCase.input, err, testCase.wantErr)
+
+ return
+ }
+
+ if !testCase.wantErr && got != testCase.want {
+ t.Errorf("ParseWorkflow(%q) = %q, want %q", testCase.input, got, testCase.want)
+ }
+ })
+ }
+}
+
+func TestWorkflow_String(t *testing.T) {
+ t.Parallel()
+
+ tests := []struct {
+ name string
+ value lunatask.Workflow
+ want string
+ }{
+ {"priority_list", lunatask.WorkflowPriorityList, "priority_list"},
+ {"now_later", lunatask.WorkflowNowLater, "now_later"},
+ {"kanban", lunatask.WorkflowKanban, "kanban"},
+ {"plan_your_days", lunatask.WorkflowPlanYourDays, "plan_your_days"},
+ {"must_should_want", lunatask.WorkflowMustShouldWant, "must_should_want"},
+ {"eisenhower", lunatask.WorkflowEisenhower, "eisenhower"},
+ }
+
+ for _, tc := range tests {
+ t.Run(tc.name, func(t *testing.T) {
+ t.Parallel()
+
+ if got := tc.value.String(); got != tc.want {
+ t.Errorf("Workflow.String() = %q, want %q", got, tc.want)
+ }
+ })
+ }
+}
+
+func TestWorkflow_ValidStatuses(t *testing.T) {
+ t.Parallel()
+
+ tests := []struct {
+ name string
+ workflow lunatask.Workflow
+ want []lunatask.TaskStatus
+ }{
+ {
+ "priority_list",
+ lunatask.WorkflowPriorityList,
+ nil,
+ },
+ {
+ "now_later",
+ lunatask.WorkflowNowLater,
+ []lunatask.TaskStatus{lunatask.StatusLater, lunatask.StatusInProgress, lunatask.StatusCompleted},
+ },
+ {
+ "kanban",
+ lunatask.WorkflowKanban,
+ []lunatask.TaskStatus{
+ lunatask.StatusLater, lunatask.StatusNext, lunatask.StatusInProgress,
+ lunatask.StatusWaiting, lunatask.StatusCompleted,
+ },
+ },
+ {
+ "plan_your_days",
+ lunatask.WorkflowPlanYourDays,
+ []lunatask.TaskStatus{lunatask.StatusCompleted},
+ },
+ {
+ "must_should_want",
+ lunatask.WorkflowMustShouldWant,
+ []lunatask.TaskStatus{lunatask.StatusCompleted},
+ },
+ {
+ "eisenhower",
+ lunatask.WorkflowEisenhower,
+ []lunatask.TaskStatus{lunatask.StatusCompleted},
+ },
+ {
+ "unknown",
+ lunatask.Workflow("unknown"),
+ nil,
+ },
+ }
+
+ for _, testCase := range tests {
+ t.Run(testCase.name, func(t *testing.T) {
+ t.Parallel()
+
+ got := testCase.workflow.ValidStatuses()
+ if !slices.Equal(got, testCase.want) {
+ t.Errorf("Workflow.ValidStatuses() = %v, want %v", got, testCase.want)
+ }
+ })
+ }
+}
+
+func TestWorkflow_UsesMotivation(t *testing.T) {
+ t.Parallel()
+
+ tests := []struct {
+ name string
+ workflow lunatask.Workflow
+ want bool
+ }{
+ {"priority_list", lunatask.WorkflowPriorityList, false},
+ {"now_later", lunatask.WorkflowNowLater, false},
+ {"kanban", lunatask.WorkflowKanban, false},
+ {"plan_your_days", lunatask.WorkflowPlanYourDays, false},
+ {"must_should_want", lunatask.WorkflowMustShouldWant, true},
+ {"eisenhower", lunatask.WorkflowEisenhower, false},
+ }
+
+ for _, tc := range tests {
+ t.Run(tc.name, func(t *testing.T) {
+ t.Parallel()
+
+ if got := tc.workflow.UsesMotivation(); got != tc.want {
+ t.Errorf("Workflow.UsesMotivation() = %v, want %v", got, tc.want)
+ }
+ })
+ }
+}
+
+func TestWorkflow_UsesEisenhower(t *testing.T) {
+ t.Parallel()
+
+ tests := []struct {
+ name string
+ workflow lunatask.Workflow
+ want bool
+ }{
+ {"priority_list", lunatask.WorkflowPriorityList, false},
+ {"now_later", lunatask.WorkflowNowLater, false},
+ {"kanban", lunatask.WorkflowKanban, false},
+ {"plan_your_days", lunatask.WorkflowPlanYourDays, false},
+ {"must_should_want", lunatask.WorkflowMustShouldWant, false},
+ {"eisenhower", lunatask.WorkflowEisenhower, true},
+ }
+
+ for _, tc := range tests {
+ t.Run(tc.name, func(t *testing.T) {
+ t.Parallel()
+
+ if got := tc.workflow.UsesEisenhower(); got != tc.want {
+ t.Errorf("Workflow.UsesEisenhower() = %v, want %v", got, tc.want)
+ }
+ })
+ }
+}
+
+func TestWorkflow_UsesScheduling(t *testing.T) {
+ t.Parallel()
+
+ tests := []struct {
+ name string
+ workflow lunatask.Workflow
+ want bool
+ }{
+ {"priority_list", lunatask.WorkflowPriorityList, false},
+ {"now_later", lunatask.WorkflowNowLater, false},
+ {"kanban", lunatask.WorkflowKanban, false},
+ {"plan_your_days", lunatask.WorkflowPlanYourDays, true},
+ {"must_should_want", lunatask.WorkflowMustShouldWant, false},
+ {"eisenhower", lunatask.WorkflowEisenhower, false},
+ }
+
+ for _, tc := range tests {
+ t.Run(tc.name, func(t *testing.T) {
+ t.Parallel()
+
+ if got := tc.workflow.UsesScheduling(); got != tc.want {
+ t.Errorf("Workflow.UsesScheduling() = %v, want %v", got, tc.want)
+ }
+ })
+ }
+}
+
+func TestWorkflow_UsesPriority(t *testing.T) {
+ t.Parallel()
+
+ tests := []struct {
+ name string
+ workflow lunatask.Workflow
+ want bool
+ }{
+ {"priority_list", lunatask.WorkflowPriorityList, true},
+ {"now_later", lunatask.WorkflowNowLater, false},
+ {"kanban", lunatask.WorkflowKanban, false},
+ {"plan_your_days", lunatask.WorkflowPlanYourDays, false},
+ {"must_should_want", lunatask.WorkflowMustShouldWant, false},
+ {"eisenhower", lunatask.WorkflowEisenhower, false},
+ }
+
+ for _, tc := range tests {
+ t.Run(tc.name, func(t *testing.T) {
+ t.Parallel()
+
+ if got := tc.workflow.UsesPriority(); got != tc.want {
+ t.Errorf("Workflow.UsesPriority() = %v, want %v", got, tc.want)
+ }
+ })
+ }
+}
+
+func TestWorkflow_Description(t *testing.T) {
+ t.Parallel()
+
+ tests := []struct {
+ name string
+ workflow lunatask.Workflow
+ contains string
+ }{
+ {"priority_list", lunatask.WorkflowPriorityList, "priority"},
+ {"now_later", lunatask.WorkflowNowLater, "in-progress"},
+ {"kanban", lunatask.WorkflowKanban, "kanban"},
+ {"plan_your_days", lunatask.WorkflowPlanYourDays, "scheduled_on"},
+ {"must_should_want", lunatask.WorkflowMustShouldWant, "must"},
+ {"eisenhower", lunatask.WorkflowEisenhower, "Eisenhower"},
+ {"unknown", lunatask.Workflow("unknown"), "Unknown"},
+ }
+
+ for _, testCase := range tests {
+ t.Run(testCase.name, func(t *testing.T) {
+ t.Parallel()
+
+ got := testCase.workflow.Description()
+ if got == "" {
+ t.Error("Workflow.Description() returned empty string")
+ }
+
+ if !contains(got, testCase.contains) {
+ t.Errorf("Workflow.Description() = %q, want to contain %q", got, testCase.contains)
+ }
+ })
+ }
+}
+
+func TestWorkflows(t *testing.T) {
+ t.Parallel()
+
+ workflows := lunatask.Workflows()
+
+ if len(workflows) != 6 {
+ t.Errorf("Workflows() returned %d workflows, want 6", len(workflows))
+ }
+
+ expected := []lunatask.Workflow{
+ lunatask.WorkflowPriorityList,
+ lunatask.WorkflowNowLater,
+ lunatask.WorkflowKanban,
+ lunatask.WorkflowPlanYourDays,
+ lunatask.WorkflowMustShouldWant,
+ lunatask.WorkflowEisenhower,
+ }
+
+ if !slices.Equal(workflows, expected) {
+ t.Errorf("Workflows() = %v, want %v", workflows, expected)
+ }
+}
+
+func contains(s, substr string) bool {
+ return len(s) >= len(substr) && (s == substr || len(substr) == 0 ||
+ (len(s) > 0 && len(substr) > 0 && findSubstring(s, substr)))
+}
+
+func findSubstring(s, substr string) bool {
+ for i := 0; i <= len(s)-len(substr); i++ {
+ if s[i:i+len(substr)] == substr {
+ return true
+ }
+ }
+
+ return false
+}