@eresearchqut/ddb-repository 1.2.0 → 1.4.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.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,17 @@
1
+ # [1.4.0](https://github.com/eresearchqut/ddb-repository/compare/v1.3.0...v1.4.0) (2025-11-20)
2
+
3
+
4
+ ### Features
5
+
6
+ * Add test for the tracking of the consumed capacity and rename test class ([d113a5a](https://github.com/eresearchqut/ddb-repository/commit/d113a5a948a7dffc80e9d3a65a11203c2249ed26))
7
+
8
+ # [1.3.0](https://github.com/eresearchqut/ddb-repository/compare/v1.2.0...v1.3.0) (2025-11-20)
9
+
10
+
11
+ ### Features
12
+
13
+ * Add test for the tracking of the consumed capacity ([4a1b23c](https://github.com/eresearchqut/ddb-repository/commit/4a1b23cadadbbb02d78507ca9d34715219fc952c))
14
+
1
15
  # [1.2.0](https://github.com/eresearchqut/ddb-repository/compare/v1.1.0...v1.2.0) (2025-11-19)
2
16
 
3
17
 
@@ -27,7 +27,7 @@ var __asyncValues = (this && this.__asyncValues) || function (o) {
27
27
  function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
28
28
  };
29
29
  Object.defineProperty(exports, "__esModule", { value: true });
30
- exports.DynamoDbRepository = exports.mapFilterExpressions = exports.mapFilterExpression = exports.FilterOperator = void 0;
30
+ exports.DynamoDbRepository = exports.consumedCapacityMiddleware = exports.mapFilterExpressions = exports.mapFilterExpression = exports.FilterOperator = void 0;
31
31
  const client_dynamodb_1 = require("@aws-sdk/client-dynamodb");
32
32
  const util_dynamodb_1 = require("@aws-sdk/util-dynamodb");
33
33
  const lodash_1 = require("lodash");
@@ -79,6 +79,21 @@ const paginate = (array, pageSize) => {
79
79
  return acc;
80
80
  }, []);
81
81
  };
82
+ const consumedCapacityMiddleware = (consumedCapacityMiddlewareConfig) => (next, context) => (args) => __awaiter(void 0, void 0, void 0, function* () {
83
+ try {
84
+ const { input } = args;
85
+ const { ReturnConsumedCapacity } = input;
86
+ const response = yield next(args);
87
+ const { output } = response;
88
+ const consumedCapacity = (0, lodash_1.get)(output, "ConsumedCapacity");
89
+ yield consumedCapacityMiddlewareConfig.onConsumedCapacity({ ReturnConsumedCapacity, ConsumedCapacity: consumedCapacity });
90
+ return response;
91
+ }
92
+ catch (error) {
93
+ throw error;
94
+ }
95
+ });
96
+ exports.consumedCapacityMiddleware = consumedCapacityMiddleware;
82
97
  class DynamoDbRepository {
83
98
  constructor(dynamoDBClient, tableName, hashKey, rangKey, returnConsumedCapacity = client_dynamodb_1.ReturnConsumedCapacity.TOTAL) {
84
99
  this.dynamoDBClient = dynamoDBClient;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eresearchqut/ddb-repository",
3
- "version": "1.2.0",
3
+ "version": "1.4.0",
4
4
  "description": "",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -23,6 +23,7 @@
23
23
  "@semantic-release/git": "^10.0.1",
24
24
  "@semantic-release/github": "^12.0.2",
25
25
  "@semantic-release/npm": "^13.1.2",
26
+ "@smithy/types": "^4.9.0",
26
27
  "@testcontainers/localstack": "^10.15.0",
27
28
  "@types/jest": "^29.5.0",
28
29
  "@types/lodash": "^4.17.20",
@@ -1,6 +1,7 @@
1
1
  import {
2
2
  BatchGetItemCommand,
3
3
  BatchGetItemCommandInput,
4
+ ConsumedCapacity,
4
5
  DeleteItemCommand,
5
6
  DynamoDBClient,
6
7
  GetItemCommand,
@@ -11,7 +12,14 @@ import {
11
12
  UpdateItemCommand,
12
13
  } from "@aws-sdk/client-dynamodb";
13
14
  import {marshall, unmarshall} from "@aws-sdk/util-dynamodb";
14
- import {replace, uniqWith, isEqual, pickBy} from "lodash";
15
+ import {replace, uniqWith, isEqual, pickBy, get} from "lodash";
16
+ import {
17
+ HandlerExecutionContext,
18
+ InitializeHandler,
19
+ InitializeHandlerArguments,
20
+ InitializeHandlerOutput,
21
+ MetadataBearer
22
+ } from "@smithy/types";
15
23
 
16
24
  const expressionAttributeKey = (key: string) => replace(key, /-/g, "_");
17
25
 
@@ -119,6 +127,35 @@ const paginate = <T>(array: Array<T>, pageSize: number) => {
119
127
  }, [] as Array<Array<T>>);
120
128
  }
121
129
 
130
+ export interface ConsumedCapacityDetail {
131
+ ReturnConsumedCapacity: ReturnConsumedCapacity | undefined
132
+ ConsumedCapacity: ConsumedCapacity | ConsumedCapacity[] | undefined
133
+ }
134
+
135
+ export interface ConsumedCapacityMiddlewareConfig {
136
+ onConsumedCapacity: (consumedCapacity: ConsumedCapacityDetail) => Promise<unknown>;
137
+ }
138
+
139
+ export const consumedCapacityMiddleware =
140
+ (consumedCapacityMiddlewareConfig: ConsumedCapacityMiddlewareConfig) =>
141
+ <Output extends MetadataBearer = MetadataBearer>(
142
+ next: InitializeHandler<any, Output>,
143
+ context: HandlerExecutionContext
144
+ ): InitializeHandler<any, Output> =>
145
+ async (args: InitializeHandlerArguments<any>): Promise<InitializeHandlerOutput<Output>> => {
146
+ try {
147
+ const {input} = args;
148
+ const {ReturnConsumedCapacity} = input;
149
+ const response = await next(args);
150
+ const {output} = response;
151
+ const consumedCapacity = get(output, "ConsumedCapacity") as ConsumedCapacity | undefined;
152
+ await consumedCapacityMiddlewareConfig.onConsumedCapacity({ReturnConsumedCapacity, ConsumedCapacity: consumedCapacity});
153
+ return response;
154
+ } catch (error) {
155
+ throw error;
156
+ }
157
+ };
158
+
122
159
  export class DynamoDbRepository<K, T> {
123
160
 
124
161
  constructor(
@@ -1,7 +1,7 @@
1
1
 
2
2
  import { DynamoDBClient, CreateTableCommand, DescribeTableCommand } from "@aws-sdk/client-dynamodb";
3
3
  import { LocalstackContainer, StartedLocalStackContainer } from "@testcontainers/localstack";
4
- import {DynamoDbRepository, FilterOperator} from "../src";
4
+ import {ConsumedCapacityDetail, consumedCapacityMiddleware, DynamoDbRepository, FilterOperator} from "../src";
5
5
 
6
6
  describe('DynamoDbRepository Integration Tests', () => {
7
7
  let container: StartedLocalStackContainer;
@@ -12,6 +12,19 @@ describe('DynamoDbRepository Integration Tests', () => {
12
12
  const tableName = 'test-table';
13
13
  const compositeTableName = 'test-composite-table';
14
14
  const gsiTableName = 'test-gsi-table';
15
+ const consumedCapacityRegister = new Array<ConsumedCapacityDetail>() ;
16
+
17
+ const getConsumedCapacity = (consumedCapacity: ConsumedCapacityDetail) => {
18
+ if (Array.isArray(consumedCapacity.ConsumedCapacity)) {
19
+ return consumedCapacity.ConsumedCapacity
20
+ .reduce((total, capacity) => total + (capacity?.CapacityUnits || 0), 0);
21
+ }
22
+ return consumedCapacity.ConsumedCapacity?.CapacityUnits || 0;
23
+ }
24
+
25
+ const sumConsumedCapacity = () =>
26
+ consumedCapacityRegister.reduce((total, value) =>
27
+ total + getConsumedCapacity(value), 0);
15
28
 
16
29
  beforeAll(async () => {
17
30
  // Start LocalStack container with DynamoDB
@@ -28,7 +41,11 @@ describe('DynamoDbRepository Integration Tests', () => {
28
41
  },
29
42
  });
30
43
 
31
- // Create the test table with simple key
44
+ dynamoDBClient.middlewareStack
45
+ .add(consumedCapacityMiddleware({onConsumedCapacity: async (consumedCapacity) =>
46
+ consumedCapacityRegister.push(consumedCapacity)}));
47
+
48
+ // Create the test table with a simple key
32
49
  await dynamoDBClient.send(
33
50
  new CreateTableCommand({
34
51
  TableName: tableName,
@@ -42,7 +59,7 @@ describe('DynamoDbRepository Integration Tests', () => {
42
59
  })
43
60
  );
44
61
 
45
- // Create the test table with composite key (partition key + sort key)
62
+ // Create the test table with a composite key (partition key + sort key)
46
63
  await dynamoDBClient.send(
47
64
  new CreateTableCommand({
48
65
  TableName: compositeTableName,
@@ -108,6 +125,10 @@ describe('DynamoDbRepository Integration Tests', () => {
108
125
  gsiRepository = new DynamoDbRepository(dynamoDBClient, gsiTableName, "userId", "itemId");
109
126
  });
110
127
 
128
+ beforeEach(async () => {
129
+ consumedCapacityRegister.splice(0, consumedCapacityRegister.length);
130
+ });
131
+
111
132
  afterAll(async () => {
112
133
  // Clean up
113
134
  if (dynamoDBClient) {
@@ -122,6 +143,7 @@ describe('DynamoDbRepository Integration Tests', () => {
122
143
  it('should return undefined when item does not exist', async () => {
123
144
  const result = await repository.getItem({ id: 'non-existent' });
124
145
  expect(result).toBeUndefined();
146
+ expect(sumConsumedCapacity()).toEqual(0.5);
125
147
  });
126
148
 
127
149
  it('should return the item when it exists', async () => {
@@ -132,6 +154,7 @@ describe('DynamoDbRepository Integration Tests', () => {
132
154
  const result = await repository.getItem(key);
133
155
 
134
156
  expect(result).toEqual(record);
157
+ expect(sumConsumedCapacity()).toEqual(2);
135
158
  });
136
159
  });
137
160
 
@@ -143,6 +166,7 @@ describe('DynamoDbRepository Integration Tests', () => {
143
166
  const result = await repository.putItem(key, record);
144
167
 
145
168
  expect(result).toEqual(record);
169
+ expect(sumConsumedCapacity()).toEqual(1.5);
146
170
  });
147
171
  });
148
172
 
@@ -155,6 +179,7 @@ describe('DynamoDbRepository Integration Tests', () => {
155
179
  await repository.deleteItem(key);
156
180
  const afterDelete = await repository.getItem(key);
157
181
  expect(afterDelete).toBeUndefined();
182
+ expect(sumConsumedCapacity()).toEqual(2);
158
183
  });
159
184
  });
160
185
 
@@ -171,6 +196,7 @@ describe('DynamoDbRepository Integration Tests', () => {
171
196
  name: 'Updated Name',
172
197
  email: 'original@example.com'
173
198
  });
199
+ expect(sumConsumedCapacity()).toEqual(3);
174
200
  });
175
201
  });
176
202
 
@@ -183,6 +209,7 @@ describe('DynamoDbRepository Integration Tests', () => {
183
209
  { id: `batch-item-${i}`, name: `Item ${i}`, age: i * 10 }
184
210
  );
185
211
  }
212
+ consumedCapacityRegister.splice(0, consumedCapacityRegister.length);
186
213
  });
187
214
 
188
215
  it('should retrieve multiple items by keys', async () => {
@@ -203,6 +230,7 @@ describe('DynamoDbRepository Integration Tests', () => {
203
230
  expect.objectContaining({ id: 'batch-item-3', name: 'Item 3', age: 30 }),
204
231
  ])
205
232
  );
233
+ expect(sumConsumedCapacity()).toEqual(1.5);
206
234
  });
207
235
 
208
236
  it('should handle empty keys array', async () => {
@@ -210,6 +238,7 @@ describe('DynamoDbRepository Integration Tests', () => {
210
238
 
211
239
  expect(results).toBeDefined();
212
240
  expect(results?.length).toBe(0);
241
+ expect(sumConsumedCapacity()).toEqual(0);
213
242
  });
214
243
 
215
244
  it('should handle non-existent keys gracefully', async () => {
@@ -230,6 +259,7 @@ describe('DynamoDbRepository Integration Tests', () => {
230
259
  const ids = existingItems?.map(item => item.id);
231
260
  expect(ids).toContain('batch-item-1');
232
261
  expect(ids).toContain('batch-item-2');
262
+ expect(sumConsumedCapacity()).toEqual(1);
233
263
  });
234
264
 
235
265
  it('should handle batch size over 100 items (pagination)', async () => {
@@ -254,6 +284,7 @@ describe('DynamoDbRepository Integration Tests', () => {
254
284
  expect(item50).toBeDefined();
255
285
  expect(item100).toBeDefined();
256
286
  expect(item150).toBeDefined();
287
+ expect(sumConsumedCapacity()).toEqual(300);
257
288
  }, 60000);
258
289
 
259
290
  it('should retrieve composite key items', async () => {
@@ -288,6 +319,7 @@ describe('DynamoDbRepository Integration Tests', () => {
288
319
  expect.objectContaining({ userId: 'user-batch-2', itemId: 'item-c', price: 300 }),
289
320
  ])
290
321
  );
322
+ expect(sumConsumedCapacity()).toEqual(6);
291
323
  });
292
324
 
293
325
  it('should maintain order independence', async () => {
@@ -307,6 +339,7 @@ describe('DynamoDbRepository Integration Tests', () => {
307
339
  expect(ids).toContain('batch-item-1');
308
340
  expect(ids).toContain('batch-item-3');
309
341
  expect(ids).toContain('batch-item-5');
342
+ expect(sumConsumedCapacity()).toEqual(1.5);
310
343
  });
311
344
 
312
345
  it('should handle duplicate keys in input', async () => {
@@ -328,6 +361,7 @@ describe('DynamoDbRepository Integration Tests', () => {
328
361
 
329
362
  expect(item1Count).toBeGreaterThanOrEqual(1);
330
363
  expect(item2Count).toBeGreaterThanOrEqual(1);
364
+ expect(sumConsumedCapacity()).toEqual(1);
331
365
  });
332
366
 
333
367
  it('should retrieve all attributes for batched items', async () => {
@@ -347,6 +381,7 @@ describe('DynamoDbRepository Integration Tests', () => {
347
381
  age: 30,
348
382
  status: 'active'
349
383
  });
384
+ expect(sumConsumedCapacity()).toEqual(2);
350
385
  });
351
386
  });
352
387
 
@@ -365,7 +400,9 @@ describe('DynamoDbRepository Integration Tests', () => {
365
400
  name: 'Test Item',
366
401
  age: 25
367
402
  });
403
+ expect(sumConsumedCapacity()).toEqual(2);
368
404
  });
405
+
369
406
  });
370
407
 
371
408
  describe('with composite key (partition + sort)', () => {
@@ -383,6 +420,7 @@ describe('DynamoDbRepository Integration Tests', () => {
383
420
  { userId, itemId: 'item-3' },
384
421
  { userId, itemId: 'item-3', name: 'Item Three', category: 'electronics', price: 150 }
385
422
  );
423
+ consumedCapacityRegister.splice(0, consumedCapacityRegister.length);
386
424
  });
387
425
 
388
426
  it('should retrieve all items for a partition key', async () => {
@@ -402,7 +440,9 @@ describe('DynamoDbRepository Integration Tests', () => {
402
440
  expect(results).toBeDefined();
403
441
  expect(results?.length).toBe(2);
404
442
  expect(results?.every(item => item.category === 'electronics')).toBe(true);
443
+ expect(sumConsumedCapacity()).toEqual(0.5);
405
444
  });
445
+
406
446
  });
407
447
 
408
448
  describe('with GSI (Global Secondary Index)', () => {
@@ -468,6 +508,7 @@ describe('DynamoDbRepository Integration Tests', () => {
468
508
 
469
509
  // Wait for GSI to be consistent
470
510
  await new Promise(resolve => setTimeout(resolve, 2000));
511
+ consumedCapacityRegister.splice(0, consumedCapacityRegister.length);
471
512
  });
472
513
 
473
514
  it('should query items using GSI and fetch full items via batchGetItems', async () => {
@@ -478,6 +519,7 @@ describe('DynamoDbRepository Integration Tests', () => {
478
519
  expect(results).toBeDefined();
479
520
  // When using index, it queries GSI then uses batchGetItems to fetch full items
480
521
  expect(Array.isArray(results)).toBe(true);
522
+ expect(sumConsumedCapacity()).toEqual(2);
481
523
  });
482
524
 
483
525
  it('should query all items with specific status using GSI', async () => {
@@ -493,6 +535,7 @@ describe('DynamoDbRepository Integration Tests', () => {
493
535
  expect(item.status).toBe('active');
494
536
  });
495
537
  }
538
+ expect(sumConsumedCapacity()).toEqual(2);
496
539
  });
497
540
 
498
541
  it('should combine GSI query with filter expressions', async () => {
@@ -511,6 +554,7 @@ describe('DynamoDbRepository Integration Tests', () => {
511
554
  expect(item.category).toBe('electronics');
512
555
  });
513
556
  }
557
+ expect(sumConsumedCapacity()).toEqual(1.5);
514
558
  });
515
559
 
516
560
  it('should return full item attributes when querying via GSI', async () => {
@@ -532,6 +576,7 @@ describe('DynamoDbRepository Integration Tests', () => {
532
576
  expect(item).toHaveProperty('createdAt');
533
577
  }
534
578
  }
579
+ expect(sumConsumedCapacity()).toEqual(2);
535
580
  });
536
581
 
537
582
  it('should handle GSI query with multiple items', async () => {
@@ -550,6 +595,7 @@ describe('DynamoDbRepository Integration Tests', () => {
550
595
  expect(item).toHaveProperty('itemId');
551
596
  });
552
597
  }
598
+ expect(sumConsumedCapacity()).toEqual(2);
553
599
  });
554
600
 
555
601
  it('should respect projection when querying GSI', async () => {
@@ -568,6 +614,7 @@ describe('DynamoDbRepository Integration Tests', () => {
568
614
  expect(item).toHaveProperty('status');
569
615
  });
570
616
  }
617
+ expect(sumConsumedCapacity()).toEqual(2);
571
618
  });
572
619
 
573
620
  it('should handle empty results from GSI query', async () => {
@@ -578,6 +625,7 @@ describe('DynamoDbRepository Integration Tests', () => {
578
625
 
579
626
  expect(results).toBeDefined();
580
627
  expect(results?.length).toBe(0);
628
+ expect(sumConsumedCapacity()).toEqual(0);
581
629
  });
582
630
  });
583
631
 
@@ -601,6 +649,7 @@ describe('DynamoDbRepository Integration Tests', () => {
601
649
  }
602
650
 
603
651
  await new Promise(resolve => setTimeout(resolve, 2000));
652
+ consumedCapacityRegister.splice(0, consumedCapacityRegister.length);
604
653
  });
605
654
 
606
655
  it('should retrieve all items across multiple pages via GSI', async () => {
@@ -611,8 +660,10 @@ describe('DynamoDbRepository Integration Tests', () => {
611
660
 
612
661
  expect(results).toBeDefined();
613
662
  expect(Array.isArray(results)).toBe(true);
663
+ expect(sumConsumedCapacity()).toEqual(62);
614
664
  // Should handle pagination internally via batchGetItems
615
665
  }, 60000);
666
+
616
667
  });
617
668
  });
618
669
 
@@ -649,6 +700,7 @@ describe('DynamoDbRepository Integration Tests', () => {
649
700
  for (const data of testData) {
650
701
  await repository.putItem({ id: data.id }, data);
651
702
  }
703
+ consumedCapacityRegister.splice(0, consumedCapacityRegister.length);
652
704
  });
653
705
 
654
706
  describe('EQUALS operator', () => {
@@ -662,6 +714,7 @@ describe('DynamoDbRepository Integration Tests', () => {
662
714
 
663
715
  expect(results).toBeDefined();
664
716
  expect(results?.every(item => item.status === 'active')).toBe(true);
717
+ expect(sumConsumedCapacity()).toEqual(0.5);
665
718
  });
666
719
 
667
720
  it('should filter items with exact number match', async () => {
@@ -676,6 +729,7 @@ describe('DynamoDbRepository Integration Tests', () => {
676
729
  if (results && results.length > 0) {
677
730
  expect(results[0].age).toBe(30);
678
731
  }
732
+ expect(sumConsumedCapacity()).toEqual(0.5);
679
733
  });
680
734
 
681
735
  it('should return empty array when no match found', async () => {
@@ -688,6 +742,7 @@ describe('DynamoDbRepository Integration Tests', () => {
688
742
 
689
743
  expect(results).toBeDefined();
690
744
  expect(results?.length).toBe(0);
745
+ expect(sumConsumedCapacity()).toEqual(0.5);
691
746
  });
692
747
  });
693
748
 
@@ -704,6 +759,7 @@ describe('DynamoDbRepository Integration Tests', () => {
704
759
  if (results && results.length > 0) {
705
760
  expect(results[0].status).not.toBe('inactive');
706
761
  }
762
+ expect(sumConsumedCapacity()).toEqual(0.5);
707
763
  });
708
764
 
709
765
  it('should filter items not matching number value', async () => {
@@ -718,6 +774,7 @@ describe('DynamoDbRepository Integration Tests', () => {
718
774
  if (results && results.length > 0) {
719
775
  expect(results[0].age).not.toBe(25);
720
776
  }
777
+ expect(sumConsumedCapacity()).toEqual(0.5);
721
778
  });
722
779
  });
723
780
 
@@ -734,6 +791,7 @@ describe('DynamoDbRepository Integration Tests', () => {
734
791
  if (results && results.length > 0) {
735
792
  expect(results.every(item => item.age && item.age > 30)).toBe(true);
736
793
  }
794
+ expect(sumConsumedCapacity()).toEqual(0.5);
737
795
  });
738
796
 
739
797
  it('should filter items greater than decimal value', async () => {
@@ -748,6 +806,7 @@ describe('DynamoDbRepository Integration Tests', () => {
748
806
  if (results && results.length > 0) {
749
807
  expect(results.every(item => item.score && item.score > 89.0)).toBe(true);
750
808
  }
809
+ expect(sumConsumedCapacity()).toEqual(0.5);
751
810
  });
752
811
 
753
812
  it('should return empty array when no items are greater', async () => {
@@ -760,6 +819,7 @@ describe('DynamoDbRepository Integration Tests', () => {
760
819
 
761
820
  expect(results).toBeDefined();
762
821
  expect(results?.length).toBe(0);
822
+ expect(sumConsumedCapacity()).toEqual(0.5);
763
823
  });
764
824
  });
765
825
 
@@ -776,6 +836,7 @@ describe('DynamoDbRepository Integration Tests', () => {
776
836
  if (results && results.length > 0) {
777
837
  expect(results.every(item => item.age && item.age >= 30)).toBe(true);
778
838
  }
839
+ expect(sumConsumedCapacity()).toEqual(0.5);
779
840
  });
780
841
 
781
842
  it('should include items with exact value', async () => {
@@ -791,6 +852,7 @@ describe('DynamoDbRepository Integration Tests', () => {
791
852
  const exactMatch = results.find(item => item.score === 95.5);
792
853
  expect(exactMatch).toBeDefined();
793
854
  }
855
+ expect(sumConsumedCapacity()).toEqual(0.5);
794
856
  });
795
857
  });
796
858
 
@@ -807,6 +869,7 @@ describe('DynamoDbRepository Integration Tests', () => {
807
869
  if (results && results.length > 0) {
808
870
  expect(results.every(item => item.age && item.age < 30)).toBe(true);
809
871
  }
872
+ expect(sumConsumedCapacity()).toEqual(0.5);
810
873
  });
811
874
 
812
875
  it('should filter items less than decimal value', async () => {
@@ -821,6 +884,7 @@ describe('DynamoDbRepository Integration Tests', () => {
821
884
  if (results && results.length > 0) {
822
885
  expect(results.every(item => item.score && item.score < 75.0)).toBe(true);
823
886
  }
887
+ expect(sumConsumedCapacity()).toEqual(0.5);
824
888
  });
825
889
  });
826
890
 
@@ -837,6 +901,7 @@ describe('DynamoDbRepository Integration Tests', () => {
837
901
  if (results && results.length > 0) {
838
902
  expect(results.every(item => item.age && item.age <= 25)).toBe(true);
839
903
  }
904
+ expect(sumConsumedCapacity()).toEqual(0.5);
840
905
  });
841
906
 
842
907
  it('should include items with exact value', async () => {
@@ -852,6 +917,7 @@ describe('DynamoDbRepository Integration Tests', () => {
852
917
  const exactMatch = results.find(item => item.score === 90.0);
853
918
  expect(exactMatch).toBeDefined();
854
919
  }
920
+ expect(sumConsumedCapacity()).toEqual(0.5);
855
921
  });
856
922
  });
857
923
 
@@ -868,6 +934,7 @@ describe('DynamoDbRepository Integration Tests', () => {
868
934
  if (results && results.length > 0) {
869
935
  expect(results.every(item => ['active', 'pending'].includes(item.status || ''))).toBe(true);
870
936
  }
937
+ expect(sumConsumedCapacity()).toEqual(0.5);
871
938
  });
872
939
 
873
940
  it('should filter items with value in array of numbers', async () => {
@@ -882,6 +949,7 @@ describe('DynamoDbRepository Integration Tests', () => {
882
949
  if (results && results.length > 0) {
883
950
  expect(results.every(item => [25, 30, 35].includes(item.age || 0))).toBe(true);
884
951
  }
952
+ expect(sumConsumedCapacity()).toEqual(0.5);
885
953
  });
886
954
 
887
955
  it('should return empty array when value not in list', async () => {
@@ -894,6 +962,7 @@ describe('DynamoDbRepository Integration Tests', () => {
894
962
 
895
963
  expect(results).toBeDefined();
896
964
  expect(results?.length).toBe(0);
965
+ expect(sumConsumedCapacity()).toEqual(0.5);
897
966
  });
898
967
 
899
968
  it('should handle single value in array', async () => {
@@ -908,6 +977,7 @@ describe('DynamoDbRepository Integration Tests', () => {
908
977
  if (results && results.length > 0) {
909
978
  expect(results[0].status).toBe('active');
910
979
  }
980
+ expect(sumConsumedCapacity()).toEqual(0.5);
911
981
  });
912
982
  });
913
983
 
@@ -924,6 +994,7 @@ describe('DynamoDbRepository Integration Tests', () => {
924
994
  if (results && results.length > 0) {
925
995
  expect(results.every(item => item.age && item.age >= 25 && item.age <= 35)).toBe(true);
926
996
  }
997
+ expect(sumConsumedCapacity()).toEqual(0.5);
927
998
  });
928
999
 
929
1000
  it('should filter items with decimal value between range', async () => {
@@ -938,6 +1009,7 @@ describe('DynamoDbRepository Integration Tests', () => {
938
1009
  if (results && results.length > 0) {
939
1010
  expect(results.every(item => item.score && item.score >= 80.0 && item.score <= 90.0)).toBe(true);
940
1011
  }
1012
+ expect(sumConsumedCapacity()).toEqual(0.5);
941
1013
  });
942
1014
 
943
1015
  it('should include boundary values', async () => {
@@ -954,6 +1026,7 @@ describe('DynamoDbRepository Integration Tests', () => {
954
1026
  const hasUpperBound = results.some(item => item.age === 40);
955
1027
  expect(hasLowerBound || hasUpperBound).toBe(true);
956
1028
  }
1029
+ expect(sumConsumedCapacity()).toEqual(0.5);
957
1030
  });
958
1031
 
959
1032
  it('should return empty array when no values in range', async () => {
@@ -966,6 +1039,7 @@ describe('DynamoDbRepository Integration Tests', () => {
966
1039
 
967
1040
  expect(results).toBeDefined();
968
1041
  expect(results?.length).toBe(0);
1042
+ expect(sumConsumedCapacity()).toEqual(0.5);
969
1043
  });
970
1044
 
971
1045
  it('should filter items with string value between range (lexicographical)', async () => {
@@ -982,6 +1056,7 @@ describe('DynamoDbRepository Integration Tests', () => {
982
1056
  item.name && item.name >= 'Alice' && item.name <= 'Diana'
983
1057
  )).toBe(true);
984
1058
  }
1059
+ expect(sumConsumedCapacity()).toEqual(0.5);
985
1060
  });
986
1061
  });
987
1062
 
@@ -1021,6 +1096,7 @@ describe('DynamoDbRepository Integration Tests', () => {
1021
1096
  item.score && item.score >= 80.0
1022
1097
  )).toBe(true);
1023
1098
  }
1099
+ expect(sumConsumedCapacity()).toEqual(0.5);
1024
1100
  });
1025
1101
  });
1026
1102
 
@@ -1037,6 +1113,7 @@ describe('DynamoDbRepository Integration Tests', () => {
1037
1113
  if (results && results.length > 0) {
1038
1114
  expect(results.every(item => item.status !== 'inactive')).toBe(true);
1039
1115
  }
1116
+ expect(sumConsumedCapacity()).toEqual(0.5);
1040
1117
  });
1041
1118
 
1042
1119
  it('should negate IN operator', async () => {
@@ -1053,6 +1130,7 @@ describe('DynamoDbRepository Integration Tests', () => {
1053
1130
  !['active', 'pending'].includes(item.status || '')
1054
1131
  )).toBe(true);
1055
1132
  }
1133
+ expect(sumConsumedCapacity()).toEqual(0.5);
1056
1134
  });
1057
1135
 
1058
1136
  it('should negate BETWEEN operator', async () => {
@@ -1069,6 +1147,7 @@ describe('DynamoDbRepository Integration Tests', () => {
1069
1147
  item.age && (item.age < 25 || item.age > 35)
1070
1148
  )).toBe(true);
1071
1149
  }
1150
+ expect(sumConsumedCapacity()).toEqual(0.5);
1072
1151
  });
1073
1152
  });
1074
1153