@eresearchqut/ddb-repository 1.3.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,10 @@
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
+
1
8
  # [1.3.0](https://github.com/eresearchqut/ddb-repository/compare/v1.2.0...v1.3.0) (2025-11-20)
2
9
 
3
10
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eresearchqut/ddb-repository",
3
- "version": "1.3.0",
3
+ "version": "1.4.0",
4
4
  "description": "",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -129,7 +129,7 @@ const paginate = <T>(array: Array<T>, pageSize: number) => {
129
129
 
130
130
  export interface ConsumedCapacityDetail {
131
131
  ReturnConsumedCapacity: ReturnConsumedCapacity | undefined
132
- ConsumedCapacity: ConsumedCapacity | undefined
132
+ ConsumedCapacity: ConsumedCapacity | ConsumedCapacity[] | undefined
133
133
  }
134
134
 
135
135
  export interface ConsumedCapacityMiddlewareConfig {
@@ -14,9 +14,17 @@ describe('DynamoDbRepository Integration Tests', () => {
14
14
  const gsiTableName = 'test-gsi-table';
15
15
  const consumedCapacityRegister = new Array<ConsumedCapacityDetail>() ;
16
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
+
17
25
  const sumConsumedCapacity = () =>
18
26
  consumedCapacityRegister.reduce((total, value) =>
19
- total + (value.ConsumedCapacity?.CapacityUnits || 0), 0);
27
+ total + getConsumedCapacity(value), 0);
20
28
 
21
29
  beforeAll(async () => {
22
30
  // Start LocalStack container with DynamoDB
@@ -34,7 +42,8 @@ describe('DynamoDbRepository Integration Tests', () => {
34
42
  });
35
43
 
36
44
  dynamoDBClient.middlewareStack
37
- .add(consumedCapacityMiddleware({onConsumedCapacity: async (consumedCapacity) => consumedCapacityRegister.push(consumedCapacity)}));
45
+ .add(consumedCapacityMiddleware({onConsumedCapacity: async (consumedCapacity) =>
46
+ consumedCapacityRegister.push(consumedCapacity)}));
38
47
 
39
48
  // Create the test table with a simple key
40
49
  await dynamoDBClient.send(
@@ -116,7 +125,7 @@ describe('DynamoDbRepository Integration Tests', () => {
116
125
  gsiRepository = new DynamoDbRepository(dynamoDBClient, gsiTableName, "userId", "itemId");
117
126
  });
118
127
 
119
- afterEach(async () => {
128
+ beforeEach(async () => {
120
129
  consumedCapacityRegister.splice(0, consumedCapacityRegister.length);
121
130
  });
122
131
 
@@ -187,6 +196,7 @@ describe('DynamoDbRepository Integration Tests', () => {
187
196
  name: 'Updated Name',
188
197
  email: 'original@example.com'
189
198
  });
199
+ expect(sumConsumedCapacity()).toEqual(3);
190
200
  });
191
201
  });
192
202
 
@@ -199,6 +209,7 @@ describe('DynamoDbRepository Integration Tests', () => {
199
209
  { id: `batch-item-${i}`, name: `Item ${i}`, age: i * 10 }
200
210
  );
201
211
  }
212
+ consumedCapacityRegister.splice(0, consumedCapacityRegister.length);
202
213
  });
203
214
 
204
215
  it('should retrieve multiple items by keys', async () => {
@@ -219,6 +230,7 @@ describe('DynamoDbRepository Integration Tests', () => {
219
230
  expect.objectContaining({ id: 'batch-item-3', name: 'Item 3', age: 30 }),
220
231
  ])
221
232
  );
233
+ expect(sumConsumedCapacity()).toEqual(1.5);
222
234
  });
223
235
 
224
236
  it('should handle empty keys array', async () => {
@@ -226,6 +238,7 @@ describe('DynamoDbRepository Integration Tests', () => {
226
238
 
227
239
  expect(results).toBeDefined();
228
240
  expect(results?.length).toBe(0);
241
+ expect(sumConsumedCapacity()).toEqual(0);
229
242
  });
230
243
 
231
244
  it('should handle non-existent keys gracefully', async () => {
@@ -246,6 +259,7 @@ describe('DynamoDbRepository Integration Tests', () => {
246
259
  const ids = existingItems?.map(item => item.id);
247
260
  expect(ids).toContain('batch-item-1');
248
261
  expect(ids).toContain('batch-item-2');
262
+ expect(sumConsumedCapacity()).toEqual(1);
249
263
  });
250
264
 
251
265
  it('should handle batch size over 100 items (pagination)', async () => {
@@ -270,6 +284,7 @@ describe('DynamoDbRepository Integration Tests', () => {
270
284
  expect(item50).toBeDefined();
271
285
  expect(item100).toBeDefined();
272
286
  expect(item150).toBeDefined();
287
+ expect(sumConsumedCapacity()).toEqual(300);
273
288
  }, 60000);
274
289
 
275
290
  it('should retrieve composite key items', async () => {
@@ -304,6 +319,7 @@ describe('DynamoDbRepository Integration Tests', () => {
304
319
  expect.objectContaining({ userId: 'user-batch-2', itemId: 'item-c', price: 300 }),
305
320
  ])
306
321
  );
322
+ expect(sumConsumedCapacity()).toEqual(6);
307
323
  });
308
324
 
309
325
  it('should maintain order independence', async () => {
@@ -323,6 +339,7 @@ describe('DynamoDbRepository Integration Tests', () => {
323
339
  expect(ids).toContain('batch-item-1');
324
340
  expect(ids).toContain('batch-item-3');
325
341
  expect(ids).toContain('batch-item-5');
342
+ expect(sumConsumedCapacity()).toEqual(1.5);
326
343
  });
327
344
 
328
345
  it('should handle duplicate keys in input', async () => {
@@ -344,6 +361,7 @@ describe('DynamoDbRepository Integration Tests', () => {
344
361
 
345
362
  expect(item1Count).toBeGreaterThanOrEqual(1);
346
363
  expect(item2Count).toBeGreaterThanOrEqual(1);
364
+ expect(sumConsumedCapacity()).toEqual(1);
347
365
  });
348
366
 
349
367
  it('should retrieve all attributes for batched items', async () => {
@@ -363,6 +381,7 @@ describe('DynamoDbRepository Integration Tests', () => {
363
381
  age: 30,
364
382
  status: 'active'
365
383
  });
384
+ expect(sumConsumedCapacity()).toEqual(2);
366
385
  });
367
386
  });
368
387
 
@@ -381,7 +400,9 @@ describe('DynamoDbRepository Integration Tests', () => {
381
400
  name: 'Test Item',
382
401
  age: 25
383
402
  });
403
+ expect(sumConsumedCapacity()).toEqual(2);
384
404
  });
405
+
385
406
  });
386
407
 
387
408
  describe('with composite key (partition + sort)', () => {
@@ -399,6 +420,7 @@ describe('DynamoDbRepository Integration Tests', () => {
399
420
  { userId, itemId: 'item-3' },
400
421
  { userId, itemId: 'item-3', name: 'Item Three', category: 'electronics', price: 150 }
401
422
  );
423
+ consumedCapacityRegister.splice(0, consumedCapacityRegister.length);
402
424
  });
403
425
 
404
426
  it('should retrieve all items for a partition key', async () => {
@@ -418,7 +440,9 @@ describe('DynamoDbRepository Integration Tests', () => {
418
440
  expect(results).toBeDefined();
419
441
  expect(results?.length).toBe(2);
420
442
  expect(results?.every(item => item.category === 'electronics')).toBe(true);
443
+ expect(sumConsumedCapacity()).toEqual(0.5);
421
444
  });
445
+
422
446
  });
423
447
 
424
448
  describe('with GSI (Global Secondary Index)', () => {
@@ -484,6 +508,7 @@ describe('DynamoDbRepository Integration Tests', () => {
484
508
 
485
509
  // Wait for GSI to be consistent
486
510
  await new Promise(resolve => setTimeout(resolve, 2000));
511
+ consumedCapacityRegister.splice(0, consumedCapacityRegister.length);
487
512
  });
488
513
 
489
514
  it('should query items using GSI and fetch full items via batchGetItems', async () => {
@@ -494,6 +519,7 @@ describe('DynamoDbRepository Integration Tests', () => {
494
519
  expect(results).toBeDefined();
495
520
  // When using index, it queries GSI then uses batchGetItems to fetch full items
496
521
  expect(Array.isArray(results)).toBe(true);
522
+ expect(sumConsumedCapacity()).toEqual(2);
497
523
  });
498
524
 
499
525
  it('should query all items with specific status using GSI', async () => {
@@ -509,6 +535,7 @@ describe('DynamoDbRepository Integration Tests', () => {
509
535
  expect(item.status).toBe('active');
510
536
  });
511
537
  }
538
+ expect(sumConsumedCapacity()).toEqual(2);
512
539
  });
513
540
 
514
541
  it('should combine GSI query with filter expressions', async () => {
@@ -527,6 +554,7 @@ describe('DynamoDbRepository Integration Tests', () => {
527
554
  expect(item.category).toBe('electronics');
528
555
  });
529
556
  }
557
+ expect(sumConsumedCapacity()).toEqual(1.5);
530
558
  });
531
559
 
532
560
  it('should return full item attributes when querying via GSI', async () => {
@@ -548,6 +576,7 @@ describe('DynamoDbRepository Integration Tests', () => {
548
576
  expect(item).toHaveProperty('createdAt');
549
577
  }
550
578
  }
579
+ expect(sumConsumedCapacity()).toEqual(2);
551
580
  });
552
581
 
553
582
  it('should handle GSI query with multiple items', async () => {
@@ -566,6 +595,7 @@ describe('DynamoDbRepository Integration Tests', () => {
566
595
  expect(item).toHaveProperty('itemId');
567
596
  });
568
597
  }
598
+ expect(sumConsumedCapacity()).toEqual(2);
569
599
  });
570
600
 
571
601
  it('should respect projection when querying GSI', async () => {
@@ -584,6 +614,7 @@ describe('DynamoDbRepository Integration Tests', () => {
584
614
  expect(item).toHaveProperty('status');
585
615
  });
586
616
  }
617
+ expect(sumConsumedCapacity()).toEqual(2);
587
618
  });
588
619
 
589
620
  it('should handle empty results from GSI query', async () => {
@@ -594,6 +625,7 @@ describe('DynamoDbRepository Integration Tests', () => {
594
625
 
595
626
  expect(results).toBeDefined();
596
627
  expect(results?.length).toBe(0);
628
+ expect(sumConsumedCapacity()).toEqual(0);
597
629
  });
598
630
  });
599
631
 
@@ -617,6 +649,7 @@ describe('DynamoDbRepository Integration Tests', () => {
617
649
  }
618
650
 
619
651
  await new Promise(resolve => setTimeout(resolve, 2000));
652
+ consumedCapacityRegister.splice(0, consumedCapacityRegister.length);
620
653
  });
621
654
 
622
655
  it('should retrieve all items across multiple pages via GSI', async () => {
@@ -627,8 +660,10 @@ describe('DynamoDbRepository Integration Tests', () => {
627
660
 
628
661
  expect(results).toBeDefined();
629
662
  expect(Array.isArray(results)).toBe(true);
663
+ expect(sumConsumedCapacity()).toEqual(62);
630
664
  // Should handle pagination internally via batchGetItems
631
665
  }, 60000);
666
+
632
667
  });
633
668
  });
634
669
 
@@ -665,6 +700,7 @@ describe('DynamoDbRepository Integration Tests', () => {
665
700
  for (const data of testData) {
666
701
  await repository.putItem({ id: data.id }, data);
667
702
  }
703
+ consumedCapacityRegister.splice(0, consumedCapacityRegister.length);
668
704
  });
669
705
 
670
706
  describe('EQUALS operator', () => {
@@ -678,6 +714,7 @@ describe('DynamoDbRepository Integration Tests', () => {
678
714
 
679
715
  expect(results).toBeDefined();
680
716
  expect(results?.every(item => item.status === 'active')).toBe(true);
717
+ expect(sumConsumedCapacity()).toEqual(0.5);
681
718
  });
682
719
 
683
720
  it('should filter items with exact number match', async () => {
@@ -692,6 +729,7 @@ describe('DynamoDbRepository Integration Tests', () => {
692
729
  if (results && results.length > 0) {
693
730
  expect(results[0].age).toBe(30);
694
731
  }
732
+ expect(sumConsumedCapacity()).toEqual(0.5);
695
733
  });
696
734
 
697
735
  it('should return empty array when no match found', async () => {
@@ -704,6 +742,7 @@ describe('DynamoDbRepository Integration Tests', () => {
704
742
 
705
743
  expect(results).toBeDefined();
706
744
  expect(results?.length).toBe(0);
745
+ expect(sumConsumedCapacity()).toEqual(0.5);
707
746
  });
708
747
  });
709
748
 
@@ -720,6 +759,7 @@ describe('DynamoDbRepository Integration Tests', () => {
720
759
  if (results && results.length > 0) {
721
760
  expect(results[0].status).not.toBe('inactive');
722
761
  }
762
+ expect(sumConsumedCapacity()).toEqual(0.5);
723
763
  });
724
764
 
725
765
  it('should filter items not matching number value', async () => {
@@ -734,6 +774,7 @@ describe('DynamoDbRepository Integration Tests', () => {
734
774
  if (results && results.length > 0) {
735
775
  expect(results[0].age).not.toBe(25);
736
776
  }
777
+ expect(sumConsumedCapacity()).toEqual(0.5);
737
778
  });
738
779
  });
739
780
 
@@ -750,6 +791,7 @@ describe('DynamoDbRepository Integration Tests', () => {
750
791
  if (results && results.length > 0) {
751
792
  expect(results.every(item => item.age && item.age > 30)).toBe(true);
752
793
  }
794
+ expect(sumConsumedCapacity()).toEqual(0.5);
753
795
  });
754
796
 
755
797
  it('should filter items greater than decimal value', async () => {
@@ -764,6 +806,7 @@ describe('DynamoDbRepository Integration Tests', () => {
764
806
  if (results && results.length > 0) {
765
807
  expect(results.every(item => item.score && item.score > 89.0)).toBe(true);
766
808
  }
809
+ expect(sumConsumedCapacity()).toEqual(0.5);
767
810
  });
768
811
 
769
812
  it('should return empty array when no items are greater', async () => {
@@ -776,6 +819,7 @@ describe('DynamoDbRepository Integration Tests', () => {
776
819
 
777
820
  expect(results).toBeDefined();
778
821
  expect(results?.length).toBe(0);
822
+ expect(sumConsumedCapacity()).toEqual(0.5);
779
823
  });
780
824
  });
781
825
 
@@ -792,6 +836,7 @@ describe('DynamoDbRepository Integration Tests', () => {
792
836
  if (results && results.length > 0) {
793
837
  expect(results.every(item => item.age && item.age >= 30)).toBe(true);
794
838
  }
839
+ expect(sumConsumedCapacity()).toEqual(0.5);
795
840
  });
796
841
 
797
842
  it('should include items with exact value', async () => {
@@ -807,6 +852,7 @@ describe('DynamoDbRepository Integration Tests', () => {
807
852
  const exactMatch = results.find(item => item.score === 95.5);
808
853
  expect(exactMatch).toBeDefined();
809
854
  }
855
+ expect(sumConsumedCapacity()).toEqual(0.5);
810
856
  });
811
857
  });
812
858
 
@@ -823,6 +869,7 @@ describe('DynamoDbRepository Integration Tests', () => {
823
869
  if (results && results.length > 0) {
824
870
  expect(results.every(item => item.age && item.age < 30)).toBe(true);
825
871
  }
872
+ expect(sumConsumedCapacity()).toEqual(0.5);
826
873
  });
827
874
 
828
875
  it('should filter items less than decimal value', async () => {
@@ -837,6 +884,7 @@ describe('DynamoDbRepository Integration Tests', () => {
837
884
  if (results && results.length > 0) {
838
885
  expect(results.every(item => item.score && item.score < 75.0)).toBe(true);
839
886
  }
887
+ expect(sumConsumedCapacity()).toEqual(0.5);
840
888
  });
841
889
  });
842
890
 
@@ -853,6 +901,7 @@ describe('DynamoDbRepository Integration Tests', () => {
853
901
  if (results && results.length > 0) {
854
902
  expect(results.every(item => item.age && item.age <= 25)).toBe(true);
855
903
  }
904
+ expect(sumConsumedCapacity()).toEqual(0.5);
856
905
  });
857
906
 
858
907
  it('should include items with exact value', async () => {
@@ -868,6 +917,7 @@ describe('DynamoDbRepository Integration Tests', () => {
868
917
  const exactMatch = results.find(item => item.score === 90.0);
869
918
  expect(exactMatch).toBeDefined();
870
919
  }
920
+ expect(sumConsumedCapacity()).toEqual(0.5);
871
921
  });
872
922
  });
873
923
 
@@ -884,6 +934,7 @@ describe('DynamoDbRepository Integration Tests', () => {
884
934
  if (results && results.length > 0) {
885
935
  expect(results.every(item => ['active', 'pending'].includes(item.status || ''))).toBe(true);
886
936
  }
937
+ expect(sumConsumedCapacity()).toEqual(0.5);
887
938
  });
888
939
 
889
940
  it('should filter items with value in array of numbers', async () => {
@@ -898,6 +949,7 @@ describe('DynamoDbRepository Integration Tests', () => {
898
949
  if (results && results.length > 0) {
899
950
  expect(results.every(item => [25, 30, 35].includes(item.age || 0))).toBe(true);
900
951
  }
952
+ expect(sumConsumedCapacity()).toEqual(0.5);
901
953
  });
902
954
 
903
955
  it('should return empty array when value not in list', async () => {
@@ -910,6 +962,7 @@ describe('DynamoDbRepository Integration Tests', () => {
910
962
 
911
963
  expect(results).toBeDefined();
912
964
  expect(results?.length).toBe(0);
965
+ expect(sumConsumedCapacity()).toEqual(0.5);
913
966
  });
914
967
 
915
968
  it('should handle single value in array', async () => {
@@ -924,6 +977,7 @@ describe('DynamoDbRepository Integration Tests', () => {
924
977
  if (results && results.length > 0) {
925
978
  expect(results[0].status).toBe('active');
926
979
  }
980
+ expect(sumConsumedCapacity()).toEqual(0.5);
927
981
  });
928
982
  });
929
983
 
@@ -940,6 +994,7 @@ describe('DynamoDbRepository Integration Tests', () => {
940
994
  if (results && results.length > 0) {
941
995
  expect(results.every(item => item.age && item.age >= 25 && item.age <= 35)).toBe(true);
942
996
  }
997
+ expect(sumConsumedCapacity()).toEqual(0.5);
943
998
  });
944
999
 
945
1000
  it('should filter items with decimal value between range', async () => {
@@ -954,6 +1009,7 @@ describe('DynamoDbRepository Integration Tests', () => {
954
1009
  if (results && results.length > 0) {
955
1010
  expect(results.every(item => item.score && item.score >= 80.0 && item.score <= 90.0)).toBe(true);
956
1011
  }
1012
+ expect(sumConsumedCapacity()).toEqual(0.5);
957
1013
  });
958
1014
 
959
1015
  it('should include boundary values', async () => {
@@ -970,6 +1026,7 @@ describe('DynamoDbRepository Integration Tests', () => {
970
1026
  const hasUpperBound = results.some(item => item.age === 40);
971
1027
  expect(hasLowerBound || hasUpperBound).toBe(true);
972
1028
  }
1029
+ expect(sumConsumedCapacity()).toEqual(0.5);
973
1030
  });
974
1031
 
975
1032
  it('should return empty array when no values in range', async () => {
@@ -982,6 +1039,7 @@ describe('DynamoDbRepository Integration Tests', () => {
982
1039
 
983
1040
  expect(results).toBeDefined();
984
1041
  expect(results?.length).toBe(0);
1042
+ expect(sumConsumedCapacity()).toEqual(0.5);
985
1043
  });
986
1044
 
987
1045
  it('should filter items with string value between range (lexicographical)', async () => {
@@ -998,6 +1056,7 @@ describe('DynamoDbRepository Integration Tests', () => {
998
1056
  item.name && item.name >= 'Alice' && item.name <= 'Diana'
999
1057
  )).toBe(true);
1000
1058
  }
1059
+ expect(sumConsumedCapacity()).toEqual(0.5);
1001
1060
  });
1002
1061
  });
1003
1062
 
@@ -1037,6 +1096,7 @@ describe('DynamoDbRepository Integration Tests', () => {
1037
1096
  item.score && item.score >= 80.0
1038
1097
  )).toBe(true);
1039
1098
  }
1099
+ expect(sumConsumedCapacity()).toEqual(0.5);
1040
1100
  });
1041
1101
  });
1042
1102
 
@@ -1053,6 +1113,7 @@ describe('DynamoDbRepository Integration Tests', () => {
1053
1113
  if (results && results.length > 0) {
1054
1114
  expect(results.every(item => item.status !== 'inactive')).toBe(true);
1055
1115
  }
1116
+ expect(sumConsumedCapacity()).toEqual(0.5);
1056
1117
  });
1057
1118
 
1058
1119
  it('should negate IN operator', async () => {
@@ -1069,6 +1130,7 @@ describe('DynamoDbRepository Integration Tests', () => {
1069
1130
  !['active', 'pending'].includes(item.status || '')
1070
1131
  )).toBe(true);
1071
1132
  }
1133
+ expect(sumConsumedCapacity()).toEqual(0.5);
1072
1134
  });
1073
1135
 
1074
1136
  it('should negate BETWEEN operator', async () => {
@@ -1085,6 +1147,7 @@ describe('DynamoDbRepository Integration Tests', () => {
1085
1147
  item.age && (item.age < 25 || item.age > 35)
1086
1148
  )).toBe(true);
1087
1149
  }
1150
+ expect(sumConsumedCapacity()).toEqual(0.5);
1088
1151
  });
1089
1152
  });
1090
1153