@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.
- package/LICENSE +29 -0
- package/README.md +77 -0
- package/dist/bin.d.ts +3 -0
- package/dist/bin.d.ts.map +1 -0
- package/dist/bin.js +81 -0
- package/dist/bin.js.map +1 -0
- package/dist/handlers.d.ts +73 -0
- package/dist/handlers.d.ts.map +1 -0
- package/dist/handlers.js +492 -0
- package/dist/handlers.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/server.d.ts +51 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +123 -0
- package/dist/server.js.map +1 -0
- package/dist/state.d.ts +73 -0
- package/dist/state.d.ts.map +1 -0
- package/dist/state.js +12 -0
- package/dist/state.js.map +1 -0
- package/package.json +58 -0
- package/src/bin.ts +86 -0
- package/src/handlers.ts +733 -0
- package/src/index.ts +3 -0
- package/src/server.ts +192 -0
- package/src/state.ts +80 -0
package/dist/server.js
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { createServer } from 'node:http';
|
|
2
|
+
import { encodeInvoiceData, parseDocument } from '@open-nav/core';
|
|
3
|
+
import { HANDLERS, MockError, errorResponse, } from './handlers.js';
|
|
4
|
+
import { createState } from './state.js';
|
|
5
|
+
/**
|
|
6
|
+
* Start a local stand-in for the Online Számla invoice service.
|
|
7
|
+
*
|
|
8
|
+
* It speaks the real XML on the real paths, verifies the request signature
|
|
9
|
+
* the way NAV does, and decides an invoice's fate by running it through this
|
|
10
|
+
* project's validator. That last part is what makes it useful: a test that
|
|
11
|
+
* submits a broken invoice sees a realistic ABORTED result with the fault
|
|
12
|
+
* code NAV would have reported.
|
|
13
|
+
*
|
|
14
|
+
* ```ts
|
|
15
|
+
* const mock = await startMockServer({ credentials });
|
|
16
|
+
* const client = new NavClient({ credentials, software, baseUrl: mock.url });
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
export async function startMockServer(options) {
|
|
20
|
+
const state = createState(options.taxpayers ?? []);
|
|
21
|
+
seedInbound(state, options.inboundInvoices ?? [], options.now ?? (() => new Date()));
|
|
22
|
+
const config = {
|
|
23
|
+
credentials: options.credentials,
|
|
24
|
+
pollsBeforeDone: options.pollsBeforeDone ?? 0,
|
|
25
|
+
validate: options.validate ?? true,
|
|
26
|
+
now: options.now ?? (() => new Date()),
|
|
27
|
+
};
|
|
28
|
+
const server = createServer((request, response) => {
|
|
29
|
+
const chunks = [];
|
|
30
|
+
request.on('data', (chunk) => chunks.push(chunk));
|
|
31
|
+
request.on('end', () => {
|
|
32
|
+
const body = Buffer.concat(chunks).toString('utf8');
|
|
33
|
+
const { status, body: responseBody } = dispatch(request.method ?? '', request.url ?? '', body, config, state);
|
|
34
|
+
response.writeHead(status, { 'content-type': 'application/xml; charset=utf-8' });
|
|
35
|
+
response.end(responseBody);
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
await new Promise((resolve) => {
|
|
39
|
+
server.listen(options.port ?? 0, options.host ?? '127.0.0.1', resolve);
|
|
40
|
+
});
|
|
41
|
+
const address = server.address();
|
|
42
|
+
const host = options.host ?? '127.0.0.1';
|
|
43
|
+
return {
|
|
44
|
+
url: `http://${host}:${address.port}/invoiceService/v1`,
|
|
45
|
+
port: address.port,
|
|
46
|
+
state,
|
|
47
|
+
server,
|
|
48
|
+
close: () => new Promise((resolve, reject) => {
|
|
49
|
+
server.close((error) => (error ? reject(error) : resolve()));
|
|
50
|
+
}),
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
/** Store seeded invoices as if they had been received. */
|
|
54
|
+
function seedInbound(state, invoices, now) {
|
|
55
|
+
for (const [index, invoice] of invoices.entries()) {
|
|
56
|
+
const head = invoice.invoiceMain.invoice?.invoiceHead;
|
|
57
|
+
state.inbound.set(invoice.invoiceNumber, {
|
|
58
|
+
invoiceNumber: invoice.invoiceNumber,
|
|
59
|
+
operation: 'CREATE',
|
|
60
|
+
supplierTaxNumber: head?.supplierInfo.supplierTaxNumber.taxpayerId ?? '',
|
|
61
|
+
...(head?.customerInfo?.customerVatData?.customerTaxNumber?.taxpayerId
|
|
62
|
+
? { customerTaxNumber: head.customerInfo.customerVatData.customerTaxNumber.taxpayerId }
|
|
63
|
+
: {}),
|
|
64
|
+
issueDate: invoice.invoiceIssueDate,
|
|
65
|
+
base64: encodeInvoiceData(invoice),
|
|
66
|
+
compressed: false,
|
|
67
|
+
invoice,
|
|
68
|
+
transactionId: `MOCKIN${(index + 1).toString().padStart(6, '0')}`,
|
|
69
|
+
index: index + 1,
|
|
70
|
+
insDate: now().toISOString(),
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
function dispatch(method, url, body, config, state) {
|
|
75
|
+
if (method !== 'POST') {
|
|
76
|
+
return {
|
|
77
|
+
status: 405,
|
|
78
|
+
body: errorResponse(config, 'INVALID_REQUEST', 'only POST is supported'),
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
const operation = url.split('?')[0]?.split('/').filter(Boolean).pop() ?? '';
|
|
82
|
+
const handler = HANDLERS[operation];
|
|
83
|
+
if (!handler) {
|
|
84
|
+
return {
|
|
85
|
+
status: 404,
|
|
86
|
+
body: errorResponse(config, 'INVALID_REQUEST', `unknown operation ${operation}`),
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
let document;
|
|
90
|
+
try {
|
|
91
|
+
// Lenient, because a caller's extra element should surface as a specific
|
|
92
|
+
// complaint from a handler rather than as an opaque parse failure.
|
|
93
|
+
document = parseDocument(body, { unknownElements: 'ignore' });
|
|
94
|
+
}
|
|
95
|
+
catch (cause) {
|
|
96
|
+
return {
|
|
97
|
+
status: 400,
|
|
98
|
+
body: errorResponse(config, 'INVALID_REQUEST', `request XML could not be parsed: ${cause.message}`),
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
state.requests.push({
|
|
102
|
+
operation,
|
|
103
|
+
requestId: document.value.header?.requestId ?? '',
|
|
104
|
+
body,
|
|
105
|
+
});
|
|
106
|
+
try {
|
|
107
|
+
return handler({
|
|
108
|
+
operation,
|
|
109
|
+
body,
|
|
110
|
+
document: { root: document.root, value: document.value },
|
|
111
|
+
}, config, state);
|
|
112
|
+
}
|
|
113
|
+
catch (error) {
|
|
114
|
+
if (error instanceof MockError) {
|
|
115
|
+
return { status: error.status, body: errorResponse(config, error.errorCode, error.message) };
|
|
116
|
+
}
|
|
117
|
+
return {
|
|
118
|
+
status: 500,
|
|
119
|
+
body: errorResponse(config, 'OPERATION_FAILED', error.message),
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
|
package/dist/state.d.ts
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import type { InvoiceData, InvoiceStatusType } from '@open-nav/core';
|
|
2
|
+
/**
|
|
3
|
+
* Everything the mock service remembers.
|
|
4
|
+
*
|
|
5
|
+
* Held in memory and exposed on the server handle, so a test can assert on
|
|
6
|
+
* what was actually received rather than only on what came back.
|
|
7
|
+
*/
|
|
8
|
+
export interface MockState {
|
|
9
|
+
/** Invoices accepted, keyed by invoice number. */
|
|
10
|
+
invoices: Map<string, StoredInvoice>;
|
|
11
|
+
/** Invoices issued *to* this taxpayer, seeded by the test. */
|
|
12
|
+
inbound: Map<string, StoredInvoice>;
|
|
13
|
+
/** Transactions, keyed by transaction id. */
|
|
14
|
+
transactions: Map<string, StoredTransaction>;
|
|
15
|
+
/** Every `requestId` seen, since NAV rejects a replayed one. */
|
|
16
|
+
requestIds: Set<string>;
|
|
17
|
+
/** Exchange tokens issued and not yet spent. */
|
|
18
|
+
tokens: Map<string, {
|
|
19
|
+
issuedAt: number;
|
|
20
|
+
spent: boolean;
|
|
21
|
+
}>;
|
|
22
|
+
/** Taxpayers the mock knows about, keyed by 8 digit core tax number. */
|
|
23
|
+
taxpayers: Map<string, MockTaxpayer>;
|
|
24
|
+
/** Requests received, newest last, for assertions. */
|
|
25
|
+
requests: Array<{
|
|
26
|
+
operation: string;
|
|
27
|
+
requestId: string;
|
|
28
|
+
body: string;
|
|
29
|
+
}>;
|
|
30
|
+
}
|
|
31
|
+
export interface StoredInvoice {
|
|
32
|
+
invoiceNumber: string;
|
|
33
|
+
/** `CREATE`, `MODIFY` or `STORNO`. */
|
|
34
|
+
operation: string;
|
|
35
|
+
supplierTaxNumber: string;
|
|
36
|
+
customerTaxNumber?: string;
|
|
37
|
+
issueDate: string;
|
|
38
|
+
/** The payload exactly as submitted, still base64. */
|
|
39
|
+
base64: string;
|
|
40
|
+
compressed: boolean;
|
|
41
|
+
invoice: InvoiceData;
|
|
42
|
+
transactionId: string;
|
|
43
|
+
index: number;
|
|
44
|
+
insDate: string;
|
|
45
|
+
}
|
|
46
|
+
export interface StoredTransaction {
|
|
47
|
+
transactionId: string;
|
|
48
|
+
insDate: string;
|
|
49
|
+
/** Poll count, used to simulate asynchronous processing. */
|
|
50
|
+
polls: number;
|
|
51
|
+
results: Array<{
|
|
52
|
+
index: number;
|
|
53
|
+
invoiceStatus: InvoiceStatusType;
|
|
54
|
+
/** Findings, mirroring NAV's split between technical and business faults. */
|
|
55
|
+
businessValidationMessages: Array<{
|
|
56
|
+
validationResultCode: 'ERROR' | 'WARN' | 'INFO';
|
|
57
|
+
validationErrorCode?: string;
|
|
58
|
+
message?: string;
|
|
59
|
+
}>;
|
|
60
|
+
}>;
|
|
61
|
+
/** Status the results settle on once processing finishes. */
|
|
62
|
+
finalStatuses: InvoiceStatusType[];
|
|
63
|
+
}
|
|
64
|
+
export interface MockTaxpayer {
|
|
65
|
+
taxNumber: string;
|
|
66
|
+
name: string;
|
|
67
|
+
shortName?: string;
|
|
68
|
+
valid: boolean;
|
|
69
|
+
vatCode?: string;
|
|
70
|
+
countyCode?: string;
|
|
71
|
+
}
|
|
72
|
+
export declare function createState(taxpayers?: MockTaxpayer[]): MockState;
|
|
73
|
+
//# sourceMappingURL=state.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../src/state.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAErE;;;;;GAKG;AACH,MAAM,WAAW,SAAS;IACxB,kDAAkD;IAClD,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IACrC,8DAA8D;IAC9D,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IACpC,6CAA6C;IAC7C,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC;IAC7C,gEAAgE;IAChE,UAAU,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACxB,gDAAgD;IAChD,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IAC1D,wEAAwE;IACxE,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IACrC,sDAAsD;IACtD,QAAQ,EAAE,KAAK,CAAC;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACzE;AAED,MAAM,WAAW,aAAa;IAC5B,aAAa,EAAE,MAAM,CAAC;IACtB,sCAAsC;IACtC,SAAS,EAAE,MAAM,CAAC;IAClB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,sDAAsD;IACtD,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,OAAO,CAAC;IACpB,OAAO,EAAE,WAAW,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,iBAAiB;IAChC,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,4DAA4D;IAC5D,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,KAAK,CAAC;QACb,KAAK,EAAE,MAAM,CAAC;QACd,aAAa,EAAE,iBAAiB,CAAC;QACjC,6EAA6E;QAC7E,0BAA0B,EAAE,KAAK,CAAC;YAChC,oBAAoB,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,CAAC;YAChD,mBAAmB,CAAC,EAAE,MAAM,CAAC;YAC7B,OAAO,CAAC,EAAE,MAAM,CAAC;SAClB,CAAC,CAAC;KACJ,CAAC,CAAC;IACH,6DAA6D;IAC7D,aAAa,EAAE,iBAAiB,EAAE,CAAC;CACpC;AAED,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,OAAO,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,wBAAgB,WAAW,CAAC,SAAS,GAAE,YAAY,EAAO,GAAG,SAAS,CAUrE"}
|
package/dist/state.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export function createState(taxpayers = []) {
|
|
2
|
+
return {
|
|
3
|
+
invoices: new Map(),
|
|
4
|
+
inbound: new Map(),
|
|
5
|
+
transactions: new Map(),
|
|
6
|
+
requestIds: new Set(),
|
|
7
|
+
tokens: new Map(),
|
|
8
|
+
taxpayers: new Map(taxpayers.map((taxpayer) => [taxpayer.taxNumber, taxpayer])),
|
|
9
|
+
requests: [],
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state.js","sourceRoot":"","sources":["../src/state.ts"],"names":[],"mappings":"AAqEA,MAAM,UAAU,WAAW,CAAC,YAA4B,EAAE;IACxD,OAAO;QACL,QAAQ,EAAE,IAAI,GAAG,EAAE;QACnB,OAAO,EAAE,IAAI,GAAG,EAAE;QAClB,YAAY,EAAE,IAAI,GAAG,EAAE;QACvB,UAAU,EAAE,IAAI,GAAG,EAAE;QACrB,MAAM,EAAE,IAAI,GAAG,EAAE;QACjB,SAAS,EAAE,IAAI,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC,CAAC;QAC/E,QAAQ,EAAE,EAAE;KACb,CAAC;AACJ,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@open-nav/mock-server",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "A local stand-in for the NAV Online Számla invoice service, for testing without credentials",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"nav",
|
|
7
|
+
"onlineszamla",
|
|
8
|
+
"hungary",
|
|
9
|
+
"invoice",
|
|
10
|
+
"mock",
|
|
11
|
+
"testing"
|
|
12
|
+
],
|
|
13
|
+
"license": "MIT",
|
|
14
|
+
"author": "open-nav contributors",
|
|
15
|
+
"homepage": "https://github.com/eshton/open-nav#readme",
|
|
16
|
+
"repository": {
|
|
17
|
+
"type": "git",
|
|
18
|
+
"url": "git+https://github.com/eshton/open-nav.git",
|
|
19
|
+
"directory": "packages/mock-server"
|
|
20
|
+
},
|
|
21
|
+
"bugs": "https://github.com/eshton/open-nav/issues",
|
|
22
|
+
"type": "module",
|
|
23
|
+
"main": "./dist/index.js",
|
|
24
|
+
"types": "./dist/index.d.ts",
|
|
25
|
+
"exports": {
|
|
26
|
+
".": {
|
|
27
|
+
"types": "./dist/index.d.ts",
|
|
28
|
+
"import": "./dist/index.js",
|
|
29
|
+
"default": "./dist/index.js"
|
|
30
|
+
},
|
|
31
|
+
"./package.json": "./package.json"
|
|
32
|
+
},
|
|
33
|
+
"bin": {
|
|
34
|
+
"open-nav-mock": "./dist/bin.js"
|
|
35
|
+
},
|
|
36
|
+
"files": [
|
|
37
|
+
"dist",
|
|
38
|
+
"src",
|
|
39
|
+
"README.md",
|
|
40
|
+
"LICENSE"
|
|
41
|
+
],
|
|
42
|
+
"sideEffects": false,
|
|
43
|
+
"engines": {
|
|
44
|
+
"node": ">=20.10.0"
|
|
45
|
+
},
|
|
46
|
+
"publishConfig": {
|
|
47
|
+
"access": "public"
|
|
48
|
+
},
|
|
49
|
+
"dependencies": {
|
|
50
|
+
"@open-nav/core": "^0.1.0"
|
|
51
|
+
},
|
|
52
|
+
"devDependencies": {
|
|
53
|
+
"@open-nav/client": "^0.1.0"
|
|
54
|
+
},
|
|
55
|
+
"scripts": {
|
|
56
|
+
"build": "tsc -b"
|
|
57
|
+
}
|
|
58
|
+
}
|
package/src/bin.ts
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { parseArgs } from 'node:util';
|
|
3
|
+
import { startMockServer } from './server.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Run the mock service standalone, so the CLI and any other integration can
|
|
7
|
+
* be pointed at it without credentials.
|
|
8
|
+
*/
|
|
9
|
+
const { values } = parseArgs({
|
|
10
|
+
args: process.argv.slice(2),
|
|
11
|
+
options: {
|
|
12
|
+
port: { type: 'string' },
|
|
13
|
+
host: { type: 'string' },
|
|
14
|
+
polls: { type: 'string' },
|
|
15
|
+
'no-validate': { type: 'boolean' },
|
|
16
|
+
help: { type: 'boolean', short: 'h' },
|
|
17
|
+
},
|
|
18
|
+
strict: true,
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
if (values.help === true) {
|
|
22
|
+
console.log(`open-nav-mock — a local stand-in for the NAV Online Számla invoice service
|
|
23
|
+
|
|
24
|
+
--port <n> Port to listen on (default 8080; 0 picks a free one)
|
|
25
|
+
--host <host> Interface to bind (default 127.0.0.1)
|
|
26
|
+
--polls <n> Polls before a transaction settles (default 0, immediate)
|
|
27
|
+
--no-validate Accept every invoice instead of validating it
|
|
28
|
+
`);
|
|
29
|
+
process.exit(0);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Obviously fake credentials, printed on startup so they can be copied.
|
|
33
|
+
const credentials = {
|
|
34
|
+
login: 'mocklogin123',
|
|
35
|
+
password: 'mock-password',
|
|
36
|
+
signKey: 'mock-sign-key-0123456789',
|
|
37
|
+
exchangeKey: '0123456789abcdef',
|
|
38
|
+
taxNumber: '99999999',
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const mock = await startMockServer({
|
|
42
|
+
credentials,
|
|
43
|
+
port: values.port === undefined ? 8080 : Number(values.port),
|
|
44
|
+
...(values.host ? { host: values.host } : {}),
|
|
45
|
+
pollsBeforeDone: values.polls === undefined ? 0 : Number(values.polls),
|
|
46
|
+
validate: values['no-validate'] !== true,
|
|
47
|
+
taxpayers: [
|
|
48
|
+
{
|
|
49
|
+
taxNumber: '99999999',
|
|
50
|
+
name: 'Értékesítő Kft',
|
|
51
|
+
shortName: 'Értékesítő',
|
|
52
|
+
valid: true,
|
|
53
|
+
vatCode: '2',
|
|
54
|
+
countyCode: '41',
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
taxNumber: '99887764',
|
|
58
|
+
name: 'Beszerző Kft',
|
|
59
|
+
shortName: 'Beszerző',
|
|
60
|
+
valid: true,
|
|
61
|
+
vatCode: '2',
|
|
62
|
+
countyCode: '02',
|
|
63
|
+
},
|
|
64
|
+
],
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
console.log(`Mock NAV invoice service listening on ${mock.url}`);
|
|
68
|
+
console.log('');
|
|
69
|
+
console.log('Point the CLI at it with these (fake) credentials:');
|
|
70
|
+
console.log('');
|
|
71
|
+
console.log(` export NAV_BASE_URL=${mock.url}`);
|
|
72
|
+
console.log(` export NAV_LOGIN=${credentials.login}`);
|
|
73
|
+
console.log(` export NAV_PASSWORD=${credentials.password}`);
|
|
74
|
+
console.log(` export NAV_SIGN_KEY=${credentials.signKey}`);
|
|
75
|
+
console.log(` export NAV_EXCHANGE_KEY=${credentials.exchangeKey}`);
|
|
76
|
+
console.log(` export NAV_TAX_NUMBER=${credentials.taxNumber}`);
|
|
77
|
+
console.log(' export NAV_SOFTWARE_ID=OPENNAVMOCK000001');
|
|
78
|
+
console.log('');
|
|
79
|
+
console.log('Then, for example: open-nav token');
|
|
80
|
+
console.log('Press Ctrl+C to stop.');
|
|
81
|
+
|
|
82
|
+
const shutdown = (): void => {
|
|
83
|
+
void mock.close().then(() => process.exit(0));
|
|
84
|
+
};
|
|
85
|
+
process.on('SIGINT', shutdown);
|
|
86
|
+
process.on('SIGTERM', shutdown);
|