@open-nav/mock-server 0.1.0 → 0.1.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/dist/server.d.ts CHANGED
@@ -21,6 +21,21 @@ export interface MockServerOptions {
21
21
  pollsBeforeDone?: number;
22
22
  /** Validate submitted invoices and abort the invalid ones. Default true. */
23
23
  validate?: boolean;
24
+ /**
25
+ * Turn away the first N HTTP requests with a throttle response, so a client's
26
+ * retry and backoff can be exercised end to end. NAV rate limits per
27
+ * taxpayer, and a bulk download makes one request per invoice, so recovering
28
+ * from a throttle is the difference between a pull that finishes and one that
29
+ * dies partway.
30
+ */
31
+ throttle?: {
32
+ /** How many requests to turn away before serving normally. */
33
+ times: number;
34
+ /** Status to answer with. 429 (Too Many Requests) by default; 503 also retried. */
35
+ status?: 429 | 503;
36
+ /** Seconds to advertise in a `Retry-After` header, if any. */
37
+ retryAfterSeconds?: number;
38
+ };
24
39
  /** Injectable clock. */
25
40
  now?: () => Date;
26
41
  }
@@ -1 +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"}
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;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE;QACT,8DAA8D;QAC9D,KAAK,EAAE,MAAM,CAAC;QACd,mFAAmF;QACnF,MAAM,CAAC,EAAE,GAAG,GAAG,GAAG,CAAC;QACnB,8DAA8D;QAC9D,iBAAiB,CAAC,EAAE,MAAM,CAAC;KAC5B,CAAC;IACF,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,CA6DrF"}
package/dist/server.js CHANGED
@@ -25,11 +25,25 @@ export async function startMockServer(options) {
25
25
  validate: options.validate ?? true,
26
26
  now: options.now ?? (() => new Date()),
27
27
  };
28
+ let throttleRemaining = options.throttle?.times ?? 0;
28
29
  const server = createServer((request, response) => {
29
30
  const chunks = [];
30
31
  request.on('data', (chunk) => chunks.push(chunk));
31
32
  request.on('end', () => {
32
33
  const body = Buffer.concat(chunks).toString('utf8');
34
+ if (throttleRemaining > 0) {
35
+ throttleRemaining -= 1;
36
+ state.throttled += 1;
37
+ const headers = {
38
+ 'content-type': 'application/xml; charset=utf-8',
39
+ };
40
+ if (options.throttle?.retryAfterSeconds !== undefined) {
41
+ headers['retry-after'] = String(options.throttle.retryAfterSeconds);
42
+ }
43
+ response.writeHead(options.throttle?.status ?? 429, headers);
44
+ response.end(errorResponse(config, 'RATE_LIMITED', 'request limit exceeded, retry later'));
45
+ return;
46
+ }
33
47
  const { status, body: responseBody } = dispatch(request.method ?? '', request.url ?? '', body, config, state);
34
48
  response.writeHead(status, { 'content-type': 'application/xml; charset=utf-8' });
35
49
  response.end(responseBody);
@@ -1 +1 @@
1
- {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
package/dist/state.d.ts CHANGED
@@ -27,6 +27,8 @@ export interface MockState {
27
27
  requestId: string;
28
28
  body: string;
29
29
  }>;
30
+ /** HTTP requests turned away by the throttle, for assertions. */
31
+ throttled: number;
30
32
  }
31
33
  export interface StoredInvoice {
32
34
  invoiceNumber: string;
@@ -1 +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"}
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;IACxE,iEAAiE;IACjE,SAAS,EAAE,MAAM,CAAC;CACnB;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,CAWrE"}
package/dist/state.js CHANGED
@@ -7,6 +7,7 @@ export function createState(taxpayers = []) {
7
7
  tokens: new Map(),
8
8
  taxpayers: new Map(taxpayers.map((taxpayer) => [taxpayer.taxNumber, taxpayer])),
9
9
  requests: [],
10
+ throttled: 0,
10
11
  };
11
12
  }
12
13
  //# sourceMappingURL=state.js.map
package/dist/state.js.map CHANGED
@@ -1 +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"}
1
+ {"version":3,"file":"state.js","sourceRoot":"","sources":["../src/state.ts"],"names":[],"mappings":"AAuEA,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;QACZ,SAAS,EAAE,CAAC;KACb,CAAC;AACJ,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-nav/mock-server",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "A local stand-in for the NAV Online Számla invoice service, for testing without credentials",
5
5
  "keywords": [
6
6
  "nav",
@@ -47,10 +47,10 @@
47
47
  "access": "public"
48
48
  },
49
49
  "dependencies": {
50
- "@open-nav/core": "^0.1.0"
50
+ "@open-nav/core": "^0.1.1"
51
51
  },
52
52
  "devDependencies": {
53
- "@open-nav/client": "^0.1.0"
53
+ "@open-nav/client": "^0.1.1"
54
54
  },
55
55
  "scripts": {
56
56
  "build": "tsc -b"
package/src/server.ts CHANGED
@@ -29,6 +29,21 @@ export interface MockServerOptions {
29
29
  pollsBeforeDone?: number;
30
30
  /** Validate submitted invoices and abort the invalid ones. Default true. */
31
31
  validate?: boolean;
32
+ /**
33
+ * Turn away the first N HTTP requests with a throttle response, so a client's
34
+ * retry and backoff can be exercised end to end. NAV rate limits per
35
+ * taxpayer, and a bulk download makes one request per invoice, so recovering
36
+ * from a throttle is the difference between a pull that finishes and one that
37
+ * dies partway.
38
+ */
39
+ throttle?: {
40
+ /** How many requests to turn away before serving normally. */
41
+ times: number;
42
+ /** Status to answer with. 429 (Too Many Requests) by default; 503 also retried. */
43
+ status?: 429 | 503;
44
+ /** Seconds to advertise in a `Retry-After` header, if any. */
45
+ retryAfterSeconds?: number;
46
+ };
32
47
  /** Injectable clock. */
33
48
  now?: () => Date;
34
49
  }
@@ -67,11 +82,28 @@ export async function startMockServer(options: MockServerOptions): Promise<MockS
67
82
  now: options.now ?? (() => new Date()),
68
83
  };
69
84
 
85
+ let throttleRemaining = options.throttle?.times ?? 0;
86
+
70
87
  const server = createServer((request, response) => {
71
88
  const chunks: Buffer[] = [];
72
89
  request.on('data', (chunk: Buffer) => chunks.push(chunk));
73
90
  request.on('end', () => {
74
91
  const body = Buffer.concat(chunks).toString('utf8');
92
+
93
+ if (throttleRemaining > 0) {
94
+ throttleRemaining -= 1;
95
+ state.throttled += 1;
96
+ const headers: Record<string, string> = {
97
+ 'content-type': 'application/xml; charset=utf-8',
98
+ };
99
+ if (options.throttle?.retryAfterSeconds !== undefined) {
100
+ headers['retry-after'] = String(options.throttle.retryAfterSeconds);
101
+ }
102
+ response.writeHead(options.throttle?.status ?? 429, headers);
103
+ response.end(errorResponse(config, 'RATE_LIMITED', 'request limit exceeded, retry later'));
104
+ return;
105
+ }
106
+
75
107
  const { status, body: responseBody } = dispatch(
76
108
  request.method ?? '',
77
109
  request.url ?? '',
package/src/state.ts CHANGED
@@ -21,6 +21,8 @@ export interface MockState {
21
21
  taxpayers: Map<string, MockTaxpayer>;
22
22
  /** Requests received, newest last, for assertions. */
23
23
  requests: Array<{ operation: string; requestId: string; body: string }>;
24
+ /** HTTP requests turned away by the throttle, for assertions. */
25
+ throttled: number;
24
26
  }
25
27
 
26
28
  export interface StoredInvoice {
@@ -76,5 +78,6 @@ export function createState(taxpayers: MockTaxpayer[] = []): MockState {
76
78
  tokens: new Map(),
77
79
  taxpayers: new Map(taxpayers.map((taxpayer) => [taxpayer.taxNumber, taxpayer])),
78
80
  requests: [],
81
+ throttled: 0,
79
82
  };
80
83
  }