@schmock/core 1.1.0 → 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 CHANGED
@@ -10,8 +10,10 @@ export declare class CallableMockInstance {
10
10
  private plugins;
11
11
  private logger;
12
12
  private requestHistory;
13
+ private callableRef;
13
14
  constructor(globalConfig?: Schmock.GlobalConfig);
14
15
  defineRoute(route: Schmock.RouteKey, generator: Schmock.Generator, config: Schmock.RouteConfig): this;
16
+ setCallableRef(ref: Schmock.CallableMockInstance): void;
15
17
  pipe(plugin: Schmock.Plugin): this;
16
18
  history(method?: Schmock.HttpMethod, path?: string): Schmock.RequestRecord[];
17
19
  called(method?: Schmock.HttpMethod, path?: string): boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"builder.d.ts","sourceRoot":"","sources":["../src/builder.ts"],"names":[],"mappings":"AAyEA;;;;GAIG;AACH,qBAAa,oBAAoB;IAOnB,OAAO,CAAC,YAAY;IANhC,OAAO,CAAC,MAAM,CAA+B;IAC7C,OAAO,CAAC,YAAY,CAA4C;IAChE,OAAO,CAAC,OAAO,CAAwB;IACvC,OAAO,CAAC,MAAM,CAAc;IAC5B,OAAO,CAAC,cAAc,CAA+B;gBAEjC,YAAY,GAAE,OAAO,CAAC,YAAiB;IAa3D,WAAW,CACT,KAAK,EAAE,OAAO,CAAC,QAAQ,EACvB,SAAS,EAAE,OAAO,CAAC,SAAS,EAC5B,MAAM,EAAE,OAAO,CAAC,WAAW,GAC1B,IAAI;IAqFP,IAAI,CAAC,MAAM,EAAE,OAAO,CAAC,MAAM,GAAG,IAAI;IAiBlC,OAAO,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,UAAU,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,EAAE;IAS5E,MAAM,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,UAAU,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO;IAS3D,SAAS,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,UAAU,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM;IAS7D,WAAW,CACT,MAAM,CAAC,EAAE,OAAO,CAAC,UAAU,EAC3B,IAAI,CAAC,EAAE,MAAM,GACZ,OAAO,CAAC,aAAa,GAAG,SAAS;IAYpC,KAAK,IAAI,IAAI;IAab,YAAY,IAAI,IAAI;IAKpB,UAAU,IAAI,IAAI;IASZ,MAAM,CACV,MAAM,EAAE,OAAO,CAAC,UAAU,EAC1B,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,OAAO,CAAC,cAAc,GAC/B,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC;IAoL5B;;;;OAIG;YACW,UAAU;IAcxB;;;;;;;OAOG;IACH,OAAO,CAAC,aAAa;IAwDrB;;;;;;;;;;OAUG;YACW,iBAAiB;IAqG/B;;;;;;;;OAQG;IACH,OAAO,CAAC,SAAS;IA0BjB;;;;;;;OAOG;IACH,OAAO,CAAC,aAAa;CActB"}
1
+ {"version":3,"file":"builder.d.ts","sourceRoot":"","sources":["../src/builder.ts"],"names":[],"mappings":"AA0EA;;;;GAIG;AACH,qBAAa,oBAAoB;IAQnB,OAAO,CAAC,YAAY;IAPhC,OAAO,CAAC,MAAM,CAA+B;IAC7C,OAAO,CAAC,YAAY,CAA4C;IAChE,OAAO,CAAC,OAAO,CAAwB;IACvC,OAAO,CAAC,MAAM,CAAc;IAC5B,OAAO,CAAC,cAAc,CAA+B;IACrD,OAAO,CAAC,WAAW,CAA2C;gBAE1C,YAAY,GAAE,OAAO,CAAC,YAAiB;IAa3D,WAAW,CACT,KAAK,EAAE,OAAO,CAAC,QAAQ,EACvB,SAAS,EAAE,OAAO,CAAC,SAAS,EAC5B,MAAM,EAAE,OAAO,CAAC,WAAW,GAC1B,IAAI;IAqFP,cAAc,CAAC,GAAG,EAAE,OAAO,CAAC,oBAAoB,GAAG,IAAI;IAIvD,IAAI,CAAC,MAAM,EAAE,OAAO,CAAC,MAAM,GAAG,IAAI;IAoBlC,OAAO,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,UAAU,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,EAAE;IAS5E,MAAM,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,UAAU,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO;IAS3D,SAAS,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,UAAU,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM;IAS7D,WAAW,CACT,MAAM,CAAC,EAAE,OAAO,CAAC,UAAU,EAC3B,IAAI,CAAC,EAAE,MAAM,GACZ,OAAO,CAAC,aAAa,GAAG,SAAS;IAYpC,KAAK,IAAI,IAAI;IAab,YAAY,IAAI,IAAI;IAKpB,UAAU,IAAI,IAAI;IASZ,MAAM,CACV,MAAM,EAAE,OAAO,CAAC,UAAU,EAC1B,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,OAAO,CAAC,cAAc,GAC/B,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC;IAoL5B;;;;OAIG;YACW,UAAU;IAcxB;;;;;;;OAOG;IACH,OAAO,CAAC,aAAa;IAwDrB;;;;;;;;;;OAUG;YACW,iBAAiB;IAqG/B;;;;;;;;OAQG;IACH,OAAO,CAAC,SAAS;IA0BjB;;;;;;;OAOG;IACH,OAAO,CAAC,aAAa;CActB"}
package/dist/builder.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { isStatusTuple } from "./constants.js";
1
2
  import { PluginError, RouteDefinitionError, RouteNotFoundError, SchmockError, } from "./errors.js";
2
3
  import { parseRouteKey } from "./parser.js";
3
4
  function errorMessage(error) {
@@ -55,6 +56,7 @@ export class CallableMockInstance {
55
56
  plugins = [];
56
57
  logger;
57
58
  requestHistory = [];
59
+ callableRef;
58
60
  constructor(globalConfig = {}) {
59
61
  this.globalConfig = globalConfig;
60
62
  this.logger = new DebugLogger(globalConfig.debug || false);
@@ -135,6 +137,9 @@ export class CallableMockInstance {
135
137
  });
136
138
  return this;
137
139
  }
140
+ setCallableRef(ref) {
141
+ this.callableRef = ref;
142
+ }
138
143
  pipe(plugin) {
139
144
  this.plugins.push(plugin);
140
145
  this.logger.log("plugin", `Registered plugin: ${plugin.name}@${plugin.version || "unknown"}`, {
@@ -143,6 +148,9 @@ export class CallableMockInstance {
143
148
  hasProcess: typeof plugin.process === "function",
144
149
  hasOnError: typeof plugin.onError === "function",
145
150
  });
151
+ if (plugin.install && this.callableRef) {
152
+ plugin.install(this.callableRef);
153
+ }
146
154
  return this;
147
155
  }
148
156
  // ===== Request Spy / History API =====
@@ -367,7 +375,7 @@ export class CallableMockInstance {
367
375
  };
368
376
  }
369
377
  // Handle tuple response format [status, body, headers?]
370
- if (Array.isArray(result) && typeof result[0] === "number") {
378
+ if (isStatusTuple(result)) {
371
379
  [status, body, headers = {}] = result;
372
380
  tupleFormat = true;
373
381
  }
@@ -3,4 +3,9 @@ export declare const ROUTE_NOT_FOUND_CODE: "ROUTE_NOT_FOUND";
3
3
  export declare const HTTP_METHODS: readonly HttpMethod[];
4
4
  export declare function isHttpMethod(method: string): method is HttpMethod;
5
5
  export declare function toHttpMethod(method: string): HttpMethod;
6
+ /**
7
+ * Check if a value is a status tuple: [status, body] or [status, body, headers]
8
+ * Guards against misinterpreting numeric arrays like [1, 2, 3] as tuples.
9
+ */
10
+ export declare function isStatusTuple(value: unknown): value is [number, unknown] | [number, unknown, Record<string, string>];
6
11
  //# sourceMappingURL=constants.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C,eAAO,MAAM,oBAAoB,EAAG,iBAA0B,CAAC;AAE/D,eAAO,MAAM,YAAY,EAAE,SAAS,UAAU,EAQpC,CAAC;AAEX,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,IAAI,UAAU,CAEjE;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU,CAMvD"}
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C,eAAO,MAAM,oBAAoB,EAAG,iBAA0B,CAAC;AAE/D,eAAO,MAAM,YAAY,EAAE,SAAS,UAAU,EAQpC,CAAC;AAEX,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,IAAI,UAAU,CAEjE;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU,CAMvD;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAC3B,KAAK,EAAE,OAAO,GACb,KAAK,IAAI,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAQxE"}
package/dist/constants.js CHANGED
@@ -18,3 +18,14 @@ export function toHttpMethod(method) {
18
18
  }
19
19
  return upper;
20
20
  }
21
+ /**
22
+ * Check if a value is a status tuple: [status, body] or [status, body, headers]
23
+ * Guards against misinterpreting numeric arrays like [1, 2, 3] as tuples.
24
+ */
25
+ export function isStatusTuple(value) {
26
+ return (Array.isArray(value) &&
27
+ (value.length === 2 || value.length === 3) &&
28
+ typeof value[0] === "number" &&
29
+ value[0] >= 100 &&
30
+ value[0] <= 599);
31
+ }
package/dist/index.d.ts CHANGED
@@ -22,7 +22,7 @@
22
22
  * @returns A callable mock instance
23
23
  */
24
24
  export declare function schmock(config?: Schmock.GlobalConfig): Schmock.CallableMockInstance;
25
- export { HTTP_METHODS, isHttpMethod, ROUTE_NOT_FOUND_CODE, toHttpMethod, } from "./constants.js";
25
+ export { HTTP_METHODS, isHttpMethod, isStatusTuple, ROUTE_NOT_FOUND_CODE, toHttpMethod, } from "./constants.js";
26
26
  export { PluginError, ResourceLimitError, ResponseGenerationError, RouteDefinitionError, RouteNotFoundError, RouteParseError, SchemaGenerationError, SchemaValidationError, SchmockError, } from "./errors.js";
27
27
  export type { CallableMockInstance, Generator, GeneratorFunction, GlobalConfig, HttpMethod, Plugin, PluginContext, PluginResult, RequestContext, RequestOptions, RequestRecord, Response, ResponseBody, ResponseResult, RouteConfig, RouteKey, StaticData, } from "./types.js";
28
28
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,OAAO,CACrB,MAAM,CAAC,EAAE,OAAO,CAAC,YAAY,GAC5B,OAAO,CAAC,oBAAoB,CA+B9B;AAGD,OAAO,EACL,YAAY,EACZ,YAAY,EACZ,oBAAoB,EACpB,YAAY,GACb,MAAM,gBAAgB,CAAC;AAExB,OAAO,EACL,WAAW,EACX,kBAAkB,EAClB,uBAAuB,EACvB,oBAAoB,EACpB,kBAAkB,EAClB,eAAe,EACf,qBAAqB,EACrB,qBAAqB,EACrB,YAAY,GACb,MAAM,aAAa,CAAC;AAErB,YAAY,EACV,oBAAoB,EACpB,SAAS,EACT,iBAAiB,EACjB,YAAY,EACZ,UAAU,EACV,MAAM,EACN,aAAa,EACb,YAAY,EACZ,cAAc,EACd,cAAc,EACd,aAAa,EACb,QAAQ,EACR,YAAY,EACZ,cAAc,EACd,WAAW,EACX,QAAQ,EACR,UAAU,GACX,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,OAAO,CACrB,MAAM,CAAC,EAAE,OAAO,CAAC,YAAY,GAC5B,OAAO,CAAC,oBAAoB,CAiC9B;AAGD,OAAO,EACL,YAAY,EACZ,YAAY,EACZ,aAAa,EACb,oBAAoB,EACpB,YAAY,GACb,MAAM,gBAAgB,CAAC;AAExB,OAAO,EACL,WAAW,EACX,kBAAkB,EAClB,uBAAuB,EACvB,oBAAoB,EACpB,kBAAkB,EAClB,eAAe,EACf,qBAAqB,EACrB,qBAAqB,EACrB,YAAY,GACb,MAAM,aAAa,CAAC;AAErB,YAAY,EACV,oBAAoB,EACpB,SAAS,EACT,iBAAiB,EACjB,YAAY,EACZ,UAAU,EACV,MAAM,EACN,aAAa,EACb,YAAY,EACZ,cAAc,EACd,cAAc,EACd,aAAa,EACb,QAAQ,EACR,YAAY,EACZ,cAAc,EACd,WAAW,EACX,QAAQ,EACR,UAAU,GACX,MAAM,YAAY,CAAC"}
package/dist/index.js CHANGED
@@ -43,9 +43,10 @@ export function schmock(config) {
43
43
  resetHistory: instance.resetHistory.bind(instance),
44
44
  resetState: instance.resetState.bind(instance),
45
45
  });
46
+ instance.setCallableRef(callableInstance);
46
47
  return callableInstance;
47
48
  }
48
49
  // Re-export constants and utilities
49
- export { HTTP_METHODS, isHttpMethod, ROUTE_NOT_FOUND_CODE, toHttpMethod, } from "./constants.js";
50
+ export { HTTP_METHODS, isHttpMethod, isStatusTuple, ROUTE_NOT_FOUND_CODE, toHttpMethod, } from "./constants.js";
50
51
  // Re-export errors
51
52
  export { PluginError, ResourceLimitError, ResponseGenerationError, RouteDefinitionError, RouteNotFoundError, RouteParseError, SchemaGenerationError, SchemaValidationError, SchmockError, } from "./errors.js";
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@schmock/core",
3
3
  "description": "Core functionality for Schmock",
4
- "version": "1.1.0",
4
+ "version": "1.2.1",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
7
7
  "types": "./dist/index.d.ts",
@@ -32,8 +32,8 @@
32
32
  "license": "MIT",
33
33
  "devDependencies": {
34
34
  "@amiceli/vitest-cucumber": "^6.2.0",
35
- "@types/node": "^25.1.0",
36
- "@vitest/ui": "^4.0.16",
37
- "vitest": "^4.0.15"
35
+ "@types/node": "^25.2.1",
36
+ "@vitest/ui": "^4.0.18",
37
+ "vitest": "^4.0.18"
38
38
  }
39
39
  }
package/src/builder.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { isStatusTuple } from "./constants.js";
1
2
  import {
2
3
  PluginError,
3
4
  RouteDefinitionError,
@@ -82,6 +83,7 @@ export class CallableMockInstance {
82
83
  private plugins: Schmock.Plugin[] = [];
83
84
  private logger: DebugLogger;
84
85
  private requestHistory: Schmock.RequestRecord[] = [];
86
+ private callableRef: Schmock.CallableMockInstance | undefined;
85
87
 
86
88
  constructor(private globalConfig: Schmock.GlobalConfig = {}) {
87
89
  this.logger = new DebugLogger(globalConfig.debug || false);
@@ -185,6 +187,10 @@ export class CallableMockInstance {
185
187
  return this;
186
188
  }
187
189
 
190
+ setCallableRef(ref: Schmock.CallableMockInstance): void {
191
+ this.callableRef = ref;
192
+ }
193
+
188
194
  pipe(plugin: Schmock.Plugin): this {
189
195
  this.plugins.push(plugin);
190
196
  this.logger.log(
@@ -197,6 +203,9 @@ export class CallableMockInstance {
197
203
  hasOnError: typeof plugin.onError === "function",
198
204
  },
199
205
  );
206
+ if (plugin.install && this.callableRef) {
207
+ plugin.install(this.callableRef);
208
+ }
200
209
  return this;
201
210
  }
202
211
 
@@ -502,7 +511,7 @@ export class CallableMockInstance {
502
511
  }
503
512
 
504
513
  // Handle tuple response format [status, body, headers?]
505
- if (Array.isArray(result) && typeof result[0] === "number") {
514
+ if (isStatusTuple(result)) {
506
515
  [status, body, headers = {}] = result;
507
516
  tupleFormat = true;
508
517
  }
package/src/constants.ts CHANGED
@@ -23,3 +23,19 @@ export function toHttpMethod(method: string): HttpMethod {
23
23
  }
24
24
  return upper;
25
25
  }
26
+
27
+ /**
28
+ * Check if a value is a status tuple: [status, body] or [status, body, headers]
29
+ * Guards against misinterpreting numeric arrays like [1, 2, 3] as tuples.
30
+ */
31
+ export function isStatusTuple(
32
+ value: unknown,
33
+ ): value is [number, unknown] | [number, unknown, Record<string, string>] {
34
+ return (
35
+ Array.isArray(value) &&
36
+ (value.length === 2 || value.length === 3) &&
37
+ typeof value[0] === "number" &&
38
+ value[0] >= 100 &&
39
+ value[0] <= 599
40
+ );
41
+ }
package/src/index.ts CHANGED
@@ -55,6 +55,8 @@ export function schmock(
55
55
  },
56
56
  );
57
57
 
58
+ instance.setCallableRef(callableInstance);
59
+
58
60
  return callableInstance;
59
61
  }
60
62
 
@@ -62,6 +64,7 @@ export function schmock(
62
64
  export {
63
65
  HTTP_METHODS,
64
66
  isHttpMethod,
67
+ isStatusTuple,
65
68
  ROUTE_NOT_FOUND_CODE,
66
69
  toHttpMethod,
67
70
  } from "./constants.js";
@@ -124,9 +124,7 @@ const namespace = fc.oneof(
124
124
  safeSeg.map((s) => `/${s}/`), // trailing slash
125
125
  fc.constant(""),
126
126
  safeSeg.map((s) => `//${s}`), // double leading slash
127
- fc
128
- .tuple(safeSeg, safeSeg)
129
- .map(([a, b]) => `/${a}/${b}`), // nested
127
+ fc.tuple(safeSeg, safeSeg).map(([a, b]) => `/${a}/${b}`), // nested
130
128
  );
131
129
 
132
130
  // ============================================================
@@ -420,6 +420,97 @@ describe("plugin system", () => {
420
420
  });
421
421
  });
422
422
 
423
+ describe("plugin install hook", () => {
424
+ it("calls install with callable instance when pipe() is invoked", () => {
425
+ const mock = schmock();
426
+ let receivedInstance: unknown;
427
+
428
+ const plugin: Schmock.Plugin = {
429
+ name: "install-test",
430
+ install(instance) {
431
+ receivedInstance = instance;
432
+ },
433
+ process: (ctx: any, res: any) => ({ context: ctx, response: res }),
434
+ };
435
+
436
+ mock.pipe(plugin);
437
+ expect(receivedInstance).toBe(mock);
438
+ });
439
+
440
+ it("works normally when plugin has no install method", async () => {
441
+ const mock = schmock();
442
+
443
+ const plugin: Schmock.Plugin = {
444
+ name: "no-install",
445
+ process: (ctx: any, res: any) => ({ context: ctx, response: res }),
446
+ };
447
+
448
+ mock("GET /test", "hello").pipe(plugin);
449
+ const response = await mock.handle("GET", "/test");
450
+ expect(response.body).toBe("hello");
451
+ });
452
+
453
+ it("allows install to register routes on the instance", async () => {
454
+ const mock = schmock();
455
+
456
+ const plugin: Schmock.Plugin = {
457
+ name: "route-installer",
458
+ install(instance) {
459
+ instance("GET /installed", { message: "from-install" });
460
+ },
461
+ process: (ctx: any, res: any) => ({ context: ctx, response: res }),
462
+ };
463
+
464
+ mock.pipe(plugin);
465
+
466
+ const response = await mock.handle("GET", "/installed");
467
+ expect(response.status).toBe(200);
468
+ expect(response.body).toEqual({ message: "from-install" });
469
+ });
470
+
471
+ it("allows install to register multiple routes", async () => {
472
+ const mock = schmock();
473
+
474
+ const plugin: Schmock.Plugin = {
475
+ name: "multi-route-installer",
476
+ install(instance) {
477
+ instance("GET /a", "route-a");
478
+ instance("POST /b", "route-b");
479
+ },
480
+ process: (ctx: any, res: any) => ({ context: ctx, response: res }),
481
+ };
482
+
483
+ mock.pipe(plugin);
484
+
485
+ const responseA = await mock.handle("GET", "/a");
486
+ expect(responseA.body).toBe("route-a");
487
+
488
+ const responseB = await mock.handle("POST", "/b");
489
+ expect(responseB.body).toBe("route-b");
490
+ });
491
+
492
+ it("routes registered in install work with generator functions", async () => {
493
+ const mock = schmock();
494
+
495
+ const plugin: Schmock.Plugin = {
496
+ name: "generator-installer",
497
+ install(instance) {
498
+ instance("GET /items/:id", (ctx) => ({
499
+ id: ctx.params.id,
500
+ name: "test",
501
+ }));
502
+ },
503
+ process: (ctx: any, res: any) => ({ context: ctx, response: res }),
504
+ };
505
+
506
+ mock.pipe(plugin);
507
+
508
+ const response = await mock.handle("GET", "/items/42");
509
+ expect(response.status).toBe(200);
510
+ expect(response.body).toEqual({ id: "42", name: "test" });
511
+ });
512
+ });
513
+
423
514
  describe("debug logging", () => {
424
515
  it("logs plugin pipeline execution with debug enabled", async () => {
425
516
  const mock = schmock({ debug: true });
@@ -3,15 +3,14 @@ import { schmock } from "./index";
3
3
 
4
4
  describe("response parsing", () => {
5
5
  describe("tuple response formats", () => {
6
- it("handles status-only tuple [status]", async () => {
6
+ it("treats single-element array [status] as data, not tuple", async () => {
7
7
  const mock = schmock();
8
8
  mock("GET /status-only", () => [204] as [number]);
9
9
 
10
10
  const response = await mock.handle("GET", "/status-only");
11
11
 
12
- expect(response.status).toBe(204);
13
- expect(response.body).toBeUndefined();
14
- expect(response.headers).toEqual({});
12
+ expect(response.status).toBe(200);
13
+ expect(response.body).toEqual([204]);
15
14
  });
16
15
 
17
16
  it("handles [status, body] tuple", async () => {
@@ -61,7 +60,7 @@ describe("response parsing", () => {
61
60
  expect(response.headers).toEqual({});
62
61
  });
63
62
 
64
- it("ignores extra tuple elements beyond [status, body, headers]", async () => {
63
+ it("treats arrays with more than 3 elements as data, not tuple", async () => {
65
64
  const mock = schmock();
66
65
  mock(
67
66
  "GET /extra",
@@ -71,8 +70,13 @@ describe("response parsing", () => {
71
70
  const response = await mock.handle("GET", "/extra");
72
71
 
73
72
  expect(response.status).toBe(200);
74
- expect(response.body).toBe("data");
75
- expect(response.headers).toEqual({});
73
+ expect(response.body).toEqual([
74
+ 200,
75
+ "data",
76
+ {},
77
+ "ignored",
78
+ "also-ignored",
79
+ ]);
76
80
  });
77
81
 
78
82
  it("treats non-numeric first element as body, not status", async () => {
@@ -342,7 +342,7 @@ describeFeature(feature, ({ Scenario }) => {
342
342
  Scenario("Response tuple format edge cases", ({ Given, When, Then, And }) => {
343
343
  Given("I create a mock with three tuple response routes", () => {
344
344
  mock = schmock();
345
- mock("GET /created", [201]);
345
+ mock("GET /created", [201, null]);
346
346
  mock("GET /with-headers", [200, { data: true }, { "x-custom": "header" }]);
347
347
  mock("GET /empty-with-status", [204, null]);
348
348
  });