deeplink_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	"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}