@htlkg/data 0.0.20 → 0.0.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/hooks/index.d.ts +88 -2
- package/dist/hooks/index.js +63 -3
- package/dist/hooks/index.js.map +1 -1
- package/dist/index.d.ts +4 -3
- package/dist/index.js +441 -3
- package/dist/index.js.map +1 -1
- package/dist/mutations/index.d.ts +338 -2
- package/dist/mutations/index.js +286 -0
- package/dist/mutations/index.js.map +1 -1
- package/dist/queries/index.d.ts +110 -2
- package/dist/queries/index.js +110 -1
- package/dist/queries/index.js.map +1 -1
- package/package.json +13 -12
- package/src/hooks/index.ts +1 -0
- package/src/hooks/useContacts.test.ts +159 -0
- package/src/hooks/useContacts.ts +176 -0
- package/src/mutations/contacts.test.ts +604 -0
- package/src/mutations/contacts.ts +554 -0
- package/src/mutations/index.ts +18 -0
- package/src/queries/contacts.test.ts +505 -0
- package/src/queries/contacts.ts +237 -0
- package/src/queries/index.ts +10 -0
|
@@ -0,0 +1,505 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Contact Query Tests
|
|
3
|
+
*
|
|
4
|
+
* Tests for contact query functions including filtering,
|
|
5
|
+
* pagination, and specialized queries.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { describe, it, expect, vi } from "vitest";
|
|
9
|
+
import {
|
|
10
|
+
getContact,
|
|
11
|
+
listContacts,
|
|
12
|
+
listContactsByBrand,
|
|
13
|
+
getContactByEmail,
|
|
14
|
+
getContactByPhone,
|
|
15
|
+
searchContacts,
|
|
16
|
+
} from "./contacts";
|
|
17
|
+
|
|
18
|
+
describe("Contact Queries", () => {
|
|
19
|
+
const mockContact = {
|
|
20
|
+
id: "contact-001",
|
|
21
|
+
brandId: "brand-123",
|
|
22
|
+
email: "john.doe@example.com",
|
|
23
|
+
phone: "+1234567890",
|
|
24
|
+
firstName: "John",
|
|
25
|
+
lastName: "Doe",
|
|
26
|
+
locale: "en-US",
|
|
27
|
+
gdprConsent: true,
|
|
28
|
+
gdprConsentDate: "2024-01-15T10:00:00Z",
|
|
29
|
+
marketingOptIn: true,
|
|
30
|
+
preferences: { theme: "dark" },
|
|
31
|
+
tags: ["vip", "returning"],
|
|
32
|
+
totalVisits: 5,
|
|
33
|
+
lastVisitDate: "2024-06-01",
|
|
34
|
+
firstVisitDate: "2023-01-15",
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
describe("getContact", () => {
|
|
38
|
+
it("should return a contact by ID", async () => {
|
|
39
|
+
const mockGet = vi.fn().mockResolvedValue({
|
|
40
|
+
data: mockContact,
|
|
41
|
+
errors: null,
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
const mockClient = {
|
|
45
|
+
models: {
|
|
46
|
+
Contact: { get: mockGet },
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const result = await getContact(mockClient, "contact-001");
|
|
51
|
+
|
|
52
|
+
expect(result).toEqual(mockContact);
|
|
53
|
+
expect(mockGet).toHaveBeenCalledWith({ id: "contact-001" });
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it("should return null when contact not found", async () => {
|
|
57
|
+
const mockGet = vi.fn().mockResolvedValue({
|
|
58
|
+
data: null,
|
|
59
|
+
errors: [{ message: "Not found" }],
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
const mockClient = {
|
|
63
|
+
models: {
|
|
64
|
+
Contact: { get: mockGet },
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
const result = await getContact(mockClient, "nonexistent");
|
|
69
|
+
|
|
70
|
+
expect(result).toBeNull();
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it("should throw on unexpected error", async () => {
|
|
74
|
+
const mockGet = vi.fn().mockRejectedValue(new Error("Network error"));
|
|
75
|
+
|
|
76
|
+
const mockClient = {
|
|
77
|
+
models: {
|
|
78
|
+
Contact: { get: mockGet },
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
await expect(getContact(mockClient, "contact-001")).rejects.toThrow(
|
|
83
|
+
"Network error"
|
|
84
|
+
);
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
describe("listContacts", () => {
|
|
89
|
+
const mockContacts = [
|
|
90
|
+
mockContact,
|
|
91
|
+
{ ...mockContact, id: "contact-002", email: "jane.doe@example.com" },
|
|
92
|
+
];
|
|
93
|
+
|
|
94
|
+
it("should return a list of contacts", async () => {
|
|
95
|
+
const mockList = vi.fn().mockResolvedValue({
|
|
96
|
+
data: mockContacts,
|
|
97
|
+
errors: null,
|
|
98
|
+
nextToken: null,
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
const mockClient = {
|
|
102
|
+
models: {
|
|
103
|
+
Contact: { list: mockList },
|
|
104
|
+
},
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
const result = await listContacts(mockClient);
|
|
108
|
+
|
|
109
|
+
expect(result.items).toHaveLength(2);
|
|
110
|
+
expect(result.items[0].id).toBe("contact-001");
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it("should pass filter options to the query", async () => {
|
|
114
|
+
const mockList = vi.fn().mockResolvedValue({
|
|
115
|
+
data: [mockContact],
|
|
116
|
+
errors: null,
|
|
117
|
+
nextToken: null,
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
const mockClient = {
|
|
121
|
+
models: {
|
|
122
|
+
Contact: { list: mockList },
|
|
123
|
+
},
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
const filter = { gdprConsent: { eq: true } };
|
|
127
|
+
await listContacts(mockClient, { filter, limit: 50 });
|
|
128
|
+
|
|
129
|
+
expect(mockList).toHaveBeenCalledWith({
|
|
130
|
+
filter,
|
|
131
|
+
limit: 50,
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it("should return nextToken for pagination", async () => {
|
|
136
|
+
const mockList = vi.fn().mockResolvedValue({
|
|
137
|
+
data: [mockContact],
|
|
138
|
+
errors: null,
|
|
139
|
+
nextToken: "token-123",
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
const mockClient = {
|
|
143
|
+
models: {
|
|
144
|
+
Contact: { list: mockList },
|
|
145
|
+
},
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
const result = await listContacts(mockClient, { limit: 1 });
|
|
149
|
+
|
|
150
|
+
expect(result.nextToken).toBe("token-123");
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it("should return empty array on GraphQL error", async () => {
|
|
154
|
+
const mockList = vi.fn().mockResolvedValue({
|
|
155
|
+
data: null,
|
|
156
|
+
errors: [{ message: "Query failed" }],
|
|
157
|
+
nextToken: undefined,
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
const mockClient = {
|
|
161
|
+
models: {
|
|
162
|
+
Contact: { list: mockList },
|
|
163
|
+
},
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
const result = await listContacts(mockClient);
|
|
167
|
+
|
|
168
|
+
expect(result.items).toHaveLength(0);
|
|
169
|
+
expect(result.nextToken).toBeUndefined();
|
|
170
|
+
});
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
describe("listContactsByBrand", () => {
|
|
174
|
+
it("should filter contacts by brand ID", async () => {
|
|
175
|
+
const mockList = vi.fn().mockResolvedValue({
|
|
176
|
+
data: [mockContact],
|
|
177
|
+
errors: null,
|
|
178
|
+
nextToken: null,
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
const mockClient = {
|
|
182
|
+
models: {
|
|
183
|
+
Contact: { list: mockList },
|
|
184
|
+
},
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
await listContactsByBrand(mockClient, "brand-123");
|
|
188
|
+
|
|
189
|
+
expect(mockList).toHaveBeenCalledWith({
|
|
190
|
+
filter: { brandId: { eq: "brand-123" } },
|
|
191
|
+
});
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
it("should pass pagination options", async () => {
|
|
195
|
+
const mockList = vi.fn().mockResolvedValue({
|
|
196
|
+
data: [mockContact],
|
|
197
|
+
errors: null,
|
|
198
|
+
nextToken: "next-page",
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
const mockClient = {
|
|
202
|
+
models: {
|
|
203
|
+
Contact: { list: mockList },
|
|
204
|
+
},
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
const result = await listContactsByBrand(mockClient, "brand-123", {
|
|
208
|
+
limit: 25,
|
|
209
|
+
nextToken: "current-page",
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
expect(mockList).toHaveBeenCalledWith({
|
|
213
|
+
filter: { brandId: { eq: "brand-123" } },
|
|
214
|
+
limit: 25,
|
|
215
|
+
nextToken: "current-page",
|
|
216
|
+
});
|
|
217
|
+
expect(result.nextToken).toBe("next-page");
|
|
218
|
+
});
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
describe("getContactByEmail", () => {
|
|
222
|
+
it("should return contact matching email and brand", async () => {
|
|
223
|
+
const mockList = vi.fn().mockResolvedValue({
|
|
224
|
+
data: [mockContact],
|
|
225
|
+
errors: null,
|
|
226
|
+
nextToken: null,
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
const mockClient = {
|
|
230
|
+
models: {
|
|
231
|
+
Contact: { list: mockList },
|
|
232
|
+
},
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
const result = await getContactByEmail(
|
|
236
|
+
mockClient,
|
|
237
|
+
"john.doe@example.com",
|
|
238
|
+
"brand-123"
|
|
239
|
+
);
|
|
240
|
+
|
|
241
|
+
expect(result).toEqual(mockContact);
|
|
242
|
+
expect(mockList).toHaveBeenCalledWith({
|
|
243
|
+
filter: {
|
|
244
|
+
and: [
|
|
245
|
+
{ email: { eq: "john.doe@example.com" } },
|
|
246
|
+
{ brandId: { eq: "brand-123" } },
|
|
247
|
+
],
|
|
248
|
+
},
|
|
249
|
+
limit: 1,
|
|
250
|
+
});
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
it("should return null when no matching contact found", async () => {
|
|
254
|
+
const mockList = vi.fn().mockResolvedValue({
|
|
255
|
+
data: [],
|
|
256
|
+
errors: null,
|
|
257
|
+
nextToken: null,
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
const mockClient = {
|
|
261
|
+
models: {
|
|
262
|
+
Contact: { list: mockList },
|
|
263
|
+
},
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
const result = await getContactByEmail(
|
|
267
|
+
mockClient,
|
|
268
|
+
"nonexistent@example.com",
|
|
269
|
+
"brand-123"
|
|
270
|
+
);
|
|
271
|
+
|
|
272
|
+
expect(result).toBeFalsy();
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
it("should return null on GraphQL error", async () => {
|
|
276
|
+
const mockList = vi.fn().mockResolvedValue({
|
|
277
|
+
data: null,
|
|
278
|
+
errors: [{ message: "Query failed" }],
|
|
279
|
+
nextToken: null,
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
const mockClient = {
|
|
283
|
+
models: {
|
|
284
|
+
Contact: { list: mockList },
|
|
285
|
+
},
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
const result = await getContactByEmail(
|
|
289
|
+
mockClient,
|
|
290
|
+
"john.doe@example.com",
|
|
291
|
+
"brand-123"
|
|
292
|
+
);
|
|
293
|
+
|
|
294
|
+
expect(result).toBeNull();
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
it("should throw on unexpected error", async () => {
|
|
298
|
+
const mockList = vi.fn().mockRejectedValue(new Error("Database error"));
|
|
299
|
+
|
|
300
|
+
const mockClient = {
|
|
301
|
+
models: {
|
|
302
|
+
Contact: { list: mockList },
|
|
303
|
+
},
|
|
304
|
+
};
|
|
305
|
+
|
|
306
|
+
await expect(
|
|
307
|
+
getContactByEmail(mockClient, "john.doe@example.com", "brand-123")
|
|
308
|
+
).rejects.toThrow("Database error");
|
|
309
|
+
});
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
describe("getContactByPhone", () => {
|
|
313
|
+
it("should return contact matching phone and brand", async () => {
|
|
314
|
+
const mockList = vi.fn().mockResolvedValue({
|
|
315
|
+
data: [mockContact],
|
|
316
|
+
errors: null,
|
|
317
|
+
nextToken: null,
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
const mockClient = {
|
|
321
|
+
models: {
|
|
322
|
+
Contact: { list: mockList },
|
|
323
|
+
},
|
|
324
|
+
};
|
|
325
|
+
|
|
326
|
+
const result = await getContactByPhone(
|
|
327
|
+
mockClient,
|
|
328
|
+
"+1234567890",
|
|
329
|
+
"brand-123"
|
|
330
|
+
);
|
|
331
|
+
|
|
332
|
+
expect(result).toEqual(mockContact);
|
|
333
|
+
expect(mockList).toHaveBeenCalledWith({
|
|
334
|
+
filter: {
|
|
335
|
+
and: [
|
|
336
|
+
{ phone: { eq: "+1234567890" } },
|
|
337
|
+
{ brandId: { eq: "brand-123" } },
|
|
338
|
+
],
|
|
339
|
+
},
|
|
340
|
+
limit: 1,
|
|
341
|
+
});
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
it("should return null when no matching contact found", async () => {
|
|
345
|
+
const mockList = vi.fn().mockResolvedValue({
|
|
346
|
+
data: [],
|
|
347
|
+
errors: null,
|
|
348
|
+
nextToken: null,
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
const mockClient = {
|
|
352
|
+
models: {
|
|
353
|
+
Contact: { list: mockList },
|
|
354
|
+
},
|
|
355
|
+
};
|
|
356
|
+
|
|
357
|
+
const result = await getContactByPhone(
|
|
358
|
+
mockClient,
|
|
359
|
+
"+9999999999",
|
|
360
|
+
"brand-123"
|
|
361
|
+
);
|
|
362
|
+
|
|
363
|
+
expect(result).toBeFalsy();
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
it("should return null on GraphQL error", async () => {
|
|
367
|
+
const mockList = vi.fn().mockResolvedValue({
|
|
368
|
+
data: null,
|
|
369
|
+
errors: [{ message: "Query failed" }],
|
|
370
|
+
nextToken: null,
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
const mockClient = {
|
|
374
|
+
models: {
|
|
375
|
+
Contact: { list: mockList },
|
|
376
|
+
},
|
|
377
|
+
};
|
|
378
|
+
|
|
379
|
+
const result = await getContactByPhone(
|
|
380
|
+
mockClient,
|
|
381
|
+
"+1234567890",
|
|
382
|
+
"brand-123"
|
|
383
|
+
);
|
|
384
|
+
|
|
385
|
+
expect(result).toBeNull();
|
|
386
|
+
});
|
|
387
|
+
|
|
388
|
+
it("should throw on unexpected error", async () => {
|
|
389
|
+
const mockList = vi.fn().mockRejectedValue(new Error("Database error"));
|
|
390
|
+
|
|
391
|
+
const mockClient = {
|
|
392
|
+
models: {
|
|
393
|
+
Contact: { list: mockList },
|
|
394
|
+
},
|
|
395
|
+
};
|
|
396
|
+
|
|
397
|
+
await expect(
|
|
398
|
+
getContactByPhone(mockClient, "+1234567890", "brand-123")
|
|
399
|
+
).rejects.toThrow("Database error");
|
|
400
|
+
});
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
describe("searchContacts", () => {
|
|
404
|
+
it("should search contacts by query string within brand", async () => {
|
|
405
|
+
const mockList = vi.fn().mockResolvedValue({
|
|
406
|
+
data: [mockContact],
|
|
407
|
+
errors: null,
|
|
408
|
+
nextToken: null,
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
const mockClient = {
|
|
412
|
+
models: {
|
|
413
|
+
Contact: { list: mockList },
|
|
414
|
+
},
|
|
415
|
+
};
|
|
416
|
+
|
|
417
|
+
await searchContacts(mockClient, "john", "brand-123");
|
|
418
|
+
|
|
419
|
+
expect(mockList).toHaveBeenCalledWith({
|
|
420
|
+
filter: {
|
|
421
|
+
and: [
|
|
422
|
+
{ brandId: { eq: "brand-123" } },
|
|
423
|
+
{
|
|
424
|
+
or: [
|
|
425
|
+
{ email: { contains: "john" } },
|
|
426
|
+
{ firstName: { contains: "john" } },
|
|
427
|
+
{ lastName: { contains: "john" } },
|
|
428
|
+
],
|
|
429
|
+
},
|
|
430
|
+
],
|
|
431
|
+
},
|
|
432
|
+
});
|
|
433
|
+
});
|
|
434
|
+
|
|
435
|
+
it("should support pagination for search queries", async () => {
|
|
436
|
+
const mockList = vi.fn().mockResolvedValue({
|
|
437
|
+
data: [mockContact],
|
|
438
|
+
errors: null,
|
|
439
|
+
nextToken: "next-page",
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
const mockClient = {
|
|
443
|
+
models: {
|
|
444
|
+
Contact: { list: mockList },
|
|
445
|
+
},
|
|
446
|
+
};
|
|
447
|
+
|
|
448
|
+
const result = await searchContacts(mockClient, "john", "brand-123", {
|
|
449
|
+
limit: 10,
|
|
450
|
+
nextToken: "current-page",
|
|
451
|
+
});
|
|
452
|
+
|
|
453
|
+
expect(result.nextToken).toBe("next-page");
|
|
454
|
+
expect(mockList).toHaveBeenCalledWith({
|
|
455
|
+
filter: {
|
|
456
|
+
and: [
|
|
457
|
+
{ brandId: { eq: "brand-123" } },
|
|
458
|
+
{
|
|
459
|
+
or: [
|
|
460
|
+
{ email: { contains: "john" } },
|
|
461
|
+
{ firstName: { contains: "john" } },
|
|
462
|
+
{ lastName: { contains: "john" } },
|
|
463
|
+
],
|
|
464
|
+
},
|
|
465
|
+
],
|
|
466
|
+
},
|
|
467
|
+
limit: 10,
|
|
468
|
+
nextToken: "current-page",
|
|
469
|
+
});
|
|
470
|
+
});
|
|
471
|
+
|
|
472
|
+
it("should return empty array on GraphQL error", async () => {
|
|
473
|
+
const mockList = vi.fn().mockResolvedValue({
|
|
474
|
+
data: null,
|
|
475
|
+
errors: [{ message: "Search failed" }],
|
|
476
|
+
nextToken: undefined,
|
|
477
|
+
});
|
|
478
|
+
|
|
479
|
+
const mockClient = {
|
|
480
|
+
models: {
|
|
481
|
+
Contact: { list: mockList },
|
|
482
|
+
},
|
|
483
|
+
};
|
|
484
|
+
|
|
485
|
+
const result = await searchContacts(mockClient, "john", "brand-123");
|
|
486
|
+
|
|
487
|
+
expect(result.items).toHaveLength(0);
|
|
488
|
+
expect(result.nextToken).toBeUndefined();
|
|
489
|
+
});
|
|
490
|
+
|
|
491
|
+
it("should throw on unexpected error", async () => {
|
|
492
|
+
const mockList = vi.fn().mockRejectedValue(new Error("Search error"));
|
|
493
|
+
|
|
494
|
+
const mockClient = {
|
|
495
|
+
models: {
|
|
496
|
+
Contact: { list: mockList },
|
|
497
|
+
},
|
|
498
|
+
};
|
|
499
|
+
|
|
500
|
+
await expect(
|
|
501
|
+
searchContacts(mockClient, "john", "brand-123")
|
|
502
|
+
).rejects.toThrow("Search error");
|
|
503
|
+
});
|
|
504
|
+
});
|
|
505
|
+
});
|