Detailed changes
@@ -33,6 +33,17 @@ func (m Motivation) String() string {
return string(m)
}
+// AllMotivations returns all valid motivation values.
+// Includes [MotivationUnknown] which clears/unsets motivation.
+func AllMotivations() []Motivation {
+ return []Motivation{
+ MotivationUnknown,
+ MotivationMust,
+ MotivationShould,
+ MotivationWant,
+ }
+}
+
// ParseMotivation parses a string to a Motivation value (case-insensitive).
// Valid values: "unknown", "must", "should", "want".
func ParseMotivation(str string) (Motivation, error) {
@@ -10,6 +10,42 @@ import (
lunatask "git.secluded.site/go-lunatask"
)
+func TestAllMotivations(t *testing.T) {
+ t.Parallel()
+
+ motivations := lunatask.AllMotivations()
+
+ // Check count
+ if got := len(motivations); got != 4 {
+ t.Fatalf("AllMotivations() returned %d values, want 4", got)
+ }
+
+ // Check order
+ expected := []lunatask.Motivation{
+ lunatask.MotivationUnknown,
+ lunatask.MotivationMust,
+ lunatask.MotivationShould,
+ lunatask.MotivationWant,
+ }
+ for i, want := range expected {
+ if motivations[i] != want {
+ t.Errorf("AllMotivations()[%d] = %q, want %q", i, motivations[i], want)
+ }
+ }
+
+ // Check roundtrip: each value should be parseable
+ for _, motivation := range motivations {
+ parsed, err := lunatask.ParseMotivation(motivation.String())
+ if err != nil {
+ t.Errorf("ParseMotivation(%q) failed: %v", motivation.String(), err)
+ }
+
+ if parsed != motivation {
+ t.Errorf("ParseMotivation(%q) = %q, want %q", motivation.String(), parsed, motivation)
+ }
+ }
+}
+
func TestParseMotivation(t *testing.T) {
t.Parallel()
@@ -55,6 +55,17 @@ func (p *Priority) Valid() bool {
return *p >= PriorityLowest && *p <= PriorityHighest
}
+// AllPriorities returns all valid priority values from lowest to highest.
+func AllPriorities() []Priority {
+ return []Priority{
+ PriorityLowest,
+ PriorityLow,
+ PriorityNormal,
+ PriorityHigh,
+ PriorityHighest,
+ }
+}
+
// ParsePriority parses a string to a Priority value (case-insensitive).
// Valid values: "lowest", "low", "normal", "high", "highest".
func ParsePriority(str string) (Priority, error) {
@@ -11,6 +11,45 @@ import (
lunatask "git.secluded.site/go-lunatask"
)
+// --- AllPriorities Function ---
+
+func TestAllPriorities(t *testing.T) {
+ t.Parallel()
+
+ priorities := lunatask.AllPriorities()
+
+ // Check count
+ if got := len(priorities); got != 5 {
+ t.Fatalf("AllPriorities() returned %d values, want 5", got)
+ }
+
+ // Check order (lowest to highest)
+ expected := []lunatask.Priority{
+ lunatask.PriorityLowest,
+ lunatask.PriorityLow,
+ lunatask.PriorityNormal,
+ lunatask.PriorityHigh,
+ lunatask.PriorityHighest,
+ }
+ for i, want := range expected {
+ if priorities[i] != want {
+ t.Errorf("AllPriorities()[%d] = %d, want %d", i, priorities[i], want)
+ }
+ }
+
+ // Check roundtrip: each value should be parseable
+ for _, priority := range priorities {
+ parsed, err := lunatask.ParsePriority(priority.String())
+ if err != nil {
+ t.Errorf("ParsePriority(%q) failed: %v", priority.String(), err)
+ }
+
+ if parsed != priority {
+ t.Errorf("ParsePriority(%q) = %d, want %d", priority.String(), parsed, priority)
+ }
+ }
+}
+
// --- Priority Type Constants ---
func TestPriority_Constants(t *testing.T) {
@@ -36,6 +36,20 @@ func (r RelationshipStrength) String() string {
return string(r)
}
+// AllRelationshipStrengths returns all valid relationship strength values
+// from closest (family) to most distant (almost strangers).
+func AllRelationshipStrengths() []RelationshipStrength {
+ return []RelationshipStrength{
+ RelationshipFamily,
+ RelationshipIntimateFriend,
+ RelationshipCloseFriend,
+ RelationshipCasualFriend,
+ RelationshipAcquaintance,
+ RelationshipBusiness,
+ RelationshipAlmostStranger,
+ }
+}
+
// ParseRelationshipStrength parses a string to a RelationshipStrength value (case-insensitive).
// Valid values: "family", "intimate-friends", "close-friends", "casual-friends",
// "acquaintances", "business-contacts", "almost-strangers".
@@ -10,6 +10,45 @@ import (
lunatask "git.secluded.site/go-lunatask"
)
+func TestAllRelationshipStrengths(t *testing.T) {
+ t.Parallel()
+
+ strengths := lunatask.AllRelationshipStrengths()
+
+ // Check count
+ if got := len(strengths); got != 7 {
+ t.Fatalf("AllRelationshipStrengths() returned %d values, want 7", got)
+ }
+
+ // Check order (closest to most distant)
+ expected := []lunatask.RelationshipStrength{
+ lunatask.RelationshipFamily,
+ lunatask.RelationshipIntimateFriend,
+ lunatask.RelationshipCloseFriend,
+ lunatask.RelationshipCasualFriend,
+ lunatask.RelationshipAcquaintance,
+ lunatask.RelationshipBusiness,
+ lunatask.RelationshipAlmostStranger,
+ }
+ for i, want := range expected {
+ if strengths[i] != want {
+ t.Errorf("AllRelationshipStrengths()[%d] = %q, want %q", i, strengths[i], want)
+ }
+ }
+
+ // Check roundtrip: each value should be parseable
+ for _, strength := range strengths {
+ parsed, err := lunatask.ParseRelationshipStrength(strength.String())
+ if err != nil {
+ t.Errorf("ParseRelationshipStrength(%q) failed: %v", strength.String(), err)
+ }
+
+ if parsed != strength {
+ t.Errorf("ParseRelationshipStrength(%q) = %q, want %q", strength.String(), parsed, strength)
+ }
+ }
+}
+
func TestParseRelationshipStrength(t *testing.T) {
t.Parallel()
@@ -5,12 +5,15 @@
package lunatask
import (
+ "encoding/json"
"errors"
"fmt"
"strings"
)
// TaskStatus represents the workflow state of a task.
+//
+//nolint:recvcheck // MarshalJSON must use value receiver, UnmarshalJSON must use pointer
type TaskStatus string
// Valid task status values.
@@ -18,7 +21,7 @@ const (
// StatusLater is the default status for new tasks.
StatusLater TaskStatus = "later"
StatusNext TaskStatus = "next"
- StatusInProgress TaskStatus = "started"
+ StatusInProgress TaskStatus = "in-progress"
StatusWaiting TaskStatus = "waiting"
StatusCompleted TaskStatus = "completed"
)
@@ -34,15 +37,61 @@ func (s TaskStatus) String() string {
return string(s)
}
+// MarshalJSON implements [json.Marshaler].
+// Translates "in-progress" to "started" for the API.
+func (s TaskStatus) MarshalJSON() ([]byte, error) {
+ val := string(s)
+ if s == StatusInProgress {
+ val = "started"
+ }
+
+ data, err := json.Marshal(val)
+ if err != nil {
+ return nil, fmt.Errorf("marshaling task status: %w", err)
+ }
+
+ return data, nil
+}
+
+// UnmarshalJSON implements [json.Unmarshaler].
+// Translates "started" from the API to "in-progress".
+func (s *TaskStatus) UnmarshalJSON(data []byte) error {
+ var val string
+ if err := json.Unmarshal(data, &val); err != nil {
+ return fmt.Errorf("task status must be a string: %w", err)
+ }
+
+ if val == "started" {
+ *s = StatusInProgress
+
+ return nil
+ }
+
+ *s = TaskStatus(val)
+
+ return nil
+}
+
+// AllTaskStatuses returns all valid task status values in workflow order.
+func AllTaskStatuses() []TaskStatus {
+ return []TaskStatus{
+ StatusLater,
+ StatusNext,
+ StatusInProgress,
+ StatusWaiting,
+ StatusCompleted,
+ }
+}
+
// ParseTaskStatus parses a string to a TaskStatus value (case-insensitive).
-// Valid values: "later", "next", "started", "in-progress", "waiting", "completed".
+// Valid values: "later", "next", "in-progress", "waiting", "completed".
func ParseTaskStatus(str string) (TaskStatus, error) {
switch strings.ToLower(str) {
case "later":
return StatusLater, nil
case "next":
return StatusNext, nil
- case "started", "in-progress":
+ case "in-progress":
return StatusInProgress, nil
case "waiting":
return StatusWaiting, nil
@@ -5,11 +5,49 @@
package lunatask_test
import (
+ "encoding/json"
"testing"
lunatask "git.secluded.site/go-lunatask"
)
+func TestAllTaskStatuses(t *testing.T) {
+ t.Parallel()
+
+ statuses := lunatask.AllTaskStatuses()
+
+ // Check count
+ if got := len(statuses); got != 5 {
+ t.Fatalf("AllTaskStatuses() returned %d values, want 5", got)
+ }
+
+ // Check order
+ expected := []lunatask.TaskStatus{
+ lunatask.StatusLater,
+ lunatask.StatusNext,
+ lunatask.StatusInProgress,
+ lunatask.StatusWaiting,
+ lunatask.StatusCompleted,
+ }
+ for i, want := range expected {
+ if statuses[i] != want {
+ t.Errorf("AllTaskStatuses()[%d] = %q, want %q", i, statuses[i], want)
+ }
+ }
+
+ // Check roundtrip: each value should be parseable
+ for _, status := range statuses {
+ parsed, err := lunatask.ParseTaskStatus(status.String())
+ if err != nil {
+ t.Errorf("ParseTaskStatus(%q) failed: %v", status.String(), err)
+ }
+
+ if parsed != status {
+ t.Errorf("ParseTaskStatus(%q) = %q, want %q", status.String(), parsed, status)
+ }
+ }
+}
+
func TestParseTaskStatus(t *testing.T) {
t.Parallel()
@@ -24,10 +62,9 @@ func TestParseTaskStatus(t *testing.T) {
{"later_mixed", "LaTeR", lunatask.StatusLater, false},
{"next_lower", "next", lunatask.StatusNext, false},
{"next_upper", "NEXT", lunatask.StatusNext, false},
- {"started_lower", "started", lunatask.StatusInProgress, false},
- {"started_upper", "STARTED", lunatask.StatusInProgress, false},
{"in_progress_lower", "in-progress", lunatask.StatusInProgress, false},
{"in_progress_upper", "IN-PROGRESS", lunatask.StatusInProgress, false},
+ {"in_progress_mixed", "In-Progress", lunatask.StatusInProgress, false},
{"waiting_lower", "waiting", lunatask.StatusWaiting, false},
{"waiting_upper", "WAITING", lunatask.StatusWaiting, false},
{"completed_lower", "completed", lunatask.StatusCompleted, false},
@@ -56,3 +93,73 @@ func TestParseTaskStatus(t *testing.T) {
})
}
}
+
+func TestTaskStatus_MarshalJSON(t *testing.T) {
+ t.Parallel()
+
+ tests := []struct {
+ name string
+ value lunatask.TaskStatus
+ want string
+ }{
+ {"later", lunatask.StatusLater, `"later"`},
+ {"next", lunatask.StatusNext, `"next"`},
+ {"in_progress_to_started", lunatask.StatusInProgress, `"started"`},
+ {"waiting", lunatask.StatusWaiting, `"waiting"`},
+ {"completed", lunatask.StatusCompleted, `"completed"`},
+ }
+
+ for _, testCase := range tests {
+ t.Run(testCase.name, func(t *testing.T) {
+ t.Parallel()
+
+ got, err := json.Marshal(testCase.value)
+ if err != nil {
+ t.Fatalf("Marshal error: %v", err)
+ }
+
+ if string(got) != testCase.want {
+ t.Errorf("json.Marshal(%q) = %s, want %s", testCase.value, got, testCase.want)
+ }
+ })
+ }
+}
+
+func TestTaskStatus_UnmarshalJSON(t *testing.T) {
+ t.Parallel()
+
+ tests := []struct {
+ name string
+ input string
+ want lunatask.TaskStatus
+ wantErr bool
+ }{
+ {"later", `"later"`, lunatask.StatusLater, false},
+ {"next", `"next"`, lunatask.StatusNext, false},
+ {"started_to_in_progress", `"started"`, lunatask.StatusInProgress, false},
+ {"in_progress", `"in-progress"`, lunatask.StatusInProgress, false},
+ {"waiting", `"waiting"`, lunatask.StatusWaiting, false},
+ {"completed", `"completed"`, lunatask.StatusCompleted, false},
+ {"invalid_type", `123`, "", true},
+ }
+
+ for _, testCase := range tests {
+ t.Run(testCase.name, func(t *testing.T) {
+ t.Parallel()
+
+ var got lunatask.TaskStatus
+
+ err := json.Unmarshal([]byte(testCase.input), &got)
+
+ if (err != nil) != testCase.wantErr {
+ t.Errorf("Unmarshal error = %v, wantErr %v", err, testCase.wantErr)
+
+ return
+ }
+
+ if !testCase.wantErr && got != testCase.want {
+ t.Errorf("Unmarshal(%s) = %q, want %q", testCase.input, got, testCase.want)
+ }
+ })
+ }
+}