@model-ts/dynamodb 4.1.0 → 4.2.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 (91) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dist/cjs/__test__/client-env-guard.test.d.ts +1 -0
  3. package/dist/cjs/__test__/client-env-guard.test.js +28 -0
  4. package/dist/cjs/__test__/client-env-guard.test.js.map +1 -0
  5. package/dist/cjs/__test__/conformance.test.d.ts +1 -0
  6. package/dist/cjs/__test__/conformance.test.js +1835 -0
  7. package/dist/cjs/__test__/conformance.test.js.map +1 -0
  8. package/dist/cjs/__test__/in-memory.spec.test.d.ts +1 -0
  9. package/dist/cjs/__test__/in-memory.spec.test.js +185 -0
  10. package/dist/cjs/__test__/in-memory.spec.test.js.map +1 -0
  11. package/dist/cjs/client.d.ts +2 -2
  12. package/dist/cjs/client.js +14 -6
  13. package/dist/cjs/client.js.map +1 -1
  14. package/dist/cjs/errors.d.ts +13 -1
  15. package/dist/cjs/errors.js +12 -1
  16. package/dist/cjs/errors.js.map +1 -1
  17. package/dist/cjs/in-memory/document-client.d.ts +45 -0
  18. package/dist/cjs/in-memory/document-client.js +795 -0
  19. package/dist/cjs/in-memory/document-client.js.map +1 -0
  20. package/dist/cjs/in-memory/expression.d.ts +42 -0
  21. package/dist/cjs/in-memory/expression.js +585 -0
  22. package/dist/cjs/in-memory/expression.js.map +1 -0
  23. package/dist/cjs/in-memory/index.d.ts +2 -0
  24. package/dist/cjs/in-memory/index.js +6 -0
  25. package/dist/cjs/in-memory/index.js.map +1 -0
  26. package/dist/cjs/in-memory/spec.d.ts +23 -0
  27. package/dist/cjs/in-memory/spec.js +141 -0
  28. package/dist/cjs/in-memory/spec.js.map +1 -0
  29. package/dist/cjs/in-memory/store.d.ts +73 -0
  30. package/dist/cjs/in-memory/store.js +267 -0
  31. package/dist/cjs/in-memory/store.js.map +1 -0
  32. package/dist/cjs/in-memory/treap.d.ts +31 -0
  33. package/dist/cjs/in-memory/treap.js +187 -0
  34. package/dist/cjs/in-memory/treap.js.map +1 -0
  35. package/dist/cjs/in-memory/utils.d.ts +10 -0
  36. package/dist/cjs/in-memory/utils.js +38 -0
  37. package/dist/cjs/in-memory/utils.js.map +1 -0
  38. package/dist/cjs/sandbox.js +44 -0
  39. package/dist/cjs/sandbox.js.map +1 -1
  40. package/dist/esm/__test__/client-env-guard.test.d.ts +1 -0
  41. package/dist/esm/__test__/client-env-guard.test.js +26 -0
  42. package/dist/esm/__test__/client-env-guard.test.js.map +1 -0
  43. package/dist/esm/__test__/conformance.test.d.ts +1 -0
  44. package/dist/esm/__test__/conformance.test.js +1833 -0
  45. package/dist/esm/__test__/conformance.test.js.map +1 -0
  46. package/dist/esm/__test__/in-memory.spec.test.d.ts +1 -0
  47. package/dist/esm/__test__/in-memory.spec.test.js +183 -0
  48. package/dist/esm/__test__/in-memory.spec.test.js.map +1 -0
  49. package/dist/esm/client.d.ts +2 -2
  50. package/dist/esm/client.js +14 -6
  51. package/dist/esm/client.js.map +1 -1
  52. package/dist/esm/errors.d.ts +13 -1
  53. package/dist/esm/errors.js +10 -0
  54. package/dist/esm/errors.js.map +1 -1
  55. package/dist/esm/in-memory/document-client.d.ts +45 -0
  56. package/dist/esm/in-memory/document-client.js +791 -0
  57. package/dist/esm/in-memory/document-client.js.map +1 -0
  58. package/dist/esm/in-memory/expression.d.ts +42 -0
  59. package/dist/esm/in-memory/expression.js +577 -0
  60. package/dist/esm/in-memory/expression.js.map +1 -0
  61. package/dist/esm/in-memory/index.d.ts +2 -0
  62. package/dist/esm/in-memory/index.js +3 -0
  63. package/dist/esm/in-memory/index.js.map +1 -0
  64. package/dist/esm/in-memory/spec.d.ts +23 -0
  65. package/dist/esm/in-memory/spec.js +138 -0
  66. package/dist/esm/in-memory/spec.js.map +1 -0
  67. package/dist/esm/in-memory/store.d.ts +73 -0
  68. package/dist/esm/in-memory/store.js +258 -0
  69. package/dist/esm/in-memory/store.js.map +1 -0
  70. package/dist/esm/in-memory/treap.d.ts +31 -0
  71. package/dist/esm/in-memory/treap.js +183 -0
  72. package/dist/esm/in-memory/treap.js.map +1 -0
  73. package/dist/esm/in-memory/utils.d.ts +10 -0
  74. package/dist/esm/in-memory/utils.js +28 -0
  75. package/dist/esm/in-memory/utils.js.map +1 -0
  76. package/dist/esm/sandbox.js +44 -0
  77. package/dist/esm/sandbox.js.map +1 -1
  78. package/package.json +2 -1
  79. package/src/__test__/client-env-guard.test.ts +31 -0
  80. package/src/__test__/conformance.test.ts +2042 -0
  81. package/src/__test__/in-memory.spec.test.ts +230 -0
  82. package/src/client.ts +17 -4
  83. package/src/errors.ts +24 -0
  84. package/src/in-memory/document-client.ts +1140 -0
  85. package/src/in-memory/expression.ts +730 -0
  86. package/src/in-memory/index.ts +2 -0
  87. package/src/in-memory/spec.ts +159 -0
  88. package/src/in-memory/store.ts +360 -0
  89. package/src/in-memory/treap.ts +239 -0
  90. package/src/in-memory/utils.ts +45 -0
  91. package/src/sandbox.ts +56 -0
@@ -0,0 +1,1833 @@
1
+ import { __awaiter, __rest } from "tslib";
2
+ import { Client } from "../client";
3
+ import { createSandbox } from "../sandbox";
4
+ import { IN_MEMORY_SPEC } from "../in-memory/spec";
5
+ import DynamoDB from "aws-sdk/clients/dynamodb";
6
+ const LOCAL_DDB = new DynamoDB({
7
+ accessKeyId: "xxx",
8
+ secretAccessKey: "xxx",
9
+ endpoint: process.env.LOCAL_ENDPOINT,
10
+ region: "local",
11
+ });
12
+ const withEngine = (engine, run) => __awaiter(void 0, void 0, void 0, function* () {
13
+ const previous = process.env.EXPERIMENTAL_DYNAMODB_IN_MEMORY;
14
+ if (engine === "memory")
15
+ process.env.EXPERIMENTAL_DYNAMODB_IN_MEMORY = "1";
16
+ else
17
+ delete process.env.EXPERIMENTAL_DYNAMODB_IN_MEMORY;
18
+ const client = new Client({ tableName: "table" });
19
+ const sandbox = yield createSandbox(client);
20
+ try {
21
+ return yield run({
22
+ engine,
23
+ client,
24
+ sandbox,
25
+ tableName: client.tableName,
26
+ });
27
+ }
28
+ finally {
29
+ yield sandbox.destroy();
30
+ if (typeof previous === "undefined")
31
+ delete process.env.EXPERIMENTAL_DYNAMODB_IN_MEMORY;
32
+ else
33
+ process.env.EXPERIMENTAL_DYNAMODB_IN_MEMORY = previous;
34
+ }
35
+ });
36
+ const normalizeScalar = (value) => {
37
+ if (typeof value === "string") {
38
+ return value.replace(/[0-9a-f]{40}/gi, "<table>");
39
+ }
40
+ return value;
41
+ };
42
+ const normalizeObject = (value) => {
43
+ if (value === undefined)
44
+ return undefined;
45
+ if (Array.isArray(value))
46
+ return value.map(normalizeObject);
47
+ if (value && typeof value === "object") {
48
+ return Object.fromEntries(Object.keys(value)
49
+ .sort()
50
+ .filter((key) => typeof value[key] !== "undefined")
51
+ .map((key) => [normalizeScalar(key), normalizeObject(value[key])]));
52
+ }
53
+ return normalizeScalar(value);
54
+ };
55
+ const normalizeSnapshot = (snapshot) => Object.fromEntries(Object.keys(snapshot)
56
+ .sort()
57
+ .map((key) => [key, normalizeObject(snapshot[key])]));
58
+ const sanitizeErrorMessage = (message) => canonicalizeValidationMessage(message
59
+ .replace(/[0-9a-f]{40}/gi, "<table>")
60
+ .replace(/\s+/g, " ")
61
+ .trim()
62
+ .replace(/\.$/, "")
63
+ .replace(/\([^)]*\)/g, (match) => {
64
+ if (match.includes("<table>"))
65
+ return "(<table>)";
66
+ return match;
67
+ }));
68
+ const canonicalizeValidationMessage = (message) => {
69
+ if (message ===
70
+ "Consistent read cannot be true when querying a GSI" ||
71
+ message ===
72
+ "Consistent reads are not supported on global secondary indexes") {
73
+ return "Consistent reads are not supported on global secondary indexes";
74
+ }
75
+ if (message === "The provided starting key is invalid" ||
76
+ message === "Exclusive Start Key must have same size as table's key schema") {
77
+ return "The provided starting key is invalid";
78
+ }
79
+ return message;
80
+ };
81
+ const normalizeError = (error) => {
82
+ var _a, _b, _c;
83
+ const code = (_a = error === null || error === void 0 ? void 0 : error.code) !== null && _a !== void 0 ? _a : (typeof (error === null || error === void 0 ? void 0 : error.name) === "string" && error.name !== "Error"
84
+ ? error.name
85
+ : "UnknownError");
86
+ if (code === "NotSupportedError") {
87
+ return {
88
+ code,
89
+ message: sanitizeErrorMessage(String((_b = error === null || error === void 0 ? void 0 : error.message) !== null && _b !== void 0 ? _b : "")),
90
+ method: error === null || error === void 0 ? void 0 : error.method,
91
+ featurePath: error === null || error === void 0 ? void 0 : error.featurePath,
92
+ reason: error === null || error === void 0 ? void 0 : error.reason,
93
+ };
94
+ }
95
+ return {
96
+ code,
97
+ message: sanitizeErrorMessage(String((_c = error === null || error === void 0 ? void 0 : error.message) !== null && _c !== void 0 ? _c : "")),
98
+ };
99
+ };
100
+ const normalizeResult = (method, value) => {
101
+ var _a, _b, _c, _d, _e;
102
+ if (!value || typeof value !== "object")
103
+ return value;
104
+ if (method === "batchGet") {
105
+ const responses = (_a = value.Responses) !== null && _a !== void 0 ? _a : {};
106
+ const normalizedResponses = Object.fromEntries(Object.keys(responses)
107
+ .sort()
108
+ .map((tableName) => [
109
+ normalizeScalar(tableName),
110
+ [...responses[tableName]].sort(compareItemsByPKSK).map(normalizeObject),
111
+ ]));
112
+ const unprocessed = (_c = (_b = value.UnprocessedKeys) !== null && _b !== void 0 ? _b : value.UnprocessedItems) !== null && _c !== void 0 ? _c : {};
113
+ const normalizedUnprocessed = Object.fromEntries(Object.keys(unprocessed)
114
+ .sort()
115
+ .map((tableName) => [normalizeScalar(tableName), normalizeObject(unprocessed[tableName])]));
116
+ return { Responses: normalizedResponses, Unprocessed: normalizedUnprocessed };
117
+ }
118
+ if (method === "delete") {
119
+ const normalized = (_d = normalizeObject(value)) !== null && _d !== void 0 ? _d : {};
120
+ if (!normalized || typeof normalized !== "object")
121
+ return normalized;
122
+ const { ConsumedCapacity: _ignored } = normalized, rest = __rest(normalized, ["ConsumedCapacity"]);
123
+ return rest;
124
+ }
125
+ if (method === "scan") {
126
+ return Object.assign(Object.assign({}, normalizeObject(value)), { Items: [...((_e = value.Items) !== null && _e !== void 0 ? _e : [])].sort(compareItemsByPKSK).map(normalizeObject) });
127
+ }
128
+ return normalizeObject(value);
129
+ };
130
+ const compareItemsByPKSK = (left, right) => {
131
+ var _a, _b, _c, _d;
132
+ const leftPK = String((_a = left === null || left === void 0 ? void 0 : left.PK) !== null && _a !== void 0 ? _a : "");
133
+ const rightPK = String((_b = right === null || right === void 0 ? void 0 : right.PK) !== null && _b !== void 0 ? _b : "");
134
+ if (leftPK !== rightPK)
135
+ return leftPK < rightPK ? -1 : 1;
136
+ const leftSK = String((_c = left === null || left === void 0 ? void 0 : left.SK) !== null && _c !== void 0 ? _c : "");
137
+ const rightSK = String((_d = right === null || right === void 0 ? void 0 : right.SK) !== null && _d !== void 0 ? _d : "");
138
+ if (leftSK !== rightSK)
139
+ return leftSK < rightSK ? -1 : 1;
140
+ return 0;
141
+ };
142
+ const runVector = (engine, vector) => __awaiter(void 0, void 0, void 0, function* () {
143
+ return withEngine(engine, (ctx) => __awaiter(void 0, void 0, void 0, function* () {
144
+ if (vector.setup) {
145
+ yield vector.setup(ctx);
146
+ }
147
+ try {
148
+ const raw = yield vector.execute(ctx);
149
+ const snapshot = normalizeSnapshot(yield ctx.sandbox.snapshot());
150
+ return {
151
+ ok: true,
152
+ value: vector.normalizeResult
153
+ ? vector.normalizeResult(raw)
154
+ : normalizeResult(vector.method, raw),
155
+ snapshot,
156
+ };
157
+ }
158
+ catch (error) {
159
+ const snapshot = normalizeSnapshot(yield ctx.sandbox.snapshot());
160
+ return {
161
+ ok: false,
162
+ error: normalizeError(error),
163
+ snapshot,
164
+ };
165
+ }
166
+ }));
167
+ });
168
+ const createSeed = (ctx) => __awaiter(void 0, void 0, void 0, function* () {
169
+ yield ctx.client.documentClient
170
+ .batchWrite({
171
+ RequestItems: {
172
+ [ctx.tableName]: [
173
+ {
174
+ PutRequest: {
175
+ Item: {
176
+ PK: "USER#1",
177
+ SK: "PROFILE#001",
178
+ GSI2PK: "EMAIL#ada@example.com",
179
+ GSI2SK: "USER#1",
180
+ name: "Ada",
181
+ age: 30,
182
+ status: "active",
183
+ score: 10,
184
+ tags: ["math", "history"],
185
+ },
186
+ },
187
+ },
188
+ {
189
+ PutRequest: {
190
+ Item: {
191
+ PK: "USER#1",
192
+ SK: "ORDER#001",
193
+ GSI2PK: "ORDER#OPEN",
194
+ GSI2SK: "2021-01-01T00:00:00.000Z",
195
+ total: 120,
196
+ currency: "USD",
197
+ status: "open",
198
+ },
199
+ },
200
+ },
201
+ {
202
+ PutRequest: {
203
+ Item: {
204
+ PK: "USER#1",
205
+ SK: "ORDER#002",
206
+ GSI2PK: "ORDER#CLOSED",
207
+ GSI2SK: "2021-01-02T00:00:00.000Z",
208
+ total: 99,
209
+ currency: "USD",
210
+ status: "closed",
211
+ },
212
+ },
213
+ },
214
+ {
215
+ PutRequest: {
216
+ Item: {
217
+ PK: "USER#2",
218
+ SK: "PROFILE#001",
219
+ GSI2PK: "EMAIL#grace@example.com",
220
+ GSI2SK: "USER#2",
221
+ name: "Grace",
222
+ age: 35,
223
+ status: "pending",
224
+ score: 20,
225
+ },
226
+ },
227
+ },
228
+ {
229
+ PutRequest: {
230
+ Item: {
231
+ PK: "USER#3",
232
+ SK: "PROFILE#001",
233
+ GSI2PK: "EMAIL#alan@example.com",
234
+ GSI2SK: "USER#3",
235
+ name: "Alan",
236
+ age: 28,
237
+ status: "active",
238
+ score: 15,
239
+ },
240
+ },
241
+ },
242
+ ],
243
+ },
244
+ })
245
+ .promise();
246
+ });
247
+ const createAuxTableIfNeeded = (ctx, tableName) => __awaiter(void 0, void 0, void 0, function* () {
248
+ if (ctx.engine !== "local")
249
+ return;
250
+ yield LOCAL_DDB.createTable({
251
+ TableName: tableName,
252
+ AttributeDefinitions: [
253
+ { AttributeName: "PK", AttributeType: "S" },
254
+ { AttributeName: "SK", AttributeType: "S" },
255
+ ],
256
+ KeySchema: [
257
+ { AttributeName: "PK", KeyType: "HASH" },
258
+ { AttributeName: "SK", KeyType: "RANGE" },
259
+ ],
260
+ BillingMode: "PAY_PER_REQUEST",
261
+ })
262
+ .promise()
263
+ .catch((error) => {
264
+ if ((error === null || error === void 0 ? void 0 : error.code) === "ResourceInUseException")
265
+ return;
266
+ throw error;
267
+ });
268
+ yield LOCAL_DDB.waitFor("tableExists", { TableName: tableName }).promise();
269
+ });
270
+ const destroyAuxTableIfNeeded = (ctx, tableName) => __awaiter(void 0, void 0, void 0, function* () {
271
+ if (ctx.engine !== "local")
272
+ return;
273
+ yield LOCAL_DDB.deleteTable({ TableName: tableName })
274
+ .promise()
275
+ .catch((error) => {
276
+ if ((error === null || error === void 0 ? void 0 : error.code) === "ResourceNotFoundException")
277
+ return;
278
+ throw error;
279
+ });
280
+ yield LOCAL_DDB.waitFor("tableNotExists", { TableName: tableName })
281
+ .promise()
282
+ .catch((error) => {
283
+ if ((error === null || error === void 0 ? void 0 : error.code) === "ResourceNotFoundException")
284
+ return;
285
+ throw error;
286
+ });
287
+ });
288
+ const baseVectorsByMethod = {
289
+ get: [
290
+ {
291
+ id: "get.existing",
292
+ method: "get",
293
+ setup: createSeed,
294
+ execute: ({ client, tableName }) => client.documentClient
295
+ .get({ TableName: tableName, Key: { PK: "USER#1", SK: "PROFILE#001" } })
296
+ .promise(),
297
+ },
298
+ {
299
+ id: "get.missing",
300
+ method: "get",
301
+ setup: createSeed,
302
+ execute: ({ client, tableName }) => client.documentClient
303
+ .get({ TableName: tableName, Key: { PK: "USER#404", SK: "PROFILE#001" } })
304
+ .promise(),
305
+ },
306
+ {
307
+ id: "get.consistent-read",
308
+ method: "get",
309
+ setup: createSeed,
310
+ execute: ({ client, tableName }) => client.documentClient
311
+ .get({
312
+ TableName: tableName,
313
+ Key: { PK: "USER#2", SK: "PROFILE#001" },
314
+ ConsistentRead: true,
315
+ })
316
+ .promise(),
317
+ },
318
+ {
319
+ id: "get.bad-key",
320
+ method: "get",
321
+ execute: ({ client, tableName }) => client.documentClient
322
+ .get({ TableName: tableName, Key: { PK: 123, SK: "A" } })
323
+ .promise(),
324
+ },
325
+ ],
326
+ put: [
327
+ {
328
+ id: "put.insert",
329
+ method: "put",
330
+ execute: ({ client, tableName }) => client.documentClient
331
+ .put({
332
+ TableName: tableName,
333
+ Item: {
334
+ PK: "USER#10",
335
+ SK: "PROFILE#001",
336
+ GSI2PK: "EMAIL#new@example.com",
337
+ GSI2SK: "USER#10",
338
+ name: "New",
339
+ score: 1,
340
+ },
341
+ })
342
+ .promise(),
343
+ },
344
+ {
345
+ id: "put.overwrite",
346
+ method: "put",
347
+ setup: createSeed,
348
+ execute: ({ client, tableName }) => client.documentClient
349
+ .put({
350
+ TableName: tableName,
351
+ Item: {
352
+ PK: "USER#1",
353
+ SK: "PROFILE#001",
354
+ GSI2PK: "EMAIL#ada@example.com",
355
+ GSI2SK: "USER#1",
356
+ name: "Ada Lovelace",
357
+ age: 31,
358
+ },
359
+ })
360
+ .promise(),
361
+ },
362
+ {
363
+ id: "put.conditional-fail",
364
+ method: "put",
365
+ setup: createSeed,
366
+ execute: ({ client, tableName }) => client.documentClient
367
+ .put({
368
+ TableName: tableName,
369
+ Item: {
370
+ PK: "USER#1",
371
+ SK: "PROFILE#001",
372
+ name: "Nope",
373
+ },
374
+ ConditionExpression: "attribute_not_exists(PK)",
375
+ })
376
+ .promise(),
377
+ },
378
+ {
379
+ id: "put.conditional-pass-with-placeholders",
380
+ method: "put",
381
+ setup: createSeed,
382
+ execute: ({ client, tableName }) => client.documentClient
383
+ .put({
384
+ TableName: tableName,
385
+ Item: {
386
+ PK: "USER#11",
387
+ SK: "PROFILE#001",
388
+ name: "Placeholder",
389
+ status: "active",
390
+ },
391
+ ConditionExpression: "attribute_not_exists(#pk) and :status = :status",
392
+ ExpressionAttributeNames: { "#pk": "PK" },
393
+ ExpressionAttributeValues: { ":status": "active" },
394
+ })
395
+ .promise(),
396
+ },
397
+ {
398
+ id: "put.bad-condition-expression",
399
+ method: "put",
400
+ execute: ({ client, tableName }) => client.documentClient
401
+ .put({
402
+ TableName: tableName,
403
+ Item: { PK: "A", SK: "A" },
404
+ ConditionExpression: "unknown_fn(PK)",
405
+ })
406
+ .promise(),
407
+ },
408
+ ],
409
+ update: [
410
+ {
411
+ id: "update.set-and-return-all-new",
412
+ method: "update",
413
+ setup: createSeed,
414
+ execute: ({ client, tableName }) => client.documentClient
415
+ .update({
416
+ TableName: tableName,
417
+ Key: { PK: "USER#1", SK: "PROFILE#001" },
418
+ UpdateExpression: "SET #name = :name, #score = :score",
419
+ ExpressionAttributeNames: { "#name": "name", "#score": "score" },
420
+ ExpressionAttributeValues: { ":name": "Ada Updated", ":score": 11 },
421
+ ReturnValues: "ALL_NEW",
422
+ })
423
+ .promise(),
424
+ },
425
+ {
426
+ id: "update.remove-gsi",
427
+ method: "update",
428
+ setup: createSeed,
429
+ execute: ({ client, tableName }) => client.documentClient
430
+ .update({
431
+ TableName: tableName,
432
+ Key: { PK: "USER#1", SK: "PROFILE#001" },
433
+ UpdateExpression: "REMOVE GSI2PK, GSI2SK",
434
+ ReturnValues: "ALL_NEW",
435
+ })
436
+ .promise(),
437
+ },
438
+ {
439
+ id: "update.upsert",
440
+ method: "update",
441
+ execute: ({ client, tableName }) => client.documentClient
442
+ .update({
443
+ TableName: tableName,
444
+ Key: { PK: "USER#99", SK: "PROFILE#001" },
445
+ UpdateExpression: "SET #name = :name",
446
+ ExpressionAttributeNames: { "#name": "name" },
447
+ ExpressionAttributeValues: { ":name": "Created" },
448
+ ReturnValues: "ALL_NEW",
449
+ })
450
+ .promise(),
451
+ },
452
+ {
453
+ id: "update.conditional-fail",
454
+ method: "update",
455
+ setup: createSeed,
456
+ execute: ({ client, tableName }) => client.documentClient
457
+ .update({
458
+ TableName: tableName,
459
+ Key: { PK: "USER#404", SK: "PROFILE#001" },
460
+ ConditionExpression: "attribute_exists(PK)",
461
+ UpdateExpression: "SET #name = :name",
462
+ ExpressionAttributeNames: { "#name": "name" },
463
+ ExpressionAttributeValues: { ":name": "Nope" },
464
+ })
465
+ .promise(),
466
+ },
467
+ {
468
+ id: "update.bad-update-expression",
469
+ method: "update",
470
+ setup: createSeed,
471
+ execute: ({ client, tableName }) => client.documentClient
472
+ .update({
473
+ TableName: tableName,
474
+ Key: { PK: "USER#1", SK: "PROFILE#001" },
475
+ UpdateExpression: "SET",
476
+ })
477
+ .promise(),
478
+ },
479
+ {
480
+ id: "update.missing-placeholder",
481
+ method: "update",
482
+ setup: createSeed,
483
+ execute: ({ client, tableName }) => client.documentClient
484
+ .update({
485
+ TableName: tableName,
486
+ Key: { PK: "USER#1", SK: "PROFILE#001" },
487
+ UpdateExpression: "SET #name = :missing",
488
+ ExpressionAttributeNames: { "#name": "name" },
489
+ })
490
+ .promise(),
491
+ },
492
+ {
493
+ id: "update.key-mutation-error",
494
+ method: "update",
495
+ setup: createSeed,
496
+ execute: ({ client, tableName }) => client.documentClient
497
+ .update({
498
+ TableName: tableName,
499
+ Key: { PK: "USER#1", SK: "PROFILE#001" },
500
+ UpdateExpression: "SET PK = :pk",
501
+ ExpressionAttributeValues: { ":pk": "USER#X" },
502
+ })
503
+ .promise(),
504
+ },
505
+ ],
506
+ delete: [
507
+ {
508
+ id: "delete.existing",
509
+ method: "delete",
510
+ setup: createSeed,
511
+ execute: ({ client, tableName }) => client.documentClient
512
+ .delete({ TableName: tableName, Key: { PK: "USER#3", SK: "PROFILE#001" } })
513
+ .promise(),
514
+ },
515
+ {
516
+ id: "delete.missing",
517
+ method: "delete",
518
+ setup: createSeed,
519
+ execute: ({ client, tableName }) => client.documentClient
520
+ .delete({ TableName: tableName, Key: { PK: "USER#404", SK: "PROFILE#001" } })
521
+ .promise(),
522
+ },
523
+ {
524
+ id: "delete.conditional-fail",
525
+ method: "delete",
526
+ setup: createSeed,
527
+ execute: ({ client, tableName }) => client.documentClient
528
+ .delete({
529
+ TableName: tableName,
530
+ Key: { PK: "USER#2", SK: "PROFILE#001" },
531
+ ConditionExpression: "#status = :status",
532
+ ExpressionAttributeNames: { "#status": "status" },
533
+ ExpressionAttributeValues: { ":status": "active" },
534
+ })
535
+ .promise(),
536
+ },
537
+ {
538
+ id: "delete.conditional-pass",
539
+ method: "delete",
540
+ setup: createSeed,
541
+ execute: ({ client, tableName }) => client.documentClient
542
+ .delete({
543
+ TableName: tableName,
544
+ Key: { PK: "USER#2", SK: "PROFILE#001" },
545
+ ConditionExpression: "#status = :status",
546
+ ExpressionAttributeNames: { "#status": "status" },
547
+ ExpressionAttributeValues: { ":status": "pending" },
548
+ })
549
+ .promise(),
550
+ },
551
+ ],
552
+ query: [
553
+ {
554
+ id: "query.hash-only",
555
+ method: "query",
556
+ setup: createSeed,
557
+ execute: ({ client, tableName }) => client.documentClient
558
+ .query({
559
+ TableName: tableName,
560
+ KeyConditionExpression: "PK = :pk",
561
+ ExpressionAttributeValues: { ":pk": "USER#1" },
562
+ })
563
+ .promise(),
564
+ },
565
+ {
566
+ id: "query.begins-with",
567
+ method: "query",
568
+ setup: createSeed,
569
+ execute: ({ client, tableName }) => client.documentClient
570
+ .query({
571
+ TableName: tableName,
572
+ KeyConditionExpression: "PK = :pk and begins_with(SK, :prefix)",
573
+ ExpressionAttributeValues: {
574
+ ":pk": "USER#1",
575
+ ":prefix": "ORDER#",
576
+ },
577
+ })
578
+ .promise(),
579
+ },
580
+ {
581
+ id: "query.range-between",
582
+ method: "query",
583
+ setup: createSeed,
584
+ execute: ({ client, tableName }) => client.documentClient
585
+ .query({
586
+ TableName: tableName,
587
+ KeyConditionExpression: "PK = :pk and SK between :from and :to",
588
+ ExpressionAttributeValues: {
589
+ ":pk": "USER#1",
590
+ ":from": "ORDER#001",
591
+ ":to": "ORDER#010",
592
+ },
593
+ })
594
+ .promise(),
595
+ },
596
+ {
597
+ id: "query.range-operator-and-backward",
598
+ method: "query",
599
+ setup: createSeed,
600
+ execute: ({ client, tableName }) => client.documentClient
601
+ .query({
602
+ TableName: tableName,
603
+ KeyConditionExpression: "PK = :pk and SK >= :from",
604
+ ExpressionAttributeValues: {
605
+ ":pk": "USER#1",
606
+ ":from": "ORDER#001",
607
+ },
608
+ ScanIndexForward: false,
609
+ })
610
+ .promise(),
611
+ },
612
+ {
613
+ id: "query.filter-limit-scanned-count",
614
+ method: "query",
615
+ setup: createSeed,
616
+ execute: ({ client, tableName }) => client.documentClient
617
+ .query({
618
+ TableName: tableName,
619
+ KeyConditionExpression: "PK = :pk",
620
+ FilterExpression: "#status = :status",
621
+ ExpressionAttributeNames: { "#status": "status" },
622
+ ExpressionAttributeValues: {
623
+ ":pk": "USER#1",
624
+ ":status": "open",
625
+ },
626
+ Limit: 1,
627
+ })
628
+ .promise(),
629
+ },
630
+ {
631
+ id: "query.pagination-exclusive-start",
632
+ method: "query",
633
+ setup: (ctx) => __awaiter(void 0, void 0, void 0, function* () {
634
+ yield createSeed(ctx);
635
+ }),
636
+ execute: ({ client, tableName }) => client.documentClient
637
+ .query({
638
+ TableName: tableName,
639
+ KeyConditionExpression: "PK = :pk",
640
+ ExpressionAttributeValues: { ":pk": "USER#1" },
641
+ Limit: 1,
642
+ })
643
+ .promise(),
644
+ },
645
+ {
646
+ id: "query.gsi",
647
+ method: "query",
648
+ setup: createSeed,
649
+ execute: ({ client, tableName }) => client.documentClient
650
+ .query({
651
+ TableName: tableName,
652
+ IndexName: "GSI2",
653
+ KeyConditionExpression: "GSI2PK = :pk",
654
+ ExpressionAttributeValues: { ":pk": "ORDER#OPEN" },
655
+ })
656
+ .promise(),
657
+ },
658
+ {
659
+ id: "query.gsi-consistent-read-error",
660
+ method: "query",
661
+ setup: createSeed,
662
+ execute: ({ client, tableName }) => client.documentClient
663
+ .query({
664
+ TableName: tableName,
665
+ IndexName: "GSI2",
666
+ KeyConditionExpression: "GSI2PK = :pk",
667
+ ExpressionAttributeValues: { ":pk": "ORDER#OPEN" },
668
+ ConsistentRead: true,
669
+ })
670
+ .promise(),
671
+ },
672
+ {
673
+ id: "query.bad-key-condition",
674
+ method: "query",
675
+ setup: createSeed,
676
+ execute: ({ client, tableName }) => client.documentClient
677
+ .query({
678
+ TableName: tableName,
679
+ KeyConditionExpression: "PK = :pk and invalid(SK, :x)",
680
+ ExpressionAttributeValues: {
681
+ ":pk": "USER#1",
682
+ ":x": "A",
683
+ },
684
+ })
685
+ .promise(),
686
+ },
687
+ ],
688
+ scan: [
689
+ {
690
+ id: "scan.all",
691
+ method: "scan",
692
+ setup: createSeed,
693
+ execute: ({ client, tableName }) => client.documentClient.scan({ TableName: tableName }).promise(),
694
+ },
695
+ {
696
+ id: "scan.filter-limit",
697
+ method: "scan",
698
+ setup: createSeed,
699
+ execute: ({ client, tableName }) => client.documentClient
700
+ .scan({
701
+ TableName: tableName,
702
+ FilterExpression: "attribute_exists(GSI2PK) and #status = :status",
703
+ ExpressionAttributeNames: { "#status": "status" },
704
+ ExpressionAttributeValues: { ":status": "active" },
705
+ })
706
+ .promise(),
707
+ },
708
+ {
709
+ id: "scan.bad-limit",
710
+ method: "scan",
711
+ setup: createSeed,
712
+ execute: ({ client, tableName }) => client.documentClient
713
+ .scan({ TableName: tableName, Limit: 0 })
714
+ .promise(),
715
+ },
716
+ ],
717
+ batchGet: [
718
+ {
719
+ id: "batch-get.basic",
720
+ method: "batchGet",
721
+ setup: createSeed,
722
+ execute: ({ client, tableName }) => client.documentClient
723
+ .batchGet({
724
+ RequestItems: {
725
+ [tableName]: {
726
+ Keys: [
727
+ { PK: "USER#1", SK: "PROFILE#001" },
728
+ { PK: "USER#2", SK: "PROFILE#001" },
729
+ ],
730
+ },
731
+ },
732
+ })
733
+ .promise(),
734
+ },
735
+ {
736
+ id: "batch-get.missing-items",
737
+ method: "batchGet",
738
+ setup: createSeed,
739
+ execute: ({ client, tableName }) => client.documentClient
740
+ .batchGet({
741
+ RequestItems: {
742
+ [tableName]: {
743
+ Keys: [
744
+ { PK: "USER#1", SK: "PROFILE#001" },
745
+ { PK: "USER#404", SK: "PROFILE#001" },
746
+ ],
747
+ },
748
+ },
749
+ })
750
+ .promise(),
751
+ },
752
+ {
753
+ id: "batch-get.duplicate-keys-error",
754
+ method: "batchGet",
755
+ setup: createSeed,
756
+ execute: ({ client, tableName }) => client.documentClient
757
+ .batchGet({
758
+ RequestItems: {
759
+ [tableName]: {
760
+ Keys: [
761
+ { PK: "USER#1", SK: "PROFILE#001" },
762
+ { PK: "USER#1", SK: "PROFILE#001" },
763
+ ],
764
+ },
765
+ },
766
+ })
767
+ .promise(),
768
+ },
769
+ {
770
+ id: "batch-get.too-many-keys-error",
771
+ method: "batchGet",
772
+ execute: ({ client, tableName }) => client.documentClient
773
+ .batchGet({
774
+ RequestItems: {
775
+ [tableName]: {
776
+ Keys: Array.from({ length: 101 }).map((_, i) => ({
777
+ PK: `USER#${i}`,
778
+ SK: "PROFILE#001",
779
+ })),
780
+ },
781
+ },
782
+ })
783
+ .promise(),
784
+ },
785
+ ],
786
+ batchWrite: [
787
+ {
788
+ id: "batch-write.put-and-delete",
789
+ method: "batchWrite",
790
+ setup: createSeed,
791
+ execute: ({ client, tableName }) => client.documentClient
792
+ .batchWrite({
793
+ RequestItems: {
794
+ [tableName]: [
795
+ {
796
+ PutRequest: {
797
+ Item: {
798
+ PK: "USER#20",
799
+ SK: "PROFILE#001",
800
+ name: "BatchPut",
801
+ },
802
+ },
803
+ },
804
+ {
805
+ DeleteRequest: {
806
+ Key: { PK: "USER#3", SK: "PROFILE#001" },
807
+ },
808
+ },
809
+ ],
810
+ },
811
+ })
812
+ .promise(),
813
+ },
814
+ {
815
+ id: "batch-write.too-many-items-error",
816
+ method: "batchWrite",
817
+ execute: ({ client, tableName }) => client.documentClient
818
+ .batchWrite({
819
+ RequestItems: {
820
+ [tableName]: Array.from({ length: 26 }).map((_, i) => ({
821
+ PutRequest: {
822
+ Item: { PK: `U#${i}`, SK: "S#1" },
823
+ },
824
+ })),
825
+ },
826
+ })
827
+ .promise(),
828
+ },
829
+ {
830
+ id: "batch-write.invalid-entry-error",
831
+ method: "batchWrite",
832
+ execute: ({ client, tableName }) => client.documentClient
833
+ .batchWrite({
834
+ RequestItems: {
835
+ [tableName]: [{ Nope: true }],
836
+ },
837
+ })
838
+ .promise(),
839
+ },
840
+ ],
841
+ transactWrite: [
842
+ {
843
+ id: "transact-write.success",
844
+ method: "transactWrite",
845
+ setup: createSeed,
846
+ execute: ({ client, tableName }) => client.documentClient
847
+ .transactWrite({
848
+ TransactItems: [
849
+ {
850
+ ConditionCheck: {
851
+ TableName: tableName,
852
+ Key: { PK: "USER#1", SK: "PROFILE#001" },
853
+ ConditionExpression: "attribute_exists(PK)",
854
+ },
855
+ },
856
+ {
857
+ Update: {
858
+ TableName: tableName,
859
+ Key: { PK: "USER#2", SK: "PROFILE#001" },
860
+ UpdateExpression: "SET #score = :score",
861
+ ExpressionAttributeNames: { "#score": "score" },
862
+ ExpressionAttributeValues: { ":score": 25 },
863
+ },
864
+ },
865
+ {
866
+ Put: {
867
+ TableName: tableName,
868
+ Item: {
869
+ PK: "USER#30",
870
+ SK: "PROFILE#001",
871
+ name: "FromTx",
872
+ },
873
+ ConditionExpression: "attribute_not_exists(PK)",
874
+ },
875
+ },
876
+ {
877
+ Delete: {
878
+ TableName: tableName,
879
+ Key: { PK: "USER#3", SK: "PROFILE#001" },
880
+ },
881
+ },
882
+ ],
883
+ })
884
+ .promise(),
885
+ },
886
+ {
887
+ id: "transact-write.conditional-fail-and-rollback",
888
+ method: "transactWrite",
889
+ setup: createSeed,
890
+ execute: ({ client, tableName }) => client.documentClient
891
+ .transactWrite({
892
+ TransactItems: [
893
+ {
894
+ Update: {
895
+ TableName: tableName,
896
+ Key: { PK: "USER#2", SK: "PROFILE#001" },
897
+ UpdateExpression: "SET #score = :score",
898
+ ExpressionAttributeNames: { "#score": "score" },
899
+ ExpressionAttributeValues: { ":score": 100 },
900
+ },
901
+ },
902
+ {
903
+ ConditionCheck: {
904
+ TableName: tableName,
905
+ Key: { PK: "USER#404", SK: "PROFILE#001" },
906
+ ConditionExpression: "attribute_exists(PK)",
907
+ },
908
+ },
909
+ ],
910
+ })
911
+ .promise(),
912
+ },
913
+ {
914
+ id: "transact-write.duplicate-target-error",
915
+ method: "transactWrite",
916
+ setup: createSeed,
917
+ execute: ({ client, tableName }) => client.documentClient
918
+ .transactWrite({
919
+ TransactItems: [
920
+ {
921
+ Update: {
922
+ TableName: tableName,
923
+ Key: { PK: "USER#2", SK: "PROFILE#001" },
924
+ UpdateExpression: "SET #score = :score",
925
+ ExpressionAttributeNames: { "#score": "score" },
926
+ ExpressionAttributeValues: { ":score": 100 },
927
+ },
928
+ },
929
+ {
930
+ Delete: {
931
+ TableName: tableName,
932
+ Key: { PK: "USER#2", SK: "PROFILE#001" },
933
+ },
934
+ },
935
+ ],
936
+ })
937
+ .promise(),
938
+ },
939
+ {
940
+ id: "transact-write.too-many-items-error",
941
+ method: "transactWrite",
942
+ execute: ({ client, tableName }) => client.documentClient
943
+ .transactWrite({
944
+ TransactItems: Array.from({ length: 101 }).map((_, i) => ({
945
+ Put: {
946
+ TableName: tableName,
947
+ Item: { PK: `TX#${i}`, SK: "S#1" },
948
+ },
949
+ })),
950
+ })
951
+ .promise(),
952
+ },
953
+ {
954
+ id: "transact-write.bad-update-expression-error",
955
+ method: "transactWrite",
956
+ setup: createSeed,
957
+ execute: ({ client, tableName }) => client.documentClient
958
+ .transactWrite({
959
+ TransactItems: [
960
+ {
961
+ Update: {
962
+ TableName: tableName,
963
+ Key: { PK: "USER#2", SK: "PROFILE#001" },
964
+ UpdateExpression: "SET",
965
+ },
966
+ },
967
+ ],
968
+ })
969
+ .promise(),
970
+ },
971
+ ],
972
+ };
973
+ const generatedVectors = Object.keys(IN_MEMORY_SPEC.methods).flatMap((method) => {
974
+ const typedMethod = method;
975
+ return baseVectorsByMethod[typedMethod];
976
+ });
977
+ const additionalDifferentialVectors = [
978
+ {
979
+ id: "query.pagination-continuity",
980
+ method: "query",
981
+ setup: createSeed,
982
+ execute: ({ client, tableName }) => __awaiter(void 0, void 0, void 0, function* () {
983
+ const p1 = yield client.documentClient
984
+ .query({
985
+ TableName: tableName,
986
+ KeyConditionExpression: "PK = :pk",
987
+ ExpressionAttributeValues: { ":pk": "USER#1" },
988
+ Limit: 1,
989
+ })
990
+ .promise();
991
+ const p2 = yield client.documentClient
992
+ .query({
993
+ TableName: tableName,
994
+ KeyConditionExpression: "PK = :pk",
995
+ ExpressionAttributeValues: { ":pk": "USER#1" },
996
+ Limit: 1,
997
+ ExclusiveStartKey: p1.LastEvaluatedKey,
998
+ })
999
+ .promise();
1000
+ const p3 = yield client.documentClient
1001
+ .query({
1002
+ TableName: tableName,
1003
+ KeyConditionExpression: "PK = :pk",
1004
+ ExpressionAttributeValues: { ":pk": "USER#1" },
1005
+ Limit: 10,
1006
+ ExclusiveStartKey: p2.LastEvaluatedKey,
1007
+ })
1008
+ .promise();
1009
+ return { p1, p2, p3 };
1010
+ }),
1011
+ },
1012
+ {
1013
+ id: "scan.pagination-continuity",
1014
+ method: "scan",
1015
+ setup: createSeed,
1016
+ execute: ({ client, tableName }) => __awaiter(void 0, void 0, void 0, function* () {
1017
+ var _a;
1018
+ const pages = [];
1019
+ let startKey = undefined;
1020
+ do {
1021
+ const page = yield client.documentClient
1022
+ .scan({
1023
+ TableName: tableName,
1024
+ Limit: 2,
1025
+ ExclusiveStartKey: startKey,
1026
+ })
1027
+ .promise();
1028
+ pages.push(page);
1029
+ startKey = page.LastEvaluatedKey;
1030
+ } while (startKey);
1031
+ const full = yield client.documentClient
1032
+ .scan({
1033
+ TableName: tableName,
1034
+ })
1035
+ .promise();
1036
+ return {
1037
+ pagedItems: pages.flatMap((page) => { var _a; return (_a = page.Items) !== null && _a !== void 0 ? _a : []; }),
1038
+ fullItems: (_a = full.Items) !== null && _a !== void 0 ? _a : [],
1039
+ pageCount: pages.length,
1040
+ };
1041
+ }),
1042
+ normalizeResult: (value) => {
1043
+ var _a, _b, _c, _d;
1044
+ return (Object.assign(Object.assign({}, normalizeObject(value)), { pagedItems: [...((_a = value.pagedItems) !== null && _a !== void 0 ? _a : [])]
1045
+ .sort(compareItemsByPKSK)
1046
+ .map(normalizeObject), fullItems: [...((_b = value.fullItems) !== null && _b !== void 0 ? _b : [])]
1047
+ .sort(compareItemsByPKSK)
1048
+ .map(normalizeObject), pageCount: value.pageCount, pagedMatchesFull: JSON.stringify([...((_c = value.pagedItems) !== null && _c !== void 0 ? _c : [])].sort(compareItemsByPKSK).map(normalizeObject)) ===
1049
+ JSON.stringify([...((_d = value.fullItems) !== null && _d !== void 0 ? _d : [])].sort(compareItemsByPKSK).map(normalizeObject)) }));
1050
+ },
1051
+ },
1052
+ {
1053
+ id: "query.bad-exclusive-start-key",
1054
+ method: "query",
1055
+ setup: createSeed,
1056
+ execute: ({ client, tableName }) => client.documentClient
1057
+ .query({
1058
+ TableName: tableName,
1059
+ IndexName: "GSI2",
1060
+ KeyConditionExpression: "GSI2PK = :pk",
1061
+ ExpressionAttributeValues: { ":pk": "ORDER#OPEN" },
1062
+ ExclusiveStartKey: { PK: "USER#1", SK: "ORDER#001" },
1063
+ })
1064
+ .promise(),
1065
+ },
1066
+ {
1067
+ id: "query.missing-expression-attribute-name",
1068
+ method: "query",
1069
+ setup: createSeed,
1070
+ execute: ({ client, tableName }) => client.documentClient
1071
+ .query({
1072
+ TableName: tableName,
1073
+ KeyConditionExpression: "#pk = :pk",
1074
+ ExpressionAttributeValues: { ":pk": "USER#1" },
1075
+ })
1076
+ .promise(),
1077
+ },
1078
+ {
1079
+ id: "query.limit-non-integer",
1080
+ method: "query",
1081
+ setup: createSeed,
1082
+ execute: ({ client, tableName }) => client.documentClient
1083
+ .query({
1084
+ TableName: tableName,
1085
+ KeyConditionExpression: "PK = :pk",
1086
+ ExpressionAttributeValues: { ":pk": "USER#1" },
1087
+ Limit: 1.5,
1088
+ })
1089
+ .promise(),
1090
+ },
1091
+ {
1092
+ id: "update.if-not-exists-existing-plus",
1093
+ method: "update",
1094
+ setup: createSeed,
1095
+ execute: ({ client, tableName }) => client.documentClient
1096
+ .update({
1097
+ TableName: tableName,
1098
+ Key: { PK: "USER#1", SK: "PROFILE#001" },
1099
+ UpdateExpression: "SET #score = if_not_exists(#score, :zero) + :inc",
1100
+ ExpressionAttributeNames: { "#score": "score" },
1101
+ ExpressionAttributeValues: { ":zero": 0, ":inc": 2 },
1102
+ ReturnValues: "ALL_NEW",
1103
+ })
1104
+ .promise(),
1105
+ },
1106
+ {
1107
+ id: "update.if-not-exists-missing-plus",
1108
+ method: "update",
1109
+ setup: createSeed,
1110
+ execute: ({ client, tableName }) => client.documentClient
1111
+ .update({
1112
+ TableName: tableName,
1113
+ Key: { PK: "USER#2", SK: "PROFILE#001" },
1114
+ UpdateExpression: "SET #visits = if_not_exists(#visits, :zero) + :inc",
1115
+ ExpressionAttributeNames: { "#visits": "visits" },
1116
+ ExpressionAttributeValues: { ":zero": 0, ":inc": 1 },
1117
+ ReturnValues: "ALL_NEW",
1118
+ })
1119
+ .promise(),
1120
+ },
1121
+ {
1122
+ id: "update.list-append-with-if-not-exists",
1123
+ method: "update",
1124
+ setup: createSeed,
1125
+ execute: ({ client, tableName }) => client.documentClient
1126
+ .update({
1127
+ TableName: tableName,
1128
+ Key: { PK: "USER#2", SK: "PROFILE#001" },
1129
+ UpdateExpression: "SET #tags = list_append(if_not_exists(#tags, :empty), :more)",
1130
+ ExpressionAttributeNames: { "#tags": "tags" },
1131
+ ExpressionAttributeValues: { ":empty": [], ":more": ["new", "vip"] },
1132
+ ReturnValues: "ALL_NEW",
1133
+ })
1134
+ .promise(),
1135
+ },
1136
+ {
1137
+ id: "scan.filter.contains",
1138
+ method: "scan",
1139
+ setup: createSeed,
1140
+ execute: ({ client, tableName }) => client.documentClient
1141
+ .scan({
1142
+ TableName: tableName,
1143
+ FilterExpression: "contains(#tags, :tag)",
1144
+ ExpressionAttributeNames: { "#tags": "tags" },
1145
+ ExpressionAttributeValues: { ":tag": "math" },
1146
+ })
1147
+ .promise(),
1148
+ },
1149
+ {
1150
+ id: "scan.filter.attribute-type-and-size",
1151
+ method: "scan",
1152
+ setup: createSeed,
1153
+ execute: ({ client, tableName }) => client.documentClient
1154
+ .scan({
1155
+ TableName: tableName,
1156
+ FilterExpression: "attribute_type(#name, :t) and size(#name) > :min",
1157
+ ExpressionAttributeNames: { "#name": "name" },
1158
+ ExpressionAttributeValues: { ":t": "S", ":min": 2 },
1159
+ })
1160
+ .promise(),
1161
+ },
1162
+ {
1163
+ id: "scan.missing-expression-attribute-value",
1164
+ method: "scan",
1165
+ setup: createSeed,
1166
+ execute: ({ client, tableName }) => client.documentClient
1167
+ .scan({
1168
+ TableName: tableName,
1169
+ FilterExpression: "#status = :missing",
1170
+ ExpressionAttributeNames: { "#status": "status" },
1171
+ })
1172
+ .promise(),
1173
+ },
1174
+ {
1175
+ id: "batch-get.multi-table",
1176
+ method: "batchGet",
1177
+ setup: createSeed,
1178
+ execute: (ctx) => __awaiter(void 0, void 0, void 0, function* () {
1179
+ const auxTable = `${ctx.tableName}-aux-batch-get`;
1180
+ yield createAuxTableIfNeeded(ctx, auxTable);
1181
+ try {
1182
+ yield ctx.client.documentClient
1183
+ .batchWrite({
1184
+ RequestItems: {
1185
+ [auxTable]: [
1186
+ {
1187
+ PutRequest: {
1188
+ Item: { PK: "AUX#1", SK: "ITEM#1", flag: true },
1189
+ },
1190
+ },
1191
+ ],
1192
+ },
1193
+ })
1194
+ .promise();
1195
+ return yield ctx.client.documentClient
1196
+ .batchGet({
1197
+ RequestItems: {
1198
+ [ctx.tableName]: {
1199
+ Keys: [{ PK: "USER#1", SK: "PROFILE#001" }],
1200
+ },
1201
+ [auxTable]: {
1202
+ Keys: [{ PK: "AUX#1", SK: "ITEM#1" }],
1203
+ },
1204
+ },
1205
+ })
1206
+ .promise();
1207
+ }
1208
+ finally {
1209
+ yield destroyAuxTableIfNeeded(ctx, auxTable);
1210
+ }
1211
+ }),
1212
+ },
1213
+ {
1214
+ id: "batch-write.multi-table",
1215
+ method: "batchWrite",
1216
+ setup: createSeed,
1217
+ normalizeResult: () => ({ UnprocessedItems: {} }),
1218
+ execute: (ctx) => __awaiter(void 0, void 0, void 0, function* () {
1219
+ const auxTable = `${ctx.tableName}-aux-batch-write`;
1220
+ yield createAuxTableIfNeeded(ctx, auxTable);
1221
+ try {
1222
+ return yield ctx.client.documentClient
1223
+ .batchWrite({
1224
+ RequestItems: {
1225
+ [ctx.tableName]: [
1226
+ {
1227
+ PutRequest: { Item: { PK: "USER#70", SK: "PROFILE#001", ok: true } },
1228
+ },
1229
+ ],
1230
+ [auxTable]: [
1231
+ {
1232
+ PutRequest: { Item: { PK: "AUX#2", SK: "ITEM#1", ok: true } },
1233
+ },
1234
+ ],
1235
+ },
1236
+ })
1237
+ .promise();
1238
+ }
1239
+ finally {
1240
+ yield destroyAuxTableIfNeeded(ctx, auxTable);
1241
+ }
1242
+ }),
1243
+ },
1244
+ {
1245
+ id: "transact-write.rollback-on-parser-error-mid-transaction",
1246
+ method: "transactWrite",
1247
+ setup: createSeed,
1248
+ execute: ({ client, tableName }) => client.documentClient
1249
+ .transactWrite({
1250
+ TransactItems: [
1251
+ {
1252
+ Update: {
1253
+ TableName: tableName,
1254
+ Key: { PK: "USER#2", SK: "PROFILE#001" },
1255
+ UpdateExpression: "SET #score = :score",
1256
+ ExpressionAttributeNames: { "#score": "score" },
1257
+ ExpressionAttributeValues: { ":score": 999 },
1258
+ },
1259
+ },
1260
+ {
1261
+ Update: {
1262
+ TableName: tableName,
1263
+ Key: { PK: "USER#1", SK: "PROFILE#001" },
1264
+ UpdateExpression: "SET",
1265
+ },
1266
+ },
1267
+ ],
1268
+ })
1269
+ .promise(),
1270
+ },
1271
+ {
1272
+ id: "transact-write.key-mutation-error",
1273
+ method: "transactWrite",
1274
+ setup: createSeed,
1275
+ execute: ({ client, tableName }) => client.documentClient
1276
+ .transactWrite({
1277
+ TransactItems: [
1278
+ {
1279
+ Update: {
1280
+ TableName: tableName,
1281
+ Key: { PK: "USER#2", SK: "PROFILE#001" },
1282
+ UpdateExpression: "SET PK = :pk",
1283
+ ExpressionAttributeValues: { ":pk": "USER#X" },
1284
+ },
1285
+ },
1286
+ ],
1287
+ })
1288
+ .promise(),
1289
+ },
1290
+ ];
1291
+ const supportedParamCoverageVectors = [
1292
+ {
1293
+ id: "coverage.get.supported",
1294
+ method: "get",
1295
+ setup: createSeed,
1296
+ coverage: { supported: ["TableName", "Key", "ConsistentRead"] },
1297
+ execute: ({ client, tableName }) => client.documentClient
1298
+ .get({
1299
+ TableName: tableName,
1300
+ Key: { PK: "USER#1", SK: "PROFILE#001" },
1301
+ ConsistentRead: true,
1302
+ })
1303
+ .promise(),
1304
+ },
1305
+ {
1306
+ id: "coverage.put.supported",
1307
+ method: "put",
1308
+ setup: createSeed,
1309
+ coverage: {
1310
+ supported: [
1311
+ "TableName",
1312
+ "Item",
1313
+ "ConditionExpression",
1314
+ "ExpressionAttributeNames",
1315
+ "ExpressionAttributeValues",
1316
+ ],
1317
+ },
1318
+ execute: ({ client, tableName }) => client.documentClient
1319
+ .put({
1320
+ TableName: tableName,
1321
+ Item: { PK: "COVER#PUT", SK: "1", status: "ok" },
1322
+ ConditionExpression: "attribute_not_exists(#pk) and :v = :v",
1323
+ ExpressionAttributeNames: { "#pk": "PK" },
1324
+ ExpressionAttributeValues: { ":v": "ok" },
1325
+ })
1326
+ .promise(),
1327
+ },
1328
+ {
1329
+ id: "coverage.update.supported",
1330
+ method: "update",
1331
+ setup: createSeed,
1332
+ coverage: {
1333
+ supported: [
1334
+ "TableName",
1335
+ "Key",
1336
+ "ConditionExpression",
1337
+ "UpdateExpression",
1338
+ "ExpressionAttributeNames",
1339
+ "ExpressionAttributeValues",
1340
+ "ReturnValues",
1341
+ ],
1342
+ },
1343
+ execute: ({ client, tableName }) => client.documentClient
1344
+ .update({
1345
+ TableName: tableName,
1346
+ Key: { PK: "USER#1", SK: "PROFILE#001" },
1347
+ ConditionExpression: "attribute_exists(PK)",
1348
+ UpdateExpression: "SET #score = :score",
1349
+ ExpressionAttributeNames: { "#score": "score" },
1350
+ ExpressionAttributeValues: { ":score": 12 },
1351
+ ReturnValues: "ALL_NEW",
1352
+ })
1353
+ .promise(),
1354
+ },
1355
+ {
1356
+ id: "coverage.delete.supported",
1357
+ method: "delete",
1358
+ setup: createSeed,
1359
+ coverage: {
1360
+ supported: [
1361
+ "TableName",
1362
+ "Key",
1363
+ "ConditionExpression",
1364
+ "ExpressionAttributeNames",
1365
+ "ExpressionAttributeValues",
1366
+ ],
1367
+ },
1368
+ execute: ({ client, tableName }) => client.documentClient
1369
+ .delete({
1370
+ TableName: tableName,
1371
+ Key: { PK: "USER#3", SK: "PROFILE#001" },
1372
+ ConditionExpression: "#status = :status",
1373
+ ExpressionAttributeNames: { "#status": "status" },
1374
+ ExpressionAttributeValues: { ":status": "active" },
1375
+ })
1376
+ .promise(),
1377
+ },
1378
+ {
1379
+ id: "coverage.query.supported",
1380
+ method: "query",
1381
+ setup: createSeed,
1382
+ coverage: {
1383
+ supported: [
1384
+ "TableName",
1385
+ "IndexName",
1386
+ "KeyConditionExpression",
1387
+ "FilterExpression",
1388
+ "ExpressionAttributeNames",
1389
+ "ExpressionAttributeValues",
1390
+ "Limit",
1391
+ "ExclusiveStartKey",
1392
+ "ScanIndexForward",
1393
+ "ConsistentRead",
1394
+ ],
1395
+ },
1396
+ execute: ({ client, tableName }) => __awaiter(void 0, void 0, void 0, function* () {
1397
+ const firstPrimary = yield client.documentClient
1398
+ .query({
1399
+ TableName: tableName,
1400
+ KeyConditionExpression: "PK = :pk",
1401
+ FilterExpression: "attribute_exists(#status)",
1402
+ ExpressionAttributeNames: { "#status": "status" },
1403
+ ExpressionAttributeValues: { ":pk": "USER#1" },
1404
+ ScanIndexForward: true,
1405
+ Limit: 1,
1406
+ })
1407
+ .promise();
1408
+ const secondPrimary = yield client.documentClient
1409
+ .query({
1410
+ TableName: tableName,
1411
+ KeyConditionExpression: "PK = :pk",
1412
+ FilterExpression: "attribute_exists(#status)",
1413
+ ExpressionAttributeNames: { "#status": "status" },
1414
+ ExpressionAttributeValues: { ":pk": "USER#1" },
1415
+ ScanIndexForward: true,
1416
+ Limit: 2,
1417
+ ExclusiveStartKey: firstPrimary.LastEvaluatedKey,
1418
+ })
1419
+ .promise();
1420
+ const gsi = yield client.documentClient
1421
+ .query({
1422
+ TableName: tableName,
1423
+ IndexName: "GSI2",
1424
+ KeyConditionExpression: "GSI2PK = :pk",
1425
+ ExpressionAttributeValues: { ":pk": "ORDER#OPEN" },
1426
+ ConsistentRead: false,
1427
+ Limit: 1,
1428
+ })
1429
+ .promise();
1430
+ return { secondPrimary, gsi };
1431
+ }),
1432
+ },
1433
+ {
1434
+ id: "coverage.scan.supported",
1435
+ method: "scan",
1436
+ setup: createSeed,
1437
+ coverage: {
1438
+ supported: [
1439
+ "TableName",
1440
+ "FilterExpression",
1441
+ "ExpressionAttributeNames",
1442
+ "ExpressionAttributeValues",
1443
+ "Limit",
1444
+ "ExclusiveStartKey",
1445
+ ],
1446
+ },
1447
+ execute: ({ client, tableName }) => __awaiter(void 0, void 0, void 0, function* () {
1448
+ const first = yield client.documentClient
1449
+ .scan({
1450
+ TableName: tableName,
1451
+ FilterExpression: "#status = :status",
1452
+ ExpressionAttributeNames: { "#status": "status" },
1453
+ ExpressionAttributeValues: { ":status": "active" },
1454
+ Limit: 1,
1455
+ })
1456
+ .promise();
1457
+ yield client.documentClient
1458
+ .scan({
1459
+ TableName: tableName,
1460
+ FilterExpression: "#status = :status",
1461
+ ExpressionAttributeNames: { "#status": "status" },
1462
+ ExpressionAttributeValues: { ":status": "active" },
1463
+ Limit: 2,
1464
+ ExclusiveStartKey: first.LastEvaluatedKey,
1465
+ })
1466
+ .promise();
1467
+ return client.documentClient
1468
+ .scan({
1469
+ TableName: tableName,
1470
+ FilterExpression: "#status = :status",
1471
+ ExpressionAttributeNames: { "#status": "status" },
1472
+ ExpressionAttributeValues: { ":status": "active" },
1473
+ })
1474
+ .promise();
1475
+ }),
1476
+ },
1477
+ {
1478
+ id: "coverage.batchGet.supported",
1479
+ method: "batchGet",
1480
+ setup: createSeed,
1481
+ coverage: { supported: ["RequestItems"] },
1482
+ execute: ({ client, tableName }) => client.documentClient
1483
+ .batchGet({
1484
+ RequestItems: { [tableName]: { Keys: [{ PK: "USER#1", SK: "PROFILE#001" }] } },
1485
+ })
1486
+ .promise(),
1487
+ },
1488
+ {
1489
+ id: "coverage.batchWrite.supported",
1490
+ method: "batchWrite",
1491
+ setup: createSeed,
1492
+ coverage: { supported: ["RequestItems"] },
1493
+ execute: ({ client, tableName }) => client.documentClient
1494
+ .batchWrite({
1495
+ RequestItems: {
1496
+ [tableName]: [{ PutRequest: { Item: { PK: "COVER#BW", SK: "1" } } }],
1497
+ },
1498
+ })
1499
+ .promise(),
1500
+ },
1501
+ {
1502
+ id: "coverage.transactWrite.supported",
1503
+ method: "transactWrite",
1504
+ setup: createSeed,
1505
+ coverage: { supported: ["TransactItems"] },
1506
+ execute: ({ client, tableName }) => client.documentClient
1507
+ .transactWrite({
1508
+ TransactItems: [
1509
+ {
1510
+ ConditionCheck: {
1511
+ TableName: tableName,
1512
+ Key: { PK: "USER#1", SK: "PROFILE#001" },
1513
+ ConditionExpression: "attribute_exists(PK)",
1514
+ },
1515
+ },
1516
+ ],
1517
+ })
1518
+ .promise(),
1519
+ },
1520
+ ];
1521
+ const unsupportedParamVectors = [
1522
+ ...Object.entries(IN_MEMORY_SPEC.methods).flatMap(([method, spec]) => {
1523
+ var _a;
1524
+ return ((_a = spec.unsupportedParams) !== null && _a !== void 0 ? _a : []).map((param) => ({
1525
+ id: `unsupported.${method}.${param}`,
1526
+ method: method,
1527
+ coverage: { unsupported: [param] },
1528
+ setup: method === "get" ||
1529
+ method === "put" ||
1530
+ method === "update" ||
1531
+ method === "delete" ||
1532
+ method === "query" ||
1533
+ method === "scan" ||
1534
+ method === "batchGet" ||
1535
+ method === "batchWrite" ||
1536
+ method === "transactWrite"
1537
+ ? createSeed
1538
+ : undefined,
1539
+ execute: (ctx) => {
1540
+ const baseByMethod = {
1541
+ get: {
1542
+ TableName: ctx.tableName,
1543
+ Key: { PK: "USER#1", SK: "PROFILE#001" },
1544
+ },
1545
+ put: {
1546
+ TableName: ctx.tableName,
1547
+ Item: { PK: "UNSUPPORTED#PUT", SK: "1" },
1548
+ },
1549
+ update: {
1550
+ TableName: ctx.tableName,
1551
+ Key: { PK: "USER#1", SK: "PROFILE#001" },
1552
+ UpdateExpression: "SET #name = :name",
1553
+ ExpressionAttributeNames: { "#name": "name" },
1554
+ ExpressionAttributeValues: { ":name": "x" },
1555
+ },
1556
+ delete: {
1557
+ TableName: ctx.tableName,
1558
+ Key: { PK: "USER#1", SK: "PROFILE#001" },
1559
+ },
1560
+ query: {
1561
+ TableName: ctx.tableName,
1562
+ KeyConditionExpression: "PK = :pk",
1563
+ ExpressionAttributeValues: { ":pk": "USER#1" },
1564
+ },
1565
+ scan: {
1566
+ TableName: ctx.tableName,
1567
+ },
1568
+ batchGet: {
1569
+ RequestItems: {
1570
+ [ctx.tableName]: {
1571
+ Keys: [{ PK: "USER#1", SK: "PROFILE#001" }],
1572
+ },
1573
+ },
1574
+ },
1575
+ batchWrite: {
1576
+ RequestItems: {
1577
+ [ctx.tableName]: [{ PutRequest: { Item: { PK: "UNSUPPORTED#BW", SK: "1" } } }],
1578
+ },
1579
+ },
1580
+ transactWrite: {
1581
+ TransactItems: [
1582
+ {
1583
+ ConditionCheck: {
1584
+ TableName: ctx.tableName,
1585
+ Key: { PK: "USER#1", SK: "PROFILE#001" },
1586
+ ConditionExpression: "attribute_exists(PK)",
1587
+ },
1588
+ },
1589
+ ],
1590
+ },
1591
+ };
1592
+ const valueByParam = {
1593
+ AttributesToGet: ["PK"],
1594
+ ProjectionExpression: "PK",
1595
+ ExpressionAttributeNames: { "#pk": "PK" },
1596
+ Expected: {},
1597
+ ReturnValues: "ALL_OLD",
1598
+ ReturnConsumedCapacity: "TOTAL",
1599
+ ReturnItemCollectionMetrics: "SIZE",
1600
+ AttributeUpdates: {},
1601
+ Select: "ALL_ATTRIBUTES",
1602
+ KeyConditions: {},
1603
+ QueryFilter: {},
1604
+ ConditionalOperator: "AND",
1605
+ Segment: 1,
1606
+ TotalSegments: 2,
1607
+ ScanFilter: {},
1608
+ ClientRequestToken: "token-1",
1609
+ };
1610
+ const params = Object.assign(Object.assign({}, baseByMethod[method]), { [param]: valueByParam[param] });
1611
+ return ctx.client.documentClient[method](params).promise();
1612
+ },
1613
+ }));
1614
+ }),
1615
+ ];
1616
+ const seededFuzzVectors = [11, 42, 99].map((seed) => ({
1617
+ id: `fuzz.seed-${seed}`,
1618
+ method: "transactWrite",
1619
+ execute: ({ client, tableName }) => __awaiter(void 0, void 0, void 0, function* () {
1620
+ var _b, _c;
1621
+ const state = {
1622
+ seed,
1623
+ next: seed,
1624
+ };
1625
+ const random = () => {
1626
+ state.next = (state.next * 48271) % 0x7fffffff;
1627
+ return state.next / 0x7fffffff;
1628
+ };
1629
+ const results = [];
1630
+ for (let i = 0; i < 80; i += 1) {
1631
+ const dice = random();
1632
+ const user = `F#${Math.floor(random() * 8)}`;
1633
+ const key = { PK: user, SK: `S#${Math.floor(random() * 5)}` };
1634
+ if (dice < 0.25) {
1635
+ try {
1636
+ yield client.documentClient
1637
+ .put({
1638
+ TableName: tableName,
1639
+ Item: Object.assign(Object.assign({}, key), { GSI2PK: `GX#${key.PK}`, GSI2SK: key.SK, score: Math.floor(random() * 100), status: random() > 0.5 ? "active" : "pending" }),
1640
+ })
1641
+ .promise();
1642
+ results.push({ op: "put", key, ok: true });
1643
+ }
1644
+ catch (error) {
1645
+ results.push({ op: "put", key, ok: false, error: normalizeError(error) });
1646
+ }
1647
+ continue;
1648
+ }
1649
+ if (dice < 0.5) {
1650
+ try {
1651
+ const mutateGsi = random() > 0.6;
1652
+ const response = yield client.documentClient
1653
+ .update({
1654
+ TableName: tableName,
1655
+ Key: key,
1656
+ UpdateExpression: mutateGsi
1657
+ ? random() > 0.5
1658
+ ? "SET #score = :score, GSI2PK = :gpk, GSI2SK = :gsk"
1659
+ : "SET #score = :score REMOVE GSI2PK, GSI2SK"
1660
+ : "SET #score = :score",
1661
+ ExpressionAttributeNames: { "#score": "score" },
1662
+ ExpressionAttributeValues: mutateGsi
1663
+ ? {
1664
+ ":score": Math.floor(random() * 100),
1665
+ ":gpk": `GX#${key.PK}`,
1666
+ ":gsk": key.SK,
1667
+ }
1668
+ : { ":score": Math.floor(random() * 100) },
1669
+ ReturnValues: "ALL_NEW",
1670
+ })
1671
+ .promise();
1672
+ results.push({
1673
+ op: "update",
1674
+ key,
1675
+ ok: true,
1676
+ attrs: normalizeObject((_b = response.Attributes) !== null && _b !== void 0 ? _b : {}),
1677
+ });
1678
+ }
1679
+ catch (error) {
1680
+ results.push({ op: "update", key, ok: false, error: normalizeError(error) });
1681
+ }
1682
+ continue;
1683
+ }
1684
+ if (dice < 0.7) {
1685
+ try {
1686
+ yield client.documentClient
1687
+ .delete({
1688
+ TableName: tableName,
1689
+ Key: key,
1690
+ ConditionExpression: "attribute_not_exists(blocked)",
1691
+ })
1692
+ .promise();
1693
+ results.push({ op: "delete", key, ok: true });
1694
+ }
1695
+ catch (error) {
1696
+ results.push({ op: "delete", key, ok: false, error: normalizeError(error) });
1697
+ }
1698
+ continue;
1699
+ }
1700
+ if (dice < 0.85) {
1701
+ try {
1702
+ const useGsi = random() > 0.5;
1703
+ const response = useGsi
1704
+ ? yield client.documentClient
1705
+ .query({
1706
+ TableName: tableName,
1707
+ IndexName: "GSI2",
1708
+ KeyConditionExpression: "GSI2PK = :pk",
1709
+ ExpressionAttributeValues: { ":pk": `GX#${user}` },
1710
+ Limit: 3,
1711
+ })
1712
+ .promise()
1713
+ : yield client.documentClient
1714
+ .query({
1715
+ TableName: tableName,
1716
+ KeyConditionExpression: "PK = :pk",
1717
+ ExpressionAttributeValues: { ":pk": user },
1718
+ Limit: 3,
1719
+ })
1720
+ .promise();
1721
+ results.push({
1722
+ op: useGsi ? "query-gsi" : "query",
1723
+ key,
1724
+ ok: true,
1725
+ count: response.Count,
1726
+ scannedCount: response.ScannedCount,
1727
+ items: normalizeObject((_c = response.Items) !== null && _c !== void 0 ? _c : []),
1728
+ });
1729
+ }
1730
+ catch (error) {
1731
+ results.push({ op: "query", key, ok: false, error: normalizeError(error) });
1732
+ }
1733
+ continue;
1734
+ }
1735
+ try {
1736
+ yield client.documentClient
1737
+ .transactWrite({
1738
+ TransactItems: [
1739
+ {
1740
+ Put: {
1741
+ TableName: tableName,
1742
+ Item: {
1743
+ PK: `${user}#tx`,
1744
+ SK: key.SK,
1745
+ score: Math.floor(random() * 100),
1746
+ },
1747
+ },
1748
+ },
1749
+ {
1750
+ ConditionCheck: {
1751
+ TableName: tableName,
1752
+ Key: key,
1753
+ ConditionExpression: random() > 0.5 ? "attribute_exists(PK)" : "attribute_not_exists(PK)",
1754
+ },
1755
+ },
1756
+ ],
1757
+ })
1758
+ .promise();
1759
+ results.push({ op: "transactWrite", key, ok: true });
1760
+ }
1761
+ catch (error) {
1762
+ results.push({
1763
+ op: "transactWrite",
1764
+ key,
1765
+ ok: false,
1766
+ error: normalizeError(error),
1767
+ });
1768
+ }
1769
+ }
1770
+ return results;
1771
+ }),
1772
+ }));
1773
+ describe("dynamodb conformance (local vs in-memory)", () => {
1774
+ const differentialVectors = [
1775
+ ...generatedVectors,
1776
+ ...additionalDifferentialVectors,
1777
+ ...supportedParamCoverageVectors,
1778
+ ...seededFuzzVectors,
1779
+ ];
1780
+ test("every supported method in the manifest has differential vector coverage", () => {
1781
+ const coveredMethods = new Set(differentialVectors.map((vector) => vector.method));
1782
+ const expectedMethods = new Set(Object.keys(IN_MEMORY_SPEC.methods));
1783
+ expect(coveredMethods).toEqual(expectedMethods);
1784
+ });
1785
+ test("supported and unsupported parameter coverage matches the spec", () => {
1786
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j;
1787
+ const supportedCoverage = new Map();
1788
+ const unsupportedCoverage = new Map();
1789
+ for (const vector of [...differentialVectors, ...unsupportedParamVectors]) {
1790
+ if (!((_a = vector.coverage) === null || _a === void 0 ? void 0 : _a.supported) && !((_b = vector.coverage) === null || _b === void 0 ? void 0 : _b.unsupported))
1791
+ continue;
1792
+ const method = vector.method;
1793
+ if (!supportedCoverage.has(method))
1794
+ supportedCoverage.set(method, new Set());
1795
+ if (!unsupportedCoverage.has(method))
1796
+ unsupportedCoverage.set(method, new Set());
1797
+ for (const param of (_d = (_c = vector.coverage) === null || _c === void 0 ? void 0 : _c.supported) !== null && _d !== void 0 ? _d : []) {
1798
+ supportedCoverage.get(method).add(param);
1799
+ }
1800
+ for (const param of (_f = (_e = vector.coverage) === null || _e === void 0 ? void 0 : _e.unsupported) !== null && _f !== void 0 ? _f : []) {
1801
+ unsupportedCoverage.get(method).add(param);
1802
+ }
1803
+ }
1804
+ for (const [method, spec] of Object.entries(IN_MEMORY_SPEC.methods)) {
1805
+ expect((_g = supportedCoverage.get(method)) !== null && _g !== void 0 ? _g : new Set()).toEqual(new Set(spec.supportedParams));
1806
+ expect((_h = unsupportedCoverage.get(method)) !== null && _h !== void 0 ? _h : new Set()).toEqual(new Set((_j = spec.unsupportedParams) !== null && _j !== void 0 ? _j : []));
1807
+ }
1808
+ });
1809
+ test.each(differentialVectors)("vector $id", (vector) => __awaiter(void 0, void 0, void 0, function* () {
1810
+ const [local, memory] = yield Promise.all([
1811
+ runVector("local", vector),
1812
+ runVector("memory", vector),
1813
+ ]);
1814
+ expect(memory).toEqual(local);
1815
+ }));
1816
+ test.each(unsupportedParamVectors)("unsupported vector $id throws deterministic NotSupportedError in memory", (vector) => __awaiter(void 0, void 0, void 0, function* () {
1817
+ const result = yield runVector("memory", vector);
1818
+ expect(result.ok).toBe(false);
1819
+ if (result.ok)
1820
+ return;
1821
+ const [methodName, param] = vector.id
1822
+ .replace("unsupported.", "")
1823
+ .split(".");
1824
+ expect(result.error).toEqual({
1825
+ code: "NotSupportedError",
1826
+ message: `${param} is not supported in in-memory mode`,
1827
+ method: methodName,
1828
+ featurePath: `${methodName}.${param}`,
1829
+ reason: `${param} is not supported in in-memory mode.`,
1830
+ });
1831
+ }));
1832
+ });
1833
+ //# sourceMappingURL=conformance.test.js.map