lib-fints 1.3.1 → 1.4.1
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/README.md +43 -40
- package/dist/camtParser.js +412 -0
- package/dist/client.js +65 -82
- package/dist/dataGroups/SepaAccount.js +17 -0
- package/dist/dataGroups/SepaAccountParameters.js +16 -0
- package/dist/dialog.js +126 -108
- package/dist/httpClient.js +17 -3
- package/dist/index.js +1 -0
- package/dist/interactions/balanceInteraction.js +1 -1
- package/dist/interactions/customerInteraction.js +6 -3
- package/dist/interactions/endDialogInteraction.js +17 -0
- package/dist/interactions/initDialogInteraction.js +18 -8
- package/dist/interactions/sepaAccountInteraction.js +48 -0
- package/dist/interactions/statementInteractionCAMT.js +54 -0
- package/dist/interactions/{statementInteraction.js → statementInteractionMT940.js} +11 -8
- package/dist/interactions/tanMediaInteraction.js +4 -0
- package/dist/segments/HICAZ.js +22 -0
- package/dist/segments/HICAZS.js +19 -0
- package/dist/segments/HISPA.js +14 -0
- package/dist/segments/HISPAS.js +14 -0
- package/dist/segments/HKCAZ.js +28 -0
- package/dist/segments/HKSPA.js +21 -0
- package/dist/segments/registry.js +16 -2
- package/dist/statement.js +1 -0
- package/dist/tests/HICAZS.test.js +18 -0
- package/dist/tests/HKCAZ.test.js +47 -0
- package/dist/tests/camtParser.test.js +673 -0
- package/dist/tests/client.test.js +642 -23
- package/dist/tests/dialog.test.js +234 -0
- package/dist/types/bankAccount.d.ts +2 -3
- package/dist/types/bankAccount.d.ts.map +1 -1
- package/dist/types/camtParser.d.ts +31 -0
- package/dist/types/camtParser.d.ts.map +1 -0
- package/dist/types/client.d.ts +10 -10
- package/dist/types/client.d.ts.map +1 -1
- package/dist/types/config.d.ts +2 -1
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/dataGroups/SepaAccount.d.ts +11 -0
- package/dist/types/dataGroups/SepaAccount.d.ts.map +1 -0
- package/dist/types/dataGroups/SepaAccountParameters.d.ts +13 -0
- package/dist/types/dataGroups/SepaAccountParameters.d.ts.map +1 -0
- package/dist/types/dialog.d.ts +12 -8
- package/dist/types/dialog.d.ts.map +1 -1
- package/dist/types/httpClient.d.ts +2 -1
- package/dist/types/httpClient.d.ts.map +1 -1
- package/dist/types/index.d.ts +2 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/interactions/customerInteraction.d.ts +7 -3
- package/dist/types/interactions/customerInteraction.d.ts.map +1 -1
- package/dist/types/interactions/endDialogInteraction.d.ts +13 -0
- package/dist/types/interactions/endDialogInteraction.d.ts.map +1 -0
- package/dist/types/interactions/initDialogInteraction.d.ts +2 -3
- package/dist/types/interactions/initDialogInteraction.d.ts.map +1 -1
- package/dist/types/interactions/sepaAccountInteraction.d.ts +17 -0
- package/dist/types/interactions/sepaAccountInteraction.d.ts.map +1 -0
- package/dist/types/interactions/{statementInteraction.d.ts → statementInteractionCAMT.d.ts} +4 -7
- package/dist/types/interactions/statementInteractionCAMT.d.ts.map +1 -0
- package/dist/types/interactions/statementInteractionMT940.d.ts +13 -0
- package/dist/types/interactions/statementInteractionMT940.d.ts.map +1 -0
- package/dist/types/interactions/tanMediaInteraction.d.ts.map +1 -1
- package/dist/types/mt940parser.d.ts +2 -38
- package/dist/types/mt940parser.d.ts.map +1 -1
- package/dist/types/segments/HICAZ.d.ts +22 -0
- package/dist/types/segments/HICAZ.d.ts.map +1 -0
- package/dist/types/segments/HICAZS.d.ts +17 -0
- package/dist/types/segments/HICAZS.d.ts.map +1 -0
- package/dist/types/segments/HISPA.d.ts +17 -0
- package/dist/types/segments/HISPA.d.ts.map +1 -0
- package/dist/types/segments/HISPAS.d.ts +14 -0
- package/dist/types/segments/HISPAS.d.ts.map +1 -0
- package/dist/types/segments/HKCAZ.d.ts +27 -0
- package/dist/types/segments/HKCAZ.d.ts.map +1 -0
- package/dist/types/segments/HKKAZ.d.ts +1 -1
- package/dist/types/segments/HKSAL.d.ts +1 -1
- package/dist/types/segments/HKSPA.d.ts +21 -0
- package/dist/types/segments/HKSPA.d.ts.map +1 -0
- package/dist/types/segments/HKTAN.d.ts +1 -1
- package/dist/types/segments/registry.d.ts.map +1 -1
- package/dist/types/statement.d.ts +39 -0
- package/dist/types/statement.d.ts.map +1 -0
- package/dist/types/tests/HICAZS.test.d.ts +2 -0
- package/dist/types/tests/HICAZS.test.d.ts.map +1 -0
- package/dist/types/tests/HKCAZ.test.d.ts +2 -0
- package/dist/types/tests/HKCAZ.test.d.ts.map +1 -0
- package/dist/types/tests/camtParser.test.d.ts +2 -0
- package/dist/types/tests/camtParser.test.d.ts.map +1 -0
- package/dist/types/tests/dialog.test.d.ts +2 -0
- package/dist/types/tests/dialog.test.d.ts.map +1 -0
- package/package.json +4 -1
- package/dist/types/interactions/statementInteraction.d.ts.map +0 -1
|
@@ -2,32 +2,651 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
|
2
2
|
import { FinTSClient } from '../client.js';
|
|
3
3
|
import { Dialog } from '../dialog.js';
|
|
4
4
|
import { FinTSConfig } from '../config.js';
|
|
5
|
+
import { Language } from '../codes.js';
|
|
6
|
+
import { AccountType } from '../bankAccount.js';
|
|
5
7
|
describe('FinTSClient', () => {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
+
const client = new FinTSClient(FinTSConfig.fromBankingInformation('product', '1.0', {
|
|
9
|
+
systemId: 'SYSTEM01',
|
|
10
|
+
bpd: {
|
|
11
|
+
version: 1,
|
|
12
|
+
url: 'https://bank.example.com/fints',
|
|
13
|
+
countryCode: 280,
|
|
14
|
+
bankId: '10020030',
|
|
15
|
+
bankName: 'Example Bank',
|
|
16
|
+
allowedTransactions: [
|
|
17
|
+
{ transId: 'HKSAL', versions: [6, 7], tanRequired: false },
|
|
18
|
+
{ transId: 'HKCAZ', versions: [1], tanRequired: false },
|
|
19
|
+
{ transId: 'HKKAZ', versions: [6], tanRequired: true },
|
|
20
|
+
{ transId: 'HKWPD', versions: [7], tanRequired: true },
|
|
21
|
+
{ transId: 'DKKKU', versions: [2], tanRequired: false },
|
|
22
|
+
],
|
|
23
|
+
maxTransactionsPerMessage: 1,
|
|
24
|
+
supportedLanguages: [Language.German],
|
|
25
|
+
supportedHbciVersions: [300],
|
|
26
|
+
supportedTanMethods: [
|
|
27
|
+
{
|
|
28
|
+
id: 1,
|
|
29
|
+
name: 'ChipTAN',
|
|
30
|
+
version: 1,
|
|
31
|
+
isDecoupled: false,
|
|
32
|
+
activeTanMediaCount: 1,
|
|
33
|
+
activeTanMedia: ['TAN-Generator 123'],
|
|
34
|
+
tanMediaRequirement: 0,
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
id: 2,
|
|
38
|
+
name: 'pushTAN',
|
|
39
|
+
version: 1,
|
|
40
|
+
isDecoupled: true,
|
|
41
|
+
activeTanMediaCount: 1,
|
|
42
|
+
activeTanMedia: ['Mobile App'],
|
|
43
|
+
tanMediaRequirement: 1,
|
|
44
|
+
},
|
|
45
|
+
],
|
|
46
|
+
availableTanMethodIds: [1, 2],
|
|
47
|
+
},
|
|
48
|
+
upd: {
|
|
49
|
+
version: 1,
|
|
50
|
+
usage: 0,
|
|
51
|
+
bankAccounts: [
|
|
52
|
+
{
|
|
53
|
+
accountNumber: '1234567890',
|
|
54
|
+
bank: {
|
|
55
|
+
bankId: '10020030',
|
|
56
|
+
country: 280,
|
|
57
|
+
},
|
|
58
|
+
iban: 'DE89370400440532013000',
|
|
59
|
+
customerId: 'customer1',
|
|
60
|
+
accountType: AccountType.CheckingAccount,
|
|
61
|
+
currency: 'EUR',
|
|
62
|
+
holder1: 'Test User',
|
|
63
|
+
allowedTransactions: [
|
|
64
|
+
{ transId: 'HKSAL', numSignatures: 1 },
|
|
65
|
+
{ transId: 'HKCAZ', numSignatures: 1 },
|
|
66
|
+
{ transId: 'HKKAZ', numSignatures: 1 },
|
|
67
|
+
],
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
accountNumber: '9876543210',
|
|
71
|
+
bank: {
|
|
72
|
+
bankId: '10020030',
|
|
73
|
+
country: 280,
|
|
74
|
+
},
|
|
75
|
+
iban: 'DE89370400440532013001',
|
|
76
|
+
customerId: 'customer1',
|
|
77
|
+
accountType: AccountType.SecuritiesAccount,
|
|
78
|
+
currency: 'EUR',
|
|
79
|
+
holder1: 'Test User',
|
|
80
|
+
allowedTransactions: [{ transId: 'HKWPD', numSignatures: 1 }],
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
accountNumber: '1111222233',
|
|
84
|
+
bank: {
|
|
85
|
+
bankId: '10020030',
|
|
86
|
+
country: 280,
|
|
87
|
+
},
|
|
88
|
+
iban: 'DE89370400440532013002',
|
|
89
|
+
customerId: 'customer1',
|
|
90
|
+
accountType: AccountType.CreditCardAccount,
|
|
91
|
+
currency: 'EUR',
|
|
92
|
+
holder1: 'Test User',
|
|
93
|
+
allowedTransactions: [{ transId: 'DKKKU', numSignatures: 1 }],
|
|
94
|
+
},
|
|
95
|
+
],
|
|
96
|
+
},
|
|
97
|
+
bankMessages: [],
|
|
98
|
+
}));
|
|
99
|
+
let dialogStartMock;
|
|
100
|
+
let dialogContinueMock;
|
|
8
101
|
beforeEach(() => {
|
|
9
|
-
|
|
10
|
-
|
|
102
|
+
dialogStartMock = vi.spyOn(Dialog.prototype, 'start');
|
|
103
|
+
dialogContinueMock = vi.spyOn(Dialog.prototype, 'continue');
|
|
11
104
|
});
|
|
12
105
|
afterEach(() => {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
106
|
+
dialogStartMock.mockRestore();
|
|
107
|
+
dialogContinueMock.mockRestore();
|
|
108
|
+
});
|
|
109
|
+
describe('getAccountBalance', () => {
|
|
110
|
+
it('returns balance response when no TAN is required', async () => {
|
|
111
|
+
dialogStartMock.mockResolvedValueOnce(new Map([
|
|
112
|
+
[
|
|
113
|
+
'HKIDN',
|
|
114
|
+
{
|
|
115
|
+
dialogId: 'DIALOG1',
|
|
116
|
+
success: true,
|
|
117
|
+
requiresTan: false,
|
|
118
|
+
bankingInformationUpdated: false,
|
|
119
|
+
bankAnswers: [{ code: 20, text: 'Auftrag ausgeführt' }],
|
|
120
|
+
},
|
|
121
|
+
],
|
|
122
|
+
[
|
|
123
|
+
'HKSAL',
|
|
124
|
+
{
|
|
125
|
+
dialogId: 'DIALOG1',
|
|
126
|
+
success: true,
|
|
127
|
+
requiresTan: false,
|
|
128
|
+
bankingInformationUpdated: false,
|
|
129
|
+
bankAnswers: [{ code: 20, text: 'Auftrag ausgeführt' }],
|
|
130
|
+
balance: { date: new Date('2025-12-23'), currency: 'EUR', balance: 1000.0 },
|
|
131
|
+
},
|
|
132
|
+
],
|
|
133
|
+
[
|
|
134
|
+
'HKEND',
|
|
135
|
+
{
|
|
136
|
+
dialogId: 'DIALOG1',
|
|
137
|
+
success: true,
|
|
138
|
+
requiresTan: false,
|
|
139
|
+
bankingInformationUpdated: false,
|
|
140
|
+
bankAnswers: [{ code: 100, text: 'Dialog beendet' }],
|
|
141
|
+
},
|
|
142
|
+
],
|
|
143
|
+
]));
|
|
144
|
+
const response = (await client.getAccountBalance('1234567890'));
|
|
145
|
+
expect(response.success).toBe(true);
|
|
146
|
+
expect(response.requiresTan).toBe(false);
|
|
147
|
+
expect(response.balance).toBeDefined();
|
|
148
|
+
expect(dialogStartMock).toHaveBeenCalledOnce();
|
|
149
|
+
expect(dialogContinueMock).toHaveBeenCalledTimes(0);
|
|
150
|
+
});
|
|
151
|
+
it('returns init dialog response when TAN is required', async () => {
|
|
152
|
+
dialogStartMock.mockResolvedValueOnce(new Map([
|
|
153
|
+
[
|
|
154
|
+
'HKIDN',
|
|
155
|
+
{
|
|
156
|
+
dialogId: 'DIALOG1',
|
|
157
|
+
success: true,
|
|
158
|
+
requiresTan: true,
|
|
159
|
+
tanReference: 'TANREF123',
|
|
160
|
+
tanChallenge: 'Bitte geben Sie Ihre TAN ein.',
|
|
161
|
+
bankingInformationUpdated: false,
|
|
162
|
+
bankAnswers: [{ code: 20, text: 'Auftrag ausgeführt' }],
|
|
163
|
+
},
|
|
164
|
+
],
|
|
165
|
+
]));
|
|
166
|
+
const response = (await client.getAccountBalance('1234567890'));
|
|
167
|
+
expect(response.success).toBe(true);
|
|
168
|
+
expect(response.requiresTan).toBe(true);
|
|
169
|
+
expect(response.tanReference).toBe('TANREF123');
|
|
170
|
+
expect(dialogStartMock).toHaveBeenCalledOnce();
|
|
171
|
+
expect(dialogContinueMock).toHaveBeenCalledTimes(0);
|
|
172
|
+
});
|
|
173
|
+
});
|
|
174
|
+
describe('getAccountBalanceWithTan', () => {
|
|
175
|
+
it('returns balance response when TAN is provided', async () => {
|
|
176
|
+
dialogContinueMock.mockResolvedValueOnce(new Map([
|
|
177
|
+
[
|
|
178
|
+
'HKSAL',
|
|
179
|
+
{
|
|
180
|
+
dialogId: 'DIALOG1',
|
|
181
|
+
success: true,
|
|
182
|
+
requiresTan: false,
|
|
183
|
+
bankingInformationUpdated: false,
|
|
184
|
+
bankAnswers: [{ code: 20, text: 'Auftrag ausgeführt' }],
|
|
185
|
+
balance: { date: new Date('2025-12-23'), currency: 'EUR', balance: 2500.0 },
|
|
186
|
+
},
|
|
187
|
+
],
|
|
188
|
+
[
|
|
189
|
+
'HKEND',
|
|
190
|
+
{
|
|
191
|
+
dialogId: 'DIALOG1',
|
|
192
|
+
success: true,
|
|
193
|
+
requiresTan: false,
|
|
194
|
+
bankingInformationUpdated: false,
|
|
195
|
+
bankAnswers: [{ code: 100, text: 'Dialog beendet' }],
|
|
196
|
+
},
|
|
197
|
+
],
|
|
198
|
+
]));
|
|
199
|
+
const response = (await client.getAccountBalanceWithTan('TANREF123', '123456'));
|
|
200
|
+
expect(response.success).toBe(true);
|
|
201
|
+
expect(response.requiresTan).toBe(false);
|
|
202
|
+
expect(response.balance).toBeDefined();
|
|
203
|
+
expect(response.balance.balance).toBe(2500.0);
|
|
204
|
+
expect(dialogContinueMock).toHaveBeenCalledWith('TANREF123', '123456');
|
|
205
|
+
});
|
|
206
|
+
});
|
|
207
|
+
describe('synchronize', () => {
|
|
208
|
+
it('returns synchronization response when no TAN is required', async () => {
|
|
209
|
+
dialogStartMock.mockResolvedValueOnce(new Map([
|
|
210
|
+
[
|
|
211
|
+
'HKIDN',
|
|
212
|
+
{
|
|
213
|
+
dialogId: 'DIALOG1',
|
|
214
|
+
success: true,
|
|
215
|
+
requiresTan: false,
|
|
216
|
+
bankingInformationUpdated: true,
|
|
217
|
+
bankAnswers: [{ code: 20, text: 'Synchronisierung erfolgreich' }],
|
|
218
|
+
},
|
|
219
|
+
],
|
|
220
|
+
[
|
|
221
|
+
'HKEND',
|
|
222
|
+
{
|
|
223
|
+
dialogId: 'DIALOG1',
|
|
224
|
+
success: true,
|
|
225
|
+
requiresTan: false,
|
|
226
|
+
bankingInformationUpdated: false,
|
|
227
|
+
bankAnswers: [{ code: 100, text: 'Dialog beendet' }],
|
|
228
|
+
},
|
|
229
|
+
],
|
|
230
|
+
]));
|
|
231
|
+
const response = await client.synchronize();
|
|
232
|
+
expect(response.success).toBe(true);
|
|
233
|
+
expect(response.requiresTan).toBe(false);
|
|
234
|
+
expect(response.bankingInformationUpdated).toBe(true);
|
|
235
|
+
expect(dialogStartMock).toHaveBeenCalledOnce();
|
|
236
|
+
});
|
|
237
|
+
it('returns sync response requiring TAN', async () => {
|
|
238
|
+
dialogStartMock.mockResolvedValueOnce(new Map([
|
|
239
|
+
[
|
|
240
|
+
'HKIDN',
|
|
241
|
+
{
|
|
242
|
+
dialogId: 'DIALOG1',
|
|
243
|
+
success: true,
|
|
244
|
+
requiresTan: true,
|
|
245
|
+
tanReference: 'SYNCTAN123',
|
|
246
|
+
tanChallenge: 'TAN für Synchronisation eingeben.',
|
|
247
|
+
bankingInformationUpdated: false,
|
|
248
|
+
bankAnswers: [{ code: 3955, text: 'TAN erforderlich' }],
|
|
249
|
+
},
|
|
250
|
+
],
|
|
251
|
+
]));
|
|
252
|
+
const response = await client.synchronize();
|
|
253
|
+
expect(response.success).toBe(true);
|
|
254
|
+
expect(response.requiresTan).toBe(true);
|
|
255
|
+
expect(response.tanReference).toBe('SYNCTAN123');
|
|
256
|
+
expect(response.tanChallenge).toBe('TAN für Synchronisation eingeben.');
|
|
257
|
+
});
|
|
258
|
+
});
|
|
259
|
+
describe('synchronizeWithTan', () => {
|
|
260
|
+
it('completes synchronization with TAN', async () => {
|
|
261
|
+
dialogContinueMock.mockResolvedValueOnce(new Map([
|
|
262
|
+
[
|
|
263
|
+
'HKIDN',
|
|
264
|
+
{
|
|
265
|
+
dialogId: 'DIALOG1',
|
|
266
|
+
success: true,
|
|
267
|
+
requiresTan: false,
|
|
268
|
+
bankingInformationUpdated: true,
|
|
269
|
+
bankAnswers: [{ code: 20, text: 'Synchronisierung erfolgreich' }],
|
|
270
|
+
},
|
|
271
|
+
],
|
|
272
|
+
[
|
|
273
|
+
'HKEND',
|
|
274
|
+
{
|
|
275
|
+
dialogId: 'DIALOG1',
|
|
276
|
+
success: true,
|
|
277
|
+
requiresTan: false,
|
|
278
|
+
bankingInformationUpdated: false,
|
|
279
|
+
bankAnswers: [{ code: 100, text: 'Dialog beendet' }],
|
|
280
|
+
},
|
|
281
|
+
],
|
|
282
|
+
]));
|
|
283
|
+
const response = await client.synchronizeWithTan('SYNCTAN123', '789012');
|
|
284
|
+
expect(response.success).toBe(true);
|
|
285
|
+
expect(response.requiresTan).toBe(false);
|
|
286
|
+
expect(response.bankingInformationUpdated).toBe(true);
|
|
287
|
+
expect(dialogContinueMock).toHaveBeenCalledWith('SYNCTAN123', '789012');
|
|
288
|
+
});
|
|
289
|
+
});
|
|
290
|
+
describe('getAccountStatements', () => {
|
|
291
|
+
it('fetches statements using CAMT format when available and preferred', async () => {
|
|
292
|
+
dialogStartMock.mockResolvedValueOnce(new Map([
|
|
293
|
+
[
|
|
294
|
+
'HKIDN',
|
|
295
|
+
{
|
|
296
|
+
dialogId: 'DIALOG1',
|
|
297
|
+
success: true,
|
|
298
|
+
requiresTan: false,
|
|
299
|
+
bankingInformationUpdated: false,
|
|
300
|
+
bankAnswers: [{ code: 20, text: 'Auftrag ausgeführt' }],
|
|
301
|
+
},
|
|
302
|
+
],
|
|
303
|
+
[
|
|
304
|
+
'HKCAZ',
|
|
305
|
+
{
|
|
306
|
+
dialogId: 'DIALOG1',
|
|
307
|
+
success: true,
|
|
308
|
+
requiresTan: false,
|
|
309
|
+
bankingInformationUpdated: false,
|
|
310
|
+
bankAnswers: [{ code: 20, text: 'Auftrag ausgeführt' }],
|
|
311
|
+
statements: [
|
|
312
|
+
{
|
|
313
|
+
account: '1234567890',
|
|
314
|
+
transactions: [],
|
|
315
|
+
openingBalance: { date: new Date('2025-12-01'), currency: 'EUR', value: 500.0 },
|
|
316
|
+
closingBalance: { date: new Date('2025-12-23'), currency: 'EUR', value: 1500.0 },
|
|
317
|
+
},
|
|
318
|
+
],
|
|
319
|
+
},
|
|
320
|
+
],
|
|
321
|
+
[
|
|
322
|
+
'HKEND',
|
|
323
|
+
{
|
|
324
|
+
dialogId: 'DIALOG1',
|
|
325
|
+
success: true,
|
|
326
|
+
requiresTan: false,
|
|
327
|
+
bankingInformationUpdated: false,
|
|
328
|
+
bankAnswers: [{ code: 100, text: 'Dialog beendet' }],
|
|
329
|
+
},
|
|
330
|
+
],
|
|
331
|
+
]));
|
|
332
|
+
const response = (await client.getAccountStatements('1234567890'));
|
|
333
|
+
expect(response.success).toBe(true);
|
|
334
|
+
expect(response.statements).toBeDefined();
|
|
335
|
+
expect(response.statements.length).toBe(1);
|
|
336
|
+
expect(response.statements[0].account).toBe('1234567890');
|
|
337
|
+
});
|
|
338
|
+
it('throws error when account does not support statements', async () => {
|
|
339
|
+
await expect(client.getAccountStatements('9999999999')).rejects.toThrow('Account 9999999999 not found in UPD');
|
|
340
|
+
});
|
|
341
|
+
it('uses MT940 when preferCamt is false', async () => {
|
|
342
|
+
dialogStartMock.mockResolvedValueOnce(new Map([
|
|
343
|
+
[
|
|
344
|
+
'HKIDN',
|
|
345
|
+
{
|
|
346
|
+
dialogId: 'DIALOG1',
|
|
347
|
+
success: true,
|
|
348
|
+
requiresTan: false,
|
|
349
|
+
bankingInformationUpdated: false,
|
|
350
|
+
bankAnswers: [{ code: 20, text: 'Auftrag ausgeführt' }],
|
|
351
|
+
},
|
|
352
|
+
],
|
|
353
|
+
[
|
|
354
|
+
'HKKAZ',
|
|
355
|
+
{
|
|
356
|
+
dialogId: 'DIALOG1',
|
|
357
|
+
success: true,
|
|
358
|
+
requiresTan: false,
|
|
359
|
+
bankingInformationUpdated: false,
|
|
360
|
+
bankAnswers: [{ code: 20, text: 'Auftrag ausgeführt' }],
|
|
361
|
+
statements: [
|
|
362
|
+
{
|
|
363
|
+
account: '1234567890',
|
|
364
|
+
transactions: [],
|
|
365
|
+
openingBalance: { date: new Date('2025-12-01'), currency: 'EUR', value: 500.0 },
|
|
366
|
+
closingBalance: { date: new Date('2025-12-23'), currency: 'EUR', value: 1500.0 },
|
|
367
|
+
},
|
|
368
|
+
],
|
|
369
|
+
},
|
|
370
|
+
],
|
|
371
|
+
[
|
|
372
|
+
'HKEND',
|
|
373
|
+
{
|
|
374
|
+
dialogId: 'DIALOG1',
|
|
375
|
+
success: true,
|
|
376
|
+
requiresTan: false,
|
|
377
|
+
bankingInformationUpdated: false,
|
|
378
|
+
bankAnswers: [{ code: 100, text: 'Dialog beendet' }],
|
|
379
|
+
},
|
|
380
|
+
],
|
|
381
|
+
]));
|
|
382
|
+
const response = (await client.getAccountStatements('1234567890', undefined, undefined, false));
|
|
383
|
+
expect(response.success).toBe(true);
|
|
384
|
+
expect(response.statements).toBeDefined();
|
|
385
|
+
expect(response.statements.length).toBe(1);
|
|
386
|
+
expect(response.statements[0].account).toBe('1234567890');
|
|
387
|
+
});
|
|
388
|
+
});
|
|
389
|
+
describe('getAccountStatementsWithTan', () => {
|
|
390
|
+
it('continues statement fetching with TAN', async () => {
|
|
391
|
+
dialogContinueMock.mockResolvedValueOnce(new Map([
|
|
392
|
+
[
|
|
393
|
+
'HKCAZ',
|
|
394
|
+
{
|
|
395
|
+
dialogId: 'DIALOG1',
|
|
396
|
+
success: true,
|
|
397
|
+
requiresTan: false,
|
|
398
|
+
bankingInformationUpdated: false,
|
|
399
|
+
bankAnswers: [{ code: 20, text: 'Auftrag ausgeführt' }],
|
|
400
|
+
statements: [
|
|
401
|
+
{
|
|
402
|
+
account: '1234567890',
|
|
403
|
+
transactions: [],
|
|
404
|
+
openingBalance: { date: new Date('2025-12-01'), currency: 'EUR', value: 500.0 },
|
|
405
|
+
closingBalance: { date: new Date('2025-12-23'), currency: 'EUR', value: 1500.0 },
|
|
406
|
+
},
|
|
407
|
+
],
|
|
408
|
+
},
|
|
409
|
+
],
|
|
410
|
+
]));
|
|
411
|
+
const response = (await client.getAccountStatementsWithTan('STATEMENTTAN123', '456789'));
|
|
412
|
+
expect(response.success).toBe(true);
|
|
413
|
+
expect(response.statements).toBeDefined();
|
|
414
|
+
expect(dialogContinueMock).toHaveBeenCalledWith('STATEMENTTAN123', '456789');
|
|
415
|
+
});
|
|
416
|
+
});
|
|
417
|
+
describe('getPortfolio', () => {
|
|
418
|
+
it('fetches portfolio information', async () => {
|
|
419
|
+
dialogStartMock.mockResolvedValueOnce(new Map([
|
|
420
|
+
[
|
|
421
|
+
'HKIDN',
|
|
422
|
+
{
|
|
423
|
+
dialogId: 'DIALOG1',
|
|
424
|
+
success: true,
|
|
425
|
+
requiresTan: false,
|
|
426
|
+
bankingInformationUpdated: false,
|
|
427
|
+
bankAnswers: [{ code: 20, text: 'Auftrag ausgeführt' }],
|
|
428
|
+
},
|
|
429
|
+
],
|
|
430
|
+
[
|
|
431
|
+
'HKWPD',
|
|
432
|
+
{
|
|
433
|
+
dialogId: 'DIALOG1',
|
|
434
|
+
success: true,
|
|
435
|
+
requiresTan: false,
|
|
436
|
+
bankingInformationUpdated: false,
|
|
437
|
+
bankAnswers: [{ code: 20, text: 'Auftrag ausgeführt' }],
|
|
438
|
+
portfolioStatement: {
|
|
439
|
+
currency: 'EUR',
|
|
440
|
+
holdings: [
|
|
441
|
+
{
|
|
442
|
+
isin: 'DE0001234567',
|
|
443
|
+
price: 25.5,
|
|
444
|
+
currency: 'EUR',
|
|
445
|
+
},
|
|
446
|
+
],
|
|
447
|
+
totalValue: 2550.0,
|
|
448
|
+
},
|
|
449
|
+
},
|
|
450
|
+
],
|
|
451
|
+
]));
|
|
452
|
+
const response = (await client.getPortfolio('9876543210'));
|
|
453
|
+
expect(response.success).toBe(true);
|
|
454
|
+
expect(response.portfolioStatement?.holdings).toBeDefined();
|
|
455
|
+
expect(response.portfolioStatement.holdings.length).toBe(1);
|
|
456
|
+
expect(response.portfolioStatement.totalValue).toBe(2550.0);
|
|
457
|
+
});
|
|
458
|
+
it('fetches portfolio with optional parameters', async () => {
|
|
459
|
+
dialogStartMock.mockResolvedValueOnce(new Map([
|
|
460
|
+
[
|
|
461
|
+
'HKIDN',
|
|
462
|
+
{
|
|
463
|
+
dialogId: 'DIALOG1',
|
|
464
|
+
success: true,
|
|
465
|
+
requiresTan: false,
|
|
466
|
+
bankingInformationUpdated: false,
|
|
467
|
+
bankAnswers: [{ code: 20, text: 'Auftrag ausgeführt' }],
|
|
468
|
+
},
|
|
469
|
+
],
|
|
470
|
+
[
|
|
471
|
+
'HKWPD',
|
|
472
|
+
{
|
|
473
|
+
dialogId: 'DIALOG1',
|
|
474
|
+
success: true,
|
|
475
|
+
requiresTan: false,
|
|
476
|
+
bankingInformationUpdated: false,
|
|
477
|
+
bankAnswers: [{ code: 20, text: 'Auftrag ausgeführt' }],
|
|
478
|
+
portfolioStatement: {
|
|
479
|
+
currency: 'EUR',
|
|
480
|
+
holdings: [
|
|
481
|
+
{
|
|
482
|
+
isin: 'DE0001234567',
|
|
483
|
+
price: 25.5,
|
|
484
|
+
},
|
|
485
|
+
],
|
|
486
|
+
totalValue: 1000.0,
|
|
487
|
+
},
|
|
488
|
+
},
|
|
489
|
+
],
|
|
490
|
+
]));
|
|
491
|
+
const response = (await client.getPortfolio('9876543210', 'EUR', '1', 10));
|
|
492
|
+
expect(response.success).toBe(true);
|
|
493
|
+
expect(response.portfolioStatement.holdings).toBeDefined();
|
|
494
|
+
expect(response.portfolioStatement.holdings.length).toBe(1);
|
|
495
|
+
expect(response.portfolioStatement.holdings[0].isin).toBe('DE0001234567');
|
|
496
|
+
});
|
|
497
|
+
});
|
|
498
|
+
describe('getPortfolioWithTan', () => {
|
|
499
|
+
it('continues portfolio fetching with TAN', async () => {
|
|
500
|
+
dialogContinueMock.mockResolvedValueOnce(new Map([
|
|
501
|
+
[
|
|
502
|
+
'HKWPD',
|
|
503
|
+
{
|
|
504
|
+
dialogId: 'DIALOG1',
|
|
505
|
+
success: true,
|
|
506
|
+
requiresTan: false,
|
|
507
|
+
bankingInformationUpdated: false,
|
|
508
|
+
bankAnswers: [{ code: 20, text: 'Auftrag ausgeführt' }],
|
|
509
|
+
portfolioStatement: {
|
|
510
|
+
currency: 'EUR',
|
|
511
|
+
holdings: [],
|
|
512
|
+
},
|
|
513
|
+
},
|
|
514
|
+
],
|
|
515
|
+
]));
|
|
516
|
+
const response = (await client.getPortfolioWithTan('PORTFOLIOTAN123', '987654'));
|
|
517
|
+
expect(response.success).toBe(true);
|
|
518
|
+
expect(response.portfolioStatement).toBeDefined();
|
|
519
|
+
expect(dialogContinueMock).toHaveBeenCalledWith('PORTFOLIOTAN123', '987654');
|
|
520
|
+
});
|
|
521
|
+
});
|
|
522
|
+
describe('getCreditCardStatements', () => {
|
|
523
|
+
it('fetches credit card statements', async () => {
|
|
524
|
+
dialogStartMock.mockResolvedValueOnce(new Map([
|
|
525
|
+
[
|
|
526
|
+
'HKIDN',
|
|
527
|
+
{
|
|
528
|
+
dialogId: 'DIALOG1',
|
|
529
|
+
success: true,
|
|
530
|
+
requiresTan: false,
|
|
531
|
+
bankingInformationUpdated: false,
|
|
532
|
+
bankAnswers: [{ code: 20, text: 'Auftrag ausgeführt' }],
|
|
533
|
+
},
|
|
534
|
+
],
|
|
535
|
+
[
|
|
536
|
+
'DKKKU',
|
|
537
|
+
{
|
|
538
|
+
dialogId: 'DIALOG1',
|
|
539
|
+
success: true,
|
|
540
|
+
requiresTan: false,
|
|
541
|
+
bankingInformationUpdated: false,
|
|
542
|
+
bankAnswers: [{ code: 20, text: 'Auftrag ausgeführt' }],
|
|
543
|
+
statements: [
|
|
544
|
+
{
|
|
545
|
+
openingBalance: { date: new Date('2025-12-01'), currency: 'EUR', value: 0.0 },
|
|
546
|
+
closingBalance: { date: new Date('2025-12-23'), currency: 'EUR', value: -150.0 },
|
|
547
|
+
transactions: [],
|
|
548
|
+
},
|
|
549
|
+
],
|
|
550
|
+
},
|
|
551
|
+
],
|
|
552
|
+
]));
|
|
553
|
+
const response = (await client.getCreditCardStatements('1111222233'));
|
|
554
|
+
expect(response.success).toBe(true);
|
|
555
|
+
expect(response.statements).toBeDefined();
|
|
556
|
+
expect(response.statements.length).toBe(1);
|
|
557
|
+
expect(response.statements[0].closingBalance?.value).toBe(-150.0);
|
|
558
|
+
});
|
|
559
|
+
});
|
|
560
|
+
describe('getCreditCardStatementsWithTan', () => {
|
|
561
|
+
it('continues credit card statement fetching with TAN', async () => {
|
|
562
|
+
dialogContinueMock.mockResolvedValueOnce(new Map([
|
|
563
|
+
[
|
|
564
|
+
'DKKKU',
|
|
565
|
+
{
|
|
566
|
+
dialogId: 'DIALOG1',
|
|
567
|
+
success: true,
|
|
568
|
+
requiresTan: false,
|
|
569
|
+
bankingInformationUpdated: false,
|
|
570
|
+
bankAnswers: [{ code: 20, text: 'Auftrag ausgeführt' }],
|
|
571
|
+
statements: [],
|
|
572
|
+
},
|
|
573
|
+
],
|
|
574
|
+
]));
|
|
575
|
+
const response = (await client.getCreditCardStatementsWithTan('CCTAN123', '111222'));
|
|
576
|
+
expect(response.success).toBe(true);
|
|
577
|
+
expect(response.statements).toBeDefined();
|
|
578
|
+
expect(dialogContinueMock).toHaveBeenCalledWith('CCTAN123', '111222');
|
|
579
|
+
});
|
|
580
|
+
});
|
|
581
|
+
describe('capability checks', () => {
|
|
582
|
+
describe('canGetAccountBalance', () => {
|
|
583
|
+
it('returns true for general capability when bank supports it', () => {
|
|
584
|
+
expect(client.canGetAccountBalance()).toBe(true);
|
|
585
|
+
});
|
|
586
|
+
it('returns true for specific account that supports balance checking', () => {
|
|
587
|
+
expect(client.canGetAccountBalance('1234567890')).toBe(true);
|
|
588
|
+
});
|
|
589
|
+
it('returns false for account that does not support balance checking', () => {
|
|
590
|
+
expect(client.canGetAccountBalance('9876543210')).toBe(false);
|
|
591
|
+
});
|
|
592
|
+
});
|
|
593
|
+
describe('canGetAccountStatements', () => {
|
|
594
|
+
it('returns true for general capability when bank supports it', () => {
|
|
595
|
+
expect(client.canGetAccountStatements()).toBe(true);
|
|
596
|
+
});
|
|
597
|
+
it('returns true for account that supports statements', () => {
|
|
598
|
+
expect(client.canGetAccountStatements('1234567890')).toBe(true);
|
|
599
|
+
});
|
|
600
|
+
it('returns false for account that does not support statements', () => {
|
|
601
|
+
expect(client.canGetAccountStatements('1111222233')).toBe(false);
|
|
602
|
+
});
|
|
603
|
+
});
|
|
604
|
+
describe('canGetPortfolio', () => {
|
|
605
|
+
it('returns true for general capability when bank supports it', () => {
|
|
606
|
+
expect(client.canGetPortfolio()).toBe(true);
|
|
607
|
+
});
|
|
608
|
+
it('returns true for depot account', () => {
|
|
609
|
+
expect(client.canGetPortfolio('9876543210')).toBe(true);
|
|
610
|
+
});
|
|
611
|
+
it('returns false for non-depot account', () => {
|
|
612
|
+
expect(client.canGetPortfolio('1234567890')).toBe(false);
|
|
613
|
+
});
|
|
614
|
+
});
|
|
615
|
+
describe('canGetCreditCardStatements', () => {
|
|
616
|
+
it('returns true for general capability when bank supports it', () => {
|
|
617
|
+
expect(client.canGetCreditCardStatements()).toBe(true);
|
|
618
|
+
});
|
|
619
|
+
it('returns true for credit card account', () => {
|
|
620
|
+
expect(client.canGetCreditCardStatements('1111222233')).toBe(true);
|
|
621
|
+
});
|
|
622
|
+
it('returns false for non-credit card account', () => {
|
|
623
|
+
expect(client.canGetCreditCardStatements('1234567890')).toBe(false);
|
|
624
|
+
});
|
|
625
|
+
});
|
|
626
|
+
});
|
|
627
|
+
describe('TAN method and media selection', () => {
|
|
628
|
+
describe('selectTanMethod', () => {
|
|
629
|
+
it('selects TAN method by ID and returns the method', () => {
|
|
630
|
+
const tanMethod = client.selectTanMethod(2);
|
|
631
|
+
expect(tanMethod).toBeDefined();
|
|
632
|
+
expect(tanMethod.id).toBe(2);
|
|
633
|
+
expect(tanMethod.name).toBe('pushTAN');
|
|
634
|
+
expect(client.config.selectedTanMethod).toBe(tanMethod);
|
|
635
|
+
});
|
|
636
|
+
it('throws error for invalid TAN method ID', () => {
|
|
637
|
+
expect(() => client.selectTanMethod(999)).toThrow();
|
|
638
|
+
});
|
|
639
|
+
});
|
|
640
|
+
describe('selectTanMedia', () => {
|
|
641
|
+
it('selects TAN media by name', () => {
|
|
642
|
+
// First select a TAN method that has TAN media
|
|
643
|
+
client.selectTanMethod(1);
|
|
644
|
+
expect(() => client.selectTanMedia('TAN-Generator 123')).not.toThrow();
|
|
645
|
+
});
|
|
646
|
+
it('throws error for invalid TAN media name', () => {
|
|
647
|
+
client.selectTanMethod(1);
|
|
648
|
+
expect(() => client.selectTanMedia('Invalid Media')).toThrow();
|
|
649
|
+
});
|
|
650
|
+
});
|
|
32
651
|
});
|
|
33
652
|
});
|