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