@accounter/server 0.0.9-alpha-20251217093036-7168648b507d62946aa287af4ea690b73b077b2d → 0.0.9-alpha-20251217131153-65f961a4072436d7f1042ea8ea4d96534cb3650e

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 (69) hide show
  1. package/CHANGELOG.md +16 -8
  2. package/dist/server/src/modules/charges-matcher/__tests__/auto-match-integration.test.js +45 -122
  3. package/dist/server/src/modules/charges-matcher/__tests__/auto-match-integration.test.js.map +1 -1
  4. package/dist/server/src/modules/charges-matcher/__tests__/auto-match.test.js +45 -29
  5. package/dist/server/src/modules/charges-matcher/__tests__/auto-match.test.js.map +1 -1
  6. package/dist/server/src/modules/charges-matcher/__tests__/charge-validator.test.js +2 -11
  7. package/dist/server/src/modules/charges-matcher/__tests__/charge-validator.test.js.map +1 -1
  8. package/dist/server/src/modules/charges-matcher/__tests__/date-confidence.test.js +25 -0
  9. package/dist/server/src/modules/charges-matcher/__tests__/date-confidence.test.js.map +1 -1
  10. package/dist/server/src/modules/charges-matcher/__tests__/document-aggregator.test.js.map +1 -1
  11. package/dist/server/src/modules/charges-matcher/__tests__/document-amount.test.js +65 -64
  12. package/dist/server/src/modules/charges-matcher/__tests__/document-amount.test.js.map +1 -1
  13. package/dist/server/src/modules/charges-matcher/__tests__/match-scorer.test.js +494 -60
  14. package/dist/server/src/modules/charges-matcher/__tests__/match-scorer.test.js.map +1 -1
  15. package/dist/server/src/modules/charges-matcher/__tests__/single-match-integration.test.js +34 -98
  16. package/dist/server/src/modules/charges-matcher/__tests__/single-match-integration.test.js.map +1 -1
  17. package/dist/server/src/modules/charges-matcher/__tests__/single-match.test.js +79 -59
  18. package/dist/server/src/modules/charges-matcher/__tests__/single-match.test.js.map +1 -1
  19. package/dist/server/src/modules/charges-matcher/helpers/charge-validator.helper.js +6 -4
  20. package/dist/server/src/modules/charges-matcher/helpers/charge-validator.helper.js.map +1 -1
  21. package/dist/server/src/modules/charges-matcher/helpers/date-confidence.helper.d.ts +9 -2
  22. package/dist/server/src/modules/charges-matcher/helpers/date-confidence.helper.js +24 -2
  23. package/dist/server/src/modules/charges-matcher/helpers/date-confidence.helper.js.map +1 -1
  24. package/dist/server/src/modules/charges-matcher/helpers/document-amount.helper.d.ts +1 -4
  25. package/dist/server/src/modules/charges-matcher/helpers/document-amount.helper.js +2 -1
  26. package/dist/server/src/modules/charges-matcher/helpers/document-amount.helper.js.map +1 -1
  27. package/dist/server/src/modules/charges-matcher/index.d.ts +0 -1
  28. package/dist/server/src/modules/charges-matcher/index.js +0 -1
  29. package/dist/server/src/modules/charges-matcher/index.js.map +1 -1
  30. package/dist/server/src/modules/charges-matcher/providers/auto-match.provider.d.ts +2 -1
  31. package/dist/server/src/modules/charges-matcher/providers/auto-match.provider.js +2 -2
  32. package/dist/server/src/modules/charges-matcher/providers/auto-match.provider.js.map +1 -1
  33. package/dist/server/src/modules/charges-matcher/providers/charges-matcher.provider.js +2 -2
  34. package/dist/server/src/modules/charges-matcher/providers/charges-matcher.provider.js.map +1 -1
  35. package/dist/server/src/modules/charges-matcher/providers/document-aggregator.d.ts +4 -5
  36. package/dist/server/src/modules/charges-matcher/providers/document-aggregator.js +5 -4
  37. package/dist/server/src/modules/charges-matcher/providers/document-aggregator.js.map +1 -1
  38. package/dist/server/src/modules/charges-matcher/providers/match-scorer.provider.d.ts +5 -3
  39. package/dist/server/src/modules/charges-matcher/providers/match-scorer.provider.js +70 -13
  40. package/dist/server/src/modules/charges-matcher/providers/match-scorer.provider.js.map +1 -1
  41. package/dist/server/src/modules/charges-matcher/providers/single-match.provider.d.ts +4 -2
  42. package/dist/server/src/modules/charges-matcher/providers/single-match.provider.js +15 -7
  43. package/dist/server/src/modules/charges-matcher/providers/single-match.provider.js.map +1 -1
  44. package/dist/server/src/modules/charges-matcher/providers/transaction-aggregator.d.ts +1 -1
  45. package/dist/server/src/modules/charges-matcher/types.d.ts +2 -4
  46. package/dist/server/src/modules/charges-matcher/types.js.map +1 -1
  47. package/package.json +2 -2
  48. package/src/modules/charges-matcher/README.md +14 -3
  49. package/src/modules/charges-matcher/__tests__/auto-match-integration.test.ts +52 -100
  50. package/src/modules/charges-matcher/__tests__/auto-match.test.ts +51 -29
  51. package/src/modules/charges-matcher/__tests__/charge-validator.test.ts +2 -13
  52. package/src/modules/charges-matcher/__tests__/date-confidence.test.ts +29 -0
  53. package/src/modules/charges-matcher/__tests__/document-aggregator.test.ts +0 -1
  54. package/src/modules/charges-matcher/__tests__/document-amount.test.ts +66 -65
  55. package/src/modules/charges-matcher/__tests__/match-scorer.test.ts +552 -60
  56. package/src/modules/charges-matcher/__tests__/single-match-integration.test.ts +43 -73
  57. package/src/modules/charges-matcher/__tests__/single-match.test.ts +81 -59
  58. package/src/modules/charges-matcher/documentation/SPEC.md +276 -4
  59. package/src/modules/charges-matcher/helpers/charge-validator.helper.ts +7 -5
  60. package/src/modules/charges-matcher/helpers/date-confidence.helper.ts +32 -2
  61. package/src/modules/charges-matcher/helpers/document-amount.helper.ts +2 -12
  62. package/src/modules/charges-matcher/index.ts +0 -1
  63. package/src/modules/charges-matcher/providers/auto-match.provider.ts +5 -3
  64. package/src/modules/charges-matcher/providers/charges-matcher.provider.ts +8 -2
  65. package/src/modules/charges-matcher/providers/document-aggregator.ts +12 -11
  66. package/src/modules/charges-matcher/providers/match-scorer.provider.ts +97 -17
  67. package/src/modules/charges-matcher/providers/single-match.provider.ts +21 -8
  68. package/src/modules/charges-matcher/providers/transaction-aggregator.ts +1 -1
  69. package/src/modules/charges-matcher/types.ts +2 -5
@@ -1,15 +1,37 @@
1
- import { describe, expect, it } from 'vitest';
1
+ import { describe, expect, it, vi } from 'vitest';
2
+ import type { Injector } from 'graphql-modules';
2
3
  import type { Document, DocumentCharge, Transaction, TransactionCharge } from '../types.js';
3
4
  import {
4
5
  findMatches,
5
6
  } from '../providers/single-match.provider.js';
6
7
  import { createMockTransaction, createMockDocument } from './test-helpers.js';
7
8
 
9
+ // Mock DI system and ClientsProvider
10
+ vi.mock('../../financial-entities/providers/clients.provider.js', () => ({
11
+ ClientsProvider: class {},
12
+ }));
13
+
8
14
  // Test user and business IDs
9
15
  const USER_ID = 'user-123';
10
16
  const BUSINESS_A = 'business-a';
11
17
  const BUSINESS_B = 'business-b';
12
18
 
19
+ // Create a mock injector for testing
20
+ const createMockInjector = () => ({
21
+ get: vi.fn((token: {name: string}) => {
22
+ if (token.name === 'ClientsProvider')
23
+ return {
24
+ getClientByIdLoader: {
25
+ load: (businessId: string) => {
26
+ const isRegisteredClient = businessId.startsWith('client-');
27
+ return Promise.resolve(isRegisteredClient ? { id: businessId } : null);
28
+ },
29
+ },
30
+ };
31
+ return null;
32
+ }),
33
+ }) as Injector;
34
+
13
35
  // Helper to create transaction charge
14
36
  function createTxCharge(
15
37
  chargeId: string,
@@ -35,7 +57,7 @@ function createDocCharge(
35
57
  describe('Single-Match Provider', () => {
36
58
  describe('findMatches', () => {
37
59
  describe('Valid Transaction Charge → Finds Document Matches', () => {
38
- it('should find matching document charges for transaction charge', () => {
60
+ it('should find matching document charges for transaction charge', async () => {
39
61
  const sourceCharge = createTxCharge('tx-charge-1', [
40
62
  createMockTransaction({
41
63
  amount: "100",
@@ -61,7 +83,7 @@ describe('Single-Match Provider', () => {
61
83
  ]),
62
84
  ];
63
85
 
64
- const results = findMatches(sourceCharge, candidateCharges, USER_ID);
86
+ const results = await findMatches(sourceCharge, candidateCharges, USER_ID, createMockInjector());
65
87
 
66
88
  expect(results).toHaveLength(2);
67
89
  expect(results[0].chargeId).toBe('doc-charge-1'); // Perfect match
@@ -70,7 +92,7 @@ describe('Single-Match Provider', () => {
70
92
  expect(results[1].confidenceScore).toBeLessThan(0.95);
71
93
  });
72
94
 
73
- it('should exclude transaction candidates when source is transaction', () => {
95
+ it('should exclude transaction candidates when source is transaction', async () => {
74
96
  const sourceCharge = createTxCharge('tx-charge-1', [createMockTransaction()]);
75
97
 
76
98
  const candidateCharges = [
@@ -78,7 +100,7 @@ describe('Single-Match Provider', () => {
78
100
  createDocCharge('doc-charge-1', [createMockDocument()]), // Should be included
79
101
  ];
80
102
 
81
- const results = findMatches(sourceCharge, candidateCharges, USER_ID);
103
+ const results = await findMatches(sourceCharge, candidateCharges, USER_ID, createMockInjector());
82
104
 
83
105
  expect(results).toHaveLength(1);
84
106
  expect(results[0].chargeId).toBe('doc-charge-1');
@@ -86,7 +108,7 @@ describe('Single-Match Provider', () => {
86
108
  });
87
109
 
88
110
  describe('Valid Document Charge → Finds Transaction Matches', () => {
89
- it('should find matching transaction charges for document charge', () => {
111
+ it('should find matching transaction charges for document charge', async () => {
90
112
  const sourceCharge = createDocCharge('doc-charge-1', [
91
113
  createMockDocument({
92
114
  total_amount: 100,
@@ -112,14 +134,14 @@ describe('Single-Match Provider', () => {
112
134
  ]),
113
135
  ];
114
136
 
115
- const results = findMatches(sourceCharge, candidateCharges, USER_ID);
137
+ const results = await findMatches(sourceCharge, candidateCharges, USER_ID, createMockInjector());
116
138
 
117
139
  expect(results).toHaveLength(2);
118
140
  expect(results[0].chargeId).toBe('tx-charge-1'); // Perfect match
119
141
  expect(results[1].chargeId).toBe('tx-charge-2'); // Partial match
120
142
  });
121
143
 
122
- it('should exclude document candidates when source is document', () => {
144
+ it('should exclude document candidates when source is document', async () => {
123
145
  const sourceCharge = createDocCharge('doc-charge-1', [createMockDocument()]);
124
146
 
125
147
  const candidateCharges = [
@@ -127,7 +149,7 @@ describe('Single-Match Provider', () => {
127
149
  createTxCharge('tx-charge-1', [createMockTransaction()]), // Should be included
128
150
  ];
129
151
 
130
- const results = findMatches(sourceCharge, candidateCharges, USER_ID);
152
+ const results = await findMatches(sourceCharge, candidateCharges, USER_ID, createMockInjector());
131
153
 
132
154
  expect(results).toHaveLength(1);
133
155
  expect(results[0].chargeId).toBe('tx-charge-1');
@@ -135,55 +157,55 @@ describe('Single-Match Provider', () => {
135
157
  });
136
158
 
137
159
  describe('Matched Charge Input → Throws Error', () => {
138
- it('should throw error if source has both transactions and documents', () => {
160
+ it('should throw error if source has both transactions and documents', async () => {
139
161
  const matchedCharge = {
140
162
  chargeId: 'matched-charge',
141
163
  transactions: [createMockTransaction()],
142
164
  documents: [createMockDocument()],
143
165
  } as any;
144
166
 
145
- expect(() => findMatches(matchedCharge, [], USER_ID)).toThrow(
167
+ await expect(findMatches(matchedCharge, [], USER_ID, createMockInjector())).rejects.toThrow(
146
168
  /already matched.*both transactions and documents/,
147
169
  );
148
170
  });
149
171
 
150
- it('should throw error if source has neither transactions nor documents', () => {
172
+ it('should throw error if source has neither transactions nor documents', async () => {
151
173
  const emptyCharge = {
152
174
  chargeId: 'empty-charge',
153
175
  transactions: [],
154
176
  documents: [],
155
177
  } as any;
156
178
 
157
- expect(() => findMatches(emptyCharge, [], USER_ID)).toThrow(
179
+ await expect(findMatches(emptyCharge, [], USER_ID, createMockInjector())).rejects.toThrow(
158
180
  /no transactions or documents/,
159
181
  );
160
182
  });
161
183
  });
162
184
 
163
185
  describe('Multiple Currencies in Source → Error Propagates', () => {
164
- it('should throw error for source with mixed currencies', () => {
186
+ it('should throw error for source with mixed currencies', async () => {
165
187
  const mixedCurrencyCharge = createTxCharge('tx-charge-1', [
166
188
  createMockTransaction({ currency: 'USD' }),
167
189
  createMockTransaction({ currency: 'EUR' }),
168
190
  ]);
169
191
 
170
- expect(() => findMatches(mixedCurrencyCharge, [], USER_ID)).toThrow(
192
+ await expect(findMatches(mixedCurrencyCharge, [], USER_ID, createMockInjector())).rejects.toThrow(
171
193
  /failed validation.*multiple currencies/,
172
194
  );
173
195
  });
174
196
 
175
- it('should throw error for source with multiple business IDs', () => {
197
+ it('should throw error for source with multiple business IDs', async () => {
176
198
  const mixedBusinessCharge = createTxCharge('tx-charge-1', [
177
199
  createMockTransaction({ business_id: BUSINESS_A }),
178
200
  createMockTransaction({ business_id: BUSINESS_B }),
179
201
  ]);
180
202
 
181
- expect(() => findMatches(mixedBusinessCharge, [], USER_ID)).toThrow(
203
+ await expect(findMatches(mixedBusinessCharge, [], USER_ID, createMockInjector())).rejects.toThrow(
182
204
  /failed validation.*multiple business/,
183
205
  );
184
206
  });
185
207
 
186
- it('should throw error for source document with invalid business extraction', () => {
208
+ it('should throw error for source document with invalid business extraction', async () => {
187
209
  const invalidDocCharge = createDocCharge('doc-charge-1', [
188
210
  createMockDocument({
189
211
  creditor_id: 'other-user',
@@ -191,20 +213,20 @@ describe('Single-Match Provider', () => {
191
213
  }),
192
214
  ]);
193
215
 
194
- expect(() => findMatches(invalidDocCharge, [], USER_ID)).toThrow(/failed validation/);
216
+ await expect(findMatches(invalidDocCharge, [], USER_ID, createMockInjector())).rejects.toThrow(/failed validation/);
195
217
  });
196
218
  });
197
219
 
198
220
  describe('No Candidates Found → Empty Array', () => {
199
- it('should return empty array when no candidates provided', () => {
221
+ it('should return empty array when no candidates provided', async () => {
200
222
  const sourceCharge = createTxCharge('tx-charge-1', [createMockTransaction()]);
201
223
 
202
- const results = findMatches(sourceCharge, [], USER_ID);
224
+ const results = await findMatches(sourceCharge, [], USER_ID, createMockInjector());
203
225
 
204
226
  expect(results).toEqual([]);
205
227
  });
206
228
 
207
- it('should return empty array when all candidates are same type', () => {
229
+ it('should return empty array when all candidates are same type', async () => {
208
230
  const sourceCharge = createTxCharge('tx-charge-1', [createMockTransaction()]);
209
231
 
210
232
  const candidateCharges = [
@@ -212,12 +234,12 @@ describe('Single-Match Provider', () => {
212
234
  createTxCharge('tx-charge-3', [createMockTransaction()]),
213
235
  ];
214
236
 
215
- const results = findMatches(sourceCharge, candidateCharges, USER_ID);
237
+ const results = await findMatches(sourceCharge, candidateCharges, USER_ID, createMockInjector());
216
238
 
217
239
  expect(results).toEqual([]);
218
240
  });
219
241
 
220
- it('should return empty array when all candidates outside date window', () => {
242
+ it('should return empty array when all candidates outside date window', async () => {
221
243
  const sourceCharge = createTxCharge('tx-charge-1', [
222
244
  createMockTransaction({ event_date: new Date('2024-01-15') }),
223
245
  ]);
@@ -231,12 +253,12 @@ describe('Single-Match Provider', () => {
231
253
  ]),
232
254
  ];
233
255
 
234
- const results = findMatches(sourceCharge, candidateCharges, USER_ID);
256
+ const results = await findMatches(sourceCharge, candidateCharges, USER_ID, createMockInjector());
235
257
 
236
258
  expect(results).toEqual([]);
237
259
  });
238
260
 
239
- it('should return empty array when all candidates fail scoring', () => {
261
+ it('should return empty array when all candidates fail scoring', async () => {
240
262
  const sourceCharge = createTxCharge('tx-charge-1', [
241
263
  createMockTransaction({ currency: 'USD' }),
242
264
  ]);
@@ -249,14 +271,14 @@ describe('Single-Match Provider', () => {
249
271
  ]),
250
272
  ];
251
273
 
252
- const results = findMatches(sourceCharge, candidateCharges, USER_ID);
274
+ const results = await findMatches(sourceCharge, candidateCharges, USER_ID, createMockInjector());
253
275
 
254
276
  expect(results).toEqual([]);
255
277
  });
256
278
  });
257
279
 
258
280
  describe('Candidate Count Handling', () => {
259
- it('should return all candidates when fewer than 5', () => {
281
+ it('should return all candidates when fewer than 5', async () => {
260
282
  const sourceCharge = createTxCharge('tx-charge-1', [createMockTransaction()]);
261
283
 
262
284
  const candidateCharges = [
@@ -265,12 +287,12 @@ describe('Single-Match Provider', () => {
265
287
  createDocCharge('doc-charge-3', [createMockDocument()]),
266
288
  ];
267
289
 
268
- const results = findMatches(sourceCharge, candidateCharges, USER_ID);
290
+ const results = await findMatches(sourceCharge, candidateCharges, USER_ID, createMockInjector());
269
291
 
270
292
  expect(results).toHaveLength(3);
271
293
  });
272
294
 
273
- it('should return top 5 when more than 5 candidates', () => {
295
+ it('should return top 5 when more than 5 candidates', async () => {
274
296
  const sourceCharge = createTxCharge('tx-charge-1', [
275
297
  createMockTransaction({ amount: "100" }),
276
298
  ]);
@@ -285,13 +307,13 @@ describe('Single-Match Provider', () => {
285
307
  createDocCharge('doc-charge-7', [createMockDocument({ total_amount: 106 })]),
286
308
  ];
287
309
 
288
- const results = findMatches(sourceCharge, candidateCharges, USER_ID);
310
+ const results = await findMatches(sourceCharge, candidateCharges, USER_ID, createMockInjector());
289
311
 
290
312
  expect(results).toHaveLength(5);
291
313
  expect(results[0].chargeId).toBe('doc-charge-1'); // Best match
292
314
  });
293
315
 
294
- it('should respect custom maxMatches option', () => {
316
+ it('should respect custom maxMatches option', async () => {
295
317
  const sourceCharge = createTxCharge('tx-charge-1', [createMockTransaction()]);
296
318
 
297
319
  const candidateCharges = [
@@ -302,14 +324,14 @@ describe('Single-Match Provider', () => {
302
324
  createDocCharge('doc-charge-5', [createMockDocument()]),
303
325
  ];
304
326
 
305
- const results = findMatches(sourceCharge, candidateCharges, USER_ID, { maxMatches: 3 });
327
+ const results = await findMatches(sourceCharge, candidateCharges, USER_ID, createMockInjector(), { maxMatches: 3 });
306
328
 
307
329
  expect(results).toHaveLength(3);
308
330
  });
309
331
  });
310
332
 
311
333
  describe('Tie-Breaking by Date Proximity', () => {
312
- it('should use date proximity as tie-breaker when confidence scores equal', () => {
334
+ it('should use date proximity as tie-breaker when confidence scores equal', async () => {
313
335
  const sourceCharge = createTxCharge('tx-charge-1', [
314
336
  createMockTransaction({
315
337
  amount: "100",
@@ -339,14 +361,14 @@ describe('Single-Match Provider', () => {
339
361
  ]),
340
362
  ];
341
363
 
342
- const results = findMatches(sourceCharge, candidateCharges, USER_ID);
364
+ const results = await findMatches(sourceCharge, candidateCharges, USER_ID, createMockInjector());
343
365
 
344
366
  expect(results[0].chargeId).toBe('doc-charge-close'); // Closest date wins
345
367
  expect(results[1].chargeId).toBe('doc-charge-medium');
346
368
  expect(results[2].chargeId).toBe('doc-charge-far');
347
369
  });
348
370
 
349
- it('should include dateProximity in results', () => {
371
+ it('should include dateProximity in results', async () => {
350
372
  const sourceCharge = createTxCharge('tx-charge-1', [
351
373
  createMockTransaction({ event_date: new Date('2024-01-15') }),
352
374
  ]);
@@ -357,14 +379,14 @@ describe('Single-Match Provider', () => {
357
379
  ]),
358
380
  ];
359
381
 
360
- const results = findMatches(sourceCharge, candidateCharges, USER_ID);
382
+ const results = await findMatches(sourceCharge, candidateCharges, USER_ID, createMockInjector());
361
383
 
362
384
  expect(results[0].dateProximity).toBe(1);
363
385
  });
364
386
  });
365
387
 
366
388
  describe('Date Window Filtering', () => {
367
- it('should filter candidates outside 12-month window by default', () => {
389
+ it('should filter candidates outside 12-month window by default', async () => {
368
390
  const sourceCharge = createTxCharge('tx-charge-1', [
369
391
  createMockTransaction({ event_date: new Date('2024-01-15') }),
370
392
  ]);
@@ -378,13 +400,13 @@ describe('Single-Match Provider', () => {
378
400
  ]),
379
401
  ];
380
402
 
381
- const results = findMatches(sourceCharge, candidateCharges, USER_ID);
403
+ const results = await findMatches(sourceCharge, candidateCharges, USER_ID, createMockInjector());
382
404
 
383
405
  expect(results).toHaveLength(1);
384
406
  expect(results[0].chargeId).toBe('doc-inside');
385
407
  });
386
408
 
387
- it('should respect custom dateWindowMonths option', () => {
409
+ it('should respect custom dateWindowMonths option', async () => {
388
410
  const sourceCharge = createTxCharge('tx-charge-1', [
389
411
  createMockTransaction({ event_date: new Date('2024-01-15') }),
390
412
  ]);
@@ -398,7 +420,7 @@ describe('Single-Match Provider', () => {
398
420
  ]),
399
421
  ];
400
422
 
401
- const results = findMatches(sourceCharge, candidateCharges, USER_ID, {
423
+ const results = await findMatches(sourceCharge, candidateCharges, USER_ID, createMockInjector(), {
402
424
  dateWindowMonths: 3,
403
425
  });
404
426
 
@@ -406,7 +428,7 @@ describe('Single-Match Provider', () => {
406
428
  expect(results[0].chargeId).toBe('doc-inside');
407
429
  });
408
430
 
409
- it('should include candidates on exact window boundary', () => {
431
+ it('should include candidates on exact window boundary', async () => {
410
432
  const sourceCharge = createTxCharge('tx-charge-1', [
411
433
  createMockTransaction({ event_date: new Date('2024-01-15') }),
412
434
  ]);
@@ -417,14 +439,14 @@ describe('Single-Match Provider', () => {
417
439
  ]),
418
440
  ];
419
441
 
420
- const results = findMatches(sourceCharge, candidateCharges, USER_ID);
442
+ const results = await findMatches(sourceCharge, candidateCharges, USER_ID, createMockInjector());
421
443
 
422
444
  expect(results).toHaveLength(1);
423
445
  });
424
446
  });
425
447
 
426
448
  describe('Fee Transactions Excluded', () => {
427
- it('should handle source with fee transactions via aggregator', () => {
449
+ it('should handle source with fee transactions via aggregator', async () => {
428
450
  const sourceCharge = createTxCharge('tx-charge-1', [
429
451
  createMockTransaction({ amount: "100", is_fee: false }),
430
452
  createMockTransaction({ amount: "5", is_fee: true }), // Should be excluded by aggregator
@@ -434,40 +456,40 @@ describe('Single-Match Provider', () => {
434
456
  createDocCharge('doc-charge-1', [createMockDocument({ total_amount: 100 })]),
435
457
  ];
436
458
 
437
- const results = findMatches(sourceCharge, candidateCharges, USER_ID);
459
+ const results = await findMatches(sourceCharge, candidateCharges, USER_ID, createMockInjector());
438
460
 
439
461
  expect(results).toHaveLength(1);
440
462
  expect(results[0].confidenceScore).toBeGreaterThan(0.95); // Matches 100, not 105
441
463
  });
442
464
 
443
- it('should throw error if all source transactions are fees', () => {
465
+ it('should throw error if all source transactions are fees', async () => {
444
466
  const allFeesCharge = createTxCharge('tx-charge-1', [
445
467
  createMockTransaction({ is_fee: true }),
446
468
  createMockTransaction({ is_fee: true }),
447
469
  ]);
448
470
 
449
- expect(() => findMatches(allFeesCharge, [], USER_ID)).toThrow(
471
+ await expect(findMatches(allFeesCharge, [], USER_ID, createMockInjector())).rejects.toThrow(
450
472
  /all transactions are marked as fees/,
451
473
  );
452
474
  });
453
475
  });
454
476
 
455
477
  describe('Same ChargeId → Throws Error', () => {
456
- it('should throw error when candidate has same chargeId as source', () => {
478
+ it('should throw error when candidate has same chargeId as source', async () => {
457
479
  const sourceCharge = createTxCharge('same-charge-id', [createMockTransaction()]);
458
480
 
459
481
  const candidateCharges = [
460
482
  createDocCharge('same-charge-id', [createMockDocument()]), // Same ID!
461
483
  ];
462
484
 
463
- expect(() => findMatches(sourceCharge, candidateCharges, USER_ID)).toThrow(
485
+ await expect(findMatches(sourceCharge, candidateCharges, USER_ID, createMockInjector())).rejects.toThrow(
464
486
  /same ID as source charge/,
465
487
  );
466
488
  });
467
489
  });
468
490
 
469
491
  describe('Various Confidence Levels', () => {
470
- it('should rank matches by confidence level correctly', () => {
492
+ it('should rank matches by confidence level correctly', async () => {
471
493
  const sourceCharge = createTxCharge('tx-charge-1', [
472
494
  createMockTransaction({
473
495
  amount: "100",
@@ -520,7 +542,7 @@ describe('Single-Match Provider', () => {
520
542
  ]),
521
543
  ];
522
544
 
523
- const results = findMatches(sourceCharge, candidateCharges, USER_ID);
545
+ const results = await findMatches(sourceCharge, candidateCharges, USER_ID, createMockInjector());
524
546
 
525
547
  expect(results).toHaveLength(4);
526
548
  expect(results[0].chargeId).toBe('perfect');
@@ -530,14 +552,14 @@ describe('Single-Match Provider', () => {
530
552
  expect(results[3].chargeId).toBe('poor');
531
553
  });
532
554
 
533
- it('should include component scores in results', () => {
555
+ it('should include component scores in results', async () => {
534
556
  const sourceCharge = createTxCharge('tx-charge-1', [createMockTransaction()]);
535
557
 
536
558
  const candidateCharges = [
537
559
  createDocCharge('doc-charge-1', [createMockDocument()]),
538
560
  ];
539
561
 
540
- const results = findMatches(sourceCharge, candidateCharges, USER_ID);
562
+ const results = await findMatches(sourceCharge, candidateCharges, USER_ID, createMockInjector());
541
563
 
542
564
  expect(results[0].components).toBeDefined();
543
565
  expect(results[0].components.amount).toBeGreaterThanOrEqual(0);
@@ -548,7 +570,7 @@ describe('Single-Match Provider', () => {
548
570
  });
549
571
 
550
572
  describe('Edge Cases', () => {
551
- it('should handle multiple transactions in source charge', () => {
573
+ it('should handle multiple transactions in source charge', async () => {
552
574
  const sourceCharge = createTxCharge('tx-charge-1', [
553
575
  createMockTransaction({ amount: "50" }),
554
576
  createMockTransaction({ amount: "50" }),
@@ -558,13 +580,13 @@ describe('Single-Match Provider', () => {
558
580
  createDocCharge('doc-charge-1', [createMockDocument({ total_amount: 100 })]),
559
581
  ];
560
582
 
561
- const results = findMatches(sourceCharge, candidateCharges, USER_ID);
583
+ const results = await findMatches(sourceCharge, candidateCharges, USER_ID, createMockInjector());
562
584
 
563
585
  expect(results).toHaveLength(1);
564
586
  expect(results[0].confidenceScore).toBeGreaterThan(0.95); // 50+50 = 100
565
587
  });
566
588
 
567
- it('should handle multiple documents in candidate charge', () => {
589
+ it('should handle multiple documents in candidate charge', async () => {
568
590
  const sourceCharge = createTxCharge('tx-charge-1', [
569
591
  createMockTransaction({ amount: "100" }),
570
592
  ]);
@@ -576,13 +598,13 @@ describe('Single-Match Provider', () => {
576
598
  ]),
577
599
  ];
578
600
 
579
- const results = findMatches(sourceCharge, candidateCharges, USER_ID);
601
+ const results = await findMatches(sourceCharge, candidateCharges, USER_ID, createMockInjector());
580
602
 
581
603
  expect(results).toHaveLength(1);
582
604
  expect(results[0].confidenceScore).toBeGreaterThan(0.95); // 60+40 = 100
583
605
  });
584
606
 
585
- it('should handle negative amounts (credit transactions)', () => {
607
+ it('should handle negative amounts (credit transactions)', async () => {
586
608
  const sourceCharge = createTxCharge('tx-charge-1', [
587
609
  createMockTransaction({ amount: "-100" }),
588
610
  ]);
@@ -598,7 +620,7 @@ describe('Single-Match Provider', () => {
598
620
  ]),
599
621
  ];
600
622
 
601
- const results = findMatches(sourceCharge, candidateCharges, USER_ID);
623
+ const results = await findMatches(sourceCharge, candidateCharges, USER_ID, createMockInjector());
602
624
 
603
625
  expect(results).toHaveLength(1);
604
626
  expect(results[0].confidenceScore).toBeGreaterThan(0.9);