@open-nav/mock-server 0.1.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.
@@ -0,0 +1,492 @@
1
+ import { randomBytes, createCipheriv } from 'node:crypto';
2
+ import { HEADER_VERSION, REQUEST_VERSION, decodeInvoiceData, decodeToXml, passwordHash, requestSignature, serializeDocument, validateInvoice, } from '@open-nav/core';
3
+ const SOFTWARE = {
4
+ softwareId: 'OPENNAVMOCK000001',
5
+ softwareName: 'open-nav mock server',
6
+ softwareOperation: 'LOCAL_SOFTWARE',
7
+ softwareMainVersion: '0.1.0',
8
+ softwareDevName: 'open-nav',
9
+ softwareDevContact: 'https://github.com/eshton/open-nav',
10
+ };
11
+ export class MockError extends Error {
12
+ errorCode;
13
+ status;
14
+ constructor(errorCode, message, status = 400) {
15
+ super(message);
16
+ this.errorCode = errorCode;
17
+ this.status = status;
18
+ }
19
+ }
20
+ function timestamp(config) {
21
+ return config.now().toISOString();
22
+ }
23
+ function header(config) {
24
+ return {
25
+ requestId: `MOCK${config.now().getTime().toString(36).toUpperCase()}`,
26
+ timestamp: timestamp(config),
27
+ requestVersion: REQUEST_VERSION,
28
+ headerVersion: HEADER_VERSION,
29
+ };
30
+ }
31
+ function ok(config) {
32
+ return { header: header(config), result: { funcCode: 'OK' }, software: SOFTWARE };
33
+ }
34
+ export function errorResponse(config, errorCode, message) {
35
+ return serializeDocument('GeneralErrorResponse', {
36
+ header: header(config),
37
+ result: { funcCode: 'ERROR', errorCode, message },
38
+ software: SOFTWARE,
39
+ technicalValidationMessages: [
40
+ { validationResultCode: 'ERROR', validationErrorCode: errorCode, message },
41
+ ],
42
+ });
43
+ }
44
+ /**
45
+ * Verify the request the way NAV does.
46
+ *
47
+ * This is the reason a mock is worth having: it recomputes the signature from
48
+ * the request's own fields and the expected sign key, so a caller that builds
49
+ * the signature wrongly finds out here rather than against the live service,
50
+ * where the only clue is an opaque INVALID_SIGNATURE.
51
+ */
52
+ export function authenticate(context, config, state, signedOperations = []) {
53
+ const value = context.document.value;
54
+ const requestId = value.header?.requestId;
55
+ const stamp = value.header?.timestamp;
56
+ if (!requestId || !stamp) {
57
+ throw new MockError('INVALID_REQUEST', 'header/requestId and header/timestamp are required');
58
+ }
59
+ if (value.header?.requestVersion !== REQUEST_VERSION) {
60
+ throw new MockError('INVALID_REQUEST', `requestVersion must be ${REQUEST_VERSION}, got ${value.header?.requestVersion}`);
61
+ }
62
+ if (!value.software) {
63
+ throw new MockError('INVALID_REQUEST', 'the software block is required on every request');
64
+ }
65
+ // NAV rejects a replayed requestId, and so does this.
66
+ if (state.requestIds.has(requestId)) {
67
+ throw new MockError('INVALID_REQUEST_ID', `requestId ${requestId} has already been used`);
68
+ }
69
+ state.requestIds.add(requestId);
70
+ const user = value.user;
71
+ if (user?.login !== config.credentials.login) {
72
+ throw new MockError('INVALID_SECURITY_USER', 'unknown technical user');
73
+ }
74
+ if (user.passwordHash?.value !== passwordHash(config.credentials.password)) {
75
+ throw new MockError('INVALID_SECURITY_USER', 'password hash does not match');
76
+ }
77
+ if (user.passwordHash?.cryptoType !== 'SHA-512') {
78
+ throw new MockError('INVALID_SECURITY_USER', 'passwordHash cryptoType must be SHA-512');
79
+ }
80
+ if (user.taxNumber !== config.credentials.taxNumber) {
81
+ throw new MockError('INVALID_SECURITY_USER', 'taxNumber does not match the technical user');
82
+ }
83
+ const expected = requestSignature(requestId, stamp, config.credentials.signKey, signedOperations);
84
+ if (user.requestSignature?.value !== expected) {
85
+ throw new MockError('INVALID_SIGNATURE', signedOperations.length > 0
86
+ ? `requestSignature does not match; expected the SHA3-512 of requestId + timestamp + signKey + ${signedOperations.length} operation hash(es) in index order`
87
+ : 'requestSignature does not match the SHA3-512 of requestId + timestamp + signKey');
88
+ }
89
+ if (user.requestSignature?.cryptoType !== 'SHA3-512') {
90
+ throw new MockError('INVALID_SIGNATURE', 'requestSignature cryptoType must be SHA3-512');
91
+ }
92
+ }
93
+ export function handleTokenExchange(context, config, state) {
94
+ authenticate(context, config, state);
95
+ // A 16 character token, encrypted the way NAV encrypts it.
96
+ const token = randomBytes(8).toString('hex').toUpperCase();
97
+ state.tokens.set(token, { issuedAt: config.now().getTime(), spent: false });
98
+ const cipher = createCipheriv('aes-128-ecb', Buffer.from(config.credentials.exchangeKey, 'utf8'), null);
99
+ cipher.setAutoPadding(false);
100
+ const encoded = Buffer.concat([
101
+ cipher.update(Buffer.from(token, 'utf8')),
102
+ cipher.final(),
103
+ ]).toString('base64');
104
+ const validFrom = config.now();
105
+ const validTo = new Date(validFrom.getTime() + 5 * 60_000);
106
+ return {
107
+ status: 200,
108
+ body: serializeDocument('TokenExchangeResponse', {
109
+ ...ok(config),
110
+ encodedExchangeToken: encoded,
111
+ tokenValidityFrom: validFrom.toISOString(),
112
+ tokenValidityTo: validTo.toISOString(),
113
+ }),
114
+ };
115
+ }
116
+ function readInvoiceOperations(context) {
117
+ const value = context.document.value;
118
+ const list = value.invoiceOperations?.invoiceOperation ?? [];
119
+ if (list.length === 0) {
120
+ throw new MockError('INVALID_REQUEST', 'invoiceOperations must contain at least one operation');
121
+ }
122
+ return {
123
+ compressed: value.invoiceOperations?.compressedContent === true,
124
+ entries: list.map((entry) => ({
125
+ index: entry.index,
126
+ operation: entry.invoiceOperation,
127
+ base64: entry.invoiceData,
128
+ })),
129
+ };
130
+ }
131
+ function consumeToken(context, state) {
132
+ const token = context.document.value.exchangeToken;
133
+ if (!token)
134
+ throw new MockError('INVALID_EXCHANGE_TOKEN', 'exchangeToken is required');
135
+ const record = state.tokens.get(token);
136
+ if (!record)
137
+ throw new MockError('INVALID_EXCHANGE_TOKEN', 'unknown exchange token');
138
+ if (record.spent) {
139
+ throw new MockError('INVALID_EXCHANGE_TOKEN', 'this exchange token has already been used');
140
+ }
141
+ record.spent = true;
142
+ }
143
+ export function handleManageInvoice(context, config, state) {
144
+ const { compressed, entries } = readInvoiceOperations(context);
145
+ authenticate(context, config, state, entries.map((entry) => ({
146
+ index: entry.index,
147
+ operation: entry.operation,
148
+ base64Payload: entry.base64,
149
+ })));
150
+ consumeToken(context, state);
151
+ const transactionId = `MOCKTX${(state.transactions.size + 1).toString().padStart(6, '0')}`;
152
+ const insDate = timestamp(config);
153
+ const results = [];
154
+ for (const entry of entries) {
155
+ let invoice;
156
+ try {
157
+ invoice = decodeInvoiceData(entry.base64, { compressed });
158
+ }
159
+ catch (cause) {
160
+ results.push({
161
+ index: entry.index,
162
+ invoiceStatus: 'ABORTED',
163
+ businessValidationMessages: [
164
+ {
165
+ validationResultCode: 'ERROR',
166
+ validationErrorCode: 'SCHEMA_VIOLATION',
167
+ message: `invoiceData could not be read: ${cause.message}`,
168
+ },
169
+ ],
170
+ });
171
+ continue;
172
+ }
173
+ // The mock rejects what the validator predicts NAV would reject, so a
174
+ // test that submits a broken invoice sees a realistic ABORTED result.
175
+ const report = config.validate
176
+ ? validateInvoice(invoice, {
177
+ operation: entry.operation,
178
+ supplierTaxNumber: config.credentials.taxNumber,
179
+ })
180
+ : { valid: true, errors: [], warnings: [], issues: [] };
181
+ if (!report.valid) {
182
+ results.push({
183
+ index: entry.index,
184
+ invoiceStatus: 'ABORTED',
185
+ businessValidationMessages: report.errors.map((issue) => ({
186
+ validationResultCode: 'ERROR',
187
+ validationErrorCode: issue.code,
188
+ message: `${issue.path}: ${issue.message}`,
189
+ })),
190
+ });
191
+ continue;
192
+ }
193
+ const head = invoice.invoiceMain.invoice?.invoiceHead;
194
+ const stored = {
195
+ invoiceNumber: invoice.invoiceNumber,
196
+ operation: entry.operation,
197
+ supplierTaxNumber: head?.supplierInfo.supplierTaxNumber.taxpayerId ?? '',
198
+ ...(head?.customerInfo?.customerVatData?.customerTaxNumber?.taxpayerId
199
+ ? { customerTaxNumber: head.customerInfo.customerVatData.customerTaxNumber.taxpayerId }
200
+ : {}),
201
+ issueDate: invoice.invoiceIssueDate,
202
+ base64: entry.base64,
203
+ compressed,
204
+ invoice,
205
+ transactionId,
206
+ index: entry.index,
207
+ insDate,
208
+ };
209
+ state.invoices.set(invoice.invoiceNumber, stored);
210
+ results.push({
211
+ index: entry.index,
212
+ invoiceStatus: 'DONE',
213
+ businessValidationMessages: report.warnings.map((issue) => ({
214
+ validationResultCode: 'WARN',
215
+ validationErrorCode: issue.code,
216
+ message: `${issue.path}: ${issue.message}`,
217
+ })),
218
+ });
219
+ }
220
+ state.transactions.set(transactionId, {
221
+ transactionId,
222
+ insDate,
223
+ polls: 0,
224
+ results,
225
+ finalStatuses: results.map((result) => result.invoiceStatus),
226
+ });
227
+ return {
228
+ status: 200,
229
+ body: serializeDocument('ManageInvoiceResponse', { ...ok(config), transactionId }),
230
+ };
231
+ }
232
+ export function handleQueryTransactionStatus(context, config, state) {
233
+ authenticate(context, config, state);
234
+ const transactionId = context.document.value.transactionId;
235
+ if (!transactionId)
236
+ throw new MockError('INVALID_REQUEST', 'transactionId is required');
237
+ const transaction = state.transactions.get(transactionId);
238
+ if (!transaction) {
239
+ // NAV answers an unknown transaction with an empty result, not an error.
240
+ return {
241
+ status: 200,
242
+ body: serializeDocument('QueryTransactionStatusResponse', { ...ok(config) }),
243
+ };
244
+ }
245
+ transaction.polls += 1;
246
+ const settled = transaction.polls > config.pollsBeforeDone;
247
+ return {
248
+ status: 200,
249
+ body: serializeDocument('QueryTransactionStatusResponse', {
250
+ ...ok(config),
251
+ processingResults: {
252
+ processingResult: transaction.results.map((result, position) => ({
253
+ index: result.index,
254
+ invoiceStatus: settled
255
+ ? (transaction.finalStatuses[position] ?? 'DONE')
256
+ : transaction.polls === 1
257
+ ? 'RECEIVED'
258
+ : 'PROCESSING',
259
+ compressedContentIndicator: false,
260
+ ...(settled && result.businessValidationMessages.length > 0
261
+ ? { businessValidationMessages: result.businessValidationMessages }
262
+ : {}),
263
+ })),
264
+ originalRequestVersion: REQUEST_VERSION,
265
+ },
266
+ }),
267
+ };
268
+ }
269
+ export function handleQueryTaxpayer(context, config, state) {
270
+ authenticate(context, config, state);
271
+ const taxNumber = context.document.value.taxNumber;
272
+ if (!taxNumber)
273
+ throw new MockError('INVALID_REQUEST', 'taxNumber is required');
274
+ const taxpayer = state.taxpayers.get(taxNumber);
275
+ return {
276
+ status: 200,
277
+ body: serializeDocument('QueryTaxpayerResponse', {
278
+ ...ok(config),
279
+ infoDate: timestamp(config),
280
+ taxpayerValidity: taxpayer?.valid ?? false,
281
+ ...(taxpayer
282
+ ? {
283
+ taxpayerData: {
284
+ taxpayerName: taxpayer.name,
285
+ ...(taxpayer.shortName ? { taxpayerShortName: taxpayer.shortName } : {}),
286
+ taxNumberDetail: {
287
+ taxpayerId: taxpayer.taxNumber,
288
+ ...(taxpayer.vatCode ? { vatCode: taxpayer.vatCode } : {}),
289
+ ...(taxpayer.countyCode ? { countyCode: taxpayer.countyCode } : {}),
290
+ },
291
+ incorporation: 'OTHER',
292
+ },
293
+ }
294
+ : {}),
295
+ }),
296
+ };
297
+ }
298
+ export function handleQueryInvoiceCheck(context, config, state) {
299
+ authenticate(context, config, state);
300
+ const query = context.document.value.invoiceNumberQuery;
301
+ const store = query?.invoiceDirection === 'INBOUND' ? state.inbound : state.invoices;
302
+ return {
303
+ status: 200,
304
+ body: serializeDocument('QueryInvoiceCheckResponse', {
305
+ ...ok(config),
306
+ invoiceCheckResult: store.has(query?.invoiceNumber ?? ''),
307
+ }),
308
+ };
309
+ }
310
+ export function handleQueryInvoiceData(context, config, state) {
311
+ authenticate(context, config, state);
312
+ const query = context.document.value.invoiceNumberQuery;
313
+ const inbound = query?.invoiceDirection === 'INBOUND';
314
+ // NAV only accepts a supplier tax number when querying as the customer.
315
+ if (!inbound && query?.supplierTaxNumber !== undefined) {
316
+ throw new MockError('INVALID_REQUEST', 'BAD_QUERY_PARAM_SUPPLIER_NOT_EXPECTED: the supplier tax number is only usable when querying as customer');
317
+ }
318
+ const store = inbound ? state.inbound : state.invoices;
319
+ const stored = store.get(query?.invoiceNumber ?? '');
320
+ if (!stored) {
321
+ return { status: 200, body: serializeDocument('QueryInvoiceDataResponse', { ...ok(config) }) };
322
+ }
323
+ return {
324
+ status: 200,
325
+ body: serializeDocument('QueryInvoiceDataResponse', {
326
+ ...ok(config),
327
+ invoiceDataResult: {
328
+ invoiceData: stored.base64,
329
+ auditData: {
330
+ insdate: stored.insDate,
331
+ insCusUser: config.credentials.login,
332
+ source: 'MGM',
333
+ transactionId: stored.transactionId,
334
+ index: stored.index,
335
+ originalRequestVersion: REQUEST_VERSION,
336
+ },
337
+ compressedContentIndicator: stored.compressed,
338
+ },
339
+ }),
340
+ };
341
+ }
342
+ export function handleQueryInvoiceDigest(context, config, state) {
343
+ authenticate(context, config, state);
344
+ const value = context.document.value;
345
+ const range = value.invoiceQueryParams?.mandatoryQueryParams?.invoiceIssueDate;
346
+ // NAV rejects a window wider than 35 days, so the mock does too: that is
347
+ // what proves a caller splits its range instead of asking for a year.
348
+ if (range?.dateFrom && range?.dateTo) {
349
+ const days = (Date.parse(`${range.dateTo}T00:00:00Z`) - Date.parse(`${range.dateFrom}T00:00:00Z`)) /
350
+ 86_400_000;
351
+ if (days > 34) {
352
+ throw new MockError('INVALID_REQUEST', 'BAD_QUERY_PARAM_RANGE_EXCEEDED: date interval defined by the query parameters must not exceed 35 days');
353
+ }
354
+ }
355
+ const store = value.invoiceDirection === 'INBOUND' ? state.inbound : state.invoices;
356
+ const matching = [...store.values()].filter((invoice) => {
357
+ if (range?.dateFrom && invoice.issueDate < range.dateFrom)
358
+ return false;
359
+ if (range?.dateTo && invoice.issueDate > range.dateTo)
360
+ return false;
361
+ return true;
362
+ });
363
+ const pageSize = 100;
364
+ const page = value.page ?? 1;
365
+ const slice = matching.slice((page - 1) * pageSize, page * pageSize);
366
+ return {
367
+ status: 200,
368
+ body: serializeDocument('QueryInvoiceDigestResponse', {
369
+ ...ok(config),
370
+ invoiceDigestResult: {
371
+ currentPage: page,
372
+ availablePage: Math.max(1, Math.ceil(matching.length / pageSize)),
373
+ ...(slice.length > 0
374
+ ? {
375
+ invoiceDigest: slice.map((invoice) => ({
376
+ invoiceNumber: invoice.invoiceNumber,
377
+ invoiceOperation: invoice.operation,
378
+ invoiceCategory: invoice.invoice.invoiceMain.invoice?.invoiceHead.invoiceDetail.invoiceCategory ??
379
+ 'NORMAL',
380
+ invoiceIssueDate: invoice.issueDate,
381
+ supplierTaxNumber: invoice.supplierTaxNumber,
382
+ supplierName: invoice.invoice.invoiceMain.invoice?.invoiceHead.supplierInfo.supplierName ?? '',
383
+ ...(invoice.customerTaxNumber
384
+ ? { customerTaxNumber: invoice.customerTaxNumber }
385
+ : {}),
386
+ transactionId: invoice.transactionId,
387
+ index: invoice.index,
388
+ insDate: invoice.insDate,
389
+ ...digestAmounts(invoice.invoice),
390
+ })),
391
+ }
392
+ : {}),
393
+ },
394
+ }),
395
+ };
396
+ }
397
+ /**
398
+ * The amount fields a digest carries.
399
+ *
400
+ * NAV's digest summarises an invoice, so a caller can decide what to fetch in
401
+ * full without downloading everything. Populating them keeps the mock useful
402
+ * for exercising code that reads a digest.
403
+ */
404
+ function digestAmounts(document) {
405
+ const invoice = document.invoiceMain.invoice;
406
+ const summary = invoice?.invoiceSummary.summaryNormal;
407
+ const currency = invoice?.invoiceHead.invoiceDetail.currencyCode;
408
+ return {
409
+ ...(currency ? { currency } : {}),
410
+ ...(summary
411
+ ? {
412
+ invoiceNetAmount: summary.invoiceNetAmount,
413
+ invoiceNetAmountHUF: summary.invoiceNetAmountHUF,
414
+ invoiceVatAmount: summary.invoiceVatAmount,
415
+ invoiceVatAmountHUF: summary.invoiceVatAmountHUF,
416
+ }
417
+ : {}),
418
+ };
419
+ }
420
+ export function handleQueryTransactionList(context, config, state) {
421
+ authenticate(context, config, state);
422
+ const page = context.document.value.page ?? 1;
423
+ const all = [...state.transactions.values()];
424
+ return {
425
+ status: 200,
426
+ body: serializeDocument('QueryTransactionListResponse', {
427
+ ...ok(config),
428
+ transactionListResult: {
429
+ currentPage: page,
430
+ availablePage: 1,
431
+ ...(all.length > 0
432
+ ? {
433
+ transaction: all.map((transaction) => ({
434
+ insDate: transaction.insDate,
435
+ insCusUser: config.credentials.login,
436
+ source: 'MGM',
437
+ transactionId: transaction.transactionId,
438
+ requestStatus: 'DONE',
439
+ technicalAnnulment: false,
440
+ originalRequestVersion: REQUEST_VERSION,
441
+ itemCount: transaction.results.length,
442
+ })),
443
+ }
444
+ : {}),
445
+ },
446
+ }),
447
+ };
448
+ }
449
+ export function handleManageAnnulment(context, config, state) {
450
+ const value = context.document.value;
451
+ const entries = value.annulmentOperations?.annulmentOperation ?? [];
452
+ if (entries.length === 0) {
453
+ throw new MockError('INVALID_REQUEST', 'annulmentOperations must contain at least one operation');
454
+ }
455
+ authenticate(context, config, state, entries.map((entry) => ({
456
+ index: entry.index,
457
+ operation: entry.annulmentOperation,
458
+ base64Payload: entry.invoiceAnnulment,
459
+ })));
460
+ consumeToken(context, state);
461
+ // Confirm the payload is readable, the way the service would.
462
+ for (const entry of entries)
463
+ decodeToXml(entry.invoiceAnnulment);
464
+ const transactionId = `MOCKAN${(state.transactions.size + 1).toString().padStart(6, '0')}`;
465
+ state.transactions.set(transactionId, {
466
+ transactionId,
467
+ insDate: timestamp(config),
468
+ polls: 0,
469
+ results: entries.map((entry) => ({
470
+ index: entry.index,
471
+ invoiceStatus: 'DONE',
472
+ businessValidationMessages: [],
473
+ })),
474
+ finalStatuses: entries.map(() => 'DONE'),
475
+ });
476
+ return {
477
+ status: 200,
478
+ body: serializeDocument('ManageAnnulmentResponse', { ...ok(config), transactionId }),
479
+ };
480
+ }
481
+ export const HANDLERS = {
482
+ tokenExchange: handleTokenExchange,
483
+ manageInvoice: handleManageInvoice,
484
+ manageAnnulment: handleManageAnnulment,
485
+ queryTransactionStatus: handleQueryTransactionStatus,
486
+ queryTransactionList: handleQueryTransactionList,
487
+ queryInvoiceData: handleQueryInvoiceData,
488
+ queryInvoiceDigest: handleQueryInvoiceDigest,
489
+ queryInvoiceCheck: handleQueryInvoiceCheck,
490
+ queryTaxpayer: handleQueryTaxpayer,
491
+ };
492
+ //# sourceMappingURL=handlers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handlers.js","sourceRoot":"","sources":["../src/handlers.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ export * from './server.js';
2
+ export * from './state.js';
3
+ export type { MockCredentials } from './handlers.js';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC;AAC3B,YAAY,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,3 @@
1
+ export * from './server.js';
2
+ export * from './state.js';
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC"}
@@ -0,0 +1,51 @@
1
+ import { type Server } from 'node:http';
2
+ import { type InvoiceData } from '@open-nav/core';
3
+ import { type MockCredentials } from './handlers.js';
4
+ import { type MockState, type MockTaxpayer } from './state.js';
5
+ export interface MockServerOptions {
6
+ /** Credentials the mock accepts. Anything else is INVALID_SECURITY_USER. */
7
+ credentials: MockCredentials;
8
+ /**
9
+ * Invoices issued *to* this taxpayer, answered by an INBOUND query.
10
+ *
11
+ * Submitting an invoice makes it outbound; there is no way to receive one,
12
+ * so inbound invoices are seeded here.
13
+ */
14
+ inboundInvoices?: InvoiceData[];
15
+ /** Taxpayers `queryTaxpayer` knows about. */
16
+ taxpayers?: MockTaxpayer[];
17
+ /** Port to listen on. 0, the default, picks a free one. */
18
+ port?: number;
19
+ host?: string;
20
+ /** Polls before a transaction settles. 0 means immediately. */
21
+ pollsBeforeDone?: number;
22
+ /** Validate submitted invoices and abort the invalid ones. Default true. */
23
+ validate?: boolean;
24
+ /** Injectable clock. */
25
+ now?: () => Date;
26
+ }
27
+ export interface MockServer {
28
+ /** Base URL to hand to a `NavClient`, including the service path. */
29
+ url: string;
30
+ port: number;
31
+ /** Everything the mock has recorded, for assertions. */
32
+ state: MockState;
33
+ close: () => Promise<void>;
34
+ server: Server;
35
+ }
36
+ /**
37
+ * Start a local stand-in for the Online Számla invoice service.
38
+ *
39
+ * It speaks the real XML on the real paths, verifies the request signature
40
+ * the way NAV does, and decides an invoice's fate by running it through this
41
+ * project's validator. That last part is what makes it useful: a test that
42
+ * submits a broken invoice sees a realistic ABORTED result with the fault
43
+ * code NAV would have reported.
44
+ *
45
+ * ```ts
46
+ * const mock = await startMockServer({ credentials });
47
+ * const client = new NavClient({ credentials, software, baseUrl: mock.url });
48
+ * ```
49
+ */
50
+ export declare function startMockServer(options: MockServerOptions): Promise<MockServer>;
51
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,KAAK,MAAM,EAAE,MAAM,WAAW,CAAC;AAEtD,OAAO,EAAoC,KAAK,WAAW,EAAE,MAAM,gBAAgB,CAAC;AACpF,OAAO,EAKL,KAAK,eAAe,EACrB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAe,KAAK,SAAS,EAAE,KAAK,YAAY,EAAE,MAAM,YAAY,CAAC;AAE5E,MAAM,WAAW,iBAAiB;IAChC,4EAA4E;IAC5E,WAAW,EAAE,eAAe,CAAC;IAC7B;;;;;OAKG;IACH,eAAe,CAAC,EAAE,WAAW,EAAE,CAAC;IAChC,6CAA6C;IAC7C,SAAS,CAAC,EAAE,YAAY,EAAE,CAAC;IAC3B,2DAA2D;IAC3D,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,+DAA+D;IAC/D,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,4EAA4E;IAC5E,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,wBAAwB;IACxB,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;CAClB;AAED,MAAM,WAAW,UAAU;IACzB,qEAAqE;IACrE,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,wDAAwD;IACxD,KAAK,EAAE,SAAS,CAAC;IACjB,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3B,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,eAAe,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,UAAU,CAAC,CA4CrF"}