@adtrackify/at-service-common 4.0.14 → 4.0.16

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 (61) hide show
  1. package/dist/cjs/__tests__/identity-cache/identity-cache-tier-routing.spec.js +29 -25
  2. package/dist/cjs/__tests__/identity-cache/identity-cache-tier-routing.spec.js.map +1 -1
  3. package/dist/cjs/__tests__/identity-cache/identity-gateway-cache-hit.spec.js +138 -0
  4. package/dist/cjs/__tests__/identity-cache/identity-gateway-cache-hit.spec.js.map +1 -0
  5. package/dist/cjs/__tests__/identity-cache/identity-gateway-cache-miss.spec.js +153 -0
  6. package/dist/cjs/__tests__/identity-cache/identity-gateway-cache-miss.spec.js.map +1 -0
  7. package/dist/cjs/__tests__/identity-cache/identity-gateway-constructor.spec.d.ts +1 -0
  8. package/dist/cjs/__tests__/identity-cache/identity-gateway-constructor.spec.js +72 -0
  9. package/dist/cjs/__tests__/identity-cache/identity-gateway-constructor.spec.js.map +1 -0
  10. package/dist/cjs/__tests__/identity-cache/identity-gateway-edge-cases.spec.d.ts +1 -0
  11. package/dist/cjs/__tests__/identity-cache/identity-gateway-edge-cases.spec.js +89 -0
  12. package/dist/cjs/__tests__/identity-cache/identity-gateway-edge-cases.spec.js.map +1 -0
  13. package/dist/cjs/__tests__/identity-cache/identity-gateway-enqueue-async.spec.d.ts +1 -0
  14. package/dist/cjs/__tests__/identity-cache/identity-gateway-enqueue-async.spec.js +136 -0
  15. package/dist/cjs/__tests__/identity-cache/identity-gateway-enqueue-async.spec.js.map +1 -0
  16. package/dist/cjs/__tests__/identity-cache/identity-gateway-stale-cache.spec.d.ts +1 -0
  17. package/dist/cjs/__tests__/identity-cache/identity-gateway-stale-cache.spec.js +224 -0
  18. package/dist/cjs/__tests__/identity-cache/identity-gateway-stale-cache.spec.js.map +1 -0
  19. package/dist/cjs/__tests__/identity-cache/identity-gateway-test-setup.d.ts +28 -0
  20. package/dist/cjs/__tests__/identity-cache/identity-gateway-test-setup.js +90 -0
  21. package/dist/cjs/__tests__/identity-cache/identity-gateway-test-setup.js.map +1 -0
  22. package/dist/cjs/services/db/identity-cache-dynamodb-service/identity-gateway-service.d.ts +11 -12
  23. package/dist/cjs/services/db/identity-cache-dynamodb-service/identity-gateway-service.js +15 -18
  24. package/dist/cjs/services/db/identity-cache-dynamodb-service/identity-gateway-service.js.map +1 -1
  25. package/dist/cjs/services/db/identity-cache-dynamodb-service/types.d.ts +2 -4
  26. package/dist/cjs/services/db/identity-cache-dynamodb-service/types.js.map +1 -1
  27. package/dist/esm/__tests__/identity-cache/identity-cache-tier-routing.spec.js +29 -25
  28. package/dist/esm/__tests__/identity-cache/identity-cache-tier-routing.spec.js.map +1 -1
  29. package/dist/esm/__tests__/identity-cache/identity-gateway-cache-hit.spec.d.ts +1 -0
  30. package/dist/esm/__tests__/identity-cache/identity-gateway-cache-hit.spec.js +136 -0
  31. package/dist/esm/__tests__/identity-cache/identity-gateway-cache-hit.spec.js.map +1 -0
  32. package/dist/esm/__tests__/identity-cache/identity-gateway-cache-miss.spec.d.ts +1 -0
  33. package/dist/esm/__tests__/identity-cache/identity-gateway-cache-miss.spec.js +151 -0
  34. package/dist/esm/__tests__/identity-cache/identity-gateway-cache-miss.spec.js.map +1 -0
  35. package/dist/esm/__tests__/identity-cache/identity-gateway-constructor.spec.d.ts +1 -0
  36. package/dist/esm/__tests__/identity-cache/identity-gateway-constructor.spec.js +70 -0
  37. package/dist/esm/__tests__/identity-cache/identity-gateway-constructor.spec.js.map +1 -0
  38. package/dist/esm/__tests__/identity-cache/identity-gateway-edge-cases.spec.d.ts +1 -0
  39. package/dist/esm/__tests__/identity-cache/identity-gateway-edge-cases.spec.js +87 -0
  40. package/dist/esm/__tests__/identity-cache/identity-gateway-edge-cases.spec.js.map +1 -0
  41. package/dist/esm/__tests__/identity-cache/identity-gateway-enqueue-async.spec.d.ts +1 -0
  42. package/dist/esm/__tests__/identity-cache/identity-gateway-enqueue-async.spec.js +134 -0
  43. package/dist/esm/__tests__/identity-cache/identity-gateway-enqueue-async.spec.js.map +1 -0
  44. package/dist/esm/__tests__/identity-cache/identity-gateway-stale-cache.spec.d.ts +1 -0
  45. package/dist/esm/__tests__/identity-cache/identity-gateway-stale-cache.spec.js +222 -0
  46. package/dist/esm/__tests__/identity-cache/identity-gateway-stale-cache.spec.js.map +1 -0
  47. package/dist/esm/__tests__/identity-cache/identity-gateway-test-setup.d.ts +28 -0
  48. package/dist/esm/__tests__/identity-cache/identity-gateway-test-setup.js +83 -0
  49. package/dist/esm/__tests__/identity-cache/identity-gateway-test-setup.js.map +1 -0
  50. package/dist/esm/services/db/identity-cache-dynamodb-service/identity-gateway-service.d.ts +11 -12
  51. package/dist/esm/services/db/identity-cache-dynamodb-service/identity-gateway-service.js +15 -18
  52. package/dist/esm/services/db/identity-cache-dynamodb-service/identity-gateway-service.js.map +1 -1
  53. package/dist/esm/services/db/identity-cache-dynamodb-service/types.d.ts +2 -4
  54. package/dist/esm/services/db/identity-cache-dynamodb-service/types.js.map +1 -1
  55. package/package.json +2 -2
  56. package/dist/cjs/__tests__/identity-cache/identity-gateway-service.spec.js +0 -1244
  57. package/dist/cjs/__tests__/identity-cache/identity-gateway-service.spec.js.map +0 -1
  58. package/dist/esm/__tests__/identity-cache/identity-gateway-service.spec.js +0 -1242
  59. package/dist/esm/__tests__/identity-cache/identity-gateway-service.spec.js.map +0 -1
  60. /package/dist/cjs/__tests__/identity-cache/{identity-gateway-service.spec.d.ts → identity-gateway-cache-hit.spec.d.ts} +0 -0
  61. /package/dist/{esm/__tests__/identity-cache/identity-gateway-service.spec.d.ts → cjs/__tests__/identity-cache/identity-gateway-cache-miss.spec.d.ts} +0 -0
@@ -0,0 +1,136 @@
1
+ import { createService, createCachedIdentity, mockGetIdentity, mockedDynamoDbClient, resetAllMocks, TEST_PIXEL_ID, IDENTITY_CACHE_WITH_CACHING_OUTCOME, } from './identity-gateway-test-setup.js';
2
+ describe('IdentityGatewayService cache hit scenarios', () => {
3
+ const pixelId = TEST_PIXEL_ID;
4
+ beforeEach(() => {
5
+ resetAllMocks();
6
+ });
7
+ describe('fresh cache hit', () => {
8
+ it('should return cached identity without calling Neptune', async () => {
9
+ const service = createService();
10
+ const cachedResponse = createCachedIdentity(pixelId, 'identity456', {
11
+ emails: ['test@email.com'],
12
+ });
13
+ mockedDynamoDbClient.safeGet.mockResolvedValueOnce(cachedResponse);
14
+ const incomingIdentity = {
15
+ identityId: 'identity456',
16
+ traits: { emails: ['test@email.com'] },
17
+ };
18
+ const result = await service.resolveIdentityWithCaching(pixelId, incomingIdentity);
19
+ expect(result.outcome).toBe(IDENTITY_CACHE_WITH_CACHING_OUTCOME.FRESH_CACHE_HIT);
20
+ expect(result.didCallNeptune).toBe(false);
21
+ expect(result.didWriteCache).toBe(false);
22
+ expect(mockGetIdentity).not.toHaveBeenCalled();
23
+ expect(mockedDynamoDbClient.safeBatchWrite).not.toHaveBeenCalled();
24
+ });
25
+ it('should treat as cache hit when merged identity is deep equal to cached', async () => {
26
+ const service = createService();
27
+ const cachedResponse = createCachedIdentity(pixelId, 'deep-equal', {
28
+ emails: ['same@email.com'],
29
+ phones: ['555-1234'],
30
+ });
31
+ mockedDynamoDbClient.safeGet.mockResolvedValueOnce(cachedResponse);
32
+ const incomingIdentity = {
33
+ identityId: 'deep-equal',
34
+ traits: {
35
+ emails: ['same@email.com'],
36
+ phones: ['555-1234'],
37
+ },
38
+ };
39
+ const result = await service.resolveIdentityWithCaching(pixelId, incomingIdentity);
40
+ expect(result.outcome).toBe(IDENTITY_CACHE_WITH_CACHING_OUTCOME.FRESH_CACHE_HIT);
41
+ expect(result.didCallNeptune).toBe(false);
42
+ expect(mockGetIdentity).not.toHaveBeenCalled();
43
+ });
44
+ });
45
+ describe('address timestamp stripping', () => {
46
+ it('should strip address timestamps before deep equality comparison', async () => {
47
+ const service = createService();
48
+ const now = Date.now();
49
+ const cachedResponse = {
50
+ pk: `identity#${pixelId}#timestamp-strip`,
51
+ pixelId,
52
+ identityId: 'timestamp-strip',
53
+ response: {
54
+ identityId: 'timestamp-strip',
55
+ traits: {
56
+ emails: ['test@email.com'],
57
+ addresses: [{ city: 'NYC', cra: now - 10000, upa: now - 5000 }],
58
+ },
59
+ },
60
+ updatedAt: new Date().toISOString(),
61
+ };
62
+ mockedDynamoDbClient.safeGet.mockResolvedValueOnce(cachedResponse);
63
+ const incomingIdentity = {
64
+ identityId: 'timestamp-strip',
65
+ traits: {
66
+ emails: ['test@email.com'],
67
+ addresses: [{ city: 'NYC', cra: now, upa: now }],
68
+ },
69
+ };
70
+ const result = await service.resolveIdentityWithCaching(pixelId, incomingIdentity);
71
+ expect(result.outcome).toBe(IDENTITY_CACHE_WITH_CACHING_OUTCOME.FRESH_CACHE_HIT);
72
+ expect(result.didCallNeptune).toBe(false);
73
+ });
74
+ });
75
+ describe('volatile-only cache hit', () => {
76
+ it('should treat VOLATILE_ONLY changes as cache hit', async () => {
77
+ const service = createService();
78
+ const cachedResponse = createCachedIdentity(pixelId, 'volatile-override', {
79
+ emails: ['same@email.com'],
80
+ });
81
+ mockedDynamoDbClient.safeGet.mockResolvedValueOnce(cachedResponse);
82
+ const incomingIdentity = {
83
+ identityId: 'volatile-override',
84
+ traits: {
85
+ emails: ['same@email.com'],
86
+ ipAddress: '10.0.0.1',
87
+ userAgent: 'Mozilla/5.0 (New)',
88
+ },
89
+ };
90
+ const result = await service.resolveIdentityWithCaching(pixelId, incomingIdentity);
91
+ expect(result.outcome).toBe(IDENTITY_CACHE_WITH_CACHING_OUTCOME.VOLATILE_ONLY_CACHE_HIT);
92
+ expect(result.didCallNeptune).toBe(false);
93
+ expect(result.didWriteCache).toBe(false);
94
+ });
95
+ it('should return merged identity for VOLATILE_ONLY tier', async () => {
96
+ const service = createService();
97
+ const cachedResponse = createCachedIdentity(pixelId, 'volatile-merged', {
98
+ emails: ['test@email.com'],
99
+ phones: ['555-cached'],
100
+ });
101
+ mockedDynamoDbClient.safeGet.mockResolvedValueOnce(cachedResponse);
102
+ const incomingIdentity = {
103
+ identityId: 'volatile-merged',
104
+ traits: {
105
+ emails: ['test@email.com'],
106
+ ipAddress: '10.0.0.1',
107
+ },
108
+ };
109
+ const result = await service.resolveIdentityWithCaching(pixelId, incomingIdentity);
110
+ expect(result.outcome).toBe(IDENTITY_CACHE_WITH_CACHING_OUTCOME.VOLATILE_ONLY_CACHE_HIT);
111
+ expect(result.identity?.traits?.emails).toContain('test@email.com');
112
+ expect(result.identity?.traits?.phones).toContain('555-cached');
113
+ expect(result.identity?.traits?.ipAddress).toBe('10.0.0.1');
114
+ });
115
+ });
116
+ describe('trait merging', () => {
117
+ it('should merge incoming traits with cached traits on cache hit', async () => {
118
+ const service = createService();
119
+ const cachedResponse = createCachedIdentity(pixelId, 'merge-test', {
120
+ emails: ['cached@email.com'],
121
+ phones: ['555-cached'],
122
+ });
123
+ mockedDynamoDbClient.safeGet.mockResolvedValueOnce(cachedResponse);
124
+ const incomingIdentity = {
125
+ identityId: 'merge-test',
126
+ traits: {
127
+ emails: ['cached@email.com'],
128
+ },
129
+ };
130
+ const result = await service.resolveIdentityWithCaching(pixelId, incomingIdentity);
131
+ expect(result.identity?.traits?.emails).toContain('cached@email.com');
132
+ expect(result.identity?.traits?.phones).toContain('555-cached');
133
+ });
134
+ });
135
+ });
136
+ //# sourceMappingURL=identity-gateway-cache-hit.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"identity-gateway-cache-hit.spec.js","sourceRoot":"","sources":["../../../../src/__tests__/identity-cache/identity-gateway-cache-hit.spec.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,151 @@
1
+ import { createService, mockGetIdentity, mockedDynamoDbClient, resetAllMocks, TEST_PIXEL_ID, IDENTITY_CACHE_WITH_CACHING_OUTCOME, } from './identity-gateway-test-setup.js';
2
+ describe('IdentityGatewayService cache miss scenarios', () => {
3
+ const pixelId = TEST_PIXEL_ID;
4
+ beforeEach(() => {
5
+ resetAllMocks();
6
+ });
7
+ describe('Neptune resolution on cache miss', () => {
8
+ it('should call Neptune with incoming identity when no cached identity', async () => {
9
+ const service = createService();
10
+ mockedDynamoDbClient.safeGet.mockResolvedValueOnce(null);
11
+ mockGetIdentity.mockResolvedValueOnce({
12
+ identityId: 'neptune-resolved-id',
13
+ traits: { emails: ['test@email.com'] },
14
+ });
15
+ const incomingIdentity = {
16
+ identityId: 'incoming-id',
17
+ traits: { emails: ['test@email.com'] },
18
+ };
19
+ const result = await service.resolveIdentityWithCaching(pixelId, incomingIdentity);
20
+ expect(mockGetIdentity).toHaveBeenCalledWith(pixelId, expect.objectContaining({
21
+ identity: expect.objectContaining({
22
+ identityId: 'incoming-id',
23
+ }),
24
+ }));
25
+ expect(result.outcome).toBe(IDENTITY_CACHE_WITH_CACHING_OUTCOME.NEPTUNE_RESOLVED);
26
+ expect(result.didReadCache).toBe(true);
27
+ expect(result.didCallNeptune).toBe(true);
28
+ });
29
+ it('should return Neptune identity on successful resolution', async () => {
30
+ const service = createService();
31
+ mockedDynamoDbClient.safeGet.mockResolvedValueOnce(null);
32
+ mockGetIdentity.mockResolvedValueOnce({
33
+ identityId: 'from-neptune',
34
+ traits: { emails: ['neptune@email.com'] },
35
+ });
36
+ const result = await service.resolveIdentityWithCaching(pixelId, { identityId: 'original', traits: { emails: ['original@email.com'] } });
37
+ expect(result.identity?.identityId).toBe('from-neptune');
38
+ expect(result.identity?.traits?.emails).toContain('neptune@email.com');
39
+ });
40
+ it('should include traitChanges with "no cached identity" reason', async () => {
41
+ const service = createService();
42
+ mockedDynamoDbClient.safeGet.mockResolvedValueOnce(null);
43
+ mockGetIdentity.mockResolvedValueOnce({
44
+ identityId: 'new-identity',
45
+ traits: { emails: ['new@email.com'] },
46
+ });
47
+ const result = await service.resolveIdentityWithCaching(pixelId, { identityId: 'incoming', traits: { emails: ['incoming@email.com'] } });
48
+ expect(result.traitChanges).toEqual({
49
+ reason: { resolved: 'no cached identity' },
50
+ });
51
+ });
52
+ it('should not write to cache (identity service handles writes)', async () => {
53
+ const service = createService();
54
+ mockedDynamoDbClient.safeGet.mockResolvedValueOnce(null);
55
+ mockGetIdentity.mockResolvedValueOnce({
56
+ identityId: 'neptune-success',
57
+ traits: { emails: ['test@email.com'], phones: ['555-1234'] },
58
+ });
59
+ const result = await service.resolveIdentityWithCaching(pixelId, { identityId: 'incoming', traits: { emails: ['test@email.com'] } });
60
+ expect(result.didWriteCache).toBe(false);
61
+ expect(result.outcome).toBe(IDENTITY_CACHE_WITH_CACHING_OUTCOME.NEPTUNE_RESOLVED);
62
+ expect(mockedDynamoDbClient.safeBatchWrite).not.toHaveBeenCalled();
63
+ });
64
+ });
65
+ describe('email pointer discovery', () => {
66
+ it('should discover identity ID from email lookup when incoming has no ID', async () => {
67
+ const service = createService();
68
+ const incomingNoId = {
69
+ traits: { emails: ['discovered@email.com'] },
70
+ };
71
+ mockedDynamoDbClient.safeGet.mockResolvedValueOnce({
72
+ pk: `email#${pixelId}#discovered@email.com`,
73
+ identityId: 'discovered-identity-id',
74
+ });
75
+ mockGetIdentity.mockResolvedValueOnce({
76
+ identityId: 'discovered-identity-id',
77
+ traits: { emails: ['discovered@email.com', 'other@email.com'] },
78
+ });
79
+ const result = await service.resolveIdentityWithCaching(pixelId, incomingNoId);
80
+ expect(result.identity?.identityId).toBe('discovered-identity-id');
81
+ });
82
+ it('should use incoming identity traits in Neptune call', async () => {
83
+ const service = createService();
84
+ mockedDynamoDbClient.safeGet.mockResolvedValueOnce({
85
+ pk: `email#${pixelId}#test@email.com`,
86
+ identityId: 'discovered-from-email',
87
+ });
88
+ mockGetIdentity.mockResolvedValueOnce({
89
+ identityId: 'neptune-resolved',
90
+ traits: { emails: ['test@email.com'] },
91
+ });
92
+ await service.resolveIdentityWithCaching(pixelId, { traits: { emails: ['test@email.com'] } });
93
+ expect(mockGetIdentity).toHaveBeenCalledWith(pixelId, expect.objectContaining({
94
+ identity: expect.objectContaining({
95
+ traits: expect.objectContaining({ emails: ['test@email.com'] }),
96
+ }),
97
+ }));
98
+ });
99
+ });
100
+ describe('Neptune resolution failure', () => {
101
+ it('should fallback to incoming identity when Neptune fails', async () => {
102
+ const service = createService();
103
+ mockedDynamoDbClient.safeGet.mockResolvedValueOnce(null);
104
+ mockGetIdentity.mockRejectedValueOnce(new Error('Neptune unavailable'));
105
+ const incomingIdentity = {
106
+ identityId: 'fallback-incoming',
107
+ traits: { emails: ['fallback@email.com'] },
108
+ };
109
+ const result = await service.resolveIdentityWithCaching(pixelId, incomingIdentity);
110
+ expect(result.outcome).toBe(IDENTITY_CACHE_WITH_CACHING_OUTCOME.NEPTUNE_RESOLUTION_FAILED);
111
+ expect(result.didFallbackToIncoming).toBe(true);
112
+ expect(result.identity?.identityId).toBe('fallback-incoming');
113
+ });
114
+ it('should not write to cache when Neptune fails', async () => {
115
+ const service = createService();
116
+ mockedDynamoDbClient.safeGet.mockResolvedValueOnce(null);
117
+ mockGetIdentity.mockRejectedValueOnce(new Error('Lambda timeout'));
118
+ const result = await service.resolveIdentityWithCaching(pixelId, { identityId: 'test', traits: {} });
119
+ expect(result.didWriteCache).toBe(false);
120
+ expect(mockedDynamoDbClient.safeBatchWrite).not.toHaveBeenCalled();
121
+ });
122
+ it('should use discovered identity ID as fallback when available', async () => {
123
+ const service = createService();
124
+ mockedDynamoDbClient.safeGet.mockResolvedValueOnce({
125
+ pk: `email#${pixelId}#test@email.com`,
126
+ identityId: 'discovered-id',
127
+ });
128
+ mockGetIdentity.mockRejectedValueOnce(new Error('Neptune error'));
129
+ const result = await service.resolveIdentityWithCaching(pixelId, { traits: { emails: ['test@email.com'] } });
130
+ expect(result.didFallbackToIncoming).toBe(true);
131
+ expect(result.identity?.identityId).toBe('discovered-id');
132
+ expect(result.identity?.traits?.emails).toContain('test@email.com');
133
+ });
134
+ });
135
+ describe('cache read failure', () => {
136
+ it('should continue to Neptune when cache read fails (fail-open)', async () => {
137
+ const service = createService();
138
+ mockedDynamoDbClient.safeGet.mockRejectedValueOnce(new Error('DynamoDB error'));
139
+ mockGetIdentity.mockResolvedValueOnce({
140
+ identityId: 'neptune-resolved',
141
+ traits: { emails: ['test@email.com'] },
142
+ });
143
+ const result = await service.resolveIdentityWithCaching(pixelId, { identityId: 'test-id', traits: { emails: ['test@email.com'] } });
144
+ expect(result.identity).toBeDefined();
145
+ expect(result.identity?.identityId).toBe('neptune-resolved');
146
+ expect(result.didReadCache).toBe(true);
147
+ expect(result.didCallNeptune).toBe(true);
148
+ });
149
+ });
150
+ });
151
+ //# sourceMappingURL=identity-gateway-cache-miss.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"identity-gateway-cache-miss.spec.js","sourceRoot":"","sources":["../../../../src/__tests__/identity-cache/identity-gateway-cache-miss.spec.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,70 @@
1
+ import { IdentityGatewayService, mockSQSClientConstructor, resetAllMocks, } from './identity-gateway-test-setup.js';
2
+ describe('IdentityGatewayService constructor()', () => {
3
+ beforeEach(() => {
4
+ resetAllMocks();
5
+ });
6
+ it('should configure with direct queue URL', () => {
7
+ const options = {
8
+ asyncUpdateQueueUrl: 'https://sqs.us-east-1.amazonaws.com/123456789/test-queue',
9
+ };
10
+ const service = new IdentityGatewayService(options);
11
+ expect(service.sqsClient).toBeDefined();
12
+ });
13
+ it('should configure with region/accountId/queueName', () => {
14
+ const options = {
15
+ awsRegion: 'us-east-1',
16
+ awsAccountId: '123456789',
17
+ asyncUpdateQueueName: 'identity-async-queue',
18
+ };
19
+ const service = new IdentityGatewayService(options);
20
+ expect(mockSQSClientConstructor).toHaveBeenCalledWith('us-east-1', '123456789', 'identity-async-queue');
21
+ expect(service.sqsClient).toBeDefined();
22
+ });
23
+ it('should fallback to environment variable for queue name', () => {
24
+ const originalEnv = process.env.IDENTIFY_ASYNC_UPDATE_QUEUE_NAME;
25
+ process.env.IDENTIFY_ASYNC_UPDATE_QUEUE_NAME = 'env-queue-name';
26
+ try {
27
+ const options = {
28
+ awsRegion: 'us-west-2',
29
+ awsAccountId: '987654321',
30
+ };
31
+ new IdentityGatewayService(options);
32
+ expect(mockSQSClientConstructor).toHaveBeenCalledWith('us-west-2', '987654321', 'env-queue-name');
33
+ }
34
+ finally {
35
+ if (originalEnv !== undefined) {
36
+ process.env.IDENTIFY_ASYNC_UPDATE_QUEUE_NAME = originalEnv;
37
+ }
38
+ else {
39
+ delete process.env.IDENTIFY_ASYNC_UPDATE_QUEUE_NAME;
40
+ }
41
+ }
42
+ });
43
+ it('should not configure SQS when region/accountId provided but no queue name', () => {
44
+ const originalEnv = process.env.IDENTIFY_ASYNC_UPDATE_QUEUE_NAME;
45
+ delete process.env.IDENTIFY_ASYNC_UPDATE_QUEUE_NAME;
46
+ try {
47
+ const options = {
48
+ awsRegion: 'us-east-1',
49
+ awsAccountId: '123456789',
50
+ };
51
+ const service = new IdentityGatewayService(options);
52
+ expect(mockSQSClientConstructor).not.toHaveBeenCalled();
53
+ expect(service.sqsClient).toBeUndefined();
54
+ }
55
+ finally {
56
+ if (originalEnv !== undefined) {
57
+ process.env.IDENTIFY_ASYNC_UPDATE_QUEUE_NAME = originalEnv;
58
+ }
59
+ }
60
+ });
61
+ it('should not configure SQS when only region provided', () => {
62
+ const options = {
63
+ awsRegion: 'us-east-1',
64
+ };
65
+ const service = new IdentityGatewayService(options);
66
+ expect(mockSQSClientConstructor).not.toHaveBeenCalled();
67
+ expect(service.sqsClient).toBeUndefined();
68
+ });
69
+ });
70
+ //# sourceMappingURL=identity-gateway-constructor.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"identity-gateway-constructor.spec.js","sourceRoot":"","sources":["../../../../src/__tests__/identity-cache/identity-gateway-constructor.spec.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,87 @@
1
+ import { createService, createServiceWithSqs, createCachedIdentity, mockGetIdentity, mockBuildAndSendMessage, mockedDynamoDbClient, mockedLogger, resetAllMocks, TEST_PIXEL_ID, IDENTITY_CACHE_WITH_CACHING_OUTCOME, } from './identity-gateway-test-setup.js';
2
+ describe('IdentityGatewayService edge cases', () => {
3
+ const pixelId = TEST_PIXEL_ID;
4
+ beforeEach(() => {
5
+ resetAllMocks();
6
+ });
7
+ describe('invalid input handling', () => {
8
+ it('should return invalid input when pixelId is undefined', async () => {
9
+ const service = createService();
10
+ const result = await service.resolveIdentityWithCaching(undefined, { identityId: 'test' });
11
+ expect(result.identity).toBeUndefined();
12
+ expect(result.outcome).toBe(IDENTITY_CACHE_WITH_CACHING_OUTCOME.INVALID_INPUT);
13
+ expect(result.didReadCache).toBe(false);
14
+ expect(result.didWriteCache).toBe(false);
15
+ });
16
+ it('should return invalid input when incomingIdentity is null', async () => {
17
+ const service = createService();
18
+ const result = await service.resolveIdentityWithCaching(pixelId, null);
19
+ expect(result.identity).toBeUndefined();
20
+ expect(result.outcome).toBe(IDENTITY_CACHE_WITH_CACHING_OUTCOME.INVALID_INPUT);
21
+ });
22
+ it('should return invalid input when both pixelId and identity are missing', async () => {
23
+ const service = createService();
24
+ const result = await service.resolveIdentityWithCaching('', undefined);
25
+ expect(result.identity).toBeUndefined();
26
+ expect(result.outcome).toBe(IDENTITY_CACHE_WITH_CACHING_OUTCOME.INVALID_INPUT);
27
+ });
28
+ });
29
+ describe('enqueue validation', () => {
30
+ it('should reject identity with undefined identityId', async () => {
31
+ const service = createServiceWithSqs();
32
+ const identity = { traits: { emails: ['test@email.com'] } };
33
+ const context = { identity };
34
+ const result = await service.enqueueAsyncIdentityUpdate({
35
+ pixelId,
36
+ identity,
37
+ context,
38
+ reason: 'anonymous user',
39
+ });
40
+ expect(result.success).toBe(false);
41
+ expect(result.error).toBe('Missing required pixelId or identityId');
42
+ expect(mockBuildAndSendMessage).not.toHaveBeenCalled();
43
+ expect(mockedLogger.warn).toHaveBeenCalledWith('IdentityGateway - enqueueAsyncIdentityUpdate: missing required params', expect.objectContaining({
44
+ pixelId,
45
+ identityId: undefined,
46
+ }));
47
+ });
48
+ });
49
+ describe('result fields consistency', () => {
50
+ it('should include all required fields in result', async () => {
51
+ const service = createService();
52
+ mockedDynamoDbClient.safeGet.mockResolvedValueOnce(null);
53
+ mockGetIdentity.mockResolvedValueOnce({
54
+ identityId: 'resolved-id',
55
+ traits: { emails: ['test@email.com'] },
56
+ });
57
+ const result = await service.resolveIdentityWithCaching(pixelId, { identityId: 'incoming-id', traits: { emails: ['test@email.com'] } });
58
+ expect(result).toEqual(expect.objectContaining({
59
+ outcome: expect.any(String),
60
+ pixelId,
61
+ identityId: 'resolved-id',
62
+ didReadCache: true,
63
+ didCallNeptune: true,
64
+ didWriteCache: false,
65
+ didSkipNeptune: false,
66
+ didFallbackToIncoming: false,
67
+ didEnqueueAsync: false,
68
+ }));
69
+ });
70
+ it('should include traitChanges when identity changes via Neptune', async () => {
71
+ const service = createService();
72
+ const cachedResponse = createCachedIdentity(pixelId, 'identity456', {
73
+ emails: ['old@email.com'],
74
+ });
75
+ mockedDynamoDbClient.safeGet.mockResolvedValueOnce(cachedResponse);
76
+ mockGetIdentity.mockResolvedValueOnce({
77
+ identityId: 'identity456',
78
+ traits: { emails: ['old@email.com', 'new@email.com'] },
79
+ });
80
+ const result = await service.resolveIdentityWithCaching(pixelId, { identityId: 'identity456', traits: { emails: ['old@email.com', 'new@email.com'] } });
81
+ if (result.outcome === IDENTITY_CACHE_WITH_CACHING_OUTCOME.NEPTUNE_RESOLVED) {
82
+ expect(result.traitChanges).toBeDefined();
83
+ }
84
+ });
85
+ });
86
+ });
87
+ //# sourceMappingURL=identity-gateway-edge-cases.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"identity-gateway-edge-cases.spec.js","sourceRoot":"","sources":["../../../../src/__tests__/identity-cache/identity-gateway-edge-cases.spec.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,134 @@
1
+ import { createService, createServiceWithSqs, mockBuildAndSendMessage, mockedLogger, resetAllMocks, TEST_PIXEL_ID, TEST_LAMBDA_ARN, } from './identity-gateway-test-setup.js';
2
+ describe('IdentityGatewayService enqueueAsyncIdentityUpdate()', () => {
3
+ const pixelId = TEST_PIXEL_ID;
4
+ const testIdentity = {
5
+ identityId: 'identity456',
6
+ traits: { emails: ['test@email.com'] },
7
+ };
8
+ const testContext = {
9
+ identity: testIdentity,
10
+ };
11
+ beforeEach(() => {
12
+ resetAllMocks();
13
+ });
14
+ it('should return error result when SQS client is not configured', async () => {
15
+ const service = createService({ identifyPrivateLambdaArn: TEST_LAMBDA_ARN });
16
+ const result = await service.enqueueAsyncIdentityUpdate({
17
+ pixelId,
18
+ identity: testIdentity,
19
+ context: testContext,
20
+ reason: 'test reason',
21
+ eventId: 'event123',
22
+ });
23
+ expect(result.success).toBe(false);
24
+ expect(result.error).toBe('SQS client not configured');
25
+ expect(result.messageId).toBeUndefined();
26
+ expect(mockedLogger.warn).toHaveBeenCalledWith('IdentityGateway - enqueueAsyncIdentityUpdate: SQS client not configured', expect.objectContaining({
27
+ pixelId,
28
+ identityId: testIdentity.identityId,
29
+ reason: 'test reason',
30
+ eventId: 'event123',
31
+ }));
32
+ });
33
+ it('should successfully enqueue message when SQS client is configured', async () => {
34
+ const service = createServiceWithSqs();
35
+ mockBuildAndSendMessage.mockResolvedValueOnce({
36
+ MessageId: 'sqs-message-id-123',
37
+ });
38
+ const result = await service.enqueueAsyncIdentityUpdate({
39
+ pixelId,
40
+ identity: testIdentity,
41
+ context: testContext,
42
+ reason: 'stale cache traits',
43
+ eventId: 'event456',
44
+ });
45
+ expect(result.success).toBe(true);
46
+ expect(result.messageId).toBe('sqs-message-id-123');
47
+ expect(result.error).toBeUndefined();
48
+ expect(mockBuildAndSendMessage).toHaveBeenCalledWith('identify.updateAsync', expect.objectContaining({
49
+ pixelId,
50
+ incomingIdentityId: testIdentity.identityId,
51
+ context: testContext,
52
+ }));
53
+ expect(mockedLogger.debug).toHaveBeenCalledWith('IdentityGateway - enqueueAsyncIdentityUpdate: message sent', expect.objectContaining({
54
+ pixelId,
55
+ identityId: testIdentity.identityId,
56
+ reason: 'stale cache traits',
57
+ eventId: 'event456',
58
+ sqsMessageId: 'sqs-message-id-123',
59
+ }));
60
+ });
61
+ it('should fail open with logging when SQS send fails', async () => {
62
+ const service = createServiceWithSqs();
63
+ mockBuildAndSendMessage.mockRejectedValueOnce(new Error('SQS send failed'));
64
+ const result = await service.enqueueAsyncIdentityUpdate({
65
+ pixelId,
66
+ identity: testIdentity,
67
+ context: testContext,
68
+ reason: 'test reason',
69
+ eventId: 'event789',
70
+ });
71
+ expect(result.success).toBe(false);
72
+ expect(result.error).toBe('SQS send failed');
73
+ expect(result.messageId).toBeUndefined();
74
+ expect(mockedLogger.warn).toHaveBeenCalledWith('IdentityGateway - enqueueAsyncIdentityUpdate: failed to enqueue (failing open)', expect.objectContaining({
75
+ error: expect.any(Error),
76
+ errorMessage: 'SQS send failed',
77
+ pixelId,
78
+ identityId: testIdentity.identityId,
79
+ reason: 'test reason',
80
+ eventId: 'event789',
81
+ }));
82
+ });
83
+ it('should build message shape matching IdentifyAsyncUpdateMessage interface', async () => {
84
+ const service = createServiceWithSqs();
85
+ mockBuildAndSendMessage.mockResolvedValueOnce({ MessageId: 'test-id' });
86
+ const identity = {
87
+ identityId: 'id-xyz',
88
+ traits: { emails: ['user@example.com'], userIds: ['user-1'] },
89
+ };
90
+ const context = { identity };
91
+ await service.enqueueAsyncIdentityUpdate({
92
+ pixelId: 'pixel-abc',
93
+ identity,
94
+ context,
95
+ reason: 'test',
96
+ });
97
+ const messageArg = mockBuildAndSendMessage.mock.calls[0][1];
98
+ expect(messageArg).toEqual({
99
+ pixelId: 'pixel-abc',
100
+ incomingIdentityId: 'id-xyz',
101
+ context,
102
+ });
103
+ });
104
+ it('should return error when identity has no identityId', async () => {
105
+ const service = createServiceWithSqs();
106
+ const identityWithoutId = {
107
+ traits: { emails: ['anon@example.com'] },
108
+ };
109
+ const context = { identity: identityWithoutId };
110
+ const result = await service.enqueueAsyncIdentityUpdate({
111
+ pixelId,
112
+ identity: identityWithoutId,
113
+ context,
114
+ reason: 'anonymous identity',
115
+ });
116
+ expect(result.success).toBe(false);
117
+ expect(result.error).toBe('Missing required pixelId or identityId');
118
+ expect(mockBuildAndSendMessage).not.toHaveBeenCalled();
119
+ });
120
+ it('should handle optional eventId parameter', async () => {
121
+ const service = createServiceWithSqs();
122
+ mockBuildAndSendMessage.mockResolvedValueOnce({ MessageId: 'test-id' });
123
+ await service.enqueueAsyncIdentityUpdate({
124
+ pixelId,
125
+ identity: testIdentity,
126
+ context: testContext,
127
+ reason: 'test',
128
+ });
129
+ expect(mockedLogger.debug).toHaveBeenCalledWith('IdentityGateway - enqueueAsyncIdentityUpdate: message sent', expect.objectContaining({
130
+ eventId: undefined,
131
+ }));
132
+ });
133
+ });
134
+ //# sourceMappingURL=identity-gateway-enqueue-async.spec.js.map