1// SPDX-FileCopyrightText: Amolith <amolith@secluded.site>
2//
3// SPDX-License-Identifier: AGPL-3.0-or-later
4
5package lunatask_test
6
7import (
8 "net/http"
9 "net/http/httptest"
10 "net/url"
11 "testing"
12 "time"
13
14 lunatask "git.secluded.site/go-lunatask"
15)
16
17const personID = "5999b945-b2b1-48c6-aa72-b251b75b3c2e"
18
19// --- ListPeople ---
20
21func TestListPeople_Success(t *testing.T) {
22 t.Parallel()
23
24 server := newJSONServer(t, "/people", peopleResponseBody)
25 defer server.Close()
26
27 client := lunatask.NewClient(testToken, lunatask.BaseURL(server.URL))
28
29 people, err := client.ListPeople(ctx(), nil)
30 if err != nil {
31 t.Fatalf("error = %v", err)
32 }
33
34 if len(people) != 2 {
35 t.Fatalf("len = %d, want 2", len(people))
36 }
37
38 person := people[0]
39 if person.ID != personID {
40 t.Errorf("ID = %q, want %q", person.ID, personID)
41 }
42
43 if person.RelationshipStrength == nil || *person.RelationshipStrength != lunatask.RelationshipBusiness {
44 t.Errorf("RelationshipStrength = %v, want %v", person.RelationshipStrength, lunatask.RelationshipBusiness)
45 }
46
47 wantCreated := time.Date(2021, 1, 10, 10, 39, 25, 0, time.UTC)
48 if !person.CreatedAt.Equal(wantCreated) {
49 t.Errorf("CreatedAt = %v, want %v", person.CreatedAt, wantCreated)
50 }
51}
52
53func TestListPeople_Filter(t *testing.T) {
54 t.Parallel()
55
56 tests := []filterTest{
57 {
58 Name: "source_only",
59 Source: ptr("salesforce"),
60 SourceID: nil,
61 WantQuery: url.Values{"source": {"salesforce"}},
62 },
63 {
64 Name: "source_and_id",
65 Source: ptr("salesforce"),
66 SourceID: ptr("sf-123"),
67 WantQuery: url.Values{"source": {"salesforce"}, "source_id": {"sf-123"}},
68 },
69 }
70
71 runFilterTests(t, "/people", `{"people": []}`, tests, func(c *lunatask.Client, source, sourceID *string) error {
72 opts := &lunatask.ListPeopleOptions{Source: source, SourceID: sourceID}
73 _, err := c.ListPeople(ctx(), opts)
74
75 return err //nolint:wrapcheck // test helper
76 })
77}
78
79func TestListPeople_Errors(t *testing.T) {
80 t.Parallel()
81
82 testErrorCases(t, func(c *lunatask.Client) error {
83 _, err := c.ListPeople(ctx(), nil)
84
85 return err //nolint:wrapcheck // test helper
86 })
87}
88
89// --- GetPerson ---
90
91func TestGetPerson_Success(t *testing.T) {
92 t.Parallel()
93
94 server := newJSONServer(t, "/people/"+personID, singlePersonResponseBody)
95 defer server.Close()
96
97 client := lunatask.NewClient(testToken, lunatask.BaseURL(server.URL))
98
99 person, err := client.GetPerson(ctx(), personID)
100 if err != nil {
101 t.Fatalf("error = %v", err)
102 }
103
104 if person == nil {
105 t.Fatal("returned nil")
106 }
107
108 if person.ID != personID {
109 t.Errorf("ID = %q, want %q", person.ID, personID)
110 }
111
112 if len(person.Sources) != 1 || person.Sources[0].Source != "salesforce" {
113 t.Errorf("Sources = %v, want salesforce source", person.Sources)
114 }
115}
116
117func TestGetPerson_Errors(t *testing.T) {
118 t.Parallel()
119
120 testErrorCases(t, func(c *lunatask.Client) error {
121 _, err := c.GetPerson(ctx(), personID)
122
123 return err //nolint:wrapcheck // test helper
124 })
125}
126
127// --- CreatePerson ---
128
129func TestCreatePerson_Success(t *testing.T) {
130 t.Parallel()
131
132 server, capture := newPOSTServer(t, "/people", singlePersonResponseBody)
133 defer server.Close()
134
135 client := lunatask.NewClient(testToken, lunatask.BaseURL(server.URL))
136
137 person, err := client.NewPerson("John", "Doe").
138 WithRelationshipStrength(lunatask.RelationshipBusiness).
139 FromSource("salesforce", "sf-123").
140 Create(ctx())
141 if err != nil {
142 t.Fatalf("error = %v", err)
143 }
144
145 if person == nil {
146 t.Fatal("returned nil")
147 }
148
149 if person.ID != personID {
150 t.Errorf("ID = %q, want %q", person.ID, personID)
151 }
152
153 assertBodyField(t, capture.Body, "first_name", "John")
154 assertBodyField(t, capture.Body, "last_name", "Doe")
155 assertBodyField(t, capture.Body, "relationship_strength", "business-contacts")
156 assertBodyField(t, capture.Body, "source", "salesforce")
157 assertBodyField(t, capture.Body, "source_id", "sf-123")
158}
159
160func TestCreatePerson_WithCustomFields(t *testing.T) {
161 t.Parallel()
162
163 server, capture := newPOSTServer(t, "/people", singlePersonResponseBody)
164 defer server.Close()
165
166 client := lunatask.NewClient(testToken, lunatask.BaseURL(server.URL))
167
168 _, err := client.NewPerson("Ada", "Lovelace").
169 WithRelationshipStrength(lunatask.RelationshipCloseFriend).
170 WithCustomField("email", "ada@example.com").
171 WithCustomField("phone", "+1234567890").
172 Create(ctx())
173 if err != nil {
174 t.Fatalf("error = %v", err)
175 }
176
177 assertBodyField(t, capture.Body, "first_name", "Ada")
178 assertBodyField(t, capture.Body, "last_name", "Lovelace")
179 assertBodyField(t, capture.Body, "relationship_strength", "close-friends")
180 assertBodyField(t, capture.Body, "email", "ada@example.com")
181 assertBodyField(t, capture.Body, "phone", "+1234567890")
182}
183
184func TestCreatePerson_Duplicate(t *testing.T) {
185 t.Parallel()
186
187 server := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, _ *http.Request) {
188 writer.WriteHeader(http.StatusNoContent)
189 }))
190 defer server.Close()
191
192 client := lunatask.NewClient(testToken, lunatask.BaseURL(server.URL))
193
194 person, err := client.NewPerson("Dup", "Person").
195 FromSource("salesforce", "dup-123").
196 Create(ctx())
197 if err != nil {
198 t.Fatalf("error = %v, want nil", err)
199 }
200
201 if person != nil {
202 t.Errorf("person = %v, want nil for duplicate", person)
203 }
204}
205
206func TestCreatePerson_Errors(t *testing.T) {
207 t.Parallel()
208
209 testErrorCases(t, func(c *lunatask.Client) error {
210 _, err := c.NewPerson("Test", "User").Create(ctx())
211
212 return err //nolint:wrapcheck // test helper
213 })
214}
215
216// --- UpdatePerson ---
217
218func TestUpdatePerson_Success(t *testing.T) {
219 t.Parallel()
220
221 server, capture := newPUTServer(t, "/people/"+personID, singlePersonResponseBody)
222 defer server.Close()
223
224 client := lunatask.NewClient(testToken, lunatask.BaseURL(server.URL))
225
226 person, err := client.NewPersonUpdate(personID).
227 FirstName("Jane").
228 LastName("Smith").
229 WithRelationshipStrength(lunatask.RelationshipCloseFriend).
230 Update(ctx())
231 if err != nil {
232 t.Fatalf("error = %v", err)
233 }
234
235 if person == nil {
236 t.Fatal("returned nil")
237 }
238
239 if person.ID != personID {
240 t.Errorf("ID = %q, want %q", person.ID, personID)
241 }
242
243 assertBodyField(t, capture.Body, "first_name", "Jane")
244 assertBodyField(t, capture.Body, "last_name", "Smith")
245 assertBodyField(t, capture.Body, "relationship_strength", "close-friends")
246}
247
248func TestUpdatePerson_AllFields(t *testing.T) {
249 t.Parallel()
250
251 server, capture := newPUTServer(t, "/people/"+personID, singlePersonResponseBody)
252 defer server.Close()
253
254 client := lunatask.NewClient(testToken, lunatask.BaseURL(server.URL))
255
256 _, err := client.NewPersonUpdate(personID).
257 FirstName("Ada").
258 LastName("Lovelace").
259 WithRelationshipStrength(lunatask.RelationshipFamily).
260 WithCustomField("email", "ada@example.com").
261 WithCustomField("phone", "+1234567890").
262 WithCustomField("birthday", "1815-12-10").
263 Update(ctx())
264 if err != nil {
265 t.Fatalf("error = %v", err)
266 }
267
268 assertBodyField(t, capture.Body, "first_name", "Ada")
269 assertBodyField(t, capture.Body, "last_name", "Lovelace")
270 assertBodyField(t, capture.Body, "relationship_strength", "family")
271 assertBodyField(t, capture.Body, "email", "ada@example.com")
272 assertBodyField(t, capture.Body, "phone", "+1234567890")
273 assertBodyField(t, capture.Body, "birthday", "1815-12-10")
274}
275
276func TestUpdatePerson_Errors(t *testing.T) {
277 t.Parallel()
278
279 testErrorCases(t, func(c *lunatask.Client) error {
280 _, err := c.NewPersonUpdate(personID).FirstName("x").Update(ctx())
281
282 return err //nolint:wrapcheck // test helper
283 })
284}
285
286// --- DeletePerson ---
287
288func TestDeletePerson_Success(t *testing.T) {
289 t.Parallel()
290
291 server := newDELETEServer(t, "/people/"+personID, singlePersonResponseBody)
292 defer server.Close()
293
294 client := lunatask.NewClient(testToken, lunatask.BaseURL(server.URL))
295
296 person, err := client.DeletePerson(ctx(), personID)
297 if err != nil {
298 t.Fatalf("error = %v", err)
299 }
300
301 if person == nil {
302 t.Fatal("returned nil")
303 }
304
305 if person.ID != personID {
306 t.Errorf("ID = %q, want %q", person.ID, personID)
307 }
308}
309
310func TestDeletePerson_Errors(t *testing.T) {
311 t.Parallel()
312
313 testErrorCases(t, func(c *lunatask.Client) error {
314 _, err := c.DeletePerson(ctx(), personID)
315
316 return err //nolint:wrapcheck // test helper
317 })
318}
319
320// --- Test Data ---
321
322const singlePersonResponseBody = `{
323 "person": {
324 "id": "5999b945-b2b1-48c6-aa72-b251b75b3c2e",
325 "relationship_strength": "business-contacts",
326 "sources": [{"source": "salesforce", "source_id": "352fd2d7-cdc0-4e91-a0a3-9d6cc9d440e7"}],
327 "created_at": "2021-01-10T10:39:25Z",
328 "updated_at": "2021-01-10T10:39:25Z"
329 }
330}`
331
332const peopleResponseBody = `{
333 "people": [
334 {
335 "id": "5999b945-b2b1-48c6-aa72-b251b75b3c2e",
336 "relationship_strength": "business-contacts",
337 "sources": [{"source": "salesforce", "source_id": "352fd2d7-cdc0-4e91-a0a3-9d6cc9d440e7"}],
338 "created_at": "2021-01-10T10:39:25Z",
339 "updated_at": "2021-01-10T10:39:25Z"
340 },
341 {
342 "id": "109cbf01-dba9-4136-8cf1-a02084ba3977",
343 "relationship_strength": "family",
344 "sources": [],
345 "created_at": "2021-01-10T10:39:25Z",
346 "updated_at": "2021-01-10T10:39:25Z"
347 }
348 ]
349}`