@typespec/http-server-js 0.58.0-alpha.12-dev.0 → 0.58.0-alpha.12-dev.2
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/.testignore +26 -0
- package/dist/generated-defs/helpers/datetime.d.ts +4 -0
- package/dist/generated-defs/helpers/datetime.d.ts.map +1 -0
- package/dist/generated-defs/helpers/datetime.js +256 -0
- package/dist/generated-defs/helpers/datetime.js.map +1 -0
- package/dist/generated-defs/helpers/index.d.ts.map +1 -1
- package/dist/generated-defs/helpers/index.js +1 -0
- package/dist/generated-defs/helpers/index.js.map +1 -1
- package/dist/src/common/declaration.js +1 -1
- package/dist/src/common/declaration.js.map +1 -1
- package/dist/src/common/reference.js +1 -1
- package/dist/src/common/reference.js.map +1 -1
- package/dist/src/common/scalar.d.ts +175 -22
- package/dist/src/common/scalar.d.ts.map +1 -1
- package/dist/src/common/scalar.js +420 -93
- package/dist/src/common/scalar.js.map +1 -1
- package/dist/src/common/serialization/index.d.ts +2 -2
- package/dist/src/common/serialization/index.d.ts.map +1 -1
- package/dist/src/common/serialization/index.js +9 -3
- package/dist/src/common/serialization/index.js.map +1 -1
- package/dist/src/common/serialization/json.d.ts +2 -2
- package/dist/src/common/serialization/json.d.ts.map +1 -1
- package/dist/src/common/serialization/json.js +144 -42
- package/dist/src/common/serialization/json.js.map +1 -1
- package/dist/src/helpers/datetime.d.ts +92 -0
- package/dist/src/helpers/datetime.d.ts.map +1 -0
- package/dist/src/helpers/datetime.js +151 -0
- package/dist/src/helpers/datetime.js.map +1 -0
- package/dist/src/http/server/index.d.ts.map +1 -1
- package/dist/src/http/server/index.js +17 -12
- package/dist/src/http/server/index.js.map +1 -1
- package/dist/src/http/server/multipart.js +1 -1
- package/dist/src/http/server/multipart.js.map +1 -1
- package/dist/src/lib.d.ts +10 -1
- package/dist/src/lib.d.ts.map +1 -1
- package/dist/src/lib.js +6 -0
- package/dist/src/lib.js.map +1 -1
- package/dist/src/util/case.d.ts +9 -0
- package/dist/src/util/case.d.ts.map +1 -1
- package/dist/src/util/case.js +18 -0
- package/dist/src/util/case.js.map +1 -1
- package/dist/src/util/differentiate.d.ts +4 -4
- package/dist/src/util/differentiate.d.ts.map +1 -1
- package/dist/src/util/differentiate.js +10 -10
- package/dist/src/util/differentiate.js.map +1 -1
- package/eng/scripts/emit-e2e.js +315 -0
- package/eng/scripts/tspconfig.yaml +6 -0
- package/generated-defs/helpers/datetime.ts +263 -0
- package/generated-defs/helpers/index.ts +1 -0
- package/package.json +23 -9
- package/src/common/declaration.ts +1 -1
- package/src/common/reference.ts +1 -1
- package/src/common/scalar.ts +709 -103
- package/src/common/serialization/index.ts +11 -4
- package/src/common/serialization/json.ts +174 -52
- package/src/helpers/datetime.ts +235 -0
- package/src/http/server/index.ts +29 -15
- package/src/http/server/multipart.ts +1 -1
- package/src/lib.ts +6 -0
- package/src/util/case.ts +19 -0
- package/src/util/differentiate.ts +15 -8
- package/temp/tsconfig.tsbuildinfo +1 -1
- package/test/datetime.test.ts +226 -0
- package/test/e2e/helpers.ts +59 -0
- package/test/e2e/http/parameters/basic/main.test.e2e.ts +36 -0
- package/test/e2e/http/parameters/body-optionality/main.test.e2e.ts +45 -0
- package/test/e2e/http/parameters/spread/main.test.e2e.ts +92 -0
- package/test/e2e/http/type/model/empty/main.test.e2e.ts +35 -0
- package/test/e2e/spector.ts +33 -0
- package/test/scalar.test.ts +345 -0
- package/vitest.config.e2e.ts +20 -0
- package/vitest.config.ts +8 -1
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import { deepStrictEqual, strictEqual, throws } from "assert";
|
|
2
|
+
import { describe, it } from "vitest";
|
|
3
|
+
import { Duration } from "../src/helpers/datetime.js";
|
|
4
|
+
|
|
5
|
+
describe("datetime", () => {
|
|
6
|
+
describe("duration", () => {
|
|
7
|
+
it("parses an ISO8601 duration string", () => {
|
|
8
|
+
deepStrictEqual(Duration.parseISO8601("P1Y2M3D"), {
|
|
9
|
+
sign: "+",
|
|
10
|
+
years: 1,
|
|
11
|
+
months: 2,
|
|
12
|
+
weeks: 0,
|
|
13
|
+
days: 3,
|
|
14
|
+
hours: 0,
|
|
15
|
+
minutes: 0,
|
|
16
|
+
seconds: 0,
|
|
17
|
+
});
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it("parses a negative ISO8601 duration string", () => {
|
|
21
|
+
deepStrictEqual(Duration.parseISO8601("-P1Y2M3D"), {
|
|
22
|
+
sign: "-",
|
|
23
|
+
years: 1,
|
|
24
|
+
months: 2,
|
|
25
|
+
weeks: 0,
|
|
26
|
+
days: 3,
|
|
27
|
+
hours: 0,
|
|
28
|
+
minutes: 0,
|
|
29
|
+
seconds: 0,
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it("parses a duration string with hours and minutes", () => {
|
|
34
|
+
deepStrictEqual(Duration.parseISO8601("PT1H2M"), {
|
|
35
|
+
sign: "+",
|
|
36
|
+
years: 0,
|
|
37
|
+
months: 0,
|
|
38
|
+
weeks: 0,
|
|
39
|
+
days: 0,
|
|
40
|
+
hours: 1,
|
|
41
|
+
minutes: 2,
|
|
42
|
+
seconds: 0,
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it("parses a duration string with fractional years", () => {
|
|
47
|
+
deepStrictEqual(Duration.parseISO8601("P1.5Y"), {
|
|
48
|
+
sign: "+",
|
|
49
|
+
years: 1.5,
|
|
50
|
+
months: 0,
|
|
51
|
+
weeks: 0,
|
|
52
|
+
days: 0,
|
|
53
|
+
hours: 0,
|
|
54
|
+
minutes: 0,
|
|
55
|
+
seconds: 0,
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it("does not parse an invalid duration string", () => {
|
|
60
|
+
throws(() => Duration.parseISO8601("1Y2M3D4H"), {
|
|
61
|
+
message: "Invalid ISO8601 duration: 1Y2M3D4H",
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it("does not parse a duration string with too many digits in a component", () => {
|
|
66
|
+
throws(
|
|
67
|
+
() =>
|
|
68
|
+
Duration.parseISO8601(
|
|
69
|
+
"P123429384502934875023948572039485720394857230948572309485723094857203948572309456789.19821374652345232304958273049582730495827340958720349857452345234529223450928347592834387456928374659238476Y",
|
|
70
|
+
),
|
|
71
|
+
{
|
|
72
|
+
message:
|
|
73
|
+
"ISO8601 duration string is too long: P123429384502934875023948572039485720394857230948572309485723094857203948572309456789.19821374652345232304958273049582730495827340958720349857452345234529223450928347592834387456928374659238476Y",
|
|
74
|
+
},
|
|
75
|
+
);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it("does not parse a duration string with an invalid group", () => {
|
|
79
|
+
throws(() => Duration.parseISO8601("P1Y2M3D4H5X"), {
|
|
80
|
+
message: "Invalid ISO8601 duration: P1Y2M3D4H5X",
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it("does not parse a duration string with missing group", () => {
|
|
85
|
+
throws(() => Duration.parseISO8601("P1Y2M3D4H5.5"), {
|
|
86
|
+
message: "Invalid ISO8601 duration: P1Y2M3D4H5.5",
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it("does not parse a duration string with multiple points", () => {
|
|
91
|
+
throws(() => Duration.parseISO8601("P1.2.3Y"), {
|
|
92
|
+
message: "Invalid ISO8601 duration: P1.2.3Y",
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it("allows comma as decimal separator", () => {
|
|
97
|
+
deepStrictEqual(Duration.parseISO8601("P1,5Y4.2DT1,005S"), {
|
|
98
|
+
sign: "+",
|
|
99
|
+
years: 1.5,
|
|
100
|
+
months: 0,
|
|
101
|
+
weeks: 0,
|
|
102
|
+
days: 4.2,
|
|
103
|
+
hours: 0,
|
|
104
|
+
minutes: 0,
|
|
105
|
+
seconds: 1.005,
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it("writes an ISO8601 duration string", () => {
|
|
110
|
+
const duration: Duration = {
|
|
111
|
+
sign: "+",
|
|
112
|
+
years: 1,
|
|
113
|
+
months: 2,
|
|
114
|
+
weeks: 3,
|
|
115
|
+
days: 4,
|
|
116
|
+
hours: 5,
|
|
117
|
+
minutes: 6,
|
|
118
|
+
seconds: 7,
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
strictEqual(Duration.toISO8601(duration), "P1Y2M3W4DT5H6M7S");
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it("writes a negative ISO8601 duration string", () => {
|
|
125
|
+
const duration: Duration = {
|
|
126
|
+
sign: "-",
|
|
127
|
+
years: 1,
|
|
128
|
+
months: 2,
|
|
129
|
+
weeks: 3,
|
|
130
|
+
days: 4,
|
|
131
|
+
hours: 5,
|
|
132
|
+
minutes: 6,
|
|
133
|
+
seconds: 7,
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
strictEqual(Duration.toISO8601(duration), "-P1Y2M3W4DT5H6M7S");
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it("writes a duration string with only years", () => {
|
|
140
|
+
const duration: Duration = {
|
|
141
|
+
sign: "+",
|
|
142
|
+
years: 1,
|
|
143
|
+
months: 0,
|
|
144
|
+
weeks: 0,
|
|
145
|
+
days: 0,
|
|
146
|
+
hours: 0,
|
|
147
|
+
minutes: 0,
|
|
148
|
+
seconds: 0,
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
strictEqual(Duration.toISO8601(duration), "P1Y");
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
it("writes a duration string with only hours", () => {
|
|
155
|
+
const duration: Duration = {
|
|
156
|
+
sign: "+",
|
|
157
|
+
years: 0,
|
|
158
|
+
months: 0,
|
|
159
|
+
weeks: 0,
|
|
160
|
+
days: 0,
|
|
161
|
+
hours: 36,
|
|
162
|
+
minutes: 0,
|
|
163
|
+
seconds: 0,
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
strictEqual(Duration.toISO8601(duration), "PT36H");
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
it("writes a duration string with fractional amounts", () => {
|
|
170
|
+
const duration: Duration = {
|
|
171
|
+
sign: "+",
|
|
172
|
+
years: 1.5,
|
|
173
|
+
months: 0,
|
|
174
|
+
weeks: 0,
|
|
175
|
+
days: 0,
|
|
176
|
+
hours: 0,
|
|
177
|
+
minutes: 0,
|
|
178
|
+
seconds: 1.005,
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
strictEqual(Duration.toISO8601(duration), "P1.5YT1.005S");
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
it("computes total seconds in durations", () => {
|
|
185
|
+
const duration = Duration.parseISO8601("PT22H96M60S");
|
|
186
|
+
|
|
187
|
+
strictEqual(Duration.totalSeconds(duration), 22 * 60 * 60 + 96 * 60 + 60);
|
|
188
|
+
strictEqual(Duration.totalSecondsBigInt(duration), 22n * 60n * 60n + 96n * 60n + 60n);
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
it("computes total seconds in durations with fractional amounts", () => {
|
|
192
|
+
const duration = Duration.parseISO8601("PT1.5H22.005S");
|
|
193
|
+
|
|
194
|
+
strictEqual(Duration.totalSeconds(duration), 1.5 * 60 * 60 + 22 + 0.005);
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
it("does not allow total seconds for durations with years, months, weeks, or days", () => {
|
|
198
|
+
const durations = ["P1Y", "P1M", "P1W", "P1D"].map((iso) => Duration.parseISO8601(iso));
|
|
199
|
+
|
|
200
|
+
for (const duration of durations) {
|
|
201
|
+
throws(() => Duration.totalSeconds(duration), {
|
|
202
|
+
message:
|
|
203
|
+
"Cannot calculate total seconds for a duration with years, months, weeks, or days.",
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
throws(() => Duration.totalSecondsBigInt(duration), {
|
|
207
|
+
message:
|
|
208
|
+
"Cannot calculate total seconds for a duration with years, months, weeks, or days.",
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
it("does not allow total seconds as bigint for durations with fractional amounts", () => {
|
|
214
|
+
const durations = ["PT1.5H", "PT1.5M", "PT1.5S", "PT1H1.5M", "PT1H1.5S", "PT1M1.5S"].map(
|
|
215
|
+
(iso) => Duration.parseISO8601(iso),
|
|
216
|
+
);
|
|
217
|
+
|
|
218
|
+
for (const duration of durations) {
|
|
219
|
+
throws(() => Duration.totalSecondsBigInt(duration), {
|
|
220
|
+
message:
|
|
221
|
+
"Cannot calculate total seconds as a BigInt for a duration with non-integer components.",
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
});
|
|
225
|
+
});
|
|
226
|
+
});
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { createServer, IncomingMessage, Server, ServerResponse } from "node:http";
|
|
2
|
+
|
|
3
|
+
interface BasicRouter {
|
|
4
|
+
/**
|
|
5
|
+
* Dispatches the request to the appropriate service based on the request path.
|
|
6
|
+
*
|
|
7
|
+
* This member function may be used directly as a handler for a Node HTTP server.
|
|
8
|
+
*
|
|
9
|
+
* @param request - The incoming HTTP request.
|
|
10
|
+
* @param response - The outgoing HTTP response.
|
|
11
|
+
*/
|
|
12
|
+
dispatch(request: IncomingMessage, response: ServerResponse): void;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function startServer(router: BasicRouter, abortSignal: AbortSignal): Promise<string> {
|
|
16
|
+
return new Promise<string>((resolve, reject) => {
|
|
17
|
+
if (abortSignal.aborted) {
|
|
18
|
+
return reject(new Error("Server start cancelled"));
|
|
19
|
+
}
|
|
20
|
+
const server = createServer((req, res) => router.dispatch(req, res));
|
|
21
|
+
const stop = () => {
|
|
22
|
+
return new Promise<void>((r) => {
|
|
23
|
+
server.close(() => r);
|
|
24
|
+
server.closeAllConnections();
|
|
25
|
+
});
|
|
26
|
+
};
|
|
27
|
+
abortSignal.addEventListener("abort", () => {
|
|
28
|
+
stop().catch(() => {});
|
|
29
|
+
});
|
|
30
|
+
server.listen(function (this: Server) {
|
|
31
|
+
const address = this.address();
|
|
32
|
+
if (!address) {
|
|
33
|
+
reject(new Error("Server address not available"));
|
|
34
|
+
stop().catch(() => {});
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
resolve(typeof address === "string" ? address : `http://localhost:${address.port}`);
|
|
39
|
+
});
|
|
40
|
+
server.on("error", reject);
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Meant to be used for the `onInternalError` handler in the router
|
|
46
|
+
* in order to log any failed test assertions from within service handlers.
|
|
47
|
+
* Purely informational as the test will fail regardless.
|
|
48
|
+
* @param ctx The HttpContext from the http-server-js router
|
|
49
|
+
*/
|
|
50
|
+
function logAssertionErrors(ctx: any, error: Error): void {
|
|
51
|
+
// eslint-disable-next-line no-console
|
|
52
|
+
console.error(error);
|
|
53
|
+
ctx.response.statusCode = 599;
|
|
54
|
+
ctx.response.end();
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export const testRouterOptions = {
|
|
58
|
+
onInternalError: logAssertionErrors,
|
|
59
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { deepStrictEqual } from "node:assert";
|
|
2
|
+
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
|
3
|
+
import { createBasicRouter } from "../../../generated/parameters/basic/src/generated/http/router.js";
|
|
4
|
+
import { startServer, testRouterOptions } from "../../../helpers.js";
|
|
5
|
+
import { runScenario } from "../../../spector.js";
|
|
6
|
+
|
|
7
|
+
describe("Parameters.Basic", () => {
|
|
8
|
+
let serverAbortController: AbortController;
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
serverAbortController = new AbortController();
|
|
11
|
+
});
|
|
12
|
+
afterEach(() => {
|
|
13
|
+
serverAbortController.abort();
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it("passes all scenarios", async () => {
|
|
17
|
+
const router = createBasicRouter(
|
|
18
|
+
{
|
|
19
|
+
async simple(ctx, body) {
|
|
20
|
+
deepStrictEqual(body, { name: "foo" });
|
|
21
|
+
return { statusCode: 204 };
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
async simple(ctx, name) {
|
|
26
|
+
deepStrictEqual(name, "foo");
|
|
27
|
+
return { statusCode: 204 };
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
testRouterOptions,
|
|
31
|
+
);
|
|
32
|
+
const baseUrl = await startServer(router, serverAbortController.signal);
|
|
33
|
+
const { status } = await runScenario("parameters/basic/**/*", baseUrl);
|
|
34
|
+
expect(status).toBe("pass");
|
|
35
|
+
});
|
|
36
|
+
});
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { deepStrictEqual } from "node:assert";
|
|
2
|
+
import { afterEach, assert, beforeEach, describe, expect, it } from "vitest";
|
|
3
|
+
import { createBodyOptionalityRouter } from "../../../generated/parameters/body-optionality/src/generated/http/router.js";
|
|
4
|
+
import { startServer, testRouterOptions } from "../../../helpers.js";
|
|
5
|
+
import { runScenario } from "../../../spector.js";
|
|
6
|
+
|
|
7
|
+
describe("Parameters.BodyOptionality", () => {
|
|
8
|
+
let serverAbortController: AbortController;
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
serverAbortController = new AbortController();
|
|
11
|
+
});
|
|
12
|
+
afterEach(() => {
|
|
13
|
+
serverAbortController.abort();
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it("passes all scenarios", async () => {
|
|
17
|
+
const router = createBodyOptionalityRouter(
|
|
18
|
+
{
|
|
19
|
+
async requiredExplicit(ctx, body) {
|
|
20
|
+
deepStrictEqual(body, { name: "foo" });
|
|
21
|
+
return { statusCode: 204 };
|
|
22
|
+
},
|
|
23
|
+
async requiredImplicit(ctx, name) {
|
|
24
|
+
deepStrictEqual(name, "foo");
|
|
25
|
+
return { statusCode: 204 };
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
async omit(ctx, options) {
|
|
30
|
+
assert.isUndefined(options?.body);
|
|
31
|
+
return { statusCode: 204 };
|
|
32
|
+
},
|
|
33
|
+
async set(ctx, options) {
|
|
34
|
+
assert(options?.body);
|
|
35
|
+
assert.deepStrictEqual(options.body, { name: "foo" });
|
|
36
|
+
return { statusCode: 204 };
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
testRouterOptions,
|
|
40
|
+
);
|
|
41
|
+
const baseUrl = await startServer(router, serverAbortController.signal);
|
|
42
|
+
const { status } = await runScenario("parameters/body-optionality/**/*", baseUrl);
|
|
43
|
+
expect(status).toBe("pass");
|
|
44
|
+
});
|
|
45
|
+
});
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { deepStrictEqual } from "node:assert";
|
|
2
|
+
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
|
3
|
+
import { createSpreadRouter } from "../../../generated/parameters/spread/src/generated/http/router.js";
|
|
4
|
+
import { startServer, testRouterOptions } from "../../../helpers.js";
|
|
5
|
+
import { runScenario } from "../../../spector.js";
|
|
6
|
+
|
|
7
|
+
describe("Parameters.Spread", () => {
|
|
8
|
+
let serverAbortController: AbortController;
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
serverAbortController = new AbortController();
|
|
11
|
+
});
|
|
12
|
+
afterEach(() => {
|
|
13
|
+
serverAbortController.abort();
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it("passes all scenarios", async () => {
|
|
17
|
+
const router = createSpreadRouter(
|
|
18
|
+
{
|
|
19
|
+
async spreadAsRequestBody(ctx, name) {
|
|
20
|
+
deepStrictEqual(name, "foo");
|
|
21
|
+
return { statusCode: 204 };
|
|
22
|
+
},
|
|
23
|
+
async spreadCompositeRequest(ctx, name, testHeader, body) {
|
|
24
|
+
deepStrictEqual(name, "foo");
|
|
25
|
+
deepStrictEqual(testHeader, "bar");
|
|
26
|
+
deepStrictEqual(body, { name: "foo" });
|
|
27
|
+
return { statusCode: 204 };
|
|
28
|
+
},
|
|
29
|
+
async spreadCompositeRequestMix(ctx, name, testHeader, prop) {
|
|
30
|
+
deepStrictEqual(name, "foo");
|
|
31
|
+
deepStrictEqual(testHeader, "bar");
|
|
32
|
+
deepStrictEqual(prop, "foo");
|
|
33
|
+
return { statusCode: 204 };
|
|
34
|
+
},
|
|
35
|
+
async spreadCompositeRequestOnlyWithBody(ctx, body) {
|
|
36
|
+
deepStrictEqual(body, { name: "foo" });
|
|
37
|
+
return { statusCode: 204 };
|
|
38
|
+
},
|
|
39
|
+
async spreadCompositeRequestWithoutBody(ctx, name, testHeader) {
|
|
40
|
+
deepStrictEqual(name, "foo");
|
|
41
|
+
deepStrictEqual(testHeader, "bar");
|
|
42
|
+
return { statusCode: 204 };
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
async spreadAsRequestBody(ctx, name) {
|
|
47
|
+
deepStrictEqual(name, "foo");
|
|
48
|
+
return { statusCode: 204 };
|
|
49
|
+
},
|
|
50
|
+
async spreadAsRequestParameter(ctx, id, xMsTestHeader, name) {
|
|
51
|
+
deepStrictEqual(id, "1");
|
|
52
|
+
deepStrictEqual(xMsTestHeader, "bar");
|
|
53
|
+
deepStrictEqual(name, "foo");
|
|
54
|
+
return { statusCode: 204 };
|
|
55
|
+
},
|
|
56
|
+
async spreadParameterWithInnerAlias(ctx, id, name, age, xMsTestHeader) {
|
|
57
|
+
deepStrictEqual(id, "1");
|
|
58
|
+
deepStrictEqual(name, "foo");
|
|
59
|
+
deepStrictEqual(age, 1);
|
|
60
|
+
deepStrictEqual(xMsTestHeader, "bar");
|
|
61
|
+
return { statusCode: 204 };
|
|
62
|
+
},
|
|
63
|
+
async spreadParameterWithInnerModel(ctx, id, name, xMsTestHeader) {
|
|
64
|
+
deepStrictEqual(id, "1");
|
|
65
|
+
deepStrictEqual(name, "foo");
|
|
66
|
+
deepStrictEqual(xMsTestHeader, "bar");
|
|
67
|
+
return { statusCode: 204 };
|
|
68
|
+
},
|
|
69
|
+
async spreadWithMultipleParameters(
|
|
70
|
+
ctx,
|
|
71
|
+
id,
|
|
72
|
+
xMsTestHeader,
|
|
73
|
+
requiredString,
|
|
74
|
+
requiredIntList,
|
|
75
|
+
options,
|
|
76
|
+
) {
|
|
77
|
+
deepStrictEqual(id, "1");
|
|
78
|
+
deepStrictEqual(xMsTestHeader, "bar");
|
|
79
|
+
deepStrictEqual(requiredString, "foo");
|
|
80
|
+
deepStrictEqual(requiredIntList, [1, 2]);
|
|
81
|
+
deepStrictEqual(options?.optionalInt, 1);
|
|
82
|
+
deepStrictEqual(options?.optionalStringList, ["foo", "bar"]);
|
|
83
|
+
return { statusCode: 204 };
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
testRouterOptions,
|
|
87
|
+
);
|
|
88
|
+
const baseUrl = await startServer(router, serverAbortController.signal);
|
|
89
|
+
const { status } = await runScenario("parameters/spread/**/*", baseUrl);
|
|
90
|
+
expect(status).toBe("pass");
|
|
91
|
+
});
|
|
92
|
+
});
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { deepStrictEqual } from "node:assert";
|
|
2
|
+
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
|
3
|
+
import { createEmptyRouter } from "../../../../generated/type/model/empty/src/generated/http/router.js";
|
|
4
|
+
import { startServer, testRouterOptions } from "../../../../helpers.js";
|
|
5
|
+
import { runScenario } from "../../../../spector.js";
|
|
6
|
+
|
|
7
|
+
describe("Type.Model.Empty", () => {
|
|
8
|
+
let serverAbortController: AbortController;
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
serverAbortController = new AbortController();
|
|
11
|
+
});
|
|
12
|
+
afterEach(() => {
|
|
13
|
+
serverAbortController.abort();
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it("passes all scenarios", async () => {
|
|
17
|
+
const router = createEmptyRouter(
|
|
18
|
+
{
|
|
19
|
+
async getEmpty(ctx) {
|
|
20
|
+
return { body: {} };
|
|
21
|
+
},
|
|
22
|
+
async postRoundTripEmpty(ctx, body) {
|
|
23
|
+
return { body };
|
|
24
|
+
},
|
|
25
|
+
async putEmpty(ctx, input) {
|
|
26
|
+
deepStrictEqual(input, {});
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
testRouterOptions,
|
|
30
|
+
);
|
|
31
|
+
const baseUrl = await startServer(router, serverAbortController.signal);
|
|
32
|
+
const { status } = await runScenario("type/model/empty/**/*", baseUrl);
|
|
33
|
+
expect(status).toBe("pass");
|
|
34
|
+
});
|
|
35
|
+
});
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { run } from "@typespec/internal-build-utils";
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
3
|
+
import { dirname, resolve } from "pathe";
|
|
4
|
+
|
|
5
|
+
// Root of `http-server-js` package so vscode test integration runs from the correct directory
|
|
6
|
+
const CWD = resolve(dirname(fileURLToPath(import.meta.url)), "..", "..");
|
|
7
|
+
|
|
8
|
+
export async function runScenario(
|
|
9
|
+
scenario: string,
|
|
10
|
+
baseUrl: string,
|
|
11
|
+
): Promise<{ status: "pass" | "fail" }> {
|
|
12
|
+
try {
|
|
13
|
+
await run(
|
|
14
|
+
"npx",
|
|
15
|
+
[
|
|
16
|
+
"tsp-spector",
|
|
17
|
+
"knock",
|
|
18
|
+
"./node_modules/@typespec/http-specs/specs",
|
|
19
|
+
"--filter",
|
|
20
|
+
scenario,
|
|
21
|
+
"--baseUrl",
|
|
22
|
+
baseUrl,
|
|
23
|
+
],
|
|
24
|
+
{
|
|
25
|
+
shell: true,
|
|
26
|
+
cwd: CWD,
|
|
27
|
+
},
|
|
28
|
+
);
|
|
29
|
+
return { status: "pass" };
|
|
30
|
+
} catch (e) {
|
|
31
|
+
return { status: "fail" };
|
|
32
|
+
}
|
|
33
|
+
}
|