@ikatec/digisac-api-sdk 2.0.0 → 2.1.0

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.
@@ -0,0 +1,460 @@
1
+ import { beforeEach, describe, expect, it, vi } from 'vitest'
2
+ import { MessagesApi } from './MessagesApi'
3
+ import type { ApiClient } from '../../core/ApiClient'
4
+ import type {
5
+ Message,
6
+ ForwardMessagesPayload,
7
+ ForwardMessagesResult,
8
+ SendVcardsPayload,
9
+ SendReactionPayload,
10
+ MagicTextPayload,
11
+ MagicTextResult,
12
+ CheckSummaryPayload,
13
+ CheckSummaryResult,
14
+ BulkDestroyPayload,
15
+ OutOfRangeResult,
16
+ } from './types'
17
+ import type { Paginated } from '../../core/types'
18
+
19
+ // ── Mock client factory ──────────────────────────────────────────────
20
+
21
+ function createMockClient(): ApiClient {
22
+ return {
23
+ setAccessToken: vi.fn().mockReturnThis(),
24
+ request: vi.fn(),
25
+ get: vi.fn(),
26
+ post: vi.fn(),
27
+ put: vi.fn(),
28
+ patch: vi.fn(),
29
+ delete: vi.fn(),
30
+ }
31
+ }
32
+
33
+ // ── Fixtures ─────────────────────────────────────────────────────────
34
+
35
+ const message: Message = {
36
+ id: 'msg-1',
37
+ isFromMe: true,
38
+ sent: true,
39
+ type: 'chat',
40
+ timestamp: '2024-01-01T00:00:00Z',
41
+ data: {},
42
+ text: 'Hello',
43
+ visible: true,
44
+ origin: null,
45
+ obfuscated: false,
46
+ reactionParentMessageId: null,
47
+ hsmId: null,
48
+ createdAt: '2024-01-01T00:00:00Z',
49
+ updatedAt: '2024-01-01T00:00:00Z',
50
+ deletedAt: null,
51
+ }
52
+
53
+ const paginated: Paginated<Message> = {
54
+ data: [message],
55
+ total: 1,
56
+ limit: 10,
57
+ skip: 0,
58
+ currentPage: 1,
59
+ lastPage: 1,
60
+ from: 1,
61
+ to: 1,
62
+ }
63
+
64
+ // ── Tests ────────────────────────────────────────────────────────────
65
+
66
+ describe('MessagesApi', () => {
67
+ let client: ReturnType<typeof createMockClient>
68
+ let api: MessagesApi
69
+
70
+ beforeEach(() => {
71
+ client = createMockClient()
72
+ api = new MessagesApi(client)
73
+ })
74
+
75
+ // ── getMany ──────────────────────────────────────────────────────
76
+
77
+ describe('getMany', () => {
78
+ it('calls client.get with /messages when no query is provided', async () => {
79
+ vi.mocked(client.get).mockResolvedValue(paginated)
80
+ const result = await api.getMany()
81
+ expect(client.get).toHaveBeenCalledWith('/messages', undefined)
82
+ expect(result).toEqual(paginated)
83
+ })
84
+
85
+ it('serializes query parameters', async () => {
86
+ vi.mocked(client.get).mockResolvedValue(paginated)
87
+ await api.getMany({ page: 2, perPage: 5 })
88
+ const url = vi.mocked(client.get).mock.calls[0][0] as string
89
+ expect(url).toContain('/messages?')
90
+ expect(url).toContain('page=2')
91
+ expect(url).toContain('perPage=5')
92
+ })
93
+
94
+ it('forwards custom headers', async () => {
95
+ vi.mocked(client.get).mockResolvedValue(paginated)
96
+ const headers = { 'X-Custom': 'value' }
97
+ await api.getMany(undefined, headers)
98
+ expect(client.get).toHaveBeenCalledWith('/messages', headers)
99
+ })
100
+ })
101
+
102
+ // ── getById ──────────────────────────────────────────────────────
103
+
104
+ describe('getById', () => {
105
+ it('calls client.get with /messages/:id', async () => {
106
+ vi.mocked(client.get).mockResolvedValue(message)
107
+ const result = await api.getById('msg-1')
108
+ expect(client.get).toHaveBeenCalledWith('/messages/msg-1', undefined)
109
+ expect(result).toEqual(message)
110
+ })
111
+
112
+ it('forwards custom headers', async () => {
113
+ vi.mocked(client.get).mockResolvedValue(message)
114
+ const headers = { 'X-Tenant': 'abc' }
115
+ await api.getById('msg-1', undefined, headers)
116
+ expect(client.get).toHaveBeenCalledWith('/messages/msg-1', headers)
117
+ })
118
+ })
119
+
120
+ // ── create ───────────────────────────────────────────────────────
121
+
122
+ describe('create', () => {
123
+ it('calls client.post with /messages and body', async () => {
124
+ vi.mocked(client.post).mockResolvedValue(message)
125
+ const result = await api.create({ text: 'Hello', contactId: 'c-1' })
126
+ expect(client.post).toHaveBeenCalledWith(
127
+ '/messages',
128
+ { text: 'Hello', contactId: 'c-1' },
129
+ undefined,
130
+ )
131
+ expect(result).toEqual(message)
132
+ })
133
+
134
+ it('forwards custom headers', async () => {
135
+ vi.mocked(client.post).mockResolvedValue(message)
136
+ const headers = { 'X-Req': 'r-1' }
137
+ await api.create({ text: 'Hi' }, headers)
138
+ expect(client.post).toHaveBeenCalledWith('/messages', { text: 'Hi' }, headers)
139
+ })
140
+ })
141
+
142
+ // ── updateById ───────────────────────────────────────────────────
143
+
144
+ describe('updateById', () => {
145
+ it('calls client.put with /messages/:id and body', async () => {
146
+ vi.mocked(client.put).mockResolvedValue(message)
147
+ const result = await api.updateById('msg-1', { text: 'Updated' })
148
+ expect(client.put).toHaveBeenCalledWith('/messages/msg-1', { text: 'Updated' }, undefined)
149
+ expect(result).toEqual(message)
150
+ })
151
+
152
+ it('forwards custom headers', async () => {
153
+ vi.mocked(client.put).mockResolvedValue(message)
154
+ const headers = { 'If-Match': 'etag' }
155
+ await api.updateById('msg-1', { text: 'x' }, headers)
156
+ expect(client.put).toHaveBeenCalledWith('/messages/msg-1', { text: 'x' }, headers)
157
+ })
158
+ })
159
+
160
+ // ── deleteById ───────────────────────────────────────────────────
161
+
162
+ describe('deleteById', () => {
163
+ it('calls client.delete with /messages/:id', async () => {
164
+ vi.mocked(client.delete).mockResolvedValue(message)
165
+ const result = await api.deleteById('msg-1')
166
+ expect(client.delete).toHaveBeenCalledWith('/messages/msg-1', undefined)
167
+ expect(result).toEqual(message)
168
+ })
169
+
170
+ it('forwards custom headers', async () => {
171
+ vi.mocked(client.delete).mockResolvedValue(message)
172
+ const headers = { 'X-Reason': 'cleanup' }
173
+ await api.deleteById('msg-1', headers)
174
+ expect(client.delete).toHaveBeenCalledWith('/messages/msg-1', headers)
175
+ })
176
+ })
177
+
178
+ // ── forward ──────────────────────────────────────────────────────
179
+
180
+ describe('forward', () => {
181
+ it('calls client.post with /messages/forward and body', async () => {
182
+ const result: ForwardMessagesResult = [{ id: 'r-1', messagesIds: ['msg-1'] }]
183
+ vi.mocked(client.post).mockResolvedValue(result)
184
+ const body: ForwardMessagesPayload = { contactsIds: ['c-1'], messagesIds: ['msg-1'] }
185
+ const res = await api.forward(body)
186
+ expect(client.post).toHaveBeenCalledWith('/messages/forward', body, undefined)
187
+ expect(res).toEqual(result)
188
+ })
189
+
190
+ it('forwards custom headers', async () => {
191
+ vi.mocked(client.post).mockResolvedValue([])
192
+ const headers = { 'X-Custom': 'h' }
193
+ await api.forward({ contactsIds: [], messagesIds: [] }, headers)
194
+ expect(client.post).toHaveBeenCalledWith(
195
+ '/messages/forward',
196
+ { contactsIds: [], messagesIds: [] },
197
+ headers,
198
+ )
199
+ })
200
+ })
201
+
202
+ // ── revoke ───────────────────────────────────────────────────────
203
+
204
+ describe('revoke', () => {
205
+ it('calls client.post with /messages/:id/revoke', async () => {
206
+ vi.mocked(client.post).mockResolvedValue(message)
207
+ const result = await api.revoke('msg-1')
208
+ expect(client.post).toHaveBeenCalledWith('/messages/msg-1/revoke', undefined, undefined)
209
+ expect(result).toEqual(message)
210
+ })
211
+
212
+ it('forwards custom headers', async () => {
213
+ vi.mocked(client.post).mockResolvedValue(message)
214
+ const headers = { 'X-Custom': 'h' }
215
+ await api.revoke('msg-1', headers)
216
+ expect(client.post).toHaveBeenCalledWith('/messages/msg-1/revoke', undefined, headers)
217
+ })
218
+ })
219
+
220
+ // ── revokeReaction ───────────────────────────────────────────────
221
+
222
+ describe('revokeReaction', () => {
223
+ it('calls client.put with /messages/:id/revoke-reaction', async () => {
224
+ vi.mocked(client.put).mockResolvedValue(true)
225
+ const result = await api.revokeReaction('msg-1')
226
+ expect(client.put).toHaveBeenCalledWith(
227
+ '/messages/msg-1/revoke-reaction',
228
+ undefined,
229
+ undefined,
230
+ )
231
+ expect(result).toBe(true)
232
+ })
233
+
234
+ it('forwards custom headers', async () => {
235
+ vi.mocked(client.put).mockResolvedValue(true)
236
+ const headers = { 'X-Custom': 'h' }
237
+ await api.revokeReaction('msg-1', headers)
238
+ expect(client.put).toHaveBeenCalledWith('/messages/msg-1/revoke-reaction', undefined, headers)
239
+ })
240
+ })
241
+
242
+ // ── sendVcards ───────────────────────────────────────────────────
243
+
244
+ describe('sendVcards', () => {
245
+ it('calls client.post with /messages/send-vcards and body', async () => {
246
+ vi.mocked(client.post).mockResolvedValue(message)
247
+ const body: SendVcardsPayload = { serviceId: 's-1', contactId: 'c-1', contactsIds: ['c-2'] }
248
+ const result = await api.sendVcards(body)
249
+ expect(client.post).toHaveBeenCalledWith('/messages/send-vcards', body, undefined)
250
+ expect(result).toEqual(message)
251
+ })
252
+
253
+ it('forwards custom headers', async () => {
254
+ vi.mocked(client.post).mockResolvedValue(message)
255
+ const headers = { 'X-Custom': 'h' }
256
+ const body: SendVcardsPayload = { serviceId: 's-1', contactId: 'c-1', contactsIds: [] }
257
+ await api.sendVcards(body, headers)
258
+ expect(client.post).toHaveBeenCalledWith('/messages/send-vcards', body, headers)
259
+ })
260
+ })
261
+
262
+ // ── sendReaction ─────────────────────────────────────────────────
263
+
264
+ describe('sendReaction', () => {
265
+ it('calls client.post with /messages/:id/send-reaction and body', async () => {
266
+ vi.mocked(client.post).mockResolvedValue(true)
267
+ const body: SendReactionPayload = { reactionEmojiRendered: '👍' }
268
+ const result = await api.sendReaction('msg-1', body)
269
+ expect(client.post).toHaveBeenCalledWith('/messages/msg-1/send-reaction', body, undefined)
270
+ expect(result).toBe(true)
271
+ })
272
+
273
+ it('forwards custom headers', async () => {
274
+ vi.mocked(client.post).mockResolvedValue(true)
275
+ const headers = { 'X-Custom': 'h' }
276
+ await api.sendReaction('msg-1', { reactionEmojiRendered: '❤️' }, headers)
277
+ expect(client.post).toHaveBeenCalledWith(
278
+ '/messages/msg-1/send-reaction',
279
+ { reactionEmojiRendered: '❤️' },
280
+ headers,
281
+ )
282
+ })
283
+ })
284
+
285
+ // ── magicText ────────────────────────────────────────────────────
286
+
287
+ describe('magicText', () => {
288
+ it('calls client.post with /messages/magic-text and body', async () => {
289
+ const magicResult: MagicTextResult = { answer: 'Improved text' }
290
+ vi.mocked(client.post).mockResolvedValue(magicResult)
291
+ const body: MagicTextPayload = { message: 'hello', tone: 'professional' }
292
+ const result = await api.magicText(body)
293
+ expect(client.post).toHaveBeenCalledWith('/messages/magic-text', body, undefined)
294
+ expect(result).toEqual(magicResult)
295
+ })
296
+
297
+ it('forwards custom headers', async () => {
298
+ vi.mocked(client.post).mockResolvedValue({ answer: null })
299
+ const headers = { 'X-Custom': 'h' }
300
+ const body: MagicTextPayload = { message: 'test', tone: 'casual' }
301
+ await api.magicText(body, headers)
302
+ expect(client.post).toHaveBeenCalledWith('/messages/magic-text', body, headers)
303
+ })
304
+ })
305
+
306
+ // ── checkSummary ─────────────────────────────────────────────────
307
+
308
+ describe('checkSummary', () => {
309
+ it('calls client.post with /messages/check-summary and body', async () => {
310
+ const checkResult: CheckSummaryResult = { hasSummary: true }
311
+ vi.mocked(client.post).mockResolvedValue(checkResult)
312
+ const body: CheckSummaryPayload = { ticketId: 't-1' }
313
+ const result = await api.checkSummary(body)
314
+ expect(client.post).toHaveBeenCalledWith('/messages/check-summary', body, undefined)
315
+ expect(result).toEqual(checkResult)
316
+ })
317
+
318
+ it('forwards custom headers', async () => {
319
+ vi.mocked(client.post).mockResolvedValue({ hasSummary: false })
320
+ const headers = { 'X-Custom': 'h' }
321
+ await api.checkSummary({ ticketId: 't-1' }, headers)
322
+ expect(client.post).toHaveBeenCalledWith(
323
+ '/messages/check-summary',
324
+ { ticketId: 't-1' },
325
+ headers,
326
+ )
327
+ })
328
+ })
329
+
330
+ // ── bulkDestroy ──────────────────────────────────────────────────
331
+
332
+ describe('bulkDestroy', () => {
333
+ it('calls client.request DELETE with /messages/many and body', async () => {
334
+ vi.mocked(client.request).mockResolvedValue(true)
335
+ const body: BulkDestroyPayload = { where: { ticketId: 't-1' } }
336
+ const result = await api.bulkDestroy(body)
337
+ expect(client.request).toHaveBeenCalledWith('DELETE', '/messages/many', body, undefined)
338
+ expect(result).toBe(true)
339
+ })
340
+
341
+ it('forwards custom headers', async () => {
342
+ vi.mocked(client.request).mockResolvedValue(true)
343
+ const headers = { 'X-Custom': 'h' }
344
+ await api.bulkDestroy({ where: {} }, headers)
345
+ expect(client.request).toHaveBeenCalledWith(
346
+ 'DELETE',
347
+ '/messages/many',
348
+ { where: {} },
349
+ headers,
350
+ )
351
+ })
352
+ })
353
+
354
+ // ── summarize ────────────────────────────────────────────────────
355
+
356
+ describe('summarize', () => {
357
+ it('calls client.post with /messages/:id/summarize', async () => {
358
+ vi.mocked(client.post).mockResolvedValue(undefined)
359
+ await api.summarize('msg-1')
360
+ expect(client.post).toHaveBeenCalledWith('/messages/msg-1/summarize', undefined, undefined)
361
+ })
362
+
363
+ it('forwards custom headers', async () => {
364
+ vi.mocked(client.post).mockResolvedValue(undefined)
365
+ const headers = { 'X-Custom': 'h' }
366
+ await api.summarize('msg-1', headers)
367
+ expect(client.post).toHaveBeenCalledWith('/messages/msg-1/summarize', undefined, headers)
368
+ })
369
+ })
370
+
371
+ // ── outOfRange ───────────────────────────────────────────────────
372
+
373
+ describe('outOfRange', () => {
374
+ it('calls client.get with /messages/:id/out-of-range without limit', async () => {
375
+ const result: OutOfRangeResult = { outOfRange: false }
376
+ vi.mocked(client.get).mockResolvedValue(result)
377
+ const res = await api.outOfRange('msg-1')
378
+ expect(client.get).toHaveBeenCalledWith('/messages/msg-1/out-of-range', undefined)
379
+ expect(res).toEqual(result)
380
+ })
381
+
382
+ it('appends limit query parameter when provided', async () => {
383
+ vi.mocked(client.get).mockResolvedValue({ outOfRange: true })
384
+ await api.outOfRange('msg-1', 50)
385
+ const url = vi.mocked(client.get).mock.calls[0][0] as string
386
+ expect(url).toContain('/messages/msg-1/out-of-range?')
387
+ expect(url).toContain('limit=50')
388
+ })
389
+
390
+ it('forwards custom headers', async () => {
391
+ vi.mocked(client.get).mockResolvedValue({ outOfRange: false })
392
+ const headers = { 'X-Custom': 'h' }
393
+ await api.outOfRange('msg-1', undefined, headers)
394
+ expect(client.get).toHaveBeenCalledWith('/messages/msg-1/out-of-range', headers)
395
+ })
396
+ })
397
+
398
+ // ── syncFile ─────────────────────────────────────────────────────
399
+
400
+ describe('syncFile', () => {
401
+ it('calls client.post with /messages/:id/sync-file', async () => {
402
+ vi.mocked(client.post).mockResolvedValue({ ok: true })
403
+ const result = await api.syncFile('msg-1')
404
+ expect(client.post).toHaveBeenCalledWith('/messages/msg-1/sync-file', undefined, undefined)
405
+ expect(result).toEqual({ ok: true })
406
+ })
407
+
408
+ it('forwards custom headers', async () => {
409
+ vi.mocked(client.post).mockResolvedValue(null)
410
+ const headers = { 'X-Custom': 'h' }
411
+ await api.syncFile('msg-1', headers)
412
+ expect(client.post).toHaveBeenCalledWith('/messages/msg-1/sync-file', undefined, headers)
413
+ })
414
+ })
415
+
416
+ // ── around ───────────────────────────────────────────────────────
417
+
418
+ describe('around', () => {
419
+ it('calls client.get with /messages/:id/around without query', async () => {
420
+ vi.mocked(client.get).mockResolvedValue([message])
421
+ const result = await api.around('msg-1')
422
+ expect(client.get).toHaveBeenCalledWith('/messages/msg-1/around', undefined)
423
+ expect(result).toEqual([message])
424
+ })
425
+
426
+ it('appends limit query parameter when provided', async () => {
427
+ vi.mocked(client.get).mockResolvedValue([])
428
+ await api.around('msg-1', { limit: 10 })
429
+ const url = vi.mocked(client.get).mock.calls[0][0] as string
430
+ expect(url).toContain('/messages/msg-1/around?')
431
+ expect(url).toContain('limit=10')
432
+ })
433
+
434
+ it('forwards custom headers', async () => {
435
+ vi.mocked(client.get).mockResolvedValue([])
436
+ const headers = { 'X-Custom': 'h' }
437
+ await api.around('msg-1', undefined, headers)
438
+ expect(client.get).toHaveBeenCalledWith('/messages/msg-1/around', headers)
439
+ })
440
+ })
441
+
442
+ // ── Error propagation ────────────────────────────────────────────
443
+
444
+ describe('error propagation', () => {
445
+ it('propagates errors from getMany', async () => {
446
+ vi.mocked(client.get).mockRejectedValue(new Error('Network error'))
447
+ await expect(api.getMany()).rejects.toThrow('Network error')
448
+ })
449
+
450
+ it('propagates errors from forward', async () => {
451
+ vi.mocked(client.post).mockRejectedValue(new Error('Bad request'))
452
+ await expect(api.forward({ contactsIds: [], messagesIds: [] })).rejects.toThrow('Bad request')
453
+ })
454
+
455
+ it('propagates errors from bulkDestroy', async () => {
456
+ vi.mocked(client.request).mockRejectedValue(new Error('Forbidden'))
457
+ await expect(api.bulkDestroy({ where: {} })).rejects.toThrow('Forbidden')
458
+ })
459
+ })
460
+ })
@@ -0,0 +1,89 @@
1
+ import { beforeEach, describe, expect, it, vi } from 'vitest'
2
+ import { ServiceAccessManagementApi } from './ServiceAccessManagementApi'
3
+ import type { ApiClient } from '../../core/ApiClient'
4
+ import type { ServiceAccessManagement } from './types'
5
+ import type { Paginated } from '../../core/types'
6
+
7
+ // ── Mock client factory ──────────────────────────────────────────────
8
+
9
+ function createMockClient(): ApiClient {
10
+ return {
11
+ setAccessToken: vi.fn().mockReturnThis(),
12
+ request: vi.fn(),
13
+ get: vi.fn(),
14
+ post: vi.fn(),
15
+ put: vi.fn(),
16
+ patch: vi.fn(),
17
+ delete: vi.fn(),
18
+ }
19
+ }
20
+
21
+ // ── Fixtures ─────────────────────────────────────────────────────────
22
+
23
+ const record: ServiceAccessManagement = {
24
+ userId: 'user-1',
25
+ serviceId: 'svc-1',
26
+ createdAt: '2024-01-01T00:00:00Z',
27
+ }
28
+
29
+ const paginated: Paginated<ServiceAccessManagement> = {
30
+ data: [record],
31
+ total: 1,
32
+ limit: 10,
33
+ skip: 0,
34
+ currentPage: 1,
35
+ lastPage: 1,
36
+ from: 1,
37
+ to: 1,
38
+ }
39
+
40
+ // ── Tests ────────────────────────────────────────────────────────────
41
+
42
+ describe('ServiceAccessManagementApi', () => {
43
+ let client: ReturnType<typeof createMockClient>
44
+ let api: ServiceAccessManagementApi
45
+
46
+ beforeEach(() => {
47
+ client = createMockClient()
48
+ api = new ServiceAccessManagementApi(client)
49
+ })
50
+
51
+ // ── getMany ──────────────────────────────────────────────────────
52
+
53
+ describe('getMany', () => {
54
+ it('calls client.get with /service-access-management when no query is provided', async () => {
55
+ vi.mocked(client.get).mockResolvedValue(paginated)
56
+ const result = await api.getMany()
57
+ expect(client.get).toHaveBeenCalledWith('/service-access-management', undefined)
58
+ expect(result).toEqual(paginated)
59
+ })
60
+
61
+ it('serializes query parameters', async () => {
62
+ vi.mocked(client.get).mockResolvedValue(paginated)
63
+ await api.getMany({ page: 2, perPage: 5 })
64
+ const url = vi.mocked(client.get).mock.calls[0][0] as string
65
+ expect(url).toContain('/service-access-management?')
66
+ expect(url).toContain('page=2')
67
+ })
68
+
69
+ it('forwards custom headers', async () => {
70
+ vi.mocked(client.get).mockResolvedValue(paginated)
71
+ const headers = { 'X-Custom': 'h' }
72
+ await api.getMany(undefined, headers)
73
+ expect(client.get).toHaveBeenCalledWith('/service-access-management', headers)
74
+ })
75
+
76
+ it('returns unpaginated array when paginate: false', async () => {
77
+ vi.mocked(client.get).mockResolvedValue([record])
78
+ const result = await api.getMany({ paginate: false })
79
+ const url = vi.mocked(client.get).mock.calls[0][0] as string
80
+ expect(url).toContain('paginate=false')
81
+ expect(result).toEqual([record])
82
+ })
83
+
84
+ it('propagates errors from client.get', async () => {
85
+ vi.mocked(client.get).mockRejectedValue(new Error('Forbidden'))
86
+ await expect(api.getMany()).rejects.toThrow('Forbidden')
87
+ })
88
+ })
89
+ })
@@ -0,0 +1,84 @@
1
+ import { beforeEach, describe, expect, it, vi } from 'vitest'
2
+ import { StickerUsersApi } from './StickerUsersApi'
3
+ import type { ApiClient } from '../../core/ApiClient'
4
+ import type { StickerUser } from './types'
5
+ import type { Paginated } from '../../core/types'
6
+
7
+ // ── Mock client factory ──────────────────────────────────────────────
8
+
9
+ function createMockClient(): ApiClient {
10
+ return {
11
+ setAccessToken: vi.fn().mockReturnThis(),
12
+ request: vi.fn(),
13
+ get: vi.fn(),
14
+ post: vi.fn(),
15
+ put: vi.fn(),
16
+ patch: vi.fn(),
17
+ delete: vi.fn(),
18
+ }
19
+ }
20
+
21
+ // ── Fixtures ─────────────────────────────────────────────────────────
22
+
23
+ const stickerUser: StickerUser = {
24
+ stickerId: 'sticker-1',
25
+ userId: 'user-1',
26
+ accountId: 'acc-1',
27
+ lastSendAt: '2024-01-01T00:00:00Z',
28
+ createdAt: '2024-01-01T00:00:00Z',
29
+ updatedAt: '2024-01-01T00:00:00Z',
30
+ }
31
+
32
+ const paginated: Paginated<StickerUser> = {
33
+ data: [stickerUser],
34
+ total: 1,
35
+ limit: 10,
36
+ skip: 0,
37
+ currentPage: 1,
38
+ lastPage: 1,
39
+ from: 1,
40
+ to: 1,
41
+ }
42
+
43
+ // ── Tests ────────────────────────────────────────────────────────────
44
+
45
+ describe('StickerUsersApi', () => {
46
+ let client: ReturnType<typeof createMockClient>
47
+ let api: StickerUsersApi
48
+
49
+ beforeEach(() => {
50
+ client = createMockClient()
51
+ api = new StickerUsersApi(client)
52
+ })
53
+
54
+ // ── getMany ──────────────────────────────────────────────────────
55
+
56
+ describe('getMany', () => {
57
+ it('calls client.get with /sticker-users when no query is provided', async () => {
58
+ vi.mocked(client.get).mockResolvedValue(paginated)
59
+ const result = await api.getMany()
60
+ expect(client.get).toHaveBeenCalledWith('/sticker-users', undefined)
61
+ expect(result).toEqual(paginated)
62
+ })
63
+
64
+ it('serializes query parameters', async () => {
65
+ vi.mocked(client.get).mockResolvedValue(paginated)
66
+ await api.getMany({ page: 2, perPage: 5 })
67
+ const url = vi.mocked(client.get).mock.calls[0][0] as string
68
+ expect(url).toContain('/sticker-users?')
69
+ expect(url).toContain('page=2')
70
+ })
71
+
72
+ it('forwards custom headers', async () => {
73
+ vi.mocked(client.get).mockResolvedValue(paginated)
74
+ const headers = { 'X-Custom': 'h' }
75
+ await api.getMany(undefined, headers)
76
+ expect(client.get).toHaveBeenCalledWith('/sticker-users', headers)
77
+ })
78
+
79
+ it('propagates errors from client.get', async () => {
80
+ vi.mocked(client.get).mockRejectedValue(new Error('Forbidden'))
81
+ await expect(api.getMany()).rejects.toThrow('Forbidden')
82
+ })
83
+ })
84
+ })