@schmock/core 1.0.4 → 1.2.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/builder.d.ts +15 -5
- package/dist/builder.d.ts.map +1 -1
- package/dist/builder.js +148 -60
- package/dist/constants.d.ts +5 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +11 -0
- package/dist/index.d.ts +3 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +20 -12
- package/dist/parser.d.ts.map +1 -1
- package/dist/parser.js +2 -17
- package/dist/types.d.ts +17 -214
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +1 -0
- package/package.json +4 -4
- package/src/builder.test.ts +2 -2
- package/src/builder.ts +226 -108
- package/src/constants.ts +17 -1
- package/src/index.ts +34 -28
- package/src/namespace.test.ts +3 -2
- package/src/parser.property.test.ts +493 -0
- package/src/parser.ts +2 -20
- package/src/plugin-system.test.ts +91 -0
- package/src/response-parsing.test.ts +11 -7
- package/src/steps/async-support.steps.ts +101 -91
- package/src/steps/basic-usage.steps.ts +49 -36
- package/src/steps/developer-experience.steps.ts +95 -97
- package/src/steps/error-handling.steps.ts +71 -72
- package/src/steps/fluent-api.steps.ts +75 -72
- package/src/steps/http-methods.steps.ts +33 -33
- package/src/steps/performance-reliability.steps.ts +52 -88
- package/src/steps/plugin-integration.steps.ts +176 -176
- package/src/steps/request-history.steps.ts +333 -0
- package/src/steps/state-concurrency.steps.ts +418 -316
- package/src/steps/stateful-workflows.steps.ts +138 -136
- package/src/types.ts +20 -271
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { describeFeature, loadFeature } from "@amiceli/vitest-cucumber";
|
|
2
2
|
import { expect } from "vitest";
|
|
3
|
+
import type { CallableMockInstance, Plugin } from "../types";
|
|
3
4
|
import { schmock } from "../index";
|
|
4
|
-
import type { CallableMockInstance } from "../types";
|
|
5
5
|
|
|
6
6
|
const feature = await loadFeature("../../features/error-handling.feature");
|
|
7
7
|
|
|
@@ -11,9 +11,9 @@ describeFeature(feature, ({ Scenario }) => {
|
|
|
11
11
|
let error: Error | null = null;
|
|
12
12
|
|
|
13
13
|
Scenario("Route not found returns 404", ({ Given, When, Then, And }) => {
|
|
14
|
-
Given("I create a mock with
|
|
14
|
+
Given("I create a mock with a GET /users route returning a user list", () => {
|
|
15
15
|
mock = schmock();
|
|
16
|
-
mock(
|
|
16
|
+
mock("GET /users", [{ id: 1, name: "John" }]);
|
|
17
17
|
});
|
|
18
18
|
|
|
19
19
|
When("I request {string}", async (_, request: string) => {
|
|
@@ -35,9 +35,9 @@ describeFeature(feature, ({ Scenario }) => {
|
|
|
35
35
|
});
|
|
36
36
|
|
|
37
37
|
Scenario("Wrong HTTP method returns 404", ({ Given, When, Then, And }) => {
|
|
38
|
-
Given("I create a mock with
|
|
38
|
+
Given("I create a mock with a GET /api/data route returning success", () => {
|
|
39
39
|
mock = schmock();
|
|
40
|
-
mock(
|
|
40
|
+
mock("GET /api/data", { success: true });
|
|
41
41
|
});
|
|
42
42
|
|
|
43
43
|
When("I request {string}", async (_, request: string) => {
|
|
@@ -55,11 +55,11 @@ describeFeature(feature, ({ Scenario }) => {
|
|
|
55
55
|
});
|
|
56
56
|
|
|
57
57
|
Scenario("Invalid route key throws RouteDefinitionError", ({ Given, Then, And }) => {
|
|
58
|
-
Given("I attempt to
|
|
58
|
+
Given("I attempt to register a route with an invalid HTTP method", () => {
|
|
59
59
|
error = null;
|
|
60
60
|
try {
|
|
61
61
|
mock = schmock();
|
|
62
|
-
mock(
|
|
62
|
+
mock("INVALID_METHOD /path" as any, "response");
|
|
63
63
|
} catch (e) {
|
|
64
64
|
error = e as Error;
|
|
65
65
|
}
|
|
@@ -67,20 +67,20 @@ describeFeature(feature, ({ Scenario }) => {
|
|
|
67
67
|
|
|
68
68
|
Then("it should throw a RouteDefinitionError", () => {
|
|
69
69
|
expect(error).not.toBeNull();
|
|
70
|
-
expect(error!.constructor.name).toBe(
|
|
70
|
+
expect(error!.constructor.name).toBe("RouteParseError");
|
|
71
71
|
});
|
|
72
72
|
|
|
73
73
|
And("the error message should contain {string}", (_, message: string) => {
|
|
74
|
-
expect(error!.message).toContain(
|
|
74
|
+
expect(error!.message).toContain("Invalid route key format");
|
|
75
75
|
});
|
|
76
76
|
});
|
|
77
77
|
|
|
78
78
|
Scenario("Empty route path throws RouteDefinitionError", ({ Given, Then, And }) => {
|
|
79
|
-
Given("I attempt to
|
|
79
|
+
Given("I attempt to register a route with an empty path", () => {
|
|
80
80
|
error = null;
|
|
81
81
|
try {
|
|
82
82
|
mock = schmock();
|
|
83
|
-
mock(
|
|
83
|
+
mock("GET " as any, "response");
|
|
84
84
|
} catch (e) {
|
|
85
85
|
error = e as Error;
|
|
86
86
|
}
|
|
@@ -88,22 +88,22 @@ describeFeature(feature, ({ Scenario }) => {
|
|
|
88
88
|
|
|
89
89
|
Then("it should throw a RouteDefinitionError", () => {
|
|
90
90
|
expect(error).not.toBeNull();
|
|
91
|
-
expect(error!.constructor.name).toBe(
|
|
91
|
+
expect(error!.constructor.name).toBe("RouteParseError");
|
|
92
92
|
});
|
|
93
93
|
|
|
94
94
|
And("the error message should contain {string}", (_, message: string) => {
|
|
95
|
-
expect(error!.message).toContain(
|
|
95
|
+
expect(error!.message).toContain("Invalid route key format");
|
|
96
96
|
});
|
|
97
97
|
});
|
|
98
98
|
|
|
99
99
|
Scenario("Plugin throws error returns 500 with PluginError", ({ Given, When, Then, And }) => {
|
|
100
|
-
Given("I create a mock with
|
|
100
|
+
Given("I create a mock with a plugin that throws {string}", (_, errorMsg: string) => {
|
|
101
101
|
mock = schmock();
|
|
102
|
-
const failingPlugin = {
|
|
103
|
-
name:
|
|
104
|
-
process: () => { throw new Error(
|
|
102
|
+
const failingPlugin: Plugin = {
|
|
103
|
+
name: "failing-plugin",
|
|
104
|
+
process: () => { throw new Error(errorMsg); },
|
|
105
105
|
};
|
|
106
|
-
mock(
|
|
106
|
+
mock("GET /test", "original").pipe(failingPlugin);
|
|
107
107
|
});
|
|
108
108
|
|
|
109
109
|
When("I request {string}", async (_, request: string) => {
|
|
@@ -125,14 +125,14 @@ describeFeature(feature, ({ Scenario }) => {
|
|
|
125
125
|
});
|
|
126
126
|
|
|
127
127
|
Scenario("Plugin onError hook recovers from failure", ({ Given, When, Then, And }) => {
|
|
128
|
-
Given("I create a mock with recoverable plugin
|
|
128
|
+
Given("I create a mock with a recoverable plugin that returns {string}", (_, recoveredBody: string) => {
|
|
129
129
|
mock = schmock();
|
|
130
|
-
const recoverablePlugin = {
|
|
131
|
-
name:
|
|
132
|
-
process: () => { throw new Error(
|
|
133
|
-
onError: () => ({ status: 200, body:
|
|
130
|
+
const recoverablePlugin: Plugin = {
|
|
131
|
+
name: "recoverable",
|
|
132
|
+
process: () => { throw new Error("Initial failure"); },
|
|
133
|
+
onError: () => ({ status: 200, body: recoveredBody, headers: {} }),
|
|
134
134
|
};
|
|
135
|
-
mock(
|
|
135
|
+
mock("GET /test", "original").pipe(recoverablePlugin);
|
|
136
136
|
});
|
|
137
137
|
|
|
138
138
|
When("I request {string}", async (_, request: string) => {
|
|
@@ -150,13 +150,13 @@ describeFeature(feature, ({ Scenario }) => {
|
|
|
150
150
|
});
|
|
151
151
|
|
|
152
152
|
Scenario("Plugin returns invalid result structure", ({ Given, When, Then, And }) => {
|
|
153
|
-
Given("I create a mock with invalid
|
|
153
|
+
Given("I create a mock with a plugin returning an invalid result", () => {
|
|
154
154
|
mock = schmock();
|
|
155
|
-
const invalidPlugin = {
|
|
156
|
-
name:
|
|
157
|
-
process: () => ({ wrongStructure: true })
|
|
155
|
+
const invalidPlugin: Plugin = {
|
|
156
|
+
name: "invalid",
|
|
157
|
+
process: () => ({ wrongStructure: true }) as any,
|
|
158
158
|
};
|
|
159
|
-
mock(
|
|
159
|
+
mock("GET /test", "original").pipe(invalidPlugin);
|
|
160
160
|
});
|
|
161
161
|
|
|
162
162
|
When("I request {string}", async (_, request: string) => {
|
|
@@ -174,9 +174,9 @@ describeFeature(feature, ({ Scenario }) => {
|
|
|
174
174
|
});
|
|
175
175
|
|
|
176
176
|
Scenario("Function generator throws error returns 500", ({ Given, When, Then, And }) => {
|
|
177
|
-
Given("I create a mock with
|
|
177
|
+
Given("I create a mock with a generator that throws {string}", (_, errorMsg: string) => {
|
|
178
178
|
mock = schmock();
|
|
179
|
-
mock(
|
|
179
|
+
mock("GET /fail", () => { throw new Error(errorMsg); });
|
|
180
180
|
});
|
|
181
181
|
|
|
182
182
|
When("I request {string}", async (_, request: string) => {
|
|
@@ -198,13 +198,13 @@ describeFeature(feature, ({ Scenario }) => {
|
|
|
198
198
|
});
|
|
199
199
|
|
|
200
200
|
Scenario("Invalid JSON generator with JSON content-type throws RouteDefinitionError", ({ Given, Then, And }) => {
|
|
201
|
-
Given("I attempt to
|
|
201
|
+
Given("I attempt to register a route with a circular reference as JSON", () => {
|
|
202
202
|
error = null;
|
|
203
203
|
try {
|
|
204
204
|
mock = schmock();
|
|
205
|
-
const circularRef:
|
|
205
|
+
const circularRef: Record<string, unknown> = {};
|
|
206
206
|
circularRef.self = circularRef;
|
|
207
|
-
mock(
|
|
207
|
+
mock("GET /invalid", circularRef, { contentType: "application/json" });
|
|
208
208
|
} catch (e) {
|
|
209
209
|
error = e as Error;
|
|
210
210
|
}
|
|
@@ -212,7 +212,7 @@ describeFeature(feature, ({ Scenario }) => {
|
|
|
212
212
|
|
|
213
213
|
Then("it should throw a RouteDefinitionError", () => {
|
|
214
214
|
expect(error).not.toBeNull();
|
|
215
|
-
expect(error!.constructor.name).toBe(
|
|
215
|
+
expect(error!.constructor.name).toBe("RouteDefinitionError");
|
|
216
216
|
});
|
|
217
217
|
|
|
218
218
|
And("the error message should contain {string}", (_, message: string) => {
|
|
@@ -221,9 +221,9 @@ describeFeature(feature, ({ Scenario }) => {
|
|
|
221
221
|
});
|
|
222
222
|
|
|
223
223
|
Scenario("Namespace mismatch returns 404", ({ Given, When, Then, And }) => {
|
|
224
|
-
Given("I create a mock with namespace
|
|
225
|
-
mock = schmock({ namespace
|
|
226
|
-
mock(
|
|
224
|
+
Given("I create a mock with namespace {string} and a GET /users route", (_, namespace: string) => {
|
|
225
|
+
mock = schmock({ namespace });
|
|
226
|
+
mock("GET /users", [{ id: 1 }]);
|
|
227
227
|
});
|
|
228
228
|
|
|
229
229
|
When("I request {string}", async (_, request: string) => {
|
|
@@ -241,17 +241,17 @@ describeFeature(feature, ({ Scenario }) => {
|
|
|
241
241
|
});
|
|
242
242
|
|
|
243
243
|
Scenario("Multiple plugin failures cascade properly", ({ Given, When, Then, And }) => {
|
|
244
|
-
Given("I create a mock with
|
|
244
|
+
Given("I create a mock with two failing plugins piped in sequence", () => {
|
|
245
245
|
mock = schmock();
|
|
246
|
-
const plugin1 = {
|
|
247
|
-
name:
|
|
248
|
-
process: () => { throw new Error(
|
|
246
|
+
const plugin1: Plugin = {
|
|
247
|
+
name: "first-fail",
|
|
248
|
+
process: () => { throw new Error("First error"); },
|
|
249
249
|
};
|
|
250
|
-
const plugin2 = {
|
|
251
|
-
name:
|
|
252
|
-
process: () => { throw new Error(
|
|
250
|
+
const plugin2: Plugin = {
|
|
251
|
+
name: "second-fail",
|
|
252
|
+
process: () => { throw new Error("Second error"); },
|
|
253
253
|
};
|
|
254
|
-
mock(
|
|
254
|
+
mock("GET /cascade", "original").pipe(plugin1).pipe(plugin2);
|
|
255
255
|
});
|
|
256
256
|
|
|
257
257
|
When("I request {string}", async (_, request: string) => {
|
|
@@ -269,14 +269,14 @@ describeFeature(feature, ({ Scenario }) => {
|
|
|
269
269
|
});
|
|
270
270
|
|
|
271
271
|
Scenario("Plugin onError hook also fails", ({ Given, When, Then, And }) => {
|
|
272
|
-
Given("I create a mock with
|
|
272
|
+
Given("I create a mock with a plugin whose error handler also throws", () => {
|
|
273
273
|
mock = schmock({ debug: true });
|
|
274
|
-
const brokenPlugin = {
|
|
275
|
-
name:
|
|
276
|
-
process: () => { throw new Error(
|
|
277
|
-
onError: () => { throw new Error(
|
|
274
|
+
const brokenPlugin: Plugin = {
|
|
275
|
+
name: "broken-handler",
|
|
276
|
+
process: () => { throw new Error("Process failed"); },
|
|
277
|
+
onError: () => { throw new Error("Handler failed"); },
|
|
278
278
|
};
|
|
279
|
-
mock(
|
|
279
|
+
mock("GET /broken", "original").pipe(brokenPlugin);
|
|
280
280
|
});
|
|
281
281
|
|
|
282
282
|
When("I request {string}", async (_, request: string) => {
|
|
@@ -294,9 +294,9 @@ describeFeature(feature, ({ Scenario }) => {
|
|
|
294
294
|
});
|
|
295
295
|
|
|
296
296
|
Scenario("Empty parameter in route returns 404", ({ Given, When, Then, And }) => {
|
|
297
|
-
Given("I create a mock with parameterized route
|
|
297
|
+
Given("I create a mock with a parameterized route {string}", (_, route: string) => {
|
|
298
298
|
mock = schmock();
|
|
299
|
-
mock(
|
|
299
|
+
mock(route as any, ({ params }: any) => ({ userId: params.id }));
|
|
300
300
|
});
|
|
301
301
|
|
|
302
302
|
When("I request {string}", async (_, request: string) => {
|
|
@@ -314,11 +314,11 @@ describeFeature(feature, ({ Scenario }) => {
|
|
|
314
314
|
});
|
|
315
315
|
|
|
316
316
|
Scenario("Async generator error handling", ({ Given, When, Then, And }) => {
|
|
317
|
-
Given("I create a mock with
|
|
317
|
+
Given("I create a mock with an async generator that throws {string}", (_, errorMsg: string) => {
|
|
318
318
|
mock = schmock();
|
|
319
|
-
mock(
|
|
319
|
+
mock("GET /async-fail", async () => {
|
|
320
320
|
await new Promise(resolve => setTimeout(resolve, 10));
|
|
321
|
-
throw new Error(
|
|
321
|
+
throw new Error(errorMsg);
|
|
322
322
|
});
|
|
323
323
|
});
|
|
324
324
|
|
|
@@ -337,9 +337,9 @@ describeFeature(feature, ({ Scenario }) => {
|
|
|
337
337
|
});
|
|
338
338
|
|
|
339
339
|
Scenario("Error responses include proper headers", ({ Given, When, Then, And }) => {
|
|
340
|
-
Given("I create a mock with
|
|
340
|
+
Given("I create a mock with a generator that throws {string}", (_, errorMsg: string) => {
|
|
341
341
|
mock = schmock();
|
|
342
|
-
mock(
|
|
342
|
+
mock("GET /error", () => { throw new Error(errorMsg); });
|
|
343
343
|
});
|
|
344
344
|
|
|
345
345
|
When("I request {string}", async (_, request: string) => {
|
|
@@ -352,7 +352,6 @@ describeFeature(feature, ({ Scenario }) => {
|
|
|
352
352
|
});
|
|
353
353
|
|
|
354
354
|
And("the content-type should be {string}", (_, contentType: string) => {
|
|
355
|
-
// Error responses don't get content-type headers automatically
|
|
356
355
|
expect(response.headers).toBeDefined();
|
|
357
356
|
});
|
|
358
357
|
|
|
@@ -362,14 +361,14 @@ describeFeature(feature, ({ Scenario }) => {
|
|
|
362
361
|
});
|
|
363
362
|
|
|
364
363
|
Scenario("Plugin onError returns response with status 0", ({ Given, When, Then, And }) => {
|
|
365
|
-
Given("I create a mock with
|
|
364
|
+
Given("I create a mock with a plugin whose error handler returns status 0", () => {
|
|
366
365
|
mock = schmock();
|
|
367
|
-
const plugin = {
|
|
368
|
-
name:
|
|
369
|
-
process: () => { throw new Error(
|
|
370
|
-
onError: () => ({ status: 0, body:
|
|
366
|
+
const plugin: Plugin = {
|
|
367
|
+
name: "zero-status",
|
|
368
|
+
process: () => { throw new Error("fail"); },
|
|
369
|
+
onError: () => ({ status: 0, body: "zero status", headers: {} }),
|
|
371
370
|
};
|
|
372
|
-
mock(
|
|
371
|
+
mock("GET /zero", "original").pipe(plugin);
|
|
373
372
|
});
|
|
374
373
|
|
|
375
374
|
When("I request {string}", async (_, request: string) => {
|
|
@@ -387,13 +386,13 @@ describeFeature(feature, ({ Scenario }) => {
|
|
|
387
386
|
});
|
|
388
387
|
|
|
389
388
|
Scenario("Plugin null/undefined return handling", ({ Given, When, Then, And }) => {
|
|
390
|
-
Given("I create a mock with
|
|
389
|
+
Given("I create a mock with a plugin that returns null", () => {
|
|
391
390
|
mock = schmock();
|
|
392
|
-
const nullPlugin = {
|
|
393
|
-
name:
|
|
394
|
-
process: () => null
|
|
391
|
+
const nullPlugin: Plugin = {
|
|
392
|
+
name: "null-plugin",
|
|
393
|
+
process: () => null as any,
|
|
395
394
|
};
|
|
396
|
-
mock(
|
|
395
|
+
mock("GET /null", "original").pipe(nullPlugin);
|
|
397
396
|
});
|
|
398
397
|
|
|
399
398
|
When("I request {string}", async (_, request: string) => {
|
|
@@ -409,4 +408,4 @@ describeFeature(feature, ({ Scenario }) => {
|
|
|
409
408
|
expect(response.body.error).toContain(errorMessage);
|
|
410
409
|
});
|
|
411
410
|
});
|
|
412
|
-
});
|
|
411
|
+
});
|
|
@@ -1,24 +1,21 @@
|
|
|
1
1
|
import { describeFeature, loadFeature } from "@amiceli/vitest-cucumber";
|
|
2
2
|
import { expect } from "vitest";
|
|
3
3
|
import { schmock } from "../index";
|
|
4
|
-
import type {
|
|
4
|
+
import type { CallableMockInstance } from "../types";
|
|
5
5
|
|
|
6
6
|
const feature = await loadFeature("../../features/fluent-api.feature");
|
|
7
7
|
|
|
8
8
|
describeFeature(feature, ({ Scenario }) => {
|
|
9
|
-
let mock:
|
|
9
|
+
let mock: CallableMockInstance;
|
|
10
10
|
let response: any;
|
|
11
11
|
let responses: any[] = [];
|
|
12
|
-
const error: Error | null = null;
|
|
13
12
|
|
|
14
13
|
Scenario("Simple route with generator function", ({ Given, When, Then }) => {
|
|
15
|
-
Given("I create a mock with
|
|
16
|
-
// Create callable mock instance
|
|
14
|
+
Given("I create a mock with a JSON generator at {string}", (_, route: string) => {
|
|
17
15
|
mock = schmock({});
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
mock('GET /users', () => [{ id: 1, name: 'John' }], {});
|
|
16
|
+
mock(route as Schmock.RouteKey, () => [{ id: 1, name: "John" }], {
|
|
17
|
+
contentType: "application/json",
|
|
18
|
+
});
|
|
22
19
|
});
|
|
23
20
|
|
|
24
21
|
When("I request {string}", async (_, request: string) => {
|
|
@@ -37,15 +34,17 @@ describeFeature(feature, ({ Scenario }) => {
|
|
|
37
34
|
({ Given, When, Then, And }) => {
|
|
38
35
|
responses = [];
|
|
39
36
|
|
|
40
|
-
Given("I create a mock with
|
|
41
|
-
// Create mock with global state
|
|
37
|
+
Given("I create a mock with a stateful counter", () => {
|
|
42
38
|
mock = schmock({ state: { count: 0 } });
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
39
|
+
mock(
|
|
40
|
+
"GET /counter",
|
|
41
|
+
({ state }) => {
|
|
42
|
+
const s = state as { count: number };
|
|
43
|
+
s.count++;
|
|
44
|
+
return { value: s.count };
|
|
45
|
+
},
|
|
46
|
+
{ contentType: "application/json" },
|
|
47
|
+
);
|
|
49
48
|
});
|
|
50
49
|
|
|
51
50
|
When("I request {string} twice", async (_, request: string) => {
|
|
@@ -66,10 +65,11 @@ describeFeature(feature, ({ Scenario }) => {
|
|
|
66
65
|
);
|
|
67
66
|
|
|
68
67
|
Scenario("Route with parameters", ({ Given, When, Then }) => {
|
|
69
|
-
Given("I create a mock with
|
|
70
|
-
// Create mock with parameter route
|
|
68
|
+
Given("I create a mock with a parameterized route {string}", (_, route: string) => {
|
|
71
69
|
mock = schmock({});
|
|
72
|
-
mock(
|
|
70
|
+
mock(route as Schmock.RouteKey, ({ params }) => ({ userId: params.id }), {
|
|
71
|
+
contentType: "application/json",
|
|
72
|
+
});
|
|
73
73
|
});
|
|
74
74
|
|
|
75
75
|
When("I request {string}", async (_, request: string) => {
|
|
@@ -84,10 +84,14 @@ describeFeature(feature, ({ Scenario }) => {
|
|
|
84
84
|
});
|
|
85
85
|
|
|
86
86
|
Scenario("Response with custom status code", ({ Given, When, Then, And }) => {
|
|
87
|
-
Given("I create a mock
|
|
88
|
-
// Create mock with custom status response
|
|
87
|
+
Given("I create a mock returning status {int} for {string}", (_, _status: number, route: string) => {
|
|
89
88
|
mock = schmock({});
|
|
90
|
-
mock(
|
|
89
|
+
mock(route as Schmock.RouteKey, ({ body }) => {
|
|
90
|
+
const b = body as Record<string, unknown>;
|
|
91
|
+
return [201, { id: 1, ...b }];
|
|
92
|
+
}, {
|
|
93
|
+
contentType: "application/json",
|
|
94
|
+
});
|
|
91
95
|
});
|
|
92
96
|
|
|
93
97
|
When(
|
|
@@ -109,11 +113,29 @@ describeFeature(feature, ({ Scenario }) => {
|
|
|
109
113
|
});
|
|
110
114
|
});
|
|
111
115
|
|
|
116
|
+
Scenario("Static data response", ({ Given, When, Then }) => {
|
|
117
|
+
Given("I create a mock with static config data at {string}", (_, route: string) => {
|
|
118
|
+
mock = schmock({});
|
|
119
|
+
mock(route as Schmock.RouteKey, { version: "1.0.0", features: ["auth"] }, {
|
|
120
|
+
contentType: "application/json",
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
When("I request {string}", async (_, request: string) => {
|
|
125
|
+
const [method, path] = request.split(" ");
|
|
126
|
+
response = await mock.handle(method as any, path);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
Then("I should receive:", (_, docString: string) => {
|
|
130
|
+
const expected = JSON.parse(docString);
|
|
131
|
+
expect(response.body).toEqual(expected);
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
|
|
112
135
|
Scenario("404 for undefined routes", ({ Given, When, Then }) => {
|
|
113
|
-
Given("I create a mock with
|
|
114
|
-
// Create mock with only one route, so other routes return 404
|
|
136
|
+
Given("I create a mock with only a {string} route", (_, route: string) => {
|
|
115
137
|
mock = schmock({});
|
|
116
|
-
mock(
|
|
138
|
+
mock(route as Schmock.RouteKey, () => [], { contentType: "application/json" });
|
|
117
139
|
});
|
|
118
140
|
|
|
119
141
|
When("I request {string}", async (_, request: string) => {
|
|
@@ -127,20 +149,22 @@ describeFeature(feature, ({ Scenario }) => {
|
|
|
127
149
|
});
|
|
128
150
|
|
|
129
151
|
Scenario("Query parameters", ({ Given, When, Then }) => {
|
|
130
|
-
Given("I create a mock
|
|
131
|
-
// Create mock that handles query parameters
|
|
152
|
+
Given("I create a mock that reads query parameters at {string}", (_, route: string) => {
|
|
132
153
|
mock = schmock({});
|
|
133
|
-
mock(
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
154
|
+
mock(
|
|
155
|
+
route as Schmock.RouteKey,
|
|
156
|
+
({ query }) => ({
|
|
157
|
+
results: [],
|
|
158
|
+
query: query.q,
|
|
159
|
+
}),
|
|
160
|
+
{ contentType: "application/json" },
|
|
161
|
+
);
|
|
137
162
|
});
|
|
138
163
|
|
|
139
164
|
When("I request {string}", async (_, request: string) => {
|
|
140
165
|
const [method, fullPath] = request.split(" ");
|
|
141
166
|
const [path, queryString] = fullPath.split("?");
|
|
142
167
|
|
|
143
|
-
// Parse query string
|
|
144
168
|
const query: Record<string, string> = {};
|
|
145
169
|
if (queryString) {
|
|
146
170
|
queryString.split("&").forEach((param) => {
|
|
@@ -159,12 +183,15 @@ describeFeature(feature, ({ Scenario }) => {
|
|
|
159
183
|
});
|
|
160
184
|
|
|
161
185
|
Scenario("Request headers access", ({ Given, When, Then }) => {
|
|
162
|
-
Given("I create a mock
|
|
163
|
-
// Create mock that accesses request headers
|
|
186
|
+
Given("I create a mock that reads headers at {string}", (_, route: string) => {
|
|
164
187
|
mock = schmock({});
|
|
165
|
-
mock(
|
|
166
|
-
|
|
167
|
-
|
|
188
|
+
mock(
|
|
189
|
+
route as Schmock.RouteKey,
|
|
190
|
+
({ headers }) => ({
|
|
191
|
+
authenticated: headers.authorization === "Bearer token123",
|
|
192
|
+
}),
|
|
193
|
+
{ contentType: "application/json" },
|
|
194
|
+
);
|
|
168
195
|
});
|
|
169
196
|
|
|
170
197
|
When(
|
|
@@ -183,28 +210,9 @@ describeFeature(feature, ({ Scenario }) => {
|
|
|
183
210
|
});
|
|
184
211
|
|
|
185
212
|
Scenario("Global configuration with namespace", ({ Given, When, Then }) => {
|
|
186
|
-
Given("I create a mock with
|
|
187
|
-
|
|
188
|
-
mock
|
|
189
|
-
mock('GET /users', () => [], {});
|
|
190
|
-
});
|
|
191
|
-
|
|
192
|
-
When("I request {string}", async (_, request: string) => {
|
|
193
|
-
const [method, path] = request.split(" ");
|
|
194
|
-
response = await mock.handle(method as any, path);
|
|
195
|
-
});
|
|
196
|
-
|
|
197
|
-
Then("I should receive:", (_, docString: string) => {
|
|
198
|
-
const expected = JSON.parse(docString);
|
|
199
|
-
expect(response.body).toEqual(expected);
|
|
200
|
-
});
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
Scenario("Static data response", ({ Given, When, Then }) => {
|
|
204
|
-
Given("I create a mock with:", (_, docString: string) => {
|
|
205
|
-
// Create mock with static data response
|
|
206
|
-
mock = schmock({});
|
|
207
|
-
mock('GET /config', { version: '1.0.0', features: ['auth'] }, {});
|
|
213
|
+
Given("I create a mock with namespace {string}", (_, namespace: string) => {
|
|
214
|
+
mock = schmock({ namespace });
|
|
215
|
+
mock("GET /users", () => [], { contentType: "application/json" });
|
|
208
216
|
});
|
|
209
217
|
|
|
210
218
|
When("I request {string}", async (_, request: string) => {
|
|
@@ -218,18 +226,19 @@ describeFeature(feature, ({ Scenario }) => {
|
|
|
218
226
|
});
|
|
219
227
|
});
|
|
220
228
|
|
|
221
|
-
Scenario("Plugin pipeline with pipe chaining", ({ Given, When, Then
|
|
222
|
-
Given("I create a mock with
|
|
223
|
-
// Create mock with plugin pipeline
|
|
229
|
+
Scenario("Plugin pipeline with pipe chaining", ({ Given, When, Then }) => {
|
|
230
|
+
Given("I create a mock with two passthrough plugins", () => {
|
|
224
231
|
mock = schmock({});
|
|
225
|
-
mock(
|
|
232
|
+
mock("GET /users", () => [{ id: 1, name: "John" }], {
|
|
233
|
+
contentType: "application/json",
|
|
234
|
+
})
|
|
226
235
|
.pipe({
|
|
227
236
|
name: "logging",
|
|
228
|
-
process: (ctx, response) => ({ context: ctx, response })
|
|
237
|
+
process: (ctx, response) => ({ context: ctx, response }),
|
|
229
238
|
})
|
|
230
239
|
.pipe({
|
|
231
240
|
name: "cors",
|
|
232
|
-
process: (ctx, response) => ({ context: ctx, response })
|
|
241
|
+
process: (ctx, response) => ({ context: ctx, response }),
|
|
233
242
|
});
|
|
234
243
|
});
|
|
235
244
|
|
|
@@ -242,11 +251,5 @@ describeFeature(feature, ({ Scenario }) => {
|
|
|
242
251
|
const expected = JSON.parse(docString);
|
|
243
252
|
expect(response.body).toEqual(expected);
|
|
244
253
|
});
|
|
245
|
-
|
|
246
|
-
And("the response should have CORS headers", () => {
|
|
247
|
-
// This would check for CORS headers if implemented
|
|
248
|
-
// For now just verify the response was processed
|
|
249
|
-
expect(response).toBeDefined();
|
|
250
|
-
});
|
|
251
254
|
});
|
|
252
255
|
});
|