@adtrackify/at-service-common 3.19.17 → 3.19.19
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/cjs/__tests__/clients/dynamodb-client.spec.js +161 -121
- package/dist/cjs/__tests__/clients/dynamodb-client.spec.js.map +1 -1
- package/dist/cjs/__tests__/db/destinations-db-service.spec.d.ts +1 -0
- package/dist/cjs/__tests__/db/destinations-db-service.spec.js +126 -0
- package/dist/cjs/__tests__/db/destinations-db-service.spec.js.map +1 -0
- package/dist/cjs/__tests__/db/shopify-app-installs-db-service.spec.d.ts +1 -0
- package/dist/cjs/__tests__/db/shopify-app-installs-db-service.spec.js +105 -0
- package/dist/cjs/__tests__/db/shopify-app-installs-db-service.spec.js.map +1 -0
- package/dist/cjs/__tests__/db/subscriptions-db-service.spec.d.ts +1 -0
- package/dist/cjs/__tests__/db/subscriptions-db-service.spec.js +96 -0
- package/dist/cjs/__tests__/db/subscriptions-db-service.spec.js.map +1 -0
- package/dist/cjs/__tests__/db/user-accounts-db-service.spec.d.ts +1 -0
- package/dist/cjs/__tests__/db/user-accounts-db-service.spec.js +77 -0
- package/dist/cjs/__tests__/db/user-accounts-db-service.spec.js.map +1 -0
- package/dist/cjs/__tests__/identity-cache/identity-cache-dynamodb-service.spec.js +56 -47
- package/dist/cjs/__tests__/identity-cache/identity-cache-dynamodb-service.spec.js.map +1 -1
- package/dist/cjs/clients/generic/dynamodb-client.js +10 -16
- package/dist/cjs/clients/generic/dynamodb-client.js.map +1 -1
- package/dist/cjs/services/db/destinations-db-service.js +2 -8
- package/dist/cjs/services/db/destinations-db-service.js.map +1 -1
- package/dist/cjs/services/db/identity-cache-dynamodb-service.d.ts +3 -3
- package/dist/cjs/services/db/identity-cache-dynamodb-service.js +55 -86
- package/dist/cjs/services/db/identity-cache-dynamodb-service.js.map +1 -1
- package/dist/cjs/services/db/shopify-app-installs-db-service.js +4 -10
- package/dist/cjs/services/db/shopify-app-installs-db-service.js.map +1 -1
- package/dist/esm/__tests__/clients/dynamodb-client.spec.js +161 -121
- package/dist/esm/__tests__/clients/dynamodb-client.spec.js.map +1 -1
- package/dist/esm/__tests__/db/destinations-db-service.spec.d.ts +1 -0
- package/dist/esm/__tests__/db/destinations-db-service.spec.js +124 -0
- package/dist/esm/__tests__/db/destinations-db-service.spec.js.map +1 -0
- package/dist/esm/__tests__/db/shopify-app-installs-db-service.spec.d.ts +1 -0
- package/dist/esm/__tests__/db/shopify-app-installs-db-service.spec.js +103 -0
- package/dist/esm/__tests__/db/shopify-app-installs-db-service.spec.js.map +1 -0
- package/dist/esm/__tests__/db/subscriptions-db-service.spec.d.ts +1 -0
- package/dist/esm/__tests__/db/subscriptions-db-service.spec.js +94 -0
- package/dist/esm/__tests__/db/subscriptions-db-service.spec.js.map +1 -0
- package/dist/esm/__tests__/db/user-accounts-db-service.spec.d.ts +1 -0
- package/dist/esm/__tests__/db/user-accounts-db-service.spec.js +75 -0
- package/dist/esm/__tests__/db/user-accounts-db-service.spec.js.map +1 -0
- package/dist/esm/__tests__/identity-cache/identity-cache-dynamodb-service.spec.js +56 -47
- package/dist/esm/__tests__/identity-cache/identity-cache-dynamodb-service.spec.js.map +1 -1
- package/dist/esm/clients/generic/dynamodb-client.js +10 -16
- package/dist/esm/clients/generic/dynamodb-client.js.map +1 -1
- package/dist/esm/services/db/destinations-db-service.js +2 -8
- package/dist/esm/services/db/destinations-db-service.js.map +1 -1
- package/dist/esm/services/db/identity-cache-dynamodb-service.d.ts +3 -3
- package/dist/esm/services/db/identity-cache-dynamodb-service.js +56 -87
- package/dist/esm/services/db/identity-cache-dynamodb-service.js.map +1 -1
- package/dist/esm/services/db/shopify-app-installs-db-service.js +4 -10
- package/dist/esm/services/db/shopify-app-installs-db-service.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
jest.mock('../../clients/generic/dynamodb-client.js', () => ({
|
|
4
|
+
DynamoDbClient: {
|
|
5
|
+
safeGet: jest.fn(),
|
|
6
|
+
safeQueryByGSI: jest.fn(),
|
|
7
|
+
safeBatchGet: jest.fn(),
|
|
8
|
+
queryAll: jest.fn(),
|
|
9
|
+
},
|
|
10
|
+
}));
|
|
11
|
+
jest.mock('../../helpers/index.js', () => ({
|
|
12
|
+
Logger: {
|
|
13
|
+
info: jest.fn(),
|
|
14
|
+
warn: jest.fn(),
|
|
15
|
+
error: jest.fn(),
|
|
16
|
+
debug: jest.fn(),
|
|
17
|
+
},
|
|
18
|
+
}));
|
|
19
|
+
const dynamodb_client_js_1 = require("../../clients/generic/dynamodb-client.js");
|
|
20
|
+
const shopify_app_installs_db_service_js_1 = require("../../services/db/shopify-app-installs-db-service.js");
|
|
21
|
+
const mockSafeBatchGet = dynamodb_client_js_1.DynamoDbClient.safeBatchGet;
|
|
22
|
+
const mockSafeGet = dynamodb_client_js_1.DynamoDbClient.safeGet;
|
|
23
|
+
describe('ShopifyAppInstallsDbService', () => {
|
|
24
|
+
const TABLE_NAME = 'test-shopify-app-installs-table';
|
|
25
|
+
const TABLE_KEY = 'id';
|
|
26
|
+
const service = new shopify_app_installs_db_service_js_1.ShopifyAppInstallsDbService(TABLE_NAME, TABLE_KEY);
|
|
27
|
+
beforeEach(() => {
|
|
28
|
+
jest.clearAllMocks();
|
|
29
|
+
});
|
|
30
|
+
afterEach(() => {
|
|
31
|
+
jest.restoreAllMocks();
|
|
32
|
+
});
|
|
33
|
+
describe('batchGetByIds', () => {
|
|
34
|
+
it('returns empty array for empty ids input', async () => {
|
|
35
|
+
mockSafeBatchGet.mockResolvedValueOnce([]);
|
|
36
|
+
const result = await service.batchGetByIds([]);
|
|
37
|
+
expect(result).toEqual([]);
|
|
38
|
+
expect(mockSafeBatchGet).toHaveBeenCalledWith(TABLE_NAME, []);
|
|
39
|
+
});
|
|
40
|
+
it('returns installs for single id', async () => {
|
|
41
|
+
const mockInstall = {
|
|
42
|
+
id: 'install-1',
|
|
43
|
+
shop: 'test-shop.myshopify.com',
|
|
44
|
+
pixelId: 'px-1',
|
|
45
|
+
};
|
|
46
|
+
mockSafeBatchGet.mockResolvedValueOnce([mockInstall]);
|
|
47
|
+
const result = await service.batchGetByIds(['install-1']);
|
|
48
|
+
expect(result).toEqual([mockInstall]);
|
|
49
|
+
expect(mockSafeBatchGet).toHaveBeenCalledWith(TABLE_NAME, [{ id: 'install-1' }]);
|
|
50
|
+
});
|
|
51
|
+
it('returns installs for multiple ids', async () => {
|
|
52
|
+
const mockInstalls = [
|
|
53
|
+
{ id: 'install-1', shop: 'shop1.myshopify.com', pixelId: 'px-1' },
|
|
54
|
+
{ id: 'install-2', shop: 'shop2.myshopify.com', pixelId: 'px-2' },
|
|
55
|
+
{ id: 'install-3', shop: 'shop3.myshopify.com', pixelId: 'px-3' },
|
|
56
|
+
];
|
|
57
|
+
mockSafeBatchGet.mockResolvedValueOnce(mockInstalls);
|
|
58
|
+
const result = await service.batchGetByIds(['install-1', 'install-2', 'install-3']);
|
|
59
|
+
expect(result).toEqual(mockInstalls);
|
|
60
|
+
expect(mockSafeBatchGet).toHaveBeenCalledWith(TABLE_NAME, [
|
|
61
|
+
{ id: 'install-1' },
|
|
62
|
+
{ id: 'install-2' },
|
|
63
|
+
{ id: 'install-3' },
|
|
64
|
+
]);
|
|
65
|
+
});
|
|
66
|
+
it('returns empty array when safeBatchGet returns null', async () => {
|
|
67
|
+
mockSafeBatchGet.mockResolvedValueOnce(null);
|
|
68
|
+
const result = await service.batchGetByIds(['install-1']);
|
|
69
|
+
expect(result).toEqual([]);
|
|
70
|
+
});
|
|
71
|
+
it('uses custom table key from constructor', async () => {
|
|
72
|
+
const customService = new shopify_app_installs_db_service_js_1.ShopifyAppInstallsDbService(TABLE_NAME, 'customKey');
|
|
73
|
+
mockSafeBatchGet.mockResolvedValueOnce([]);
|
|
74
|
+
await customService.batchGetByIds(['install-1', 'install-2']);
|
|
75
|
+
expect(mockSafeBatchGet).toHaveBeenCalledWith(TABLE_NAME, [
|
|
76
|
+
{ customKey: 'install-1' },
|
|
77
|
+
{ customKey: 'install-2' },
|
|
78
|
+
]);
|
|
79
|
+
});
|
|
80
|
+
it('uses default table key "id" when not specified', async () => {
|
|
81
|
+
const defaultKeyService = new shopify_app_installs_db_service_js_1.ShopifyAppInstallsDbService('another-table');
|
|
82
|
+
mockSafeBatchGet.mockResolvedValueOnce([]);
|
|
83
|
+
await defaultKeyService.batchGetByIds(['install-1']);
|
|
84
|
+
expect(mockSafeBatchGet).toHaveBeenCalledWith('another-table', [{ id: 'install-1' }]);
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
describe('getShopifyAppInstallById', () => {
|
|
88
|
+
it('returns install when found', async () => {
|
|
89
|
+
const mockInstall = {
|
|
90
|
+
id: 'install-1',
|
|
91
|
+
shop: 'test-shop.myshopify.com',
|
|
92
|
+
};
|
|
93
|
+
mockSafeGet.mockResolvedValueOnce(mockInstall);
|
|
94
|
+
const result = await service.getShopifyAppInstallById('id', 'install-1');
|
|
95
|
+
expect(result).toEqual(mockInstall);
|
|
96
|
+
expect(mockSafeGet).toHaveBeenCalledWith(TABLE_NAME, 'id', 'install-1');
|
|
97
|
+
});
|
|
98
|
+
it('returns null when not found', async () => {
|
|
99
|
+
mockSafeGet.mockResolvedValueOnce(null);
|
|
100
|
+
const result = await service.getShopifyAppInstallById('id', 'nonexistent');
|
|
101
|
+
expect(result).toBeNull();
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
//# sourceMappingURL=shopify-app-installs-db-service.spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shopify-app-installs-db-service.spec.js","sourceRoot":"","sources":["../../../../src/__tests__/db/shopify-app-installs-db-service.spec.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
jest.mock('../../clients/generic/dynamodb-client.js', () => ({
|
|
4
|
+
DynamoDbClient: {
|
|
5
|
+
safeGet: jest.fn(),
|
|
6
|
+
safeQueryByGSI: jest.fn(),
|
|
7
|
+
safeBatchQueryByGSI: jest.fn(),
|
|
8
|
+
},
|
|
9
|
+
}));
|
|
10
|
+
jest.mock('../../helpers/index.js', () => ({
|
|
11
|
+
Logger: {
|
|
12
|
+
info: jest.fn(),
|
|
13
|
+
warn: jest.fn(),
|
|
14
|
+
error: jest.fn(),
|
|
15
|
+
debug: jest.fn(),
|
|
16
|
+
},
|
|
17
|
+
}));
|
|
18
|
+
const dynamodb_client_js_1 = require("../../clients/generic/dynamodb-client.js");
|
|
19
|
+
const subscriptions_db_service_js_1 = require("../../services/db/subscriptions-db-service.js");
|
|
20
|
+
const mockSafeBatchQueryByGSI = dynamodb_client_js_1.DynamoDbClient.safeBatchQueryByGSI;
|
|
21
|
+
const mockSafeQueryByGSI = dynamodb_client_js_1.DynamoDbClient.safeQueryByGSI;
|
|
22
|
+
describe('SubscriptionsDbService', () => {
|
|
23
|
+
const TABLE_NAME = 'test-subscriptions-table';
|
|
24
|
+
const service = new subscriptions_db_service_js_1.SubscriptionsDbService(TABLE_NAME);
|
|
25
|
+
beforeEach(() => {
|
|
26
|
+
jest.clearAllMocks();
|
|
27
|
+
});
|
|
28
|
+
afterEach(() => {
|
|
29
|
+
jest.restoreAllMocks();
|
|
30
|
+
});
|
|
31
|
+
describe('batchGetByAccountIds', () => {
|
|
32
|
+
const INDEX_NAME = 'GSI_ACCOUNT_ID';
|
|
33
|
+
const ACCOUNT_ID_KEY = 'accountId';
|
|
34
|
+
it('returns empty array for empty accountIds input', async () => {
|
|
35
|
+
mockSafeBatchQueryByGSI.mockResolvedValueOnce([]);
|
|
36
|
+
const result = await service.batchGetByAccountIds(INDEX_NAME, ACCOUNT_ID_KEY, []);
|
|
37
|
+
expect(result).toEqual([]);
|
|
38
|
+
expect(mockSafeBatchQueryByGSI).toHaveBeenCalledWith(TABLE_NAME, INDEX_NAME, ACCOUNT_ID_KEY, [], 10);
|
|
39
|
+
});
|
|
40
|
+
it('returns subscriptions for single accountId', async () => {
|
|
41
|
+
const mockSubscription = {
|
|
42
|
+
id: 'sub-1',
|
|
43
|
+
accountId: 'acc-1',
|
|
44
|
+
plan: 'pro',
|
|
45
|
+
};
|
|
46
|
+
mockSafeBatchQueryByGSI.mockResolvedValueOnce([mockSubscription]);
|
|
47
|
+
const result = await service.batchGetByAccountIds(INDEX_NAME, ACCOUNT_ID_KEY, ['acc-1']);
|
|
48
|
+
expect(result).toEqual([mockSubscription]);
|
|
49
|
+
expect(mockSafeBatchQueryByGSI).toHaveBeenCalledWith(TABLE_NAME, INDEX_NAME, ACCOUNT_ID_KEY, ['acc-1'], 10);
|
|
50
|
+
});
|
|
51
|
+
it('returns subscriptions for multiple accountIds', async () => {
|
|
52
|
+
const mockSubscriptions = [
|
|
53
|
+
{ id: 'sub-1', accountId: 'acc-1', plan: 'pro' },
|
|
54
|
+
{ id: 'sub-2', accountId: 'acc-2', plan: 'starter' },
|
|
55
|
+
{ id: 'sub-3', accountId: 'acc-2', plan: 'addon' },
|
|
56
|
+
];
|
|
57
|
+
mockSafeBatchQueryByGSI.mockResolvedValueOnce(mockSubscriptions);
|
|
58
|
+
const result = await service.batchGetByAccountIds(INDEX_NAME, ACCOUNT_ID_KEY, ['acc-1', 'acc-2']);
|
|
59
|
+
expect(result).toEqual(mockSubscriptions);
|
|
60
|
+
expect(mockSafeBatchQueryByGSI).toHaveBeenCalledWith(TABLE_NAME, INDEX_NAME, ACCOUNT_ID_KEY, ['acc-1', 'acc-2'], 10);
|
|
61
|
+
});
|
|
62
|
+
it('uses custom concurrency parameter', async () => {
|
|
63
|
+
mockSafeBatchQueryByGSI.mockResolvedValueOnce([]);
|
|
64
|
+
await service.batchGetByAccountIds(INDEX_NAME, ACCOUNT_ID_KEY, ['acc-1'], 5);
|
|
65
|
+
expect(mockSafeBatchQueryByGSI).toHaveBeenCalledWith(TABLE_NAME, INDEX_NAME, ACCOUNT_ID_KEY, ['acc-1'], 5);
|
|
66
|
+
});
|
|
67
|
+
it('calls safeBatchQueryByGSI with correct table name from constructor', async () => {
|
|
68
|
+
const customService = new subscriptions_db_service_js_1.SubscriptionsDbService('custom-table');
|
|
69
|
+
mockSafeBatchQueryByGSI.mockResolvedValueOnce([]);
|
|
70
|
+
await customService.batchGetByAccountIds(INDEX_NAME, ACCOUNT_ID_KEY, ['acc-1']);
|
|
71
|
+
expect(mockSafeBatchQueryByGSI).toHaveBeenCalledWith('custom-table', INDEX_NAME, ACCOUNT_ID_KEY, ['acc-1'], 10);
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
describe('getSubscriptionByAccountId', () => {
|
|
75
|
+
it('returns first subscription when found', async () => {
|
|
76
|
+
const mockSubscriptions = [
|
|
77
|
+
{ id: 'sub-1', accountId: 'acc-1' },
|
|
78
|
+
{ id: 'sub-2', accountId: 'acc-1' },
|
|
79
|
+
];
|
|
80
|
+
mockSafeQueryByGSI.mockResolvedValueOnce(mockSubscriptions);
|
|
81
|
+
const result = await service.getSubscriptionByAccountId('GSI', 'accountId', 'acc-1');
|
|
82
|
+
expect(result).toEqual({ id: 'sub-1', accountId: 'acc-1' });
|
|
83
|
+
});
|
|
84
|
+
it('returns null when no subscriptions found', async () => {
|
|
85
|
+
mockSafeQueryByGSI.mockResolvedValueOnce([]);
|
|
86
|
+
const result = await service.getSubscriptionByAccountId('GSI', 'accountId', 'acc-1');
|
|
87
|
+
expect(result).toBeNull();
|
|
88
|
+
});
|
|
89
|
+
it('returns null when query returns null', async () => {
|
|
90
|
+
mockSafeQueryByGSI.mockResolvedValueOnce(null);
|
|
91
|
+
const result = await service.getSubscriptionByAccountId('GSI', 'accountId', 'acc-1');
|
|
92
|
+
expect(result).toBeNull();
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
//# sourceMappingURL=subscriptions-db-service.spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"subscriptions-db-service.spec.js","sourceRoot":"","sources":["../../../../src/__tests__/db/subscriptions-db-service.spec.ts"],"names":[],"mappings":";;AAIA,IAAI,CAAC,IAAI,CAAC,0CAA0C,EAAE,GAAG,EAAE,CAAC,CAAC;IAC3D,cAAc,EAAE;QACd,OAAO,EAAE,IAAI,CAAC,EAAE,EAAE;QAClB,cAAc,EAAE,IAAI,CAAC,EAAE,EAAE;QACzB,mBAAmB,EAAE,IAAI,CAAC,EAAE,EAAE;KAC/B;CACF,CAAC,CAAC,CAAC;AAEJ,IAAI,CAAC,IAAI,CAAC,wBAAwB,EAAE,GAAG,EAAE,CAAC,CAAC;IACzC,MAAM,EAAE;QACN,IAAI,EAAE,IAAI,CAAC,EAAE,EAAE;QACf,IAAI,EAAE,IAAI,CAAC,EAAE,EAAE;QACf,KAAK,EAAE,IAAI,CAAC,EAAE,EAAE;QAChB,KAAK,EAAE,IAAI,CAAC,EAAE,EAAE;KACjB;CACF,CAAC,CAAC,CAAC;AAEJ,iFAA0E;AAC1E,+FAAuF;AAEvF,MAAM,uBAAuB,GAAG,mCAAc,CAAC,mBAAgC,CAAC;AAChF,MAAM,kBAAkB,GAAG,mCAAc,CAAC,cAA2B,CAAC;AAEtE,QAAQ,CAAC,wBAAwB,EAAE,GAAG,EAAE;IACtC,MAAM,UAAU,GAAG,0BAA0B,CAAC;IAC9C,MAAM,OAAO,GAAG,IAAI,oDAAsB,CAAC,UAAU,CAAC,CAAC;IAEvD,UAAU,CAAC,GAAG,EAAE;QACd,IAAI,CAAC,aAAa,EAAE,CAAC;IACvB,CAAC,CAAC,CAAC;IAEH,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,CAAC,eAAe,EAAE,CAAC;IACzB,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,sBAAsB,EAAE,GAAG,EAAE;QACpC,MAAM,UAAU,GAAG,gBAAgB,CAAC;QACpC,MAAM,cAAc,GAAG,WAAW,CAAC;QAEnC,EAAE,CAAC,gDAAgD,EAAE,KAAK,IAAI,EAAE;YAC9D,uBAAuB,CAAC,qBAAqB,CAAC,EAAE,CAAC,CAAC;YAElD,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,oBAAoB,CAAC,UAAU,EAAE,cAAc,EAAE,EAAE,CAAC,CAAC;YAElF,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;YAC3B,MAAM,CAAC,uBAAuB,CAAC,CAAC,oBAAoB,CAClD,UAAU,EACV,UAAU,EACV,cAAc,EACd,EAAE,EACF,EAAE,CACH,CAAC;QACJ,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,4CAA4C,EAAE,KAAK,IAAI,EAAE;YAC1D,MAAM,gBAAgB,GAAG;gBACvB,EAAE,EAAE,OAAO;gBACX,SAAS,EAAE,OAAO;gBAClB,IAAI,EAAE,KAAK;aACZ,CAAC;YACF,uBAAuB,CAAC,qBAAqB,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC;YAElE,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,oBAAoB,CAAC,UAAU,EAAE,cAAc,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;YAEzF,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC;YAC3C,MAAM,CAAC,uBAAuB,CAAC,CAAC,oBAAoB,CAClD,UAAU,EACV,UAAU,EACV,cAAc,EACd,CAAC,OAAO,CAAC,EACT,EAAE,CACH,CAAC;QACJ,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,+CAA+C,EAAE,KAAK,IAAI,EAAE;YAC7D,MAAM,iBAAiB,GAAG;gBACxB,EAAE,EAAE,EAAE,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE;gBAChD,EAAE,EAAE,EAAE,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE;gBACpD,EAAE,EAAE,EAAE,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE;aACnD,CAAC;YACF,uBAAuB,CAAC,qBAAqB,CAAC,iBAAiB,CAAC,CAAC;YAEjE,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,oBAAoB,CAAC,UAAU,EAAE,cAAc,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC;YAElG,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC;YAC1C,MAAM,CAAC,uBAAuB,CAAC,CAAC,oBAAoB,CAClD,UAAU,EACV,UAAU,EACV,cAAc,EACd,CAAC,OAAO,EAAE,OAAO,CAAC,EAClB,EAAE,CACH,CAAC;QACJ,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,mCAAmC,EAAE,KAAK,IAAI,EAAE;YACjD,uBAAuB,CAAC,qBAAqB,CAAC,EAAE,CAAC,CAAC;YAElD,MAAM,OAAO,CAAC,oBAAoB,CAAC,UAAU,EAAE,cAAc,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC;YAE7E,MAAM,CAAC,uBAAuB,CAAC,CAAC,oBAAoB,CAClD,UAAU,EACV,UAAU,EACV,cAAc,EACd,CAAC,OAAO,CAAC,EACT,CAAC,CACF,CAAC;QACJ,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,oEAAoE,EAAE,KAAK,IAAI,EAAE;YAClF,MAAM,aAAa,GAAG,IAAI,oDAAsB,CAAC,cAAc,CAAC,CAAC;YACjE,uBAAuB,CAAC,qBAAqB,CAAC,EAAE,CAAC,CAAC;YAElD,MAAM,aAAa,CAAC,oBAAoB,CAAC,UAAU,EAAE,cAAc,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;YAEhF,MAAM,CAAC,uBAAuB,CAAC,CAAC,oBAAoB,CAClD,cAAc,EACd,UAAU,EACV,cAAc,EACd,CAAC,OAAO,CAAC,EACT,EAAE,CACH,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,4BAA4B,EAAE,GAAG,EAAE;QAC1C,EAAE,CAAC,uCAAuC,EAAE,KAAK,IAAI,EAAE;YACrD,MAAM,iBAAiB,GAAG;gBACxB,EAAE,EAAE,EAAE,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE;gBACnC,EAAE,EAAE,EAAE,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE;aACpC,CAAC;YACF,kBAAkB,CAAC,qBAAqB,CAAC,iBAAiB,CAAC,CAAC;YAE5D,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,0BAA0B,CAAC,KAAK,EAAE,WAAW,EAAE,OAAO,CAAC,CAAC;YAErF,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,EAAE,EAAE,EAAE,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,CAAC;QAC9D,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,0CAA0C,EAAE,KAAK,IAAI,EAAE;YACxD,kBAAkB,CAAC,qBAAqB,CAAC,EAAE,CAAC,CAAC;YAE7C,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,0BAA0B,CAAC,KAAK,EAAE,WAAW,EAAE,OAAO,CAAC,CAAC;YAErF,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,EAAE,CAAC;QAC5B,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,sCAAsC,EAAE,KAAK,IAAI,EAAE;YACpD,kBAAkB,CAAC,qBAAqB,CAAC,IAAI,CAAC,CAAC;YAE/C,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,0BAA0B,CAAC,KAAK,EAAE,WAAW,EAAE,OAAO,CAAC,CAAC;YAErF,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,EAAE,CAAC;QAC5B,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
jest.mock('../../clients/generic/dynamodb-client.js', () => ({
|
|
4
|
+
DynamoDbClient: {
|
|
5
|
+
safeBatchQueryByGSI: jest.fn(),
|
|
6
|
+
},
|
|
7
|
+
}));
|
|
8
|
+
const dynamodb_client_js_1 = require("../../clients/generic/dynamodb-client.js");
|
|
9
|
+
const user_accounts_db_service_js_1 = require("../../services/db/user-accounts-db-service.js");
|
|
10
|
+
const mockSafeBatchQueryByGSI = dynamodb_client_js_1.DynamoDbClient.safeBatchQueryByGSI;
|
|
11
|
+
describe('UserAccountsDbService', () => {
|
|
12
|
+
const TABLE_NAME = 'test-user-accounts-table';
|
|
13
|
+
const service = new user_accounts_db_service_js_1.UserAccountsDbService(TABLE_NAME);
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
jest.clearAllMocks();
|
|
16
|
+
});
|
|
17
|
+
afterEach(() => {
|
|
18
|
+
jest.restoreAllMocks();
|
|
19
|
+
});
|
|
20
|
+
describe('batchGetByAccountIds', () => {
|
|
21
|
+
const INDEX_NAME = 'GSI_ACCOUNT_ID';
|
|
22
|
+
const ACCOUNT_ID_KEY = 'accountId';
|
|
23
|
+
it('returns empty array for empty accountIds input', async () => {
|
|
24
|
+
mockSafeBatchQueryByGSI.mockResolvedValueOnce([]);
|
|
25
|
+
const result = await service.batchGetByAccountIds(INDEX_NAME, ACCOUNT_ID_KEY, []);
|
|
26
|
+
expect(result).toEqual([]);
|
|
27
|
+
expect(mockSafeBatchQueryByGSI).toHaveBeenCalledWith(TABLE_NAME, INDEX_NAME, ACCOUNT_ID_KEY, [], 10);
|
|
28
|
+
});
|
|
29
|
+
it('returns user-account records for single accountId', async () => {
|
|
30
|
+
const mockRecord = {
|
|
31
|
+
userId: 'user-1',
|
|
32
|
+
accountId: 'acc-1',
|
|
33
|
+
role: 'admin',
|
|
34
|
+
};
|
|
35
|
+
mockSafeBatchQueryByGSI.mockResolvedValueOnce([mockRecord]);
|
|
36
|
+
const result = await service.batchGetByAccountIds(INDEX_NAME, ACCOUNT_ID_KEY, ['acc-1']);
|
|
37
|
+
expect(result).toEqual([mockRecord]);
|
|
38
|
+
expect(mockSafeBatchQueryByGSI).toHaveBeenCalledWith(TABLE_NAME, INDEX_NAME, ACCOUNT_ID_KEY, ['acc-1'], 10);
|
|
39
|
+
});
|
|
40
|
+
it('returns user-account records for multiple accountIds', async () => {
|
|
41
|
+
const mockRecords = [
|
|
42
|
+
{ userId: 'user-1', accountId: 'acc-1', role: 'admin' },
|
|
43
|
+
{ userId: 'user-2', accountId: 'acc-1', role: 'member' },
|
|
44
|
+
{ userId: 'user-3', accountId: 'acc-2', role: 'owner' },
|
|
45
|
+
];
|
|
46
|
+
mockSafeBatchQueryByGSI.mockResolvedValueOnce(mockRecords);
|
|
47
|
+
const result = await service.batchGetByAccountIds(INDEX_NAME, ACCOUNT_ID_KEY, ['acc-1', 'acc-2']);
|
|
48
|
+
expect(result).toEqual(mockRecords);
|
|
49
|
+
expect(mockSafeBatchQueryByGSI).toHaveBeenCalledWith(TABLE_NAME, INDEX_NAME, ACCOUNT_ID_KEY, ['acc-1', 'acc-2'], 10);
|
|
50
|
+
});
|
|
51
|
+
it('uses custom concurrency parameter', async () => {
|
|
52
|
+
mockSafeBatchQueryByGSI.mockResolvedValueOnce([]);
|
|
53
|
+
await service.batchGetByAccountIds(INDEX_NAME, ACCOUNT_ID_KEY, ['acc-1'], 5);
|
|
54
|
+
expect(mockSafeBatchQueryByGSI).toHaveBeenCalledWith(TABLE_NAME, INDEX_NAME, ACCOUNT_ID_KEY, ['acc-1'], 5);
|
|
55
|
+
});
|
|
56
|
+
it('uses default concurrency of 10', async () => {
|
|
57
|
+
mockSafeBatchQueryByGSI.mockResolvedValueOnce([]);
|
|
58
|
+
await service.batchGetByAccountIds(INDEX_NAME, ACCOUNT_ID_KEY, ['acc-1', 'acc-2', 'acc-3']);
|
|
59
|
+
expect(mockSafeBatchQueryByGSI).toHaveBeenCalledWith(TABLE_NAME, INDEX_NAME, ACCOUNT_ID_KEY, ['acc-1', 'acc-2', 'acc-3'], 10);
|
|
60
|
+
});
|
|
61
|
+
it('calls safeBatchQueryByGSI with correct table name from constructor', async () => {
|
|
62
|
+
const customService = new user_accounts_db_service_js_1.UserAccountsDbService('custom-table');
|
|
63
|
+
mockSafeBatchQueryByGSI.mockResolvedValueOnce([]);
|
|
64
|
+
await customService.batchGetByAccountIds(INDEX_NAME, ACCOUNT_ID_KEY, ['acc-1']);
|
|
65
|
+
expect(mockSafeBatchQueryByGSI).toHaveBeenCalledWith('custom-table', INDEX_NAME, ACCOUNT_ID_KEY, ['acc-1'], 10);
|
|
66
|
+
});
|
|
67
|
+
it('uses default table key "userId" when not specified', async () => {
|
|
68
|
+
const defaultKeyService = new user_accounts_db_service_js_1.UserAccountsDbService('another-table');
|
|
69
|
+
expect(defaultKeyService.TABLE_KEY).toBe('userId');
|
|
70
|
+
});
|
|
71
|
+
it('uses custom table key when specified', async () => {
|
|
72
|
+
const customKeyService = new user_accounts_db_service_js_1.UserAccountsDbService('another-table', 'customKey');
|
|
73
|
+
expect(customKeyService.TABLE_KEY).toBe('customKey');
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
//# sourceMappingURL=user-accounts-db-service.spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"user-accounts-db-service.spec.js","sourceRoot":"","sources":["../../../../src/__tests__/db/user-accounts-db-service.spec.ts"],"names":[],"mappings":"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"}
|
|
@@ -29,6 +29,15 @@ const mockedDynamoDbClient = index_js_1.DynamoDbClient;
|
|
|
29
29
|
describe('IdentityCacheDynamoDbService', () => {
|
|
30
30
|
beforeEach(() => {
|
|
31
31
|
jest.clearAllMocks();
|
|
32
|
+
mockedDynamoDbClient.safeGet.mockReset();
|
|
33
|
+
mockedDynamoDbClient.safePut.mockReset();
|
|
34
|
+
mockedDynamoDbClient.safeDelete.mockReset();
|
|
35
|
+
mockedDynamoDbClient.safeBatchGet.mockReset();
|
|
36
|
+
mockedDynamoDbClient.safeBatchWrite.mockReset();
|
|
37
|
+
mockedDynamoDbClient.safeQueryByGSI.mockReset();
|
|
38
|
+
mockedDynamoDbClient.batchWrite.mockReset();
|
|
39
|
+
mockInvokeFunction.mockReset();
|
|
40
|
+
identity_cache_dynamodb_service_js_1.IdentityCacheDynamoDbService.lambdaInvokeClient = undefined;
|
|
32
41
|
});
|
|
33
42
|
describe('getIdentityWithCaching', () => {
|
|
34
43
|
const pixelId = 'pixel123';
|
|
@@ -155,7 +164,7 @@ describe('IdentityCacheDynamoDbService', () => {
|
|
|
155
164
|
const incomingIdentity = {
|
|
156
165
|
traits: { emails: ['test@email.com'] },
|
|
157
166
|
};
|
|
158
|
-
mockedDynamoDbClient.
|
|
167
|
+
mockedDynamoDbClient.safeGet.mockResolvedValueOnce(null);
|
|
159
168
|
mockInvokeFunction.mockResolvedValueOnce({
|
|
160
169
|
statusCode: 200,
|
|
161
170
|
body: JSON.stringify({
|
|
@@ -175,7 +184,7 @@ describe('IdentityCacheDynamoDbService', () => {
|
|
|
175
184
|
const incomingIdentity = {
|
|
176
185
|
traits: { emails: ['test@email.com'] },
|
|
177
186
|
};
|
|
178
|
-
mockedDynamoDbClient.
|
|
187
|
+
mockedDynamoDbClient.safeGet.mockResolvedValueOnce(null);
|
|
179
188
|
mockInvokeFunction.mockResolvedValueOnce({
|
|
180
189
|
statusCode: 200,
|
|
181
190
|
body: JSON.stringify({ identity: undefined }),
|
|
@@ -209,7 +218,7 @@ describe('IdentityCacheDynamoDbService', () => {
|
|
|
209
218
|
const incomingIdentity = {
|
|
210
219
|
traits: { emails: ['test@email.com'] },
|
|
211
220
|
};
|
|
212
|
-
mockedDynamoDbClient.
|
|
221
|
+
mockedDynamoDbClient.safeGet.mockResolvedValueOnce(null);
|
|
213
222
|
mockInvokeFunction.mockResolvedValueOnce({
|
|
214
223
|
statusCode: 200,
|
|
215
224
|
body: JSON.stringify({
|
|
@@ -239,26 +248,22 @@ describe('IdentityCacheDynamoDbService', () => {
|
|
|
239
248
|
it('should build correct pk for email lookup (lowercase)', async () => {
|
|
240
249
|
const pixelId = 'pixel123';
|
|
241
250
|
const email = 'Test@Email.COM';
|
|
242
|
-
mockedDynamoDbClient.
|
|
251
|
+
mockedDynamoDbClient.safeGet.mockResolvedValueOnce(null);
|
|
243
252
|
await identity_cache_dynamodb_service_js_1.IdentityCacheDynamoDbService.getIdentityFromCache(pixelId, {
|
|
244
253
|
traits: { emails: [email] },
|
|
245
254
|
});
|
|
246
|
-
expect(mockedDynamoDbClient.
|
|
247
|
-
{ pk: `email#${pixelId}#test@email.com` },
|
|
248
|
-
]);
|
|
255
|
+
expect(mockedDynamoDbClient.safeGet).toHaveBeenCalledWith(expect.any(String), 'pk', `email#${pixelId}#test@email.com`);
|
|
249
256
|
});
|
|
250
257
|
});
|
|
251
|
-
describe('buildUserIdPk', () => {
|
|
252
|
-
it('should
|
|
258
|
+
describe('buildUserIdPk (no longer used for lookup)', () => {
|
|
259
|
+
it('should NOT lookup by userId (optimization: email-only secondary lookup)', async () => {
|
|
253
260
|
const pixelId = 'pixel123';
|
|
254
261
|
const userId = ' user789 ';
|
|
255
|
-
mockedDynamoDbClient.
|
|
262
|
+
mockedDynamoDbClient.safeGet.mockResolvedValueOnce(null);
|
|
256
263
|
await identity_cache_dynamodb_service_js_1.IdentityCacheDynamoDbService.getIdentityFromCache(pixelId, {
|
|
257
264
|
traits: { userIds: [userId] },
|
|
258
265
|
});
|
|
259
|
-
expect(mockedDynamoDbClient.safeBatchGet).
|
|
260
|
-
{ pk: `user_id#${pixelId}#user789` },
|
|
261
|
-
]);
|
|
266
|
+
expect(mockedDynamoDbClient.safeBatchGet).not.toHaveBeenCalled();
|
|
262
267
|
});
|
|
263
268
|
});
|
|
264
269
|
});
|
|
@@ -324,66 +329,62 @@ describe('IdentityCacheDynamoDbService', () => {
|
|
|
324
329
|
});
|
|
325
330
|
});
|
|
326
331
|
describe('without identityId (secondary lookup)', () => {
|
|
327
|
-
it('should use
|
|
328
|
-
const
|
|
332
|
+
it('should use single get for email lookup (pointer-based)', async () => {
|
|
333
|
+
const pointerRecord = {
|
|
329
334
|
pk: `email#${pixelId}#test@email.com`,
|
|
330
335
|
pixelId,
|
|
331
336
|
identityId: 'resolved-id',
|
|
332
|
-
|
|
333
|
-
identityId: 'resolved-id',
|
|
334
|
-
traits: { emails: ['test@email.com'] },
|
|
335
|
-
},
|
|
337
|
+
gsi1pk: 'resolved-id',
|
|
336
338
|
updatedAt: new Date().toISOString(),
|
|
337
339
|
};
|
|
338
|
-
mockedDynamoDbClient.
|
|
340
|
+
mockedDynamoDbClient.safeGet.mockResolvedValueOnce(pointerRecord);
|
|
339
341
|
const result = await identity_cache_dynamodb_service_js_1.IdentityCacheDynamoDbService.getIdentityFromCache(pixelId, {
|
|
340
342
|
traits: { emails: ['test@email.com'], userIds: ['user123'] },
|
|
341
343
|
});
|
|
342
|
-
expect(mockedDynamoDbClient.
|
|
343
|
-
|
|
344
|
-
{ pk: `user_id#${pixelId}#user123` },
|
|
345
|
-
]);
|
|
344
|
+
expect(mockedDynamoDbClient.safeGet).toHaveBeenCalledWith(expect.any(String), 'pk', `email#${pixelId}#test@email.com`);
|
|
345
|
+
expect(mockedDynamoDbClient.safeBatchGet).not.toHaveBeenCalled();
|
|
346
346
|
expect(result.resolvedIdentity).toBeDefined();
|
|
347
347
|
expect(result.resolvedIdentity?.traits?.emails).toContain('test@email.com');
|
|
348
348
|
expect(result.isCacheStale).toBe(true);
|
|
349
349
|
});
|
|
350
|
-
it('should
|
|
351
|
-
const
|
|
350
|
+
it('should return discovered identityId with stale flag for Neptune resolution', async () => {
|
|
351
|
+
const pointerRecord = {
|
|
352
352
|
pk: `email#${pixelId}#test@email.com`,
|
|
353
353
|
pixelId,
|
|
354
|
-
identityId: '
|
|
355
|
-
|
|
356
|
-
updatedAt: new Date().toISOString(),
|
|
357
|
-
};
|
|
358
|
-
const response2 = {
|
|
359
|
-
pk: `user_id#${pixelId}#user123`,
|
|
360
|
-
pixelId,
|
|
361
|
-
identityId: 'identity-2',
|
|
362
|
-
response: { identityId: 'identity-2' },
|
|
354
|
+
identityId: 'discovered-id',
|
|
355
|
+
gsi1pk: 'discovered-id',
|
|
363
356
|
updatedAt: new Date().toISOString(),
|
|
364
357
|
};
|
|
365
|
-
mockedDynamoDbClient.
|
|
358
|
+
mockedDynamoDbClient.safeGet.mockResolvedValueOnce(pointerRecord);
|
|
366
359
|
const result = await identity_cache_dynamodb_service_js_1.IdentityCacheDynamoDbService.getIdentityFromCache(pixelId, {
|
|
367
360
|
traits: { emails: ['test@email.com'], userIds: ['user123'] },
|
|
368
361
|
});
|
|
369
|
-
expect(result.resolvedIdentity).
|
|
362
|
+
expect(result.resolvedIdentity).toBeDefined();
|
|
363
|
+
expect(result.resolvedIdentity?.identityId).toBe('discovered-id');
|
|
370
364
|
expect(result.isCacheStale).toBe(true);
|
|
371
365
|
});
|
|
372
366
|
it('should return cache miss when no secondary keys match', async () => {
|
|
373
|
-
mockedDynamoDbClient.
|
|
367
|
+
mockedDynamoDbClient.safeGet.mockResolvedValueOnce(null);
|
|
374
368
|
const result = await identity_cache_dynamodb_service_js_1.IdentityCacheDynamoDbService.getIdentityFromCache(pixelId, {
|
|
375
369
|
traits: { emails: ['unknown@email.com'] },
|
|
376
370
|
});
|
|
377
371
|
expect(result.resolvedIdentity).toBeUndefined();
|
|
378
372
|
expect(result.isCacheStale).toBe(true);
|
|
379
373
|
});
|
|
380
|
-
it('should return cache miss when no
|
|
374
|
+
it('should return cache miss when no emails to lookup', async () => {
|
|
375
|
+
const result = await identity_cache_dynamodb_service_js_1.IdentityCacheDynamoDbService.getIdentityFromCache(pixelId, {
|
|
376
|
+
traits: { userIds: ['user123'] },
|
|
377
|
+
});
|
|
378
|
+
expect(result.resolvedIdentity).toBeUndefined();
|
|
379
|
+
expect(result.isCacheStale).toBe(true);
|
|
380
|
+
expect(mockedDynamoDbClient.safeGet).not.toHaveBeenCalled();
|
|
381
|
+
});
|
|
382
|
+
it('should return cache miss when empty traits', async () => {
|
|
381
383
|
const result = await identity_cache_dynamodb_service_js_1.IdentityCacheDynamoDbService.getIdentityFromCache(pixelId, {
|
|
382
384
|
traits: {},
|
|
383
385
|
});
|
|
384
386
|
expect(result.resolvedIdentity).toBeUndefined();
|
|
385
387
|
expect(result.isCacheStale).toBe(true);
|
|
386
|
-
expect(mockedDynamoDbClient.safeBatchGet).not.toHaveBeenCalled();
|
|
387
388
|
});
|
|
388
389
|
});
|
|
389
390
|
describe('error handling (fail-open)', () => {
|
|
@@ -395,8 +396,8 @@ describe('IdentityCacheDynamoDbService', () => {
|
|
|
395
396
|
expect(result.resolvedIdentity).toBeUndefined();
|
|
396
397
|
expect(result.isCacheStale).toBe(true);
|
|
397
398
|
});
|
|
398
|
-
it('should return cache miss on
|
|
399
|
-
mockedDynamoDbClient.
|
|
399
|
+
it('should return cache miss on safeGet error for email lookup', async () => {
|
|
400
|
+
mockedDynamoDbClient.safeGet.mockRejectedValueOnce(new Error('DynamoDB error'));
|
|
400
401
|
const result = await identity_cache_dynamodb_service_js_1.IdentityCacheDynamoDbService.getIdentityFromCache(pixelId, {
|
|
401
402
|
traits: { emails: ['test@email.com'] },
|
|
402
403
|
});
|
|
@@ -419,7 +420,7 @@ describe('IdentityCacheDynamoDbService', () => {
|
|
|
419
420
|
});
|
|
420
421
|
describe('updateIdentityCache', () => {
|
|
421
422
|
const pixelId = 'pixel123';
|
|
422
|
-
it('should write identity with
|
|
423
|
+
it('should write identity with email pointer (no full blob on secondary keys)', async () => {
|
|
423
424
|
const identity = {
|
|
424
425
|
identityId: 'identity456',
|
|
425
426
|
traits: {
|
|
@@ -430,13 +431,21 @@ describe('IdentityCacheDynamoDbService', () => {
|
|
|
430
431
|
await identity_cache_dynamodb_service_js_1.IdentityCacheDynamoDbService.updateIdentityCache(pixelId, identity);
|
|
431
432
|
expect(mockedDynamoDbClient.safeBatchWrite).toHaveBeenCalledTimes(1);
|
|
432
433
|
const writtenItems = mockedDynamoDbClient.safeBatchWrite.mock.calls[0][1];
|
|
433
|
-
expect(writtenItems).toHaveLength(
|
|
434
|
+
expect(writtenItems).toHaveLength(2);
|
|
434
435
|
const pks = writtenItems.map((item) => item.pk);
|
|
435
436
|
expect(pks).toContain(`identity#${pixelId}#identity456`);
|
|
436
437
|
expect(pks).toContain(`email#${pixelId}#test@email.com`);
|
|
437
|
-
expect(pks).toContain(`
|
|
438
|
-
expect(pks).toContain(`user_id#${pixelId}#
|
|
439
|
-
expect(pks).toContain(`
|
|
438
|
+
expect(pks).not.toContain(`user_id#${pixelId}#user1`);
|
|
439
|
+
expect(pks).not.toContain(`user_id#${pixelId}#user2`);
|
|
440
|
+
expect(pks).not.toContain(`email#${pixelId}#other@email.com`);
|
|
441
|
+
const identityItem = writtenItems.find((item) => item.pk.startsWith('identity#'));
|
|
442
|
+
expect(identityItem).toBeDefined();
|
|
443
|
+
expect(identityItem.response).toBeDefined();
|
|
444
|
+
expect(identityItem.response.identityId).toBe('identity456');
|
|
445
|
+
const emailItem = writtenItems.find((item) => item.pk.startsWith('email#'));
|
|
446
|
+
expect(emailItem).toBeDefined();
|
|
447
|
+
expect(emailItem.response).toBeUndefined();
|
|
448
|
+
expect(emailItem.identityId).toBe('identity456');
|
|
440
449
|
});
|
|
441
450
|
it('should not throw on safeBatchWrite error (fail-open)', async () => {
|
|
442
451
|
mockedDynamoDbClient.safeBatchWrite.mockRejectedValueOnce(new Error('DynamoDB error'));
|