@schmock/cli 1.1.0 → 1.4.0
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/cli.d.ts +15 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +109 -4
- package/dist/index.js +2745 -1499
- package/package.json +6 -6
- package/src/cli.test.ts +0 -71
- package/src/cli.ts +0 -241
- package/src/index.ts +0 -11
- package/src/steps/cli-standalone.steps.ts +0 -222
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@schmock/cli",
|
|
3
3
|
"description": "CLI for running Schmock mock servers from OpenAPI specs",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.4.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
7
7
|
"types": "./dist/index.d.ts",
|
|
@@ -9,8 +9,7 @@
|
|
|
9
9
|
"schmock": "./dist/index.js"
|
|
10
10
|
},
|
|
11
11
|
"files": [
|
|
12
|
-
"dist"
|
|
13
|
-
"src"
|
|
12
|
+
"dist"
|
|
14
13
|
],
|
|
15
14
|
"exports": {
|
|
16
15
|
".": {
|
|
@@ -28,12 +27,13 @@
|
|
|
28
27
|
"test:watch": "vitest --watch",
|
|
29
28
|
"test:bdd": "vitest run --config vitest.config.bdd.ts",
|
|
30
29
|
"lint": "biome check src/*.ts",
|
|
31
|
-
"lint:fix": "biome check --write --unsafe src/*.ts"
|
|
30
|
+
"lint:fix": "biome check --write --unsafe src/*.ts",
|
|
31
|
+
"check:publish": "publint && attw --pack --ignore-rules cjs-resolves-to-esm"
|
|
32
32
|
},
|
|
33
33
|
"license": "MIT",
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@schmock/core": "
|
|
36
|
-
"@schmock/openapi": "
|
|
35
|
+
"@schmock/core": "^1.4.0",
|
|
36
|
+
"@schmock/openapi": "^1.4.0"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
39
|
"@amiceli/vitest-cucumber": "^6.2.0",
|
package/src/cli.test.ts
DELETED
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
2
|
-
import { parseCliArgs } from "./cli";
|
|
3
|
-
|
|
4
|
-
describe("parseCliArgs", () => {
|
|
5
|
-
it("parses --spec flag", () => {
|
|
6
|
-
const result = parseCliArgs(["--spec", "petstore.yaml"]);
|
|
7
|
-
expect(result.spec).toBe("petstore.yaml");
|
|
8
|
-
});
|
|
9
|
-
|
|
10
|
-
it("parses --port flag", () => {
|
|
11
|
-
const result = parseCliArgs(["--spec", "x.yaml", "--port", "8080"]);
|
|
12
|
-
expect(result.port).toBe(8080);
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
it("parses --hostname flag", () => {
|
|
16
|
-
const result = parseCliArgs(["--spec", "x.yaml", "--hostname", "0.0.0.0"]);
|
|
17
|
-
expect(result.hostname).toBe("0.0.0.0");
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
it("parses --seed flag", () => {
|
|
21
|
-
const result = parseCliArgs(["--spec", "x.yaml", "--seed", "seed.json"]);
|
|
22
|
-
expect(result.seed).toBe("seed.json");
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
it("parses --cors flag", () => {
|
|
26
|
-
const result = parseCliArgs(["--spec", "x.yaml", "--cors"]);
|
|
27
|
-
expect(result.cors).toBe(true);
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
it("parses --debug flag", () => {
|
|
31
|
-
const result = parseCliArgs(["--spec", "x.yaml", "--debug"]);
|
|
32
|
-
expect(result.debug).toBe(true);
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
it("parses -h / --help flag", () => {
|
|
36
|
-
const result = parseCliArgs(["-h"]);
|
|
37
|
-
expect(result.help).toBe(true);
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
it("defaults port to undefined when not provided", () => {
|
|
41
|
-
const result = parseCliArgs(["--spec", "x.yaml"]);
|
|
42
|
-
expect(result.port).toBeUndefined();
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
it("defaults cors to false", () => {
|
|
46
|
-
const result = parseCliArgs(["--spec", "x.yaml"]);
|
|
47
|
-
expect(result.cors).toBe(false);
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
it("defaults spec to empty string when not provided", () => {
|
|
51
|
-
const result = parseCliArgs([]);
|
|
52
|
-
expect(result.spec).toBe("");
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
it("parses positional spec argument", () => {
|
|
56
|
-
const result = parseCliArgs(["petstore.yaml"]);
|
|
57
|
-
expect(result.spec).toBe("petstore.yaml");
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
it("positional spec works with other flags", () => {
|
|
61
|
-
const result = parseCliArgs(["petstore.yaml", "--port", "8080", "--cors"]);
|
|
62
|
-
expect(result.spec).toBe("petstore.yaml");
|
|
63
|
-
expect(result.port).toBe(8080);
|
|
64
|
-
expect(result.cors).toBe(true);
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
it("--spec flag takes precedence over positional", () => {
|
|
68
|
-
const result = parseCliArgs(["positional.yaml", "--spec", "flag.yaml"]);
|
|
69
|
-
expect(result.spec).toBe("flag.yaml");
|
|
70
|
-
});
|
|
71
|
-
});
|
package/src/cli.ts
DELETED
|
@@ -1,241 +0,0 @@
|
|
|
1
|
-
/// <reference path="../../../types/schmock.d.ts" />
|
|
2
|
-
|
|
3
|
-
import { readFileSync } from "node:fs";
|
|
4
|
-
import type { Server } from "node:http";
|
|
5
|
-
import { createServer } from "node:http";
|
|
6
|
-
import { parseArgs } from "node:util";
|
|
7
|
-
import { schmock, toHttpMethod } from "@schmock/core";
|
|
8
|
-
import type { SeedConfig } from "@schmock/openapi";
|
|
9
|
-
import { openapi } from "@schmock/openapi";
|
|
10
|
-
|
|
11
|
-
export interface CliOptions {
|
|
12
|
-
spec: string;
|
|
13
|
-
port?: number;
|
|
14
|
-
hostname?: string;
|
|
15
|
-
seed?: string;
|
|
16
|
-
cors?: boolean;
|
|
17
|
-
debug?: boolean;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export interface CliServer {
|
|
21
|
-
server: Server;
|
|
22
|
-
port: number;
|
|
23
|
-
hostname: string;
|
|
24
|
-
close(): void;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const CORS_HEADERS: Record<string, string> = {
|
|
28
|
-
"access-control-allow-origin": "*",
|
|
29
|
-
"access-control-allow-methods": "GET, POST, PUT, PATCH, DELETE, OPTIONS",
|
|
30
|
-
"access-control-allow-headers": "Content-Type, Authorization",
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
function isSeedSource(value: unknown): value is SeedConfig[string] {
|
|
34
|
-
return (
|
|
35
|
-
Array.isArray(value) ||
|
|
36
|
-
typeof value === "string" ||
|
|
37
|
-
(typeof value === "object" && value !== null && "count" in value)
|
|
38
|
-
);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
function loadSeedFile(seedPath: string): SeedConfig {
|
|
42
|
-
const raw = readFileSync(seedPath, "utf-8");
|
|
43
|
-
const parsed: unknown = JSON.parse(raw);
|
|
44
|
-
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
45
|
-
throw new Error(
|
|
46
|
-
`Seed file must contain a JSON object, got: ${typeof parsed}`,
|
|
47
|
-
);
|
|
48
|
-
}
|
|
49
|
-
const result: SeedConfig = {};
|
|
50
|
-
for (const [key, value] of Object.entries(parsed)) {
|
|
51
|
-
if (isSeedSource(value)) {
|
|
52
|
-
result[key] = value;
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
return result;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
export async function createCliServer(options: CliOptions): Promise<CliServer> {
|
|
59
|
-
const mock = schmock({ debug: options.debug });
|
|
60
|
-
|
|
61
|
-
const openapiOptions: Parameters<typeof openapi>[0] = {
|
|
62
|
-
spec: options.spec,
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
if (options.seed) {
|
|
66
|
-
openapiOptions.seed = loadSeedFile(options.seed);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
const plugin = await openapi(openapiOptions);
|
|
70
|
-
mock.pipe(plugin);
|
|
71
|
-
|
|
72
|
-
const hostname = options.hostname ?? "127.0.0.1";
|
|
73
|
-
const port = options.port ?? 3000;
|
|
74
|
-
const cors = options.cors ?? false;
|
|
75
|
-
|
|
76
|
-
const httpServer = createServer((req, res) => {
|
|
77
|
-
// Handle CORS preflight
|
|
78
|
-
if (cors && req.method === "OPTIONS") {
|
|
79
|
-
res.writeHead(204, CORS_HEADERS);
|
|
80
|
-
res.end();
|
|
81
|
-
return;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
const url = new URL(req.url ?? "/", `http://${req.headers.host}`);
|
|
85
|
-
const method = toHttpMethod(req.method ?? "GET");
|
|
86
|
-
const path = url.pathname;
|
|
87
|
-
|
|
88
|
-
const headers: Record<string, string> = {};
|
|
89
|
-
for (const [key, value] of Object.entries(req.headers)) {
|
|
90
|
-
if (typeof value === "string") {
|
|
91
|
-
headers[key] = value;
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
const query: Record<string, string> = {};
|
|
96
|
-
url.searchParams.forEach((value, key) => {
|
|
97
|
-
query[key] = value;
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
const chunks: Buffer[] = [];
|
|
101
|
-
req.on("data", (chunk: Buffer) => chunks.push(chunk));
|
|
102
|
-
req.on("end", () => {
|
|
103
|
-
const raw = Buffer.concat(chunks).toString();
|
|
104
|
-
let body: unknown;
|
|
105
|
-
const contentType = headers["content-type"] ?? "";
|
|
106
|
-
if (raw && contentType.includes("json")) {
|
|
107
|
-
try {
|
|
108
|
-
body = JSON.parse(raw);
|
|
109
|
-
} catch {
|
|
110
|
-
body = raw;
|
|
111
|
-
}
|
|
112
|
-
} else if (raw) {
|
|
113
|
-
body = raw;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
void mock
|
|
117
|
-
.handle(method, path, { headers, body, query })
|
|
118
|
-
.then((schmockResponse) => {
|
|
119
|
-
const responseHeaders: Record<string, string> = {
|
|
120
|
-
...schmockResponse.headers,
|
|
121
|
-
};
|
|
122
|
-
|
|
123
|
-
if (cors) {
|
|
124
|
-
Object.assign(responseHeaders, CORS_HEADERS);
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
if (
|
|
128
|
-
!responseHeaders["content-type"] &&
|
|
129
|
-
schmockResponse.body !== undefined &&
|
|
130
|
-
typeof schmockResponse.body !== "string"
|
|
131
|
-
) {
|
|
132
|
-
responseHeaders["content-type"] = "application/json";
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
const responseBody =
|
|
136
|
-
schmockResponse.body === undefined
|
|
137
|
-
? undefined
|
|
138
|
-
: typeof schmockResponse.body === "string"
|
|
139
|
-
? schmockResponse.body
|
|
140
|
-
: JSON.stringify(schmockResponse.body);
|
|
141
|
-
|
|
142
|
-
res.writeHead(schmockResponse.status, responseHeaders);
|
|
143
|
-
res.end(responseBody);
|
|
144
|
-
});
|
|
145
|
-
});
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
return new Promise((resolve, reject) => {
|
|
149
|
-
httpServer.on("error", reject);
|
|
150
|
-
httpServer.listen(port, hostname, () => {
|
|
151
|
-
const addr = httpServer.address();
|
|
152
|
-
const actualPort =
|
|
153
|
-
addr !== null && typeof addr === "object" ? addr.port : port;
|
|
154
|
-
|
|
155
|
-
resolve({
|
|
156
|
-
server: httpServer,
|
|
157
|
-
port: actualPort,
|
|
158
|
-
hostname,
|
|
159
|
-
close() {
|
|
160
|
-
httpServer.close();
|
|
161
|
-
},
|
|
162
|
-
});
|
|
163
|
-
});
|
|
164
|
-
});
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
export function parseCliArgs(args: string[]): CliOptions & { help: boolean } {
|
|
168
|
-
const { values, positionals } = parseArgs({
|
|
169
|
-
args,
|
|
170
|
-
options: {
|
|
171
|
-
spec: { type: "string" },
|
|
172
|
-
port: { type: "string" },
|
|
173
|
-
hostname: { type: "string" },
|
|
174
|
-
seed: { type: "string" },
|
|
175
|
-
cors: { type: "boolean", default: false },
|
|
176
|
-
debug: { type: "boolean", default: false },
|
|
177
|
-
help: { type: "boolean", short: "h", default: false },
|
|
178
|
-
},
|
|
179
|
-
strict: true,
|
|
180
|
-
allowPositionals: true,
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
const spec = values.spec ?? positionals[0] ?? "";
|
|
184
|
-
|
|
185
|
-
return {
|
|
186
|
-
spec,
|
|
187
|
-
port: values.port ? Number(values.port) : undefined,
|
|
188
|
-
hostname: values.hostname,
|
|
189
|
-
seed: values.seed,
|
|
190
|
-
cors: values.cors,
|
|
191
|
-
debug: values.debug,
|
|
192
|
-
help: values.help ?? false,
|
|
193
|
-
};
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
const USAGE = `Usage: schmock <spec> [options]
|
|
197
|
-
schmock --spec <path> [options]
|
|
198
|
-
|
|
199
|
-
Options:
|
|
200
|
-
--spec <path> OpenAPI/Swagger spec file (or pass as first argument)
|
|
201
|
-
--port <number> Port to listen on (default: 3000)
|
|
202
|
-
--hostname <host> Hostname to bind to (default: 127.0.0.1)
|
|
203
|
-
--seed <path> JSON file with seed data
|
|
204
|
-
--cors Enable CORS for all responses
|
|
205
|
-
--debug Enable debug logging
|
|
206
|
-
-h, --help Show this help message
|
|
207
|
-
`;
|
|
208
|
-
|
|
209
|
-
export async function run(args: string[]): Promise<void> {
|
|
210
|
-
const options = parseCliArgs(args);
|
|
211
|
-
|
|
212
|
-
if (options.help) {
|
|
213
|
-
process.stderr.write(USAGE);
|
|
214
|
-
return;
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
if (!options.spec) {
|
|
218
|
-
process.stderr.write("Error: --spec is required\n\n");
|
|
219
|
-
process.stderr.write(USAGE);
|
|
220
|
-
process.exitCode = 1;
|
|
221
|
-
return;
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
const cliServer = await createCliServer(options);
|
|
225
|
-
|
|
226
|
-
process.stderr.write(
|
|
227
|
-
`Schmock server running on http://${cliServer.hostname}:${cliServer.port}\n`,
|
|
228
|
-
);
|
|
229
|
-
process.stderr.write(`Spec: ${options.spec}\n`);
|
|
230
|
-
if (options.cors) {
|
|
231
|
-
process.stderr.write("CORS: enabled\n");
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
const shutdown = () => {
|
|
235
|
-
process.stderr.write("\nShutting down...\n");
|
|
236
|
-
cliServer.close();
|
|
237
|
-
};
|
|
238
|
-
|
|
239
|
-
process.on("SIGINT", shutdown);
|
|
240
|
-
process.on("SIGTERM", shutdown);
|
|
241
|
-
}
|
package/src/index.ts
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bun
|
|
2
|
-
|
|
3
|
-
/// <reference path="../../../types/schmock.d.ts" />
|
|
4
|
-
|
|
5
|
-
export type { CliOptions, CliServer } from "./cli.js";
|
|
6
|
-
export { createCliServer, parseCliArgs, run } from "./cli.js";
|
|
7
|
-
|
|
8
|
-
// Run CLI when invoked directly
|
|
9
|
-
import { run } from "./cli.js";
|
|
10
|
-
|
|
11
|
-
void run(process.argv.slice(2));
|
|
@@ -1,222 +0,0 @@
|
|
|
1
|
-
import { resolve } from "node:path";
|
|
2
|
-
import { writeFileSync } from "node:fs";
|
|
3
|
-
import { tmpdir } from "node:os";
|
|
4
|
-
import { describeFeature, loadFeature } from "@amiceli/vitest-cucumber";
|
|
5
|
-
import { expect } from "vitest";
|
|
6
|
-
import type { CliServer } from "../cli";
|
|
7
|
-
import { createCliServer } from "../cli";
|
|
8
|
-
|
|
9
|
-
const feature = await loadFeature("../../features/cli-standalone.feature");
|
|
10
|
-
const fixturesDir = resolve(
|
|
11
|
-
import.meta.dirname,
|
|
12
|
-
"../../../openapi/src/__fixtures__",
|
|
13
|
-
);
|
|
14
|
-
|
|
15
|
-
describeFeature(feature, ({ Scenario }) => {
|
|
16
|
-
let cliServer: CliServer;
|
|
17
|
-
let httpResponse: Response;
|
|
18
|
-
let specPath: string;
|
|
19
|
-
let seedPath: string;
|
|
20
|
-
let thrownError: Error | undefined;
|
|
21
|
-
|
|
22
|
-
function baseUrl(): string {
|
|
23
|
-
return `http://${cliServer.hostname}:${cliServer.port}`;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
Scenario(
|
|
27
|
-
"Start server from a spec file",
|
|
28
|
-
({ Given, When, Then, And }) => {
|
|
29
|
-
Given("I have a petstore spec file", () => {
|
|
30
|
-
specPath = resolve(fixturesDir, "petstore-openapi3.json");
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
When("I create a CLI server from the spec", async () => {
|
|
34
|
-
cliServer = await createCliServer({ spec: specPath, port: 0 });
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
Then("the CLI server should be running", () => {
|
|
38
|
-
expect(cliServer.port).toBeGreaterThan(0);
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
When("I fetch {string} from the CLI server", async (_, route: string) => {
|
|
42
|
-
const [, path] = route.split(" ");
|
|
43
|
-
httpResponse = await fetch(`${baseUrl()}${path}`);
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
Then("the CLI response status should be {int}", (_, status: number) => {
|
|
47
|
-
expect(httpResponse.status).toBe(status);
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
And("the CLI response body should be an array", async () => {
|
|
51
|
-
const body = await httpResponse.json();
|
|
52
|
-
expect(Array.isArray(body)).toBe(true);
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
When("I stop the CLI server", () => {
|
|
56
|
-
cliServer.close();
|
|
57
|
-
});
|
|
58
|
-
},
|
|
59
|
-
);
|
|
60
|
-
|
|
61
|
-
Scenario("Serve with seed data", ({ Given, When, And, Then }) => {
|
|
62
|
-
Given("I have a petstore spec file", () => {
|
|
63
|
-
specPath = resolve(fixturesDir, "petstore-openapi3.json");
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
And("I have a seed data file with pets", () => {
|
|
67
|
-
seedPath = resolve(tmpdir(), `schmock-test-seed-${Date.now()}.json`);
|
|
68
|
-
const seedData = {
|
|
69
|
-
pets: [{ id: 1, name: "Buddy", tag: "dog" }],
|
|
70
|
-
};
|
|
71
|
-
writeFileSync(seedPath, JSON.stringify(seedData));
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
When("I create a CLI server with seed data", async () => {
|
|
75
|
-
cliServer = await createCliServer({
|
|
76
|
-
spec: specPath,
|
|
77
|
-
port: 0,
|
|
78
|
-
seed: seedPath,
|
|
79
|
-
});
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
And("I fetch {string} from the CLI server", async (_, route: string) => {
|
|
83
|
-
const [, path] = route.split(" ");
|
|
84
|
-
httpResponse = await fetch(`${baseUrl()}${path}`);
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
Then("the CLI response status should be {int}", (_, status: number) => {
|
|
88
|
-
expect(httpResponse.status).toBe(status);
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
And("the CLI response body should contain the seeded pet", async () => {
|
|
92
|
-
const body = await httpResponse.json();
|
|
93
|
-
expect(Array.isArray(body)).toBe(true);
|
|
94
|
-
const pets = body as Array<{ name: string }>;
|
|
95
|
-
expect(pets.some((p) => p.name === "Buddy")).toBe(true);
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
When("I stop the CLI server", () => {
|
|
99
|
-
cliServer.close();
|
|
100
|
-
});
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
Scenario("Custom port", ({ Given, When, Then }) => {
|
|
104
|
-
Given("I have a petstore spec file", () => {
|
|
105
|
-
specPath = resolve(fixturesDir, "petstore-openapi3.json");
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
When("I create a CLI server on port {int}", async (_, port: number) => {
|
|
109
|
-
cliServer = await createCliServer({ spec: specPath, port });
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
Then(
|
|
113
|
-
"the CLI server should be running on port {int}",
|
|
114
|
-
(_, port: number) => {
|
|
115
|
-
expect(cliServer.port).toBe(port);
|
|
116
|
-
cliServer.close();
|
|
117
|
-
},
|
|
118
|
-
);
|
|
119
|
-
|
|
120
|
-
When("I stop the CLI server", () => {
|
|
121
|
-
cliServer.close();
|
|
122
|
-
});
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
Scenario("CORS headers on responses", ({ Given, When, Then, And }) => {
|
|
126
|
-
Given("I have a petstore spec file", () => {
|
|
127
|
-
specPath = resolve(fixturesDir, "petstore-openapi3.json");
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
When("I create a CLI server with CORS enabled", async () => {
|
|
131
|
-
cliServer = await createCliServer({
|
|
132
|
-
spec: specPath,
|
|
133
|
-
port: 0,
|
|
134
|
-
cors: true,
|
|
135
|
-
});
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
And("I fetch {string} from the CLI server", async (_, route: string) => {
|
|
139
|
-
const [, path] = route.split(" ");
|
|
140
|
-
httpResponse = await fetch(`${baseUrl()}${path}`);
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
Then("the CLI response should have CORS headers", () => {
|
|
144
|
-
expect(httpResponse.headers.get("access-control-allow-origin")).toBe(
|
|
145
|
-
"*",
|
|
146
|
-
);
|
|
147
|
-
});
|
|
148
|
-
|
|
149
|
-
When("I stop the CLI server", () => {
|
|
150
|
-
cliServer.close();
|
|
151
|
-
});
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
Scenario("CORS preflight OPTIONS request", ({ Given, When, Then, And }) => {
|
|
155
|
-
Given("I have a petstore spec file", () => {
|
|
156
|
-
specPath = resolve(fixturesDir, "petstore-openapi3.json");
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
When("I create a CLI server with CORS enabled", async () => {
|
|
160
|
-
cliServer = await createCliServer({
|
|
161
|
-
spec: specPath,
|
|
162
|
-
port: 0,
|
|
163
|
-
cors: true,
|
|
164
|
-
});
|
|
165
|
-
});
|
|
166
|
-
|
|
167
|
-
And("I send an OPTIONS preflight to the CLI server", async () => {
|
|
168
|
-
httpResponse = await fetch(`${baseUrl()}/pets`, {
|
|
169
|
-
method: "OPTIONS",
|
|
170
|
-
});
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
Then("the CLI response status should be {int}", (_, status: number) => {
|
|
174
|
-
expect(httpResponse.status).toBe(status);
|
|
175
|
-
});
|
|
176
|
-
|
|
177
|
-
And("the CLI response should have CORS headers", () => {
|
|
178
|
-
expect(httpResponse.headers.get("access-control-allow-origin")).toBe(
|
|
179
|
-
"*",
|
|
180
|
-
);
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
When("I stop the CLI server", () => {
|
|
184
|
-
cliServer.close();
|
|
185
|
-
});
|
|
186
|
-
});
|
|
187
|
-
|
|
188
|
-
Scenario("Missing spec shows usage error", ({ When, Then }) => {
|
|
189
|
-
When("I create a CLI server without a spec", async () => {
|
|
190
|
-
try {
|
|
191
|
-
await createCliServer({ spec: "" });
|
|
192
|
-
thrownError = undefined;
|
|
193
|
-
} catch (error) {
|
|
194
|
-
thrownError = error instanceof Error ? error : new Error(String(error));
|
|
195
|
-
}
|
|
196
|
-
});
|
|
197
|
-
|
|
198
|
-
Then("the CLI should throw a missing spec error", () => {
|
|
199
|
-
expect(thrownError).toBeDefined();
|
|
200
|
-
});
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
Scenario("Invalid spec shows error", ({ Given, When, Then }) => {
|
|
204
|
-
Given("I have an invalid spec file", () => {
|
|
205
|
-
specPath = resolve(tmpdir(), `schmock-invalid-${Date.now()}.json`);
|
|
206
|
-
writeFileSync(specPath, '{ "not": "a spec" }');
|
|
207
|
-
});
|
|
208
|
-
|
|
209
|
-
When("I create a CLI server from the invalid spec", async () => {
|
|
210
|
-
try {
|
|
211
|
-
await createCliServer({ spec: specPath });
|
|
212
|
-
thrownError = undefined;
|
|
213
|
-
} catch (error) {
|
|
214
|
-
thrownError = error instanceof Error ? error : new Error(String(error));
|
|
215
|
-
}
|
|
216
|
-
});
|
|
217
|
-
|
|
218
|
-
Then("the CLI should throw a parse error", () => {
|
|
219
|
-
expect(thrownError).toBeDefined();
|
|
220
|
-
});
|
|
221
|
-
});
|
|
222
|
-
});
|