people_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	"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}`