@schmock/core 2.3.0 → 2.3.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/LICENSE +21 -0
- package/README.md +35 -0
- package/dist/abort.d.ts +3 -0
- package/dist/abort.d.ts.map +1 -0
- package/dist/abort.js +30 -0
- package/dist/builder.d.ts +31 -10
- package/dist/builder.d.ts.map +1 -1
- package/dist/builder.js +760 -190
- package/dist/constants.d.ts +38 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +123 -2
- package/dist/errors.d.ts +14 -3
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +41 -6
- package/dist/helpers.d.ts +9 -0
- package/dist/helpers.d.ts.map +1 -1
- package/dist/helpers.js +18 -2
- package/dist/http-helpers.d.ts +39 -4
- package/dist/http-helpers.d.ts.map +1 -1
- package/dist/http-helpers.js +147 -49
- package/dist/index.d.ts +292 -34
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -3
- package/dist/interceptor.d.ts +11 -1
- package/dist/interceptor.d.ts.map +1 -1
- package/dist/interceptor.js +316 -169
- package/dist/parser.d.ts.map +1 -1
- package/dist/parser.js +12 -3
- package/dist/plugin-pipeline.d.ts +3 -3
- package/dist/plugin-pipeline.d.ts.map +1 -1
- package/dist/plugin-pipeline.js +29 -14
- package/dist/response-normalizer.d.ts +16 -0
- package/dist/response-normalizer.d.ts.map +1 -0
- package/dist/response-normalizer.js +316 -0
- package/dist/response-parser.d.ts.map +1 -1
- package/dist/response-parser.js +39 -2
- package/dist/route-matcher.d.ts +3 -0
- package/dist/route-matcher.d.ts.map +1 -1
- package/dist/route-matcher.js +12 -6
- package/dist/types.d.ts +7 -2
- package/dist/types.d.ts.map +1 -1
- package/package.json +14 -6
- package/src/audit-core-builder.test.ts +0 -218
- package/src/audit-response-guard.test.ts +0 -38
- package/src/binary.test.ts +0 -126
- package/src/binary.ts +0 -11
- package/src/builder.test.ts +0 -289
- package/src/builder.ts +0 -748
- package/src/constants.test.ts +0 -99
- package/src/constants.ts +0 -73
- package/src/debug.test.ts +0 -241
- package/src/delay.test.ts +0 -319
- package/src/dist-shape.test.ts +0 -35
- package/src/errors.test.ts +0 -223
- package/src/errors.ts +0 -130
- package/src/factory.test.ts +0 -133
- package/src/helpers.test.ts +0 -147
- package/src/helpers.ts +0 -58
- package/src/http-helpers.test.ts +0 -39
- package/src/http-helpers.ts +0 -149
- package/src/index.ts +0 -152
- package/src/interceptor.test.ts +0 -488
- package/src/interceptor.ts +0 -407
- package/src/namespace.test.ts +0 -274
- package/src/parser.property.test.ts +0 -630
- package/src/parser.test.ts +0 -148
- package/src/parser.ts +0 -64
- package/src/plugin-pipeline.ts +0 -208
- package/src/plugin-system.test.ts +0 -697
- package/src/response-parser.ts +0 -114
- package/src/response-parsing.test.ts +0 -333
- package/src/route-matcher.ts +0 -69
- package/src/route-matching.test.ts +0 -394
- package/src/server.test.ts +0 -234
- package/src/smart-defaults.test.ts +0 -361
- package/src/steps/async-support.steps.ts +0 -421
- package/src/steps/audit-core-builder.steps.ts +0 -188
- package/src/steps/audit-onerror-tuple.steps.ts +0 -83
- package/src/steps/basic-usage.steps.ts +0 -245
- package/src/steps/developer-experience.steps.ts +0 -204
- package/src/steps/error-handling.steps.ts +0 -555
- package/src/steps/fetch-interceptor.steps.ts +0 -457
- package/src/steps/fluent-api.steps.ts +0 -255
- package/src/steps/http-methods.steps.ts +0 -342
- package/src/steps/lifecycle-events.steps.ts +0 -149
- package/src/steps/performance-reliability.steps.ts +0 -90
- package/src/steps/plugin-integration.steps.ts +0 -334
- package/src/steps/request-history.steps.ts +0 -458
- package/src/steps/response-delay.steps.ts +0 -88
- package/src/steps/route-key-format.steps.ts +0 -99
- package/src/steps/standalone-server.steps.ts +0 -227
- package/src/steps/state-concurrency.steps.ts +0 -167
- package/src/types.ts +0 -36
|
@@ -1,555 +0,0 @@
|
|
|
1
|
-
import { describeFeature, loadFeature } from "@amiceli/vitest-cucumber";
|
|
2
|
-
import { expect } from "vitest";
|
|
3
|
-
import { schmock, toHttpMethod, toRouteKey } from "../index";
|
|
4
|
-
import type { CallableMockInstance, Plugin } from "../types";
|
|
5
|
-
|
|
6
|
-
const feature = await loadFeature("../../features/error-handling.feature");
|
|
7
|
-
|
|
8
|
-
function parseRequest(request: string): {
|
|
9
|
-
method: Schmock.HttpMethod;
|
|
10
|
-
path: string;
|
|
11
|
-
} {
|
|
12
|
-
const separator = request.indexOf(" ");
|
|
13
|
-
const methodText = request.slice(0, separator);
|
|
14
|
-
const path = request.slice(separator + 1);
|
|
15
|
-
if (separator <= 0 || !path.startsWith("/")) {
|
|
16
|
-
throw new Error(`Expected request in METHOD /path format, got: ${request}`);
|
|
17
|
-
}
|
|
18
|
-
return { method: toHttpMethod(methodText), path };
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
function requireRecord(
|
|
22
|
-
value: unknown,
|
|
23
|
-
description: string,
|
|
24
|
-
): Record<string, unknown> {
|
|
25
|
-
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
26
|
-
throw new Error(`Expected ${description} to be an object`);
|
|
27
|
-
}
|
|
28
|
-
return Object.fromEntries(Object.entries(value));
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function responseBodyString(response: Schmock.Response, field: string): string {
|
|
32
|
-
const value = requireRecord(response.body, "response body")[field];
|
|
33
|
-
if (typeof value !== "string") {
|
|
34
|
-
throw new Error(`Expected response body field ${field} to be a string`);
|
|
35
|
-
}
|
|
36
|
-
return value;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
describeFeature(feature, ({ Scenario }) => {
|
|
40
|
-
let mock: CallableMockInstance;
|
|
41
|
-
let response: Schmock.Response;
|
|
42
|
-
|
|
43
|
-
Scenario("Route not found returns 404", ({ Given, When, Then, And }) => {
|
|
44
|
-
Given(
|
|
45
|
-
"I create a mock with a GET /users route returning a user list",
|
|
46
|
-
() => {
|
|
47
|
-
mock = schmock();
|
|
48
|
-
mock("GET /users", [{ id: 1, name: "John" }]);
|
|
49
|
-
},
|
|
50
|
-
);
|
|
51
|
-
|
|
52
|
-
When("I request {string}", async (_, request: string) => {
|
|
53
|
-
const { method, path } = parseRequest(request);
|
|
54
|
-
response = await mock.handle(method, path);
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
Then("I should receive status {int}", (_, status: number) => {
|
|
58
|
-
expect(response.status).toBe(status);
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
And(
|
|
62
|
-
"the response should contain error {string}",
|
|
63
|
-
(_, errorMessage: string) => {
|
|
64
|
-
expect(responseBodyString(response, "error")).toContain(errorMessage);
|
|
65
|
-
},
|
|
66
|
-
);
|
|
67
|
-
|
|
68
|
-
And(
|
|
69
|
-
"the response should have error code {string}",
|
|
70
|
-
(_, errorCode: string) => {
|
|
71
|
-
expect(responseBodyString(response, "code")).toBe(errorCode);
|
|
72
|
-
},
|
|
73
|
-
);
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
Scenario("Wrong HTTP method returns 404", ({ Given, When, Then, And }) => {
|
|
77
|
-
Given(
|
|
78
|
-
"I create a mock with a GET /api/data route returning success",
|
|
79
|
-
() => {
|
|
80
|
-
mock = schmock();
|
|
81
|
-
mock("GET /api/data", { success: true });
|
|
82
|
-
},
|
|
83
|
-
);
|
|
84
|
-
|
|
85
|
-
When("I request {string}", async (_, request: string) => {
|
|
86
|
-
const { method, path } = parseRequest(request);
|
|
87
|
-
response = await mock.handle(method, path);
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
Then("I should receive status {int}", (_, status: number) => {
|
|
91
|
-
expect(response.status).toBe(status);
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
And(
|
|
95
|
-
"the response should contain error {string}",
|
|
96
|
-
(_, errorMessage: string) => {
|
|
97
|
-
expect(responseBodyString(response, "error")).toContain(errorMessage);
|
|
98
|
-
},
|
|
99
|
-
);
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
Scenario(
|
|
103
|
-
"Plugin throws error returns 500 with PluginError",
|
|
104
|
-
({ Given, When, Then, And }) => {
|
|
105
|
-
Given(
|
|
106
|
-
"I create a mock with a plugin that throws {string}",
|
|
107
|
-
(_, errorMsg: string) => {
|
|
108
|
-
mock = schmock();
|
|
109
|
-
const failingPlugin: Plugin = {
|
|
110
|
-
name: "failing-plugin",
|
|
111
|
-
process: () => {
|
|
112
|
-
throw new Error(errorMsg);
|
|
113
|
-
},
|
|
114
|
-
};
|
|
115
|
-
mock("GET /test", "original").pipe(failingPlugin);
|
|
116
|
-
},
|
|
117
|
-
);
|
|
118
|
-
|
|
119
|
-
When("I request {string}", async (_, request: string) => {
|
|
120
|
-
const { method, path } = parseRequest(request);
|
|
121
|
-
response = await mock.handle(method, path);
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
Then("I should receive status {int}", (_, status: number) => {
|
|
125
|
-
expect(response.status).toBe(status);
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
And(
|
|
129
|
-
"the response should contain error {string}",
|
|
130
|
-
(_, errorMessage: string) => {
|
|
131
|
-
expect(responseBodyString(response, "error")).toContain(errorMessage);
|
|
132
|
-
},
|
|
133
|
-
);
|
|
134
|
-
|
|
135
|
-
And(
|
|
136
|
-
"the response should have error code {string}",
|
|
137
|
-
(_, errorCode: string) => {
|
|
138
|
-
expect(responseBodyString(response, "code")).toBe(errorCode);
|
|
139
|
-
},
|
|
140
|
-
);
|
|
141
|
-
},
|
|
142
|
-
);
|
|
143
|
-
|
|
144
|
-
Scenario(
|
|
145
|
-
"Plugin onError hook recovers from failure",
|
|
146
|
-
({ Given, When, Then, And }) => {
|
|
147
|
-
Given(
|
|
148
|
-
"I create a mock with a recoverable plugin that returns {string}",
|
|
149
|
-
(_, recoveredBody: string) => {
|
|
150
|
-
mock = schmock();
|
|
151
|
-
const recoverablePlugin: Plugin = {
|
|
152
|
-
name: "recoverable",
|
|
153
|
-
process: () => {
|
|
154
|
-
throw new Error("Initial failure");
|
|
155
|
-
},
|
|
156
|
-
onError: () => ({ status: 200, body: recoveredBody, headers: {} }),
|
|
157
|
-
};
|
|
158
|
-
mock("GET /test", "original").pipe(recoverablePlugin);
|
|
159
|
-
},
|
|
160
|
-
);
|
|
161
|
-
|
|
162
|
-
When("I request {string}", async (_, request: string) => {
|
|
163
|
-
const { method, path } = parseRequest(request);
|
|
164
|
-
response = await mock.handle(method, path);
|
|
165
|
-
});
|
|
166
|
-
|
|
167
|
-
Then("I should receive status {int}", (_, status: number) => {
|
|
168
|
-
expect(response.status).toBe(status);
|
|
169
|
-
});
|
|
170
|
-
|
|
171
|
-
And("I should receive text {string}", (_, expectedText: string) => {
|
|
172
|
-
expect(response.body).toBe(expectedText);
|
|
173
|
-
});
|
|
174
|
-
},
|
|
175
|
-
);
|
|
176
|
-
|
|
177
|
-
Scenario(
|
|
178
|
-
"Plugin returns invalid result structure",
|
|
179
|
-
({ Given, When, Then, And }) => {
|
|
180
|
-
Given("I create a mock with a plugin returning an invalid result", () => {
|
|
181
|
-
mock = schmock();
|
|
182
|
-
const invalidPlugin: Plugin = {
|
|
183
|
-
name: "invalid",
|
|
184
|
-
process: (context) => ({ context }),
|
|
185
|
-
};
|
|
186
|
-
Object.defineProperty(invalidPlugin, "process", {
|
|
187
|
-
value: () => ({ wrongStructure: true }),
|
|
188
|
-
});
|
|
189
|
-
mock("GET /test", "original").pipe(invalidPlugin);
|
|
190
|
-
});
|
|
191
|
-
|
|
192
|
-
When("I request {string}", async (_, request: string) => {
|
|
193
|
-
const { method, path } = parseRequest(request);
|
|
194
|
-
response = await mock.handle(method, path);
|
|
195
|
-
});
|
|
196
|
-
|
|
197
|
-
Then("I should receive status {int}", (_, status: number) => {
|
|
198
|
-
expect(response.status).toBe(status);
|
|
199
|
-
});
|
|
200
|
-
|
|
201
|
-
And(
|
|
202
|
-
"the response should contain error {string}",
|
|
203
|
-
(_, errorMessage: string) => {
|
|
204
|
-
expect(responseBodyString(response, "error")).toContain(errorMessage);
|
|
205
|
-
},
|
|
206
|
-
);
|
|
207
|
-
},
|
|
208
|
-
);
|
|
209
|
-
|
|
210
|
-
Scenario(
|
|
211
|
-
"Function generator throws error returns 500",
|
|
212
|
-
({ Given, When, Then, And }) => {
|
|
213
|
-
Given(
|
|
214
|
-
"I create a mock with a generator that throws {string}",
|
|
215
|
-
(_, errorMsg: string) => {
|
|
216
|
-
mock = schmock();
|
|
217
|
-
mock("GET /fail", () => {
|
|
218
|
-
throw new Error(errorMsg);
|
|
219
|
-
});
|
|
220
|
-
},
|
|
221
|
-
);
|
|
222
|
-
|
|
223
|
-
When("I request {string}", async (_, request: string) => {
|
|
224
|
-
const { method, path } = parseRequest(request);
|
|
225
|
-
response = await mock.handle(method, path);
|
|
226
|
-
});
|
|
227
|
-
|
|
228
|
-
Then("I should receive status {int}", (_, status: number) => {
|
|
229
|
-
expect(response.status).toBe(status);
|
|
230
|
-
});
|
|
231
|
-
|
|
232
|
-
And(
|
|
233
|
-
"the response should contain error {string}",
|
|
234
|
-
(_, errorMessage: string) => {
|
|
235
|
-
expect(responseBodyString(response, "error")).toContain(errorMessage);
|
|
236
|
-
},
|
|
237
|
-
);
|
|
238
|
-
|
|
239
|
-
And(
|
|
240
|
-
"the response should have error code {string}",
|
|
241
|
-
(_, errorCode: string) => {
|
|
242
|
-
expect(responseBodyString(response, "code")).toBe(errorCode);
|
|
243
|
-
},
|
|
244
|
-
);
|
|
245
|
-
},
|
|
246
|
-
);
|
|
247
|
-
|
|
248
|
-
Scenario("Namespace mismatch returns 404", ({ Given, When, Then, And }) => {
|
|
249
|
-
Given(
|
|
250
|
-
"I create a mock with namespace {string} and a GET /users route",
|
|
251
|
-
(_, namespace: string) => {
|
|
252
|
-
mock = schmock({ namespace });
|
|
253
|
-
mock("GET /users", [{ id: 1 }]);
|
|
254
|
-
},
|
|
255
|
-
);
|
|
256
|
-
|
|
257
|
-
When("I request {string}", async (_, request: string) => {
|
|
258
|
-
const { method, path } = parseRequest(request);
|
|
259
|
-
response = await mock.handle(method, path);
|
|
260
|
-
});
|
|
261
|
-
|
|
262
|
-
Then("I should receive status {int}", (_, status: number) => {
|
|
263
|
-
expect(response.status).toBe(status);
|
|
264
|
-
});
|
|
265
|
-
|
|
266
|
-
And(
|
|
267
|
-
"the response should contain error {string}",
|
|
268
|
-
(_, errorMessage: string) => {
|
|
269
|
-
expect(responseBodyString(response, "error")).toContain(errorMessage);
|
|
270
|
-
},
|
|
271
|
-
);
|
|
272
|
-
});
|
|
273
|
-
|
|
274
|
-
Scenario(
|
|
275
|
-
"Multiple plugin failures cascade properly",
|
|
276
|
-
({ Given, When, Then, And }) => {
|
|
277
|
-
Given(
|
|
278
|
-
"I create a mock with two failing plugins piped in sequence",
|
|
279
|
-
() => {
|
|
280
|
-
mock = schmock();
|
|
281
|
-
const plugin1: Plugin = {
|
|
282
|
-
name: "first-fail",
|
|
283
|
-
process: () => {
|
|
284
|
-
throw new Error("First error");
|
|
285
|
-
},
|
|
286
|
-
};
|
|
287
|
-
const plugin2: Plugin = {
|
|
288
|
-
name: "second-fail",
|
|
289
|
-
process: () => {
|
|
290
|
-
throw new Error("Second error");
|
|
291
|
-
},
|
|
292
|
-
};
|
|
293
|
-
mock("GET /cascade", "original").pipe(plugin1).pipe(plugin2);
|
|
294
|
-
},
|
|
295
|
-
);
|
|
296
|
-
|
|
297
|
-
When("I request {string}", async (_, request: string) => {
|
|
298
|
-
const { method, path } = parseRequest(request);
|
|
299
|
-
response = await mock.handle(method, path);
|
|
300
|
-
});
|
|
301
|
-
|
|
302
|
-
Then("I should receive status {int}", (_, status: number) => {
|
|
303
|
-
expect(response.status).toBe(status);
|
|
304
|
-
});
|
|
305
|
-
|
|
306
|
-
And(
|
|
307
|
-
"the response should contain error {string}",
|
|
308
|
-
(_, errorMessage: string) => {
|
|
309
|
-
expect(responseBodyString(response, "error")).toContain(errorMessage);
|
|
310
|
-
},
|
|
311
|
-
);
|
|
312
|
-
},
|
|
313
|
-
);
|
|
314
|
-
|
|
315
|
-
Scenario("Plugin onError hook also fails", ({ Given, When, Then, And }) => {
|
|
316
|
-
Given(
|
|
317
|
-
"I create a mock with a plugin whose error handler also throws",
|
|
318
|
-
() => {
|
|
319
|
-
mock = schmock({ debug: true });
|
|
320
|
-
const brokenPlugin: Plugin = {
|
|
321
|
-
name: "broken-handler",
|
|
322
|
-
process: () => {
|
|
323
|
-
throw new Error("Process failed");
|
|
324
|
-
},
|
|
325
|
-
onError: () => {
|
|
326
|
-
throw new Error("Handler failed");
|
|
327
|
-
},
|
|
328
|
-
};
|
|
329
|
-
mock("GET /broken", "original").pipe(brokenPlugin);
|
|
330
|
-
},
|
|
331
|
-
);
|
|
332
|
-
|
|
333
|
-
When("I request {string}", async (_, request: string) => {
|
|
334
|
-
const { method, path } = parseRequest(request);
|
|
335
|
-
response = await mock.handle(method, path);
|
|
336
|
-
});
|
|
337
|
-
|
|
338
|
-
Then("I should receive status {int}", (_, status: number) => {
|
|
339
|
-
expect(response.status).toBe(status);
|
|
340
|
-
});
|
|
341
|
-
|
|
342
|
-
And(
|
|
343
|
-
"the response should contain error {string}",
|
|
344
|
-
(_, errorMessage: string) => {
|
|
345
|
-
expect(responseBodyString(response, "error")).toContain(errorMessage);
|
|
346
|
-
},
|
|
347
|
-
);
|
|
348
|
-
});
|
|
349
|
-
|
|
350
|
-
Scenario(
|
|
351
|
-
"Downstream error handler recovers an earlier plugin failure",
|
|
352
|
-
({ Given, When, Then, And }) => {
|
|
353
|
-
Given("I create a failing plugin followed by a recovery plugin", () => {
|
|
354
|
-
mock = schmock();
|
|
355
|
-
mock("GET /downstream-recovery", "original")
|
|
356
|
-
.pipe({
|
|
357
|
-
name: "upstream-failure",
|
|
358
|
-
process() {
|
|
359
|
-
throw new Error("upstream failed");
|
|
360
|
-
},
|
|
361
|
-
})
|
|
362
|
-
.pipe({
|
|
363
|
-
name: "downstream-recovery",
|
|
364
|
-
process(context, currentResponse) {
|
|
365
|
-
return { context, response: currentResponse };
|
|
366
|
-
},
|
|
367
|
-
onError() {
|
|
368
|
-
return [
|
|
369
|
-
503,
|
|
370
|
-
{
|
|
371
|
-
error: "Recovered by downstream plugin",
|
|
372
|
-
code: "RECOVERED_DOWNSTREAM",
|
|
373
|
-
},
|
|
374
|
-
];
|
|
375
|
-
},
|
|
376
|
-
});
|
|
377
|
-
});
|
|
378
|
-
|
|
379
|
-
When("I request {string}", async (_, request: string) => {
|
|
380
|
-
const { method, path } = parseRequest(request);
|
|
381
|
-
response = await mock.handle(method, path);
|
|
382
|
-
});
|
|
383
|
-
|
|
384
|
-
Then("I should receive status {int}", (_, status: number) => {
|
|
385
|
-
expect(response.status).toBe(status);
|
|
386
|
-
});
|
|
387
|
-
|
|
388
|
-
And("the response should have error code {string}", (_, code: string) => {
|
|
389
|
-
expect(responseBodyString(response, "code")).toBe(code);
|
|
390
|
-
});
|
|
391
|
-
},
|
|
392
|
-
);
|
|
393
|
-
|
|
394
|
-
Scenario(
|
|
395
|
-
"Empty parameter in route returns 404",
|
|
396
|
-
({ Given, When, Then, And }) => {
|
|
397
|
-
Given(
|
|
398
|
-
"I create a mock with a parameterized route {string}",
|
|
399
|
-
(_, route: string) => {
|
|
400
|
-
mock = schmock();
|
|
401
|
-
const { method, path } = parseRequest(route);
|
|
402
|
-
mock(toRouteKey(method, path), ({ params }) => ({
|
|
403
|
-
userId: params.id,
|
|
404
|
-
}));
|
|
405
|
-
},
|
|
406
|
-
);
|
|
407
|
-
|
|
408
|
-
When("I request {string}", async (_, request: string) => {
|
|
409
|
-
const { method, path } = parseRequest(request);
|
|
410
|
-
response = await mock.handle(method, path);
|
|
411
|
-
});
|
|
412
|
-
|
|
413
|
-
Then("I should receive status {int}", (_, status: number) => {
|
|
414
|
-
expect(response.status).toBe(status);
|
|
415
|
-
});
|
|
416
|
-
|
|
417
|
-
And(
|
|
418
|
-
"the response should contain error {string}",
|
|
419
|
-
(_, errorMessage: string) => {
|
|
420
|
-
expect(responseBodyString(response, "error")).toContain(errorMessage);
|
|
421
|
-
},
|
|
422
|
-
);
|
|
423
|
-
},
|
|
424
|
-
);
|
|
425
|
-
|
|
426
|
-
Scenario("Async generator error handling", ({ Given, When, Then, And }) => {
|
|
427
|
-
Given(
|
|
428
|
-
"I create a mock with an async generator that throws {string}",
|
|
429
|
-
(_, errorMsg: string) => {
|
|
430
|
-
mock = schmock();
|
|
431
|
-
mock("GET /async-fail", async () => {
|
|
432
|
-
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
433
|
-
throw new Error(errorMsg);
|
|
434
|
-
});
|
|
435
|
-
},
|
|
436
|
-
);
|
|
437
|
-
|
|
438
|
-
When("I request {string}", async (_, request: string) => {
|
|
439
|
-
const { method, path } = parseRequest(request);
|
|
440
|
-
response = await mock.handle(method, path);
|
|
441
|
-
});
|
|
442
|
-
|
|
443
|
-
Then("I should receive status {int}", (_, status: number) => {
|
|
444
|
-
expect(response.status).toBe(status);
|
|
445
|
-
});
|
|
446
|
-
|
|
447
|
-
And(
|
|
448
|
-
"the response should contain error {string}",
|
|
449
|
-
(_, errorMessage: string) => {
|
|
450
|
-
expect(responseBodyString(response, "error")).toContain(errorMessage);
|
|
451
|
-
},
|
|
452
|
-
);
|
|
453
|
-
});
|
|
454
|
-
|
|
455
|
-
Scenario(
|
|
456
|
-
"Error responses include proper headers",
|
|
457
|
-
({ Given, When, Then, And }) => {
|
|
458
|
-
Given(
|
|
459
|
-
"I create a mock with a generator that throws {string}",
|
|
460
|
-
(_, errorMsg: string) => {
|
|
461
|
-
mock = schmock();
|
|
462
|
-
mock("GET /error", () => {
|
|
463
|
-
throw new Error(errorMsg);
|
|
464
|
-
});
|
|
465
|
-
},
|
|
466
|
-
);
|
|
467
|
-
|
|
468
|
-
When("I request {string}", async (_, request: string) => {
|
|
469
|
-
const { method, path } = parseRequest(request);
|
|
470
|
-
response = await mock.handle(method, path);
|
|
471
|
-
});
|
|
472
|
-
|
|
473
|
-
Then("I should receive status {int}", (_, status: number) => {
|
|
474
|
-
expect(response.status).toBe(status);
|
|
475
|
-
});
|
|
476
|
-
|
|
477
|
-
And("the content-type should be {string}", (_, contentType: string) => {
|
|
478
|
-
expect(response.headers["content-type"]).toBe(contentType);
|
|
479
|
-
});
|
|
480
|
-
|
|
481
|
-
And(
|
|
482
|
-
"the response body should be a structured {string} error for {string}",
|
|
483
|
-
(_, code: string, message: string) => {
|
|
484
|
-
expect(response.body).toEqual({ error: message, code });
|
|
485
|
-
},
|
|
486
|
-
);
|
|
487
|
-
},
|
|
488
|
-
);
|
|
489
|
-
|
|
490
|
-
Scenario(
|
|
491
|
-
"Plugin onError returns response with status 0",
|
|
492
|
-
({ Given, When, Then, And }) => {
|
|
493
|
-
Given(
|
|
494
|
-
"I create a mock with a plugin whose error handler returns status 0",
|
|
495
|
-
() => {
|
|
496
|
-
mock = schmock();
|
|
497
|
-
const plugin: Plugin = {
|
|
498
|
-
name: "zero-status",
|
|
499
|
-
process: () => {
|
|
500
|
-
throw new Error("fail");
|
|
501
|
-
},
|
|
502
|
-
onError: () => ({ status: 0, body: "zero status", headers: {} }),
|
|
503
|
-
};
|
|
504
|
-
mock("GET /zero", "original").pipe(plugin);
|
|
505
|
-
},
|
|
506
|
-
);
|
|
507
|
-
|
|
508
|
-
When("I request {string}", async (_, request: string) => {
|
|
509
|
-
const { method, path } = parseRequest(request);
|
|
510
|
-
response = await mock.handle(method, path);
|
|
511
|
-
});
|
|
512
|
-
|
|
513
|
-
Then("I should receive status {int}", (_, status: number) => {
|
|
514
|
-
expect(response.status).toBe(status);
|
|
515
|
-
});
|
|
516
|
-
|
|
517
|
-
And("I should receive text {string}", (_, expectedText: string) => {
|
|
518
|
-
expect(response.body).toBe(expectedText);
|
|
519
|
-
});
|
|
520
|
-
},
|
|
521
|
-
);
|
|
522
|
-
|
|
523
|
-
Scenario(
|
|
524
|
-
"Plugin null/undefined return handling",
|
|
525
|
-
({ Given, When, Then, And }) => {
|
|
526
|
-
Given("I create a mock with a plugin that returns null", () => {
|
|
527
|
-
mock = schmock();
|
|
528
|
-
const nullPlugin: Plugin = {
|
|
529
|
-
name: "null-plugin",
|
|
530
|
-
process: (context) => ({ context }),
|
|
531
|
-
};
|
|
532
|
-
Object.defineProperty(nullPlugin, "process", {
|
|
533
|
-
value: () => null,
|
|
534
|
-
});
|
|
535
|
-
mock("GET /null", "original").pipe(nullPlugin);
|
|
536
|
-
});
|
|
537
|
-
|
|
538
|
-
When("I request {string}", async (_, request: string) => {
|
|
539
|
-
const { method, path } = parseRequest(request);
|
|
540
|
-
response = await mock.handle(method, path);
|
|
541
|
-
});
|
|
542
|
-
|
|
543
|
-
Then("I should receive status {int}", (_, status: number) => {
|
|
544
|
-
expect(response.status).toBe(status);
|
|
545
|
-
});
|
|
546
|
-
|
|
547
|
-
And(
|
|
548
|
-
"the response should contain error {string}",
|
|
549
|
-
(_, errorMessage: string) => {
|
|
550
|
-
expect(responseBodyString(response, "error")).toContain(errorMessage);
|
|
551
|
-
},
|
|
552
|
-
);
|
|
553
|
-
},
|
|
554
|
-
);
|
|
555
|
-
});
|