@schmock/core 2.2.3 → 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.
Files changed (94) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +35 -0
  3. package/dist/abort.d.ts +3 -0
  4. package/dist/abort.d.ts.map +1 -0
  5. package/dist/abort.js +30 -0
  6. package/dist/binary.d.ts +8 -0
  7. package/dist/binary.d.ts.map +1 -0
  8. package/dist/binary.js +9 -0
  9. package/dist/builder.d.ts +31 -10
  10. package/dist/builder.d.ts.map +1 -1
  11. package/dist/builder.js +804 -203
  12. package/dist/constants.d.ts +38 -0
  13. package/dist/constants.d.ts.map +1 -1
  14. package/dist/constants.js +123 -2
  15. package/dist/errors.d.ts +14 -3
  16. package/dist/errors.d.ts.map +1 -1
  17. package/dist/errors.js +41 -6
  18. package/dist/helpers.d.ts +9 -0
  19. package/dist/helpers.d.ts.map +1 -1
  20. package/dist/helpers.js +18 -2
  21. package/dist/http-helpers.d.ts +52 -5
  22. package/dist/http-helpers.d.ts.map +1 -1
  23. package/dist/http-helpers.js +153 -37
  24. package/dist/index.d.ts +381 -60
  25. package/dist/index.d.ts.map +1 -1
  26. package/dist/index.js +9 -3
  27. package/dist/interceptor.d.ts +11 -1
  28. package/dist/interceptor.d.ts.map +1 -1
  29. package/dist/interceptor.js +354 -156
  30. package/dist/parser.d.ts.map +1 -1
  31. package/dist/parser.js +12 -3
  32. package/dist/plugin-pipeline.d.ts +11 -4
  33. package/dist/plugin-pipeline.d.ts.map +1 -1
  34. package/dist/plugin-pipeline.js +117 -39
  35. package/dist/response-normalizer.d.ts +16 -0
  36. package/dist/response-normalizer.d.ts.map +1 -0
  37. package/dist/response-normalizer.js +316 -0
  38. package/dist/response-parser.d.ts.map +1 -1
  39. package/dist/response-parser.js +85 -18
  40. package/dist/route-matcher.d.ts +3 -0
  41. package/dist/route-matcher.d.ts.map +1 -1
  42. package/dist/route-matcher.js +12 -6
  43. package/dist/types.d.ts +7 -2
  44. package/dist/types.d.ts.map +1 -1
  45. package/package.json +18 -8
  46. package/src/audit-core-builder.test.ts +0 -218
  47. package/src/audit-response-guard.test.ts +0 -38
  48. package/src/builder.test.ts +0 -289
  49. package/src/builder.ts +0 -701
  50. package/src/constants.test.ts +0 -99
  51. package/src/constants.ts +0 -73
  52. package/src/debug.test.ts +0 -241
  53. package/src/delay.test.ts +0 -319
  54. package/src/dist-shape.test.ts +0 -35
  55. package/src/errors.test.ts +0 -223
  56. package/src/errors.ts +0 -130
  57. package/src/factory.test.ts +0 -133
  58. package/src/helpers.test.ts +0 -147
  59. package/src/helpers.ts +0 -58
  60. package/src/http-helpers.ts +0 -113
  61. package/src/index.ts +0 -151
  62. package/src/interceptor.test.ts +0 -291
  63. package/src/interceptor.ts +0 -320
  64. package/src/namespace.test.ts +0 -274
  65. package/src/parser.property.test.ts +0 -594
  66. package/src/parser.test.ts +0 -148
  67. package/src/parser.ts +0 -64
  68. package/src/plugin-pipeline.ts +0 -103
  69. package/src/plugin-system.test.ts +0 -602
  70. package/src/response-parser.ts +0 -75
  71. package/src/response-parsing.test.ts +0 -333
  72. package/src/route-matcher.ts +0 -69
  73. package/src/route-matching.test.ts +0 -394
  74. package/src/server.test.ts +0 -165
  75. package/src/smart-defaults.test.ts +0 -361
  76. package/src/steps/async-support.steps.ts +0 -400
  77. package/src/steps/audit-core-builder.steps.ts +0 -188
  78. package/src/steps/audit-onerror-tuple.steps.ts +0 -99
  79. package/src/steps/basic-usage.steps.ts +0 -245
  80. package/src/steps/developer-experience.steps.ts +0 -204
  81. package/src/steps/error-handling.steps.ts +0 -345
  82. package/src/steps/fluent-api.steps.ts +0 -255
  83. package/src/steps/http-methods.steps.ts +0 -331
  84. package/src/steps/interceptor.steps.ts +0 -260
  85. package/src/steps/lifecycle-events.steps.ts +0 -142
  86. package/src/steps/performance-reliability.steps.ts +0 -423
  87. package/src/steps/plugin-integration.steps.ts +0 -280
  88. package/src/steps/request-history.steps.ts +0 -276
  89. package/src/steps/response-delay.steps.ts +0 -88
  90. package/src/steps/route-key-format.steps.ts +0 -99
  91. package/src/steps/standalone-server.steps.ts +0 -233
  92. package/src/steps/state-concurrency.steps.ts +0 -739
  93. package/src/steps/stateful-workflows.steps.ts +0 -353
  94. package/src/types.ts +0 -36
@@ -1,353 +0,0 @@
1
- import { describeFeature, loadFeature } from "@amiceli/vitest-cucumber";
2
- import { expect } from "vitest";
3
- import { schmock } from "../index";
4
- import type { CallableMockInstance } from "../types";
5
-
6
- interface CartItem {
7
- id: number;
8
- name: string;
9
- price: number;
10
- quantity: number;
11
- }
12
-
13
- interface UserRecord {
14
- username: string;
15
- password: string;
16
- profile: { name: string; role: string };
17
- }
18
-
19
- interface SessionRecord {
20
- user: string;
21
- profile: { name: string; role: string };
22
- loginTime: string;
23
- }
24
-
25
- const feature = await loadFeature("../../features/stateful-workflows.feature");
26
-
27
- describeFeature(feature, ({ Scenario }) => {
28
- let mock: CallableMockInstance;
29
- let response: any;
30
- let sessionToken: string;
31
- let addedItemIds: number[] = [];
32
-
33
- Scenario("Shopping cart workflow", ({ Given, When, Then, And }) => {
34
- addedItemIds = [];
35
-
36
- Given("I create a stateful shopping cart mock", () => {
37
- const items: CartItem[] = [];
38
- let total = 0;
39
-
40
- mock = schmock();
41
- mock("GET /cart", () => ({
42
- items,
43
- total,
44
- count: items.length,
45
- }));
46
- mock("POST /cart/add", ({ body }) => {
47
- const b = body as Record<string, unknown>;
48
- const item: CartItem = {
49
- id: Date.now(),
50
- name: b.name as string,
51
- price: b.price as number,
52
- quantity: (b.quantity as number) || 1,
53
- };
54
- items.push(item);
55
- total += item.price * item.quantity;
56
- return {
57
- message: "Item added to cart",
58
- added: item,
59
- cart: {
60
- items,
61
- total,
62
- count: items.length,
63
- },
64
- };
65
- });
66
- mock("DELETE /cart/:id", ({ params }) => {
67
- const itemId = parseInt(params.id);
68
- const itemIndex = items.findIndex((item) => item.id === itemId);
69
- if (itemIndex === -1) {
70
- return [404, { error: "Item not found in cart" }];
71
- }
72
- const removedItem = items[itemIndex];
73
- total -= removedItem.price * removedItem.quantity;
74
- items.splice(itemIndex, 1);
75
- return {
76
- message: "Item removed from cart",
77
- item: removedItem,
78
- cart: {
79
- items,
80
- total,
81
- count: items.length,
82
- },
83
- };
84
- });
85
- mock("POST /cart/clear", () => {
86
- items.length = 0;
87
- total = 0;
88
- return {
89
- message: "Cart cleared",
90
- cart: {
91
- items,
92
- total,
93
- count: items.length,
94
- },
95
- };
96
- });
97
- });
98
-
99
- When("I request {string}", async (_, request: string) => {
100
- const [method, path] = request.split(" ");
101
- response = await mock.handle(method as any, path);
102
- });
103
-
104
- Then("the response should show an empty cart with count {int}", (_, expectedCount: number) => {
105
- expect(response.body.count).toBe(expectedCount);
106
- expect(response.body.items).toEqual([]);
107
- expect(response.body.total).toBe(0);
108
- });
109
-
110
- When("I add item with name {string} and price {string}", async (_, itemName: string, priceStr: string) => {
111
- const price = parseFloat(priceStr);
112
- response = await mock.handle("POST", "/cart/add", {
113
- body: { name: itemName, price }
114
- });
115
- addedItemIds.push(response.body.added.id);
116
- });
117
-
118
- And("I add second item with name {string} and price {string}", async (_, itemName: string, priceStr: string) => {
119
- const price = parseFloat(priceStr);
120
- response = await mock.handle("POST", "/cart/add", {
121
- body: { name: itemName, price }
122
- });
123
- addedItemIds.push(response.body.added.id);
124
- });
125
-
126
- Then("the cart should contain {int} items", async (_, expectedCount: number) => {
127
- const cartResponse = await mock.handle("GET", "/cart");
128
- const body = cartResponse.body as { items: CartItem[]; total: number; count: number };
129
- expect(body.count).toBe(expectedCount);
130
- });
131
-
132
- Then("the cart should contain {int} item", async (_, expectedCount: number) => {
133
- const cartResponse = await mock.handle("GET", "/cart");
134
- const body = cartResponse.body as { items: CartItem[]; total: number; count: number };
135
- expect(body.count).toBe(expectedCount);
136
- });
137
-
138
- And("the initial cart total should be {string}", async (_, expectedTotalStr: string) => {
139
- const expectedTotal = parseFloat(expectedTotalStr);
140
- const cartResponse = await mock.handle("GET", "/cart");
141
- const body = cartResponse.body as { items: CartItem[]; total: number; count: number };
142
- expect(body.total).toBe(expectedTotal);
143
- });
144
-
145
- And("the final cart total should be {string}", async (_, expectedTotalStr: string) => {
146
- const expectedTotal = parseFloat(expectedTotalStr);
147
- const cartResponse = await mock.handle("GET", "/cart");
148
- const body = cartResponse.body as { items: CartItem[]; total: number; count: number };
149
- expect(body.total).toBe(expectedTotal);
150
- });
151
-
152
- When("I remove the first item from the cart", async () => {
153
- const cartResponse = await mock.handle("GET", "/cart");
154
- const body = cartResponse.body as { items: CartItem[]; total: number; count: number };
155
- const firstItemId = body.items[0].id;
156
- response = await mock.handle("DELETE", `/cart/${firstItemId}`);
157
- });
158
- });
159
-
160
- Scenario("User session simulation", ({ Given, When, Then, And }) => {
161
- sessionToken = "";
162
-
163
- Given("I create a session-based authentication mock", () => {
164
- const users: UserRecord[] = [
165
- { username: "admin", password: "secret", profile: { name: "Admin User", role: "administrator" } },
166
- { username: "user1", password: "pass123", profile: { name: "Regular User", role: "user" } },
167
- ];
168
- const sessions: Record<string, SessionRecord> = {};
169
-
170
- mock = schmock();
171
- mock("POST /auth/login", ({ body }) => {
172
- const b = body as Record<string, unknown>;
173
- const user = users.find((u) => u.username === b.username && u.password === b.password);
174
- if (!user) {
175
- return [401, { error: "Invalid credentials" }];
176
- }
177
- const token = "token-" + user.username + "-" + Date.now();
178
- sessions[token] = {
179
- user: user.username,
180
- profile: user.profile,
181
- loginTime: new Date().toISOString(),
182
- };
183
- return {
184
- message: "Login successful",
185
- token,
186
- user: user.profile,
187
- };
188
- });
189
- mock("GET /profile", ({ headers }) => {
190
- const token = headers.authorization ? headers.authorization.replace("Bearer ", "") : "";
191
- if (!token || !sessions[token]) {
192
- return [401, { error: "Unauthorized" }];
193
- }
194
- const session = sessions[token];
195
- return {
196
- user: session.user,
197
- profile: session.profile,
198
- loginTime: session.loginTime,
199
- session: {
200
- active: true,
201
- },
202
- };
203
- });
204
- mock("POST /auth/logout", ({ headers }) => {
205
- const token = headers.authorization ? headers.authorization.replace("Bearer ", "") : "";
206
- if (!token || !sessions[token]) {
207
- return [401, { error: "Unauthorized" }];
208
- }
209
- delete sessions[token];
210
- return { message: "Logged out successfully" };
211
- });
212
- });
213
-
214
- When("I login with username {string} and password {string}", async (_, username: string, password: string) => {
215
- response = await mock.handle("POST", "/auth/login", {
216
- body: { username, password }
217
- });
218
- if (response.body.token) {
219
- sessionToken = response.body.token;
220
- }
221
- });
222
-
223
- Then("I should receive a session token", () => {
224
- expect(response.body.token).toBeDefined();
225
- expect(typeof response.body.token).toBe('string');
226
- expect(response.body.token.length).toBeGreaterThan(0);
227
- });
228
-
229
- And("the response should contain user info with role {string}", (_, expectedRole: string) => {
230
- expect(response.body.user).toBeDefined();
231
- expect(response.body.user.role).toBe(expectedRole);
232
- });
233
-
234
- When("I request {string} with the session token", async (_, path: string) => {
235
- response = await mock.handle("GET", path, {
236
- headers: { authorization: `Bearer ${sessionToken}` }
237
- });
238
- });
239
-
240
- Then("I should get my profile information", () => {
241
- expect(response.body.user).toBeDefined();
242
- expect(response.body.session).toBeDefined();
243
- });
244
-
245
- And("the session should be marked as active", () => {
246
- expect(response.body.session.active).toBe(true);
247
- });
248
-
249
- When("I logout with the session token", async () => {
250
- response = await mock.handle("POST", "/auth/logout", {
251
- headers: { authorization: `Bearer ${sessionToken}` }
252
- });
253
- });
254
-
255
- Then("the logout should be successful", () => {
256
- expect(response.body.message).toContain("Logged out successfully");
257
- });
258
-
259
- When("I request {string} with the same token", async (_, path: string) => {
260
- response = await mock.handle("GET", path, {
261
- headers: { authorization: `Bearer ${sessionToken}` }
262
- });
263
- });
264
-
265
- Then("I should get a {int} unauthorized response", (_, expectedStatus: number) => {
266
- expect(response.status).toBe(expectedStatus);
267
- });
268
- });
269
-
270
- Scenario("Multi-user state isolation", ({ Given, When, Then, And }) => {
271
- Given("I create a multi-user counter mock", () => {
272
- const counters: Record<string, number> = {};
273
-
274
- mock = schmock();
275
- mock("POST /counter/:userId/increment", ({ params }) => {
276
- const userId = params.userId;
277
- if (!counters[userId]) {
278
- counters[userId] = 0;
279
- }
280
- counters[userId]++;
281
- return {
282
- userId,
283
- count: counters[userId],
284
- message: "Counter incremented for user " + userId,
285
- };
286
- });
287
- mock("GET /counter/:userId", ({ params }) => {
288
- const userId = params.userId;
289
- const count = counters[userId] || 0;
290
- return {
291
- userId,
292
- count,
293
- };
294
- });
295
- mock("GET /counters/summary", () => {
296
- const totalCount = Object.values(counters).reduce((sum, count) => sum + count, 0);
297
- const userCount = Object.keys(counters).length;
298
- return {
299
- totalCounts: totalCount,
300
- totalUsers: userCount,
301
- counters,
302
- };
303
- });
304
- });
305
-
306
- When("user {string} increments their counter {int} times", async (_, userId: string, times: number) => {
307
- for (let i = 0; i < times; i++) {
308
- await mock.handle("POST", `/counter/${userId}/increment`);
309
- }
310
- });
311
-
312
- And("user {string} increments their counter {int} times", async (_, userId: string, times: number) => {
313
- for (let i = 0; i < times; i++) {
314
- await mock.handle("POST", `/counter/${userId}/increment`);
315
- }
316
- });
317
-
318
- Then("{string}'s counter should be {int}", async (_, userId: string, expectedCount: number) => {
319
- const response = await mock.handle("GET", `/counter/${userId}`);
320
- const body = response.body as { userId: string; count: number };
321
- expect(body.count).toBe(expectedCount);
322
- });
323
-
324
- And("{string}'s counter should be {int}", async (_, userId: string, expectedCount: number) => {
325
- const response = await mock.handle("GET", `/counter/${userId}`);
326
- const body = response.body as { userId: string; count: number };
327
- expect(body.count).toBe(expectedCount);
328
- });
329
-
330
- And("the summary should show {int} total users", async (_, expectedUsers: number) => {
331
- const response = await mock.handle("GET", "/counters/summary");
332
- const body = response.body as { totalCounts: number; totalUsers: number; counters: Record<string, number> };
333
- expect(body.totalUsers).toBe(expectedUsers);
334
- });
335
-
336
- And("the summary should show total counts of {int}", async (_, expectedTotal: number) => {
337
- const response = await mock.handle("GET", "/counters/summary");
338
- const body = response.body as { totalCounts: number; totalUsers: number; counters: Record<string, number> };
339
- expect(body.totalCounts).toBe(expectedTotal);
340
- });
341
-
342
- And("each user's state should be independent", async () => {
343
- const aliceResponse = await mock.handle("GET", "/counter/alice");
344
- const bobResponse = await mock.handle("GET", "/counter/bob");
345
- const aliceBody = aliceResponse.body as { userId: string; count: number };
346
- const bobBody = bobResponse.body as { userId: string; count: number };
347
-
348
- expect(aliceBody.count).toBe(3);
349
- expect(bobBody.count).toBe(2);
350
- expect(aliceBody.count).not.toBe(bobBody.count);
351
- });
352
- });
353
- });
package/src/types.ts DELETED
@@ -1,36 +0,0 @@
1
- /// <reference path="../schmock.d.ts" />
2
-
3
- // Re-export ambient types for consumers
4
- export type HttpMethod = Schmock.HttpMethod;
5
- export type RouteKey = Schmock.RouteKey;
6
- export type ResponseBody = Schmock.ResponseBody;
7
- export type ResponseResult = Schmock.ResponseResult;
8
- export type RequestContext = Schmock.RequestContext;
9
- export type Response = Schmock.Response;
10
- export type RequestOptions = Schmock.RequestOptions;
11
- export type GlobalConfig = Schmock.GlobalConfig;
12
- export type RouteConfig = Schmock.RouteConfig;
13
- export type Generator = Schmock.Generator;
14
- export type GeneratorFunction = Schmock.GeneratorFunction;
15
- export type CallableMockInstance = Schmock.CallableMockInstance;
16
- export type Plugin = Schmock.Plugin;
17
- export type PluginContext = Schmock.PluginContext;
18
- export type PluginResult = Schmock.PluginResult;
19
- export type StaticData = Schmock.StaticData;
20
- export type RequestRecord = Schmock.RequestRecord;
21
- export type ServerInfo = Schmock.ServerInfo;
22
- export type RouteInfo = Schmock.RouteInfo;
23
- export type SchemaGenerationContext = Schmock.SchemaGenerationContext;
24
- export type FakerPluginOptions = Schmock.FakerPluginOptions;
25
- export type ExpressAdapterOptions = Schmock.ExpressAdapterOptions;
26
- export type AngularAdapterOptions = Schmock.AngularAdapterOptions;
27
- export type OpenApiOptions = Schmock.OpenApiOptions;
28
- export type SeedSource = Schmock.SeedSource;
29
- export type SeedConfig = Schmock.SeedConfig;
30
- export type CliOptions = Schmock.CliOptions;
31
- export type CliServer = Schmock.CliServer;
32
- export type AdapterRequest = Schmock.AdapterRequest;
33
- export type AdapterResponse = Schmock.AdapterResponse;
34
- export type InterceptOptions = Schmock.InterceptOptions;
35
- export type InterceptHandle = Schmock.InterceptHandle;
36
- export type AdapterRequestOverride = Schmock.AdapterRequestOverride;