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
14func TestParseDeepLink(t *testing.T) {
15 t.Parallel()
16
17 validUUID := "12345678-1234-1234-1234-123456789012"
18 validUUID2 := "abcdef12-3456-7890-abcd-ef1234567890"
19
20 tests := []struct {
21 name string
22 input string
23 wantResource lunatask.Resource
24 wantUUID string
25 wantErr error
26 }{
27 // Valid deep links
28 {"task_link", "lunatask://tasks/" + validUUID, lunatask.ResourceTask, validUUID, nil},
29 {"area_link", "lunatask://areas/" + validUUID, lunatask.ResourceArea, validUUID, nil},
30 {"goal_link", "lunatask://goals/" + validUUID, lunatask.ResourceGoal, validUUID, nil},
31 {"note_link", "lunatask://notes/" + validUUID, lunatask.ResourceNote, validUUID, nil},
32 {"person_link", "lunatask://people/" + validUUID, lunatask.ResourcePerson, validUUID, nil},
33 {"notebook_link", "lunatask://notebooks/" + validUUID, lunatask.ResourceNotebook, validUUID, nil},
34
35 // Plain UUIDs
36 {"plain_uuid", validUUID2, "", validUUID2, nil},
37 {"uuid_only", validUUID, "", validUUID, nil},
38
39 // Invalid inputs
40 {"empty", "", "", "", lunatask.ErrInvalidDeepLink},
41 {"invalid_resource", "lunatask://invalid/" + validUUID, "", "", lunatask.ErrInvalidResource},
42 {"missing_uuid", "lunatask://tasks/", "", "", lunatask.ErrInvalidDeepLink},
43 {"missing_resource", "lunatask:///" + validUUID, "", "", lunatask.ErrInvalidDeepLink},
44 {"malformed", "lunatask://tasks", "", "", lunatask.ErrInvalidDeepLink},
45 {"invalid_uuid_in_link", "lunatask://tasks/not-a-uuid", "", "", lunatask.ErrInvalidUUID},
46 {"invalid_plain_uuid", "not-a-valid-uuid", "", "", lunatask.ErrInvalidUUID},
47 }
48
49 for _, testCase := range tests {
50 t.Run(testCase.name, func(t *testing.T) {
51 t.Parallel()
52
53 resource, uuid, err := lunatask.ParseDeepLink(testCase.input)
54
55 if testCase.wantErr != nil {
56 if !errors.Is(err, testCase.wantErr) {
57 t.Errorf("ParseDeepLink(%q) error = %v, want %v", testCase.input, err, testCase.wantErr)
58 }
59
60 return
61 }
62
63 if err != nil {
64 t.Errorf("ParseDeepLink(%q) unexpected error = %v", testCase.input, err)
65
66 return
67 }
68
69 if resource != testCase.wantResource {
70 t.Errorf("ParseDeepLink(%q) resource = %q, want %q", testCase.input, resource, testCase.wantResource)
71 }
72
73 if uuid != testCase.wantUUID {
74 t.Errorf("ParseDeepLink(%q) uuid = %q, want %q", testCase.input, uuid, testCase.wantUUID)
75 }
76 })
77 }
78}
79
80func TestBuildDeepLink(t *testing.T) {
81 t.Parallel()
82
83 validUUID := "12345678-1234-1234-1234-123456789012"
84
85 tests := []struct {
86 name string
87 resource lunatask.Resource
88 id string
89 want string
90 wantErr error
91 }{
92 // Valid builds
93 {"task", lunatask.ResourceTask, validUUID, "lunatask://tasks/" + validUUID, nil},
94 {"area", lunatask.ResourceArea, validUUID, "lunatask://areas/" + validUUID, nil},
95 {"goal", lunatask.ResourceGoal, validUUID, "lunatask://goals/" + validUUID, nil},
96 {"note", lunatask.ResourceNote, validUUID, "lunatask://notes/" + validUUID, nil},
97 {"person", lunatask.ResourcePerson, validUUID, "lunatask://people/" + validUUID, nil},
98 {"notebook", lunatask.ResourceNotebook, validUUID, "lunatask://notebooks/" + validUUID, nil},
99
100 // Invalid inputs
101 {"empty_id", lunatask.ResourceTask, "", "", lunatask.ErrInvalidUUID},
102 {"invalid_resource", lunatask.Resource("invalid"), validUUID, "", lunatask.ErrInvalidResource},
103 {"invalid_uuid", lunatask.ResourceTask, "not-a-uuid", "", lunatask.ErrInvalidUUID},
104 }
105
106 for _, testCase := range tests {
107 t.Run(testCase.name, func(t *testing.T) {
108 t.Parallel()
109
110 got, err := lunatask.BuildDeepLink(testCase.resource, testCase.id)
111
112 if testCase.wantErr != nil {
113 if !errors.Is(err, testCase.wantErr) {
114 t.Errorf("BuildDeepLink(%q, %q) error = %v, want %v", testCase.resource, testCase.id, err, testCase.wantErr)
115 }
116
117 return
118 }
119
120 if err != nil {
121 t.Errorf("BuildDeepLink(%q, %q) unexpected error = %v", testCase.resource, testCase.id, err)
122
123 return
124 }
125
126 if got != testCase.want {
127 t.Errorf("BuildDeepLink(%q, %q) = %q, want %q", testCase.resource, testCase.id, got, testCase.want)
128 }
129 })
130 }
131}
132
133func TestValidateUUID(t *testing.T) {
134 t.Parallel()
135
136 tests := []struct {
137 name string
138 input string
139 wantErr bool
140 }{
141 {"valid_uuid", "12345678-1234-1234-1234-123456789012", false},
142 {"valid_uuid_lowercase", "abcdef12-3456-7890-abcd-ef1234567890", false},
143 {"valid_uuid_uppercase", "ABCDEF12-3456-7890-ABCD-EF1234567890", false},
144 {"valid_uuid_mixed", "AbCdEf12-3456-7890-AbCd-Ef1234567890", false},
145 {"empty", "", true},
146 {"invalid_format", "not-a-uuid", true},
147 {"too_short", "12345678-1234-1234-1234", true},
148 {"too_long", "12345678-1234-1234-1234-1234567890123", true},
149 {"missing_hyphens", "12345678123412341234123456789012", false}, // google/uuid accepts this
150 }
151
152 for _, testCase := range tests {
153 t.Run(testCase.name, func(t *testing.T) {
154 t.Parallel()
155
156 err := lunatask.ValidateUUID(testCase.input)
157 if (err != nil) != testCase.wantErr {
158 t.Errorf("ValidateUUID(%q) error = %v, wantErr %v", testCase.input, err, testCase.wantErr)
159 }
160
161 if testCase.wantErr && err != nil && !errors.Is(err, lunatask.ErrInvalidUUID) {
162 t.Errorf("ValidateUUID(%q) error = %v, want wrapped ErrInvalidUUID", testCase.input, err)
163 }
164 })
165 }
166}
167
168func TestResource_Valid(t *testing.T) {
169 t.Parallel()
170
171 tests := []struct {
172 name string
173 resource lunatask.Resource
174 want bool
175 }{
176 {"area", lunatask.ResourceArea, true},
177 {"goal", lunatask.ResourceGoal, true},
178 {"task", lunatask.ResourceTask, true},
179 {"note", lunatask.ResourceNote, true},
180 {"person", lunatask.ResourcePerson, true},
181 {"notebook", lunatask.ResourceNotebook, true},
182 {"invalid", lunatask.Resource("invalid"), false},
183 {"empty", lunatask.Resource(""), false},
184 }
185
186 for _, testCase := range tests {
187 t.Run(testCase.name, func(t *testing.T) {
188 t.Parallel()
189
190 if got := testCase.resource.Valid(); got != testCase.want {
191 t.Errorf("Resource(%q).Valid() = %v, want %v", testCase.resource, got, testCase.want)
192 }
193 })
194 }
195}
196
197func TestResource_String(t *testing.T) {
198 t.Parallel()
199
200 tests := []struct {
201 name string
202 resource lunatask.Resource
203 want string
204 }{
205 {"area", lunatask.ResourceArea, "areas"},
206 {"goal", lunatask.ResourceGoal, "goals"},
207 {"task", lunatask.ResourceTask, "tasks"},
208 {"note", lunatask.ResourceNote, "notes"},
209 {"person", lunatask.ResourcePerson, "people"},
210 {"notebook", lunatask.ResourceNotebook, "notebooks"},
211 }
212
213 for _, testCase := range tests {
214 t.Run(testCase.name, func(t *testing.T) {
215 t.Parallel()
216
217 if got := testCase.resource.String(); got != testCase.want {
218 t.Errorf("Resource.String() = %q, want %q", got, testCase.want)
219 }
220 })
221 }
222}