autotel-subscribers 4.0.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.
Files changed (87) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +669 -0
  3. package/dist/amplitude.cjs +2486 -0
  4. package/dist/amplitude.cjs.map +1 -0
  5. package/dist/amplitude.d.cts +49 -0
  6. package/dist/amplitude.d.ts +49 -0
  7. package/dist/amplitude.js +2463 -0
  8. package/dist/amplitude.js.map +1 -0
  9. package/dist/event-subscriber-base-CnF3V56W.d.cts +182 -0
  10. package/dist/event-subscriber-base-CnF3V56W.d.ts +182 -0
  11. package/dist/factories.cjs +16660 -0
  12. package/dist/factories.cjs.map +1 -0
  13. package/dist/factories.d.cts +304 -0
  14. package/dist/factories.d.ts +304 -0
  15. package/dist/factories.js +16624 -0
  16. package/dist/factories.js.map +1 -0
  17. package/dist/index.cjs +16575 -0
  18. package/dist/index.cjs.map +1 -0
  19. package/dist/index.d.cts +179 -0
  20. package/dist/index.d.ts +179 -0
  21. package/dist/index.js +16539 -0
  22. package/dist/index.js.map +1 -0
  23. package/dist/middleware.cjs +220 -0
  24. package/dist/middleware.cjs.map +1 -0
  25. package/dist/middleware.d.cts +227 -0
  26. package/dist/middleware.d.ts +227 -0
  27. package/dist/middleware.js +208 -0
  28. package/dist/middleware.js.map +1 -0
  29. package/dist/mixpanel.cjs +2940 -0
  30. package/dist/mixpanel.cjs.map +1 -0
  31. package/dist/mixpanel.d.cts +47 -0
  32. package/dist/mixpanel.d.ts +47 -0
  33. package/dist/mixpanel.js +2932 -0
  34. package/dist/mixpanel.js.map +1 -0
  35. package/dist/posthog.cjs +4115 -0
  36. package/dist/posthog.cjs.map +1 -0
  37. package/dist/posthog.d.cts +299 -0
  38. package/dist/posthog.d.ts +299 -0
  39. package/dist/posthog.js +4113 -0
  40. package/dist/posthog.js.map +1 -0
  41. package/dist/segment.cjs +6822 -0
  42. package/dist/segment.cjs.map +1 -0
  43. package/dist/segment.d.cts +49 -0
  44. package/dist/segment.d.ts +49 -0
  45. package/dist/segment.js +6794 -0
  46. package/dist/segment.js.map +1 -0
  47. package/dist/slack.cjs +368 -0
  48. package/dist/slack.cjs.map +1 -0
  49. package/dist/slack.d.cts +126 -0
  50. package/dist/slack.d.ts +126 -0
  51. package/dist/slack.js +366 -0
  52. package/dist/slack.js.map +1 -0
  53. package/dist/webhook.cjs +100 -0
  54. package/dist/webhook.cjs.map +1 -0
  55. package/dist/webhook.d.cts +53 -0
  56. package/dist/webhook.d.ts +53 -0
  57. package/dist/webhook.js +98 -0
  58. package/dist/webhook.js.map +1 -0
  59. package/examples/quickstart-custom-subscriber.ts +144 -0
  60. package/examples/subscriber-bigquery.ts +219 -0
  61. package/examples/subscriber-databricks.ts +280 -0
  62. package/examples/subscriber-kafka.ts +326 -0
  63. package/examples/subscriber-kinesis.ts +307 -0
  64. package/examples/subscriber-posthog.ts +421 -0
  65. package/examples/subscriber-pubsub.ts +336 -0
  66. package/examples/subscriber-snowflake.ts +232 -0
  67. package/package.json +141 -0
  68. package/src/amplitude.test.ts +231 -0
  69. package/src/amplitude.ts +148 -0
  70. package/src/event-subscriber-base.ts +325 -0
  71. package/src/factories.ts +197 -0
  72. package/src/index.ts +50 -0
  73. package/src/middleware.ts +489 -0
  74. package/src/mixpanel.test.ts +194 -0
  75. package/src/mixpanel.ts +134 -0
  76. package/src/mock-event-subscriber.ts +333 -0
  77. package/src/posthog.test.ts +629 -0
  78. package/src/posthog.ts +530 -0
  79. package/src/segment.test.ts +228 -0
  80. package/src/segment.ts +148 -0
  81. package/src/slack.ts +383 -0
  82. package/src/streaming-event-subscriber.ts +323 -0
  83. package/src/testing/index.ts +37 -0
  84. package/src/testing/mock-webhook-server.ts +242 -0
  85. package/src/testing/subscriber-test-harness.ts +365 -0
  86. package/src/webhook.test.ts +264 -0
  87. package/src/webhook.ts +158 -0
@@ -0,0 +1,629 @@
1
+ import { beforeEach, describe, expect, it, vi } from 'vitest';
2
+ import { PostHogSubscriber } from './posthog';
3
+ import type { PostHog } from 'posthog-node';
4
+
5
+ // Mock the posthog-node module
6
+ const mockCapture = vi.fn();
7
+ const mockShutdown = vi.fn(() => Promise.resolve());
8
+ const mockIsFeatureEnabled = vi.fn();
9
+ const mockGetFeatureFlag = vi.fn();
10
+ const mockGetAllFlags = vi.fn();
11
+ const mockReloadFeatureFlags = vi.fn();
12
+ const mockIdentify = vi.fn();
13
+ const mockGroupIdentify = vi.fn();
14
+ const mockDebug = vi.fn();
15
+ const mockOn = vi.fn();
16
+
17
+ vi.mock('posthog-node', () => ({
18
+ PostHog: vi.fn(function (this: any) {
19
+ this.capture = mockCapture;
20
+ this.shutdown = mockShutdown;
21
+ this.isFeatureEnabled = mockIsFeatureEnabled;
22
+ this.getFeatureFlag = mockGetFeatureFlag;
23
+ this.getAllFlags = mockGetAllFlags;
24
+ this.reloadFeatureFlags = mockReloadFeatureFlags;
25
+ this.identify = mockIdentify;
26
+ this.groupIdentify = mockGroupIdentify;
27
+ this.debug = mockDebug;
28
+ this.on = mockOn;
29
+ }),
30
+ }));
31
+
32
+ describe('PostHogSubscriber', () => {
33
+ beforeEach(() => {
34
+ mockCapture.mockClear();
35
+ mockShutdown.mockClear();
36
+ mockIsFeatureEnabled.mockClear();
37
+ mockGetFeatureFlag.mockClear();
38
+ mockGetAllFlags.mockClear();
39
+ mockReloadFeatureFlags.mockClear();
40
+ mockIdentify.mockClear();
41
+ mockGroupIdentify.mockClear();
42
+ mockDebug.mockClear();
43
+ mockOn.mockClear();
44
+ });
45
+
46
+ describe('initialization', () => {
47
+ it('should initialize with valid config', async () => {
48
+ const adapter = new PostHogSubscriber({
49
+ apiKey: 'phc_test_key',
50
+ host: 'https://us.i.posthog.com',
51
+ });
52
+
53
+ // Wait for initialization
54
+ await new Promise((resolve) => setTimeout(resolve, 100));
55
+
56
+ expect(adapter).toBeDefined();
57
+ });
58
+
59
+ it('should initialize with default host', async () => {
60
+ const adapter = new PostHogSubscriber({
61
+ apiKey: 'phc_test_key',
62
+ });
63
+
64
+ await new Promise((resolve) => setTimeout(resolve, 100));
65
+
66
+ expect(adapter).toBeDefined();
67
+ });
68
+
69
+ it('should not initialize when disabled', () => {
70
+ const adapter = new PostHogSubscriber({
71
+ apiKey: 'phc_test_key',
72
+ enabled: false,
73
+ });
74
+
75
+ expect(adapter).toBeDefined();
76
+ });
77
+
78
+ it('should accept custom PostHog client', async () => {
79
+ const customClient = {
80
+ capture: mockCapture,
81
+ shutdown: mockShutdown,
82
+ } as unknown as PostHog;
83
+
84
+ const adapter = new PostHogSubscriber({
85
+ client: customClient,
86
+ });
87
+
88
+ await new Promise((resolve) => setTimeout(resolve, 100));
89
+
90
+ expect(adapter).toBeDefined();
91
+ });
92
+
93
+ it('should throw if no apiKey and no client provided', () => {
94
+ expect(() => new PostHogSubscriber({})).toThrow(
95
+ 'PostHogSubscriber requires either apiKey or client to be provided',
96
+ );
97
+ });
98
+
99
+ it('should setup error handling when onError is provided', async () => {
100
+ const onError = vi.fn();
101
+ const adapter = new PostHogSubscriber({
102
+ apiKey: 'phc_test_key',
103
+ onError,
104
+ });
105
+
106
+ await new Promise((resolve) => setTimeout(resolve, 100));
107
+
108
+ expect(adapter).toBeDefined();
109
+ expect(mockOn).toHaveBeenCalledWith('error', onError);
110
+ });
111
+
112
+ it('should enable debug mode when debug is true', async () => {
113
+ const adapter = new PostHogSubscriber({
114
+ apiKey: 'phc_test_key',
115
+ debug: true,
116
+ });
117
+
118
+ await new Promise((resolve) => setTimeout(resolve, 100));
119
+
120
+ expect(adapter).toBeDefined();
121
+ expect(mockDebug).toHaveBeenCalled();
122
+ });
123
+
124
+ it('should pass serverless config options', async () => {
125
+ const { PostHog } = await import('posthog-node');
126
+
127
+ const adapter = new PostHogSubscriber({
128
+ apiKey: 'phc_test_key',
129
+ flushAt: 1,
130
+ flushInterval: 0,
131
+ disableGeoip: true,
132
+ requestTimeout: 5000,
133
+ sendFeatureFlags: false,
134
+ });
135
+
136
+ await new Promise((resolve) => setTimeout(resolve, 100));
137
+
138
+ expect(adapter).toBeDefined();
139
+ expect(PostHog).toHaveBeenCalledWith('phc_test_key', {
140
+ host: 'https://us.i.posthog.com',
141
+ flushAt: 1,
142
+ flushInterval: 0,
143
+ disableGeoip: true,
144
+ requestTimeout: 5000,
145
+ sendFeatureFlagEvent: false,
146
+ });
147
+ });
148
+ });
149
+
150
+ describe('trackEvent', () => {
151
+ it('should track event with attributes', async () => {
152
+ const adapter = new PostHogSubscriber({
153
+ apiKey: 'phc_test_key',
154
+ });
155
+
156
+ await new Promise((resolve) => setTimeout(resolve, 100));
157
+
158
+ await adapter.trackEvent('order.completed', {
159
+ userId: 'user-123',
160
+ amount: 99.99,
161
+ });
162
+
163
+ await new Promise((resolve) => setTimeout(resolve, 100));
164
+
165
+ expect(mockCapture).toHaveBeenCalledWith({
166
+ distinctId: 'user-123',
167
+ event: 'order.completed',
168
+ properties: {
169
+ userId: 'user-123',
170
+ amount: 99.99,
171
+ },
172
+ });
173
+ });
174
+
175
+ it('should use user_id if userId is not present', async () => {
176
+ const adapter = new PostHogSubscriber({
177
+ apiKey: 'phc_test_key',
178
+ });
179
+
180
+ await new Promise((resolve) => setTimeout(resolve, 100));
181
+
182
+ await adapter.trackEvent('order.completed', {
183
+ user_id: 'user-456',
184
+ });
185
+
186
+ await new Promise((resolve) => setTimeout(resolve, 100));
187
+
188
+ expect(mockCapture).toHaveBeenCalledWith({
189
+ distinctId: 'user-456',
190
+ event: 'order.completed',
191
+ properties: {
192
+ user_id: 'user-456',
193
+ },
194
+ });
195
+ });
196
+
197
+ it('should use anonymous if no userId is present', async () => {
198
+ const adapter = new PostHogSubscriber({
199
+ apiKey: 'phc_test_key',
200
+ });
201
+
202
+ await new Promise((resolve) => setTimeout(resolve, 100));
203
+
204
+ await adapter.trackEvent('page.viewed');
205
+
206
+ await new Promise((resolve) => setTimeout(resolve, 100));
207
+
208
+ expect(mockCapture).toHaveBeenCalledWith({
209
+ distinctId: 'anonymous',
210
+ event: 'page.viewed',
211
+ properties: undefined,
212
+ });
213
+ });
214
+
215
+ it('should not track when disabled', async () => {
216
+ const adapter = new PostHogSubscriber({
217
+ apiKey: 'phc_test_key',
218
+ enabled: false,
219
+ });
220
+
221
+ await adapter.trackEvent('order.completed', { userId: 'user-123' });
222
+
223
+ // Should not call capture
224
+ expect(mockCapture).not.toHaveBeenCalled();
225
+ });
226
+
227
+ it('should include groups in capture payload', async () => {
228
+ const adapter = new PostHogSubscriber({
229
+ apiKey: 'phc_test_key',
230
+ });
231
+
232
+ await new Promise((resolve) => setTimeout(resolve, 100));
233
+
234
+ await adapter.trackEvent('feature.used', {
235
+ userId: 'user-123',
236
+ groups: { company: 'acme-corp' } as any,
237
+ });
238
+
239
+ await new Promise((resolve) => setTimeout(resolve, 100));
240
+
241
+ expect(mockCapture).toHaveBeenCalledWith({
242
+ distinctId: 'user-123',
243
+ event: 'feature.used',
244
+ properties: {
245
+ userId: 'user-123',
246
+ groups: { company: 'acme-corp' },
247
+ },
248
+ groups: { company: 'acme-corp' },
249
+ });
250
+ });
251
+ });
252
+
253
+ describe('trackFunnelStep', () => {
254
+ it('should track funnel step', async () => {
255
+ const adapter = new PostHogSubscriber({
256
+ apiKey: 'phc_test_key',
257
+ });
258
+
259
+ await new Promise((resolve) => setTimeout(resolve, 100));
260
+
261
+ await adapter.trackFunnelStep('checkout', 'started', {
262
+ userId: 'user-123',
263
+ cartValue: 150,
264
+ });
265
+
266
+ await new Promise((resolve) => setTimeout(resolve, 100));
267
+
268
+ expect(mockCapture).toHaveBeenCalledWith(
269
+ expect.objectContaining({
270
+ distinctId: 'user-123',
271
+ event: 'checkout.started',
272
+ properties: expect.objectContaining({
273
+ userId: 'user-123',
274
+ cartValue: 150,
275
+ }),
276
+ }),
277
+ );
278
+ });
279
+ });
280
+
281
+ describe('trackOutcome', () => {
282
+ it('should track outcome', async () => {
283
+ const adapter = new PostHogSubscriber({
284
+ apiKey: 'phc_test_key',
285
+ });
286
+
287
+ await new Promise((resolve) => setTimeout(resolve, 100));
288
+
289
+ await adapter.trackOutcome('payment.processing', 'success', {
290
+ userId: 'user-123',
291
+ transactionId: 'txn-789',
292
+ });
293
+
294
+ await new Promise((resolve) => setTimeout(resolve, 100));
295
+
296
+ expect(mockCapture).toHaveBeenCalledWith(
297
+ expect.objectContaining({
298
+ distinctId: 'user-123',
299
+ event: 'payment.processing.success',
300
+ properties: expect.objectContaining({
301
+ userId: 'user-123',
302
+ transactionId: 'txn-789',
303
+ }),
304
+ }),
305
+ );
306
+ });
307
+ });
308
+
309
+ describe('trackValue', () => {
310
+ it('should track value', async () => {
311
+ const adapter = new PostHogSubscriber({
312
+ apiKey: 'phc_test_key',
313
+ });
314
+
315
+ await new Promise((resolve) => setTimeout(resolve, 100));
316
+
317
+ await adapter.trackValue('revenue', 99.99, {
318
+ userId: 'user-123',
319
+ currency: 'USD',
320
+ });
321
+
322
+ await new Promise((resolve) => setTimeout(resolve, 100));
323
+
324
+ expect(mockCapture).toHaveBeenCalledWith(
325
+ expect.objectContaining({
326
+ distinctId: 'user-123',
327
+ event: 'revenue',
328
+ properties: expect.objectContaining({
329
+ value: 99.99,
330
+ userId: 'user-123',
331
+ currency: 'USD',
332
+ }),
333
+ }),
334
+ );
335
+ });
336
+ });
337
+
338
+ describe('feature flags', () => {
339
+ it('should check if feature is enabled', async () => {
340
+ mockIsFeatureEnabled.mockResolvedValue(true);
341
+
342
+ const adapter = new PostHogSubscriber({
343
+ apiKey: 'phc_test_key',
344
+ });
345
+
346
+ await new Promise((resolve) => setTimeout(resolve, 100));
347
+
348
+ const isEnabled = await adapter.isFeatureEnabled('new-checkout', 'user-123');
349
+
350
+ expect(isEnabled).toBe(true);
351
+ expect(mockIsFeatureEnabled).toHaveBeenCalledWith('new-checkout', 'user-123', undefined);
352
+ });
353
+
354
+ it('should check feature with options', async () => {
355
+ mockIsFeatureEnabled.mockResolvedValue(true);
356
+
357
+ const adapter = new PostHogSubscriber({
358
+ apiKey: 'phc_test_key',
359
+ });
360
+
361
+ await new Promise((resolve) => setTimeout(resolve, 100));
362
+
363
+ const options = {
364
+ groups: { company: 'acme-corp' },
365
+ personProperties: { plan: 'premium' },
366
+ };
367
+
368
+ await adapter.isFeatureEnabled('beta-features', 'user-123', options);
369
+
370
+ expect(mockIsFeatureEnabled).toHaveBeenCalledWith('beta-features', 'user-123', options);
371
+ });
372
+
373
+ it('should return false when feature check fails', async () => {
374
+ mockIsFeatureEnabled.mockRejectedValue(new Error('Network error'));
375
+ const onError = vi.fn();
376
+
377
+ const adapter = new PostHogSubscriber({
378
+ apiKey: 'phc_test_key',
379
+ onError,
380
+ });
381
+
382
+ await new Promise((resolve) => setTimeout(resolve, 100));
383
+
384
+ const isEnabled = await adapter.isFeatureEnabled('new-checkout', 'user-123');
385
+
386
+ expect(isEnabled).toBe(false);
387
+ expect(onError).toHaveBeenCalled();
388
+ });
389
+
390
+ it('should get feature flag value', async () => {
391
+ mockGetFeatureFlag.mockResolvedValue('test-variant');
392
+
393
+ const adapter = new PostHogSubscriber({
394
+ apiKey: 'phc_test_key',
395
+ });
396
+
397
+ await new Promise((resolve) => setTimeout(resolve, 100));
398
+
399
+ const variant = await adapter.getFeatureFlag('experiment', 'user-123');
400
+
401
+ expect(variant).toBe('test-variant');
402
+ expect(mockGetFeatureFlag).toHaveBeenCalledWith('experiment', 'user-123', undefined);
403
+ });
404
+
405
+ it('should get all flags', async () => {
406
+ const flags = { 'new-checkout': true, experiment: 'test' };
407
+ mockGetAllFlags.mockResolvedValue(flags);
408
+
409
+ const adapter = new PostHogSubscriber({
410
+ apiKey: 'phc_test_key',
411
+ });
412
+
413
+ await new Promise((resolve) => setTimeout(resolve, 100));
414
+
415
+ const result = await adapter.getAllFlags('user-123');
416
+
417
+ expect(result).toEqual(flags);
418
+ expect(mockGetAllFlags).toHaveBeenCalledWith('user-123', undefined);
419
+ });
420
+
421
+ it('should reload feature flags', async () => {
422
+ const adapter = new PostHogSubscriber({
423
+ apiKey: 'phc_test_key',
424
+ });
425
+
426
+ await new Promise((resolve) => setTimeout(resolve, 100));
427
+
428
+ await adapter.reloadFeatureFlags();
429
+
430
+ expect(mockReloadFeatureFlags).toHaveBeenCalled();
431
+ });
432
+
433
+ it('should not call feature flags when disabled', async () => {
434
+ const adapter = new PostHogSubscriber({
435
+ apiKey: 'phc_test_key',
436
+ enabled: false,
437
+ });
438
+
439
+ const isEnabled = await adapter.isFeatureEnabled('test', 'user-123');
440
+ const variant = await adapter.getFeatureFlag('test', 'user-123');
441
+ const flags = await adapter.getAllFlags('user-123');
442
+ await adapter.reloadFeatureFlags();
443
+
444
+ expect(isEnabled).toBe(false);
445
+ expect(variant).toBeUndefined();
446
+ expect(flags).toEqual({});
447
+ expect(mockIsFeatureEnabled).not.toHaveBeenCalled();
448
+ expect(mockGetFeatureFlag).not.toHaveBeenCalled();
449
+ expect(mockGetAllFlags).not.toHaveBeenCalled();
450
+ expect(mockReloadFeatureFlags).not.toHaveBeenCalled();
451
+ });
452
+ });
453
+
454
+ describe('person and group events', () => {
455
+ it('should identify user with properties', async () => {
456
+ const adapter = new PostHogSubscriber({
457
+ apiKey: 'phc_test_key',
458
+ });
459
+
460
+ await new Promise((resolve) => setTimeout(resolve, 100));
461
+
462
+ await adapter.identify('user-123', {
463
+ $set: {
464
+ email: 'user@example.com',
465
+ plan: 'premium',
466
+ },
467
+ });
468
+
469
+ expect(mockIdentify).toHaveBeenCalledWith({
470
+ distinctId: 'user-123',
471
+ properties: {
472
+ $set: {
473
+ email: 'user@example.com',
474
+ plan: 'premium',
475
+ },
476
+ },
477
+ });
478
+ });
479
+
480
+ it('should identify user with $set_once', async () => {
481
+ const adapter = new PostHogSubscriber({
482
+ apiKey: 'phc_test_key',
483
+ });
484
+
485
+ await new Promise((resolve) => setTimeout(resolve, 100));
486
+
487
+ await adapter.identify('user-123', {
488
+ $set_once: {
489
+ signup_date: '2025-01-17',
490
+ },
491
+ });
492
+
493
+ expect(mockIdentify).toHaveBeenCalledWith({
494
+ distinctId: 'user-123',
495
+ properties: {
496
+ $set_once: {
497
+ signup_date: '2025-01-17',
498
+ },
499
+ },
500
+ });
501
+ });
502
+
503
+ it('should identify group', async () => {
504
+ const adapter = new PostHogSubscriber({
505
+ apiKey: 'phc_test_key',
506
+ });
507
+
508
+ await new Promise((resolve) => setTimeout(resolve, 100));
509
+
510
+ await adapter.groupIdentify('company', 'acme-corp', {
511
+ $set: {
512
+ name: 'Acme Corporation',
513
+ industry: 'saas',
514
+ employees: 500,
515
+ },
516
+ });
517
+
518
+ expect(mockGroupIdentify).toHaveBeenCalledWith({
519
+ groupType: 'company',
520
+ groupKey: 'acme-corp',
521
+ properties: {
522
+ $set: {
523
+ name: 'Acme Corporation',
524
+ industry: 'saas',
525
+ employees: 500,
526
+ },
527
+ },
528
+ });
529
+ });
530
+
531
+ it('should track event with groups', async () => {
532
+ const adapter = new PostHogSubscriber({
533
+ apiKey: 'phc_test_key',
534
+ });
535
+
536
+ await new Promise((resolve) => setTimeout(resolve, 100));
537
+
538
+ await adapter.trackEventWithGroups(
539
+ 'feature.used',
540
+ {
541
+ userId: 'user-123',
542
+ feature: 'events',
543
+ },
544
+ { company: 'acme-corp' },
545
+ );
546
+
547
+ await new Promise((resolve) => setTimeout(resolve, 100));
548
+
549
+ expect(mockCapture).toHaveBeenCalledWith({
550
+ distinctId: 'user-123',
551
+ event: 'feature.used',
552
+ properties: {
553
+ userId: 'user-123',
554
+ feature: 'events',
555
+ groups: { company: 'acme-corp' },
556
+ },
557
+ groups: { company: 'acme-corp' },
558
+ });
559
+ });
560
+
561
+ it('should handle identify errors', async () => {
562
+ mockIdentify.mockImplementation(() => {
563
+ throw new Error('Network error');
564
+ });
565
+
566
+ const onError = vi.fn();
567
+ const adapter = new PostHogSubscriber({
568
+ apiKey: 'phc_test_key',
569
+ onError,
570
+ });
571
+
572
+ await new Promise((resolve) => setTimeout(resolve, 100));
573
+
574
+ await adapter.identify('user-123', { $set: { email: 'test@example.com' } });
575
+
576
+ expect(onError).toHaveBeenCalled();
577
+ });
578
+
579
+ it('should not call identify when disabled', async () => {
580
+ const adapter = new PostHogSubscriber({
581
+ apiKey: 'phc_test_key',
582
+ enabled: false,
583
+ });
584
+
585
+ await adapter.identify('user-123', { $set: { email: 'test@example.com' } });
586
+ await adapter.groupIdentify('company', 'acme-corp', {});
587
+
588
+ expect(mockIdentify).not.toHaveBeenCalled();
589
+ expect(mockGroupIdentify).not.toHaveBeenCalled();
590
+ });
591
+ });
592
+
593
+ describe('shutdown', () => {
594
+ it('should call shutdown on PostHog instance', async () => {
595
+ const adapter = new PostHogSubscriber({
596
+ apiKey: 'phc_test_key',
597
+ });
598
+
599
+ await new Promise((resolve) => setTimeout(resolve, 100));
600
+ await adapter.shutdown();
601
+
602
+ expect(mockShutdown).toHaveBeenCalled();
603
+ });
604
+
605
+ it('should not throw when shutting down disabled adapter', async () => {
606
+ const adapter = new PostHogSubscriber({
607
+ apiKey: 'phc_test_key',
608
+ enabled: false,
609
+ });
610
+
611
+ await expect(adapter.shutdown()).resolves.not.toThrow();
612
+ });
613
+
614
+ it('should handle shutdown errors', async () => {
615
+ mockShutdown.mockRejectedValue(new Error('Shutdown error'));
616
+ const onError = vi.fn();
617
+
618
+ const adapter = new PostHogSubscriber({
619
+ apiKey: 'phc_test_key',
620
+ onError,
621
+ });
622
+
623
+ await new Promise((resolve) => setTimeout(resolve, 100));
624
+ await adapter.shutdown();
625
+
626
+ expect(onError).toHaveBeenCalled();
627
+ });
628
+ });
629
+ });