@zudojs/testing 1.1.2 → 1.2.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/README.md +138 -2
- package/dist/assertions/httpAssertions.core.d.ts +2 -2
- package/dist/httpTestClient/httpTestClient.cookieJar.d.ts +30 -0
- package/dist/httpTestClient/httpTestClient.cookieJar.js +107 -0
- package/dist/httpTestClient/httpTestClient.factory.d.ts +25 -0
- package/dist/httpTestClient/httpTestClient.factory.js +84 -0
- package/dist/httpTestClient/httpTestClient.target.d.ts +16 -0
- package/dist/httpTestClient/httpTestClient.target.js +80 -0
- package/dist/httpTestClient/httpTestClient.type.d.ts +74 -0
- package/dist/httpTestClient/httpTestClient.type.js +5 -0
- package/dist/httpTestClient/httpTestRequest/httpTestRequest.core.d.ts +10 -0
- package/dist/httpTestClient/httpTestRequest/httpTestRequest.core.js +97 -0
- package/dist/httpTestClient/httpTestRequest/httpTestRequest.encode.d.ts +26 -0
- package/dist/httpTestClient/httpTestRequest/httpTestRequest.encode.js +73 -0
- package/dist/httpTestClient/httpTestRequest/httpTestRequest.execute.d.ts +26 -0
- package/dist/httpTestClient/httpTestRequest/httpTestRequest.execute.js +58 -0
- package/dist/httpTestClient/httpTestRequest/httpTestRequest.type.d.ts +53 -0
- package/dist/httpTestClient/httpTestRequest/httpTestRequest.type.js +5 -0
- package/dist/httpTestClient/httpTestRequest/index.d.ts +13 -0
- package/dist/httpTestClient/httpTestRequest/index.js +10 -0
- package/dist/httpTestClient/httpTestResponse/httpTestResponse.core.d.ts +15 -0
- package/dist/httpTestClient/httpTestResponse/httpTestResponse.core.js +71 -0
- package/dist/httpTestClient/httpTestResponse/httpTestResponse.expect.d.ts +19 -0
- package/dist/httpTestClient/httpTestResponse/httpTestResponse.expect.js +74 -0
- package/dist/httpTestClient/httpTestResponse/httpTestResponse.match.d.ts +15 -0
- package/dist/httpTestClient/httpTestResponse/httpTestResponse.match.js +52 -0
- package/dist/httpTestClient/httpTestResponse/httpTestResponse.type.d.ts +44 -0
- package/dist/httpTestClient/httpTestResponse/httpTestResponse.type.js +5 -0
- package/dist/httpTestClient/httpTestResponse/index.d.ts +12 -0
- package/dist/httpTestClient/httpTestResponse/index.js +10 -0
- package/dist/httpTestClient/httpTestTransport/httpTestTransport.fetch.d.ts +15 -0
- package/dist/httpTestClient/httpTestTransport/httpTestTransport.fetch.js +65 -0
- package/dist/httpTestClient/httpTestTransport/httpTestTransport.node.d.ts +18 -0
- package/dist/httpTestClient/httpTestTransport/httpTestTransport.node.js +94 -0
- package/dist/httpTestClient/httpTestTransport/httpTestTransport.server.d.ts +23 -0
- package/dist/httpTestClient/httpTestTransport/httpTestTransport.server.js +76 -0
- package/dist/httpTestClient/httpTestTransport/httpTestTransport.type.d.ts +46 -0
- package/dist/httpTestClient/httpTestTransport/httpTestTransport.type.js +9 -0
- package/dist/httpTestClient/httpTestTransport/httpTestTransport.zudo.d.ts +25 -0
- package/dist/httpTestClient/httpTestTransport/httpTestTransport.zudo.js +71 -0
- package/dist/httpTestClient/httpTestTransport/index.d.ts +15 -0
- package/dist/httpTestClient/httpTestTransport/index.js +13 -0
- package/dist/httpTestClient/index.d.ts +18 -0
- package/dist/httpTestClient/index.js +13 -0
- package/dist/httpTesting/httpRequest/httpRequest.builder.d.ts +13 -0
- package/dist/httpTesting/httpRequest/httpRequest.builder.js +64 -0
- package/dist/httpTesting/httpRequest/httpRequest.factory.d.ts +17 -0
- package/dist/httpTesting/httpRequest/httpRequest.factory.js +25 -0
- package/dist/httpTesting/{httpRequest.core.d.ts → httpRequest/httpRequest.type.d.ts} +6 -21
- package/dist/httpTesting/httpRequest/httpRequest.type.js +5 -0
- package/dist/httpTesting/httpRequest/index.d.ts +10 -0
- package/dist/httpTesting/httpRequest/index.js +9 -0
- package/dist/httpTesting/{httpResponse.helpers.d.ts → httpResponse/httpResponse.helpers.d.ts} +1 -1
- package/dist/httpTesting/{httpResponse.type.d.ts → httpResponse/httpResponse.type.d.ts} +1 -1
- package/dist/httpTesting/httpResponse/index.d.ts +10 -0
- package/dist/httpTesting/httpResponse/index.js +9 -0
- package/dist/httpTesting/index.d.ts +6 -6
- package/dist/httpTesting/index.js +4 -4
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/mocking/spy.core.d.ts +6 -1
- package/dist/mocking/spy.core.js +1 -3
- package/dist/mocking/stub.core.d.ts +3 -1
- package/dist/mocking/stub.core.js +15 -2
- package/dist/testApplication/index.d.ts +1 -1
- package/dist/testApplication/index.js +1 -1
- package/dist/testApplication/testApplication.core.d.ts +35 -5
- package/dist/testApplication/testApplication.core.js +18 -3
- package/dist/testEventBus/index.d.ts +2 -2
- package/dist/testEventBus/index.js +1 -1
- package/dist/testEventBus/testEventBus.core.d.ts +12 -45
- package/dist/testEventBus/testEventBus.core.js +55 -46
- package/dist/testEventBus/testEventBus.type.d.ts +40 -0
- package/dist/testEventBus/testEventBus.type.js +2 -0
- package/dist/testMessageBus/index.d.ts +1 -1
- package/dist/testMessageBus/index.js +1 -1
- package/dist/testMessageBus/testMessageBus.core.d.ts +20 -24
- package/dist/testMessageBus/testMessageBus.core.js +45 -47
- package/dist/testQueue/index.d.ts +2 -2
- package/dist/testQueue/index.js +1 -1
- package/dist/testQueue/testQueue.core.d.ts +19 -38
- package/dist/testQueue/testQueue.core.js +34 -30
- package/dist/testQueue/testQueue.type.d.ts +30 -0
- package/dist/testQueue/testQueue.type.js +2 -0
- package/dist/testQueue/testQueue.view.d.ts +37 -0
- package/dist/testQueue/testQueue.view.js +44 -0
- package/dist/testStorage/testStorageMemory.core.d.ts +6 -1
- package/dist/testStorage/testStorageMemory.core.js +13 -3
- package/package.json +17 -17
- package/dist/httpTesting/httpRequest.core.js +0 -114
- package/dist/httpTesting/httpStatusCode.type.d.ts +0 -10
- package/dist/httpTesting/httpStatusCode.type.js +0 -9
- /package/dist/httpTesting/{httpResponse.core.d.ts → httpResponse/httpResponse.core.d.ts} +0 -0
- /package/dist/httpTesting/{httpResponse.core.js → httpResponse/httpResponse.core.js} +0 -0
- /package/dist/httpTesting/{httpResponse.helpers.js → httpResponse/httpResponse.helpers.js} +0 -0
- /package/dist/httpTesting/{httpResponse.type.js → httpResponse/httpResponse.type.js} +0 -0
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @zudojs/testing — HTTP test client responses.
|
|
3
|
+
*
|
|
4
|
+
* The response object the test client resolves with, and the expectations
|
|
5
|
+
* `.expect()` / `.expectJson()` / `.expectText()` run against it.
|
|
6
|
+
*/
|
|
7
|
+
export type { HttpTestRequestSummary, HttpTestResponse, } from "./httpTestResponse.type.js";
|
|
8
|
+
export { createHttpTestResponse, previewBody, } from "./httpTestResponse.core.js";
|
|
9
|
+
export { findPartialDifference } from "./httpTestResponse.match.js";
|
|
10
|
+
export { expectHeader, expectJson, expectStatus, expectText, } from "./httpTestResponse.expect.js";
|
|
11
|
+
export type { HttpTestExpectation } from "./httpTestResponse.expect.js";
|
|
12
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @zudojs/testing — HTTP test client responses.
|
|
3
|
+
*
|
|
4
|
+
* The response object the test client resolves with, and the expectations
|
|
5
|
+
* `.expect()` / `.expectJson()` / `.expectText()` run against it.
|
|
6
|
+
*/
|
|
7
|
+
export { createHttpTestResponse, previewBody, } from "./httpTestResponse.core.js";
|
|
8
|
+
export { findPartialDifference } from "./httpTestResponse.match.js";
|
|
9
|
+
export { expectHeader, expectJson, expectStatus, expectText, } from "./httpTestResponse.expect.js";
|
|
10
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @zudojs/testing — in-process fetch transport.
|
|
3
|
+
*
|
|
4
|
+
* Calls a web-standard `(Request) => Response` handler directly: no socket,
|
|
5
|
+
* no port. The handler receives an `AbortSignal` that fires on timeout.
|
|
6
|
+
*/
|
|
7
|
+
import type { FetchHandler, HttpTestTransport } from "./httpTestTransport.type.js";
|
|
8
|
+
/**
|
|
9
|
+
* Transport that dispatches to a fetch handler in-process.
|
|
10
|
+
*
|
|
11
|
+
* @param handler - The web-standard handler.
|
|
12
|
+
* @param origin - Origin the request URL is resolved against.
|
|
13
|
+
*/
|
|
14
|
+
export declare function createFetchTransport(handler: FetchHandler, origin?: string): HttpTestTransport;
|
|
15
|
+
//# sourceMappingURL=httpTestTransport.fetch.d.ts.map
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @zudojs/testing — in-process fetch transport.
|
|
3
|
+
*
|
|
4
|
+
* Calls a web-standard `(Request) => Response` handler directly: no socket,
|
|
5
|
+
* no port. The handler receives an `AbortSignal` that fires on timeout.
|
|
6
|
+
*/
|
|
7
|
+
import { NetworkError, TimeoutError, TimeoutOperation } from "@zudojs/errors";
|
|
8
|
+
const NULL_BODY_STATUSES = new Set([101, 103, 204, 205, 304]);
|
|
9
|
+
async function dispatch(handler, origin, raw, signal) {
|
|
10
|
+
const request = new Request(new URL(raw.target, origin), {
|
|
11
|
+
method: raw.method,
|
|
12
|
+
headers: raw.headers,
|
|
13
|
+
body: raw.body === undefined ? null : raw.body,
|
|
14
|
+
signal,
|
|
15
|
+
});
|
|
16
|
+
let response;
|
|
17
|
+
try {
|
|
18
|
+
response = await handler(request);
|
|
19
|
+
}
|
|
20
|
+
catch (error) {
|
|
21
|
+
throw new NetworkError(`${raw.method} ${raw.target} failed: the fetch handler threw instead of returning a Response.`, { cause: error, endpoint: request.url, method: raw.method });
|
|
22
|
+
}
|
|
23
|
+
if (!(response instanceof Response)) {
|
|
24
|
+
throw new NetworkError(`${raw.method} ${raw.target} failed: the fetch handler returned ${typeof response}, not a Response.`, { endpoint: request.url, method: raw.method });
|
|
25
|
+
}
|
|
26
|
+
const empty = raw.method === "HEAD" || NULL_BODY_STATUSES.has(response.status);
|
|
27
|
+
return {
|
|
28
|
+
status: response.status,
|
|
29
|
+
statusText: response.statusText,
|
|
30
|
+
headers: new Headers(response.headers),
|
|
31
|
+
body: empty
|
|
32
|
+
? new Uint8Array()
|
|
33
|
+
: new Uint8Array(await response.arrayBuffer()),
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Transport that dispatches to a fetch handler in-process.
|
|
38
|
+
*
|
|
39
|
+
* @param handler - The web-standard handler.
|
|
40
|
+
* @param origin - Origin the request URL is resolved against.
|
|
41
|
+
*/
|
|
42
|
+
export function createFetchTransport(handler, origin = "http://localhost") {
|
|
43
|
+
const send = (raw) => {
|
|
44
|
+
const controller = new AbortController();
|
|
45
|
+
const label = `${raw.method} ${raw.target}`;
|
|
46
|
+
let timer;
|
|
47
|
+
const timeout = new Promise((_, reject) => {
|
|
48
|
+
timer = setTimeout(() => {
|
|
49
|
+
const error = new TimeoutError(`${label} timed out after ${raw.timeoutMs} ms.`, {
|
|
50
|
+
operation: TimeoutOperation.REQUEST,
|
|
51
|
+
timeoutMs: raw.timeoutMs,
|
|
52
|
+
target: label,
|
|
53
|
+
});
|
|
54
|
+
controller.abort(error);
|
|
55
|
+
reject(error);
|
|
56
|
+
}, raw.timeoutMs);
|
|
57
|
+
});
|
|
58
|
+
return Promise.race([
|
|
59
|
+
dispatch(handler, origin, raw, controller.signal),
|
|
60
|
+
timeout,
|
|
61
|
+
]).finally(() => clearTimeout(timer));
|
|
62
|
+
};
|
|
63
|
+
return { origin, send, close: async () => undefined };
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=httpTestTransport.fetch.js.map
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @zudojs/testing — Node transport.
|
|
3
|
+
*
|
|
4
|
+
* Sends requests over a real socket with `node:http`/`node:https`. Each
|
|
5
|
+
* request uses its own connection (`agent: false`), so closing the server
|
|
6
|
+
* never waits on a pooled keep-alive socket.
|
|
7
|
+
*/
|
|
8
|
+
import type { HttpTestTransport, RawHttpRequest, RawHttpResponse } from "./httpTestTransport.type.js";
|
|
9
|
+
/**
|
|
10
|
+
* Sends one request to `origin` and resolves with the complete response.
|
|
11
|
+
*
|
|
12
|
+
* Rejects with a `TimeoutError` when no complete response arrives within
|
|
13
|
+
* `request.timeoutMs`, and with a `NetworkError` when the connection fails.
|
|
14
|
+
*/
|
|
15
|
+
export declare function sendNodeRequest(origin: string, request: RawHttpRequest): Promise<RawHttpResponse>;
|
|
16
|
+
/** A transport that talks to an already-listening origin and owns nothing. */
|
|
17
|
+
export declare function createOriginTransport(origin: string, close?: () => Promise<void>): HttpTestTransport;
|
|
18
|
+
//# sourceMappingURL=httpTestTransport.node.d.ts.map
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @zudojs/testing — Node transport.
|
|
3
|
+
*
|
|
4
|
+
* Sends requests over a real socket with `node:http`/`node:https`. Each
|
|
5
|
+
* request uses its own connection (`agent: false`), so closing the server
|
|
6
|
+
* never waits on a pooled keep-alive socket.
|
|
7
|
+
*/
|
|
8
|
+
import { request as httpRequest } from "node:http";
|
|
9
|
+
import { request as httpsRequest } from "node:https";
|
|
10
|
+
import { NetworkError, TimeoutError, TimeoutOperation } from "@zudojs/errors";
|
|
11
|
+
function toOutgoingHeaders(headers) {
|
|
12
|
+
const outgoing = {};
|
|
13
|
+
headers.forEach((value, name) => {
|
|
14
|
+
outgoing[name] = value;
|
|
15
|
+
});
|
|
16
|
+
return outgoing;
|
|
17
|
+
}
|
|
18
|
+
function toHeaders(message) {
|
|
19
|
+
const headers = new Headers();
|
|
20
|
+
const raw = message.rawHeaders;
|
|
21
|
+
for (let index = 0; index + 1 < raw.length; index += 2) {
|
|
22
|
+
headers.append(raw[index], raw[index + 1]);
|
|
23
|
+
}
|
|
24
|
+
return headers;
|
|
25
|
+
}
|
|
26
|
+
function collect(message) {
|
|
27
|
+
return new Promise((resolve, reject) => {
|
|
28
|
+
const chunks = [];
|
|
29
|
+
message.on("data", (chunk) => chunks.push(chunk));
|
|
30
|
+
message.on("error", reject);
|
|
31
|
+
message.on("end", () => {
|
|
32
|
+
resolve({
|
|
33
|
+
status: message.statusCode ?? 0,
|
|
34
|
+
statusText: message.statusMessage ?? "",
|
|
35
|
+
headers: toHeaders(message),
|
|
36
|
+
body: new Uint8Array(Buffer.concat(chunks)),
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Sends one request to `origin` and resolves with the complete response.
|
|
43
|
+
*
|
|
44
|
+
* Rejects with a `TimeoutError` when no complete response arrives within
|
|
45
|
+
* `request.timeoutMs`, and with a `NetworkError` when the connection fails.
|
|
46
|
+
*/
|
|
47
|
+
export function sendNodeRequest(origin, request) {
|
|
48
|
+
const url = new URL(request.target, origin);
|
|
49
|
+
const send = url.protocol === "https:" ? httpsRequest : httpRequest;
|
|
50
|
+
const label = `${request.method} ${request.target}`;
|
|
51
|
+
return new Promise((resolve, reject) => {
|
|
52
|
+
const headers = toOutgoingHeaders(request.headers);
|
|
53
|
+
if (request.body !== undefined && headers["content-length"] === undefined) {
|
|
54
|
+
headers["content-length"] = String(request.body.byteLength);
|
|
55
|
+
}
|
|
56
|
+
const outgoing = send(url, {
|
|
57
|
+
method: request.method,
|
|
58
|
+
headers,
|
|
59
|
+
agent: false,
|
|
60
|
+
});
|
|
61
|
+
const timer = setTimeout(() => {
|
|
62
|
+
outgoing.destroy(new TimeoutError(`${label} timed out after ${request.timeoutMs} ms.`, {
|
|
63
|
+
operation: TimeoutOperation.REQUEST,
|
|
64
|
+
timeoutMs: request.timeoutMs,
|
|
65
|
+
target: label,
|
|
66
|
+
}));
|
|
67
|
+
}, request.timeoutMs);
|
|
68
|
+
outgoing.on("response", (message) => {
|
|
69
|
+
collect(message)
|
|
70
|
+
.then(resolve, reject)
|
|
71
|
+
.finally(() => clearTimeout(timer));
|
|
72
|
+
});
|
|
73
|
+
outgoing.on("error", (error) => {
|
|
74
|
+
clearTimeout(timer);
|
|
75
|
+
reject(error instanceof TimeoutError
|
|
76
|
+
? error
|
|
77
|
+
: new NetworkError(`${label} failed: ${error.message}`, {
|
|
78
|
+
cause: error,
|
|
79
|
+
endpoint: url.href,
|
|
80
|
+
method: request.method,
|
|
81
|
+
}));
|
|
82
|
+
});
|
|
83
|
+
outgoing.end(request.body === undefined ? undefined : Buffer.from(request.body));
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
/** A transport that talks to an already-listening origin and owns nothing. */
|
|
87
|
+
export function createOriginTransport(origin, close = async () => undefined) {
|
|
88
|
+
return {
|
|
89
|
+
origin,
|
|
90
|
+
send: (request) => sendNodeRequest(origin, request),
|
|
91
|
+
close,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=httpTestTransport.node.js.map
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @zudojs/testing — ephemeral Node servers.
|
|
3
|
+
*
|
|
4
|
+
* Starts a Node server on `127.0.0.1:0` (the OS picks a free port) and closes
|
|
5
|
+
* it again, including any open connections, when the client is closed.
|
|
6
|
+
*/
|
|
7
|
+
import type { Server as NetServer } from "node:net";
|
|
8
|
+
import type { HttpTestTransport, NodeRequestListener } from "./httpTestTransport.type.js";
|
|
9
|
+
/**
|
|
10
|
+
* Builds a requestable origin from a bound address. A wildcard bind
|
|
11
|
+
* (`0.0.0.0`, `::`) is reached through loopback.
|
|
12
|
+
*/
|
|
13
|
+
export declare function formatOrigin(host: string, port: number, secure?: boolean): string;
|
|
14
|
+
/**
|
|
15
|
+
* Transport for a Node server. A server that is already listening is used
|
|
16
|
+
* where it is and left running; one that is not is started on an ephemeral
|
|
17
|
+
* port, unref'd so a forgotten `close()` cannot hold the process open, and
|
|
18
|
+
* closed by the transport.
|
|
19
|
+
*/
|
|
20
|
+
export declare function createNodeServerTransport(server: NetServer): Promise<HttpTestTransport>;
|
|
21
|
+
/** Transport for a bare `(req, res)` listener, served on an ephemeral port. */
|
|
22
|
+
export declare function createListenerTransport(listener: NodeRequestListener): Promise<HttpTestTransport>;
|
|
23
|
+
//# sourceMappingURL=httpTestTransport.server.d.ts.map
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @zudojs/testing — ephemeral Node servers.
|
|
3
|
+
*
|
|
4
|
+
* Starts a Node server on `127.0.0.1:0` (the OS picks a free port) and closes
|
|
5
|
+
* it again, including any open connections, when the client is closed.
|
|
6
|
+
*/
|
|
7
|
+
import { createServer } from "node:http";
|
|
8
|
+
import { createOriginTransport } from "./httpTestTransport.node.js";
|
|
9
|
+
function originOf(server, secure) {
|
|
10
|
+
const address = server.address();
|
|
11
|
+
if (address === null || typeof address === "string") {
|
|
12
|
+
throw new TypeError("createHttpTestClient needs a TCP server; a pipe or unix socket address cannot be requested.");
|
|
13
|
+
}
|
|
14
|
+
return formatOrigin(address.address, address.port, secure);
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Builds a requestable origin from a bound address. A wildcard bind
|
|
18
|
+
* (`0.0.0.0`, `::`) is reached through loopback.
|
|
19
|
+
*/
|
|
20
|
+
export function formatOrigin(host, port, secure = false) {
|
|
21
|
+
const reachable = host === "0.0.0.0" || host === ""
|
|
22
|
+
? "127.0.0.1"
|
|
23
|
+
: host === "::"
|
|
24
|
+
? "::1"
|
|
25
|
+
: host;
|
|
26
|
+
const bracketed = reachable.includes(":") ? `[${reachable}]` : reachable;
|
|
27
|
+
return `${secure ? "https" : "http"}://${bracketed}:${port}`;
|
|
28
|
+
}
|
|
29
|
+
function isTlsServer(server) {
|
|
30
|
+
return typeof server.addContext === "function";
|
|
31
|
+
}
|
|
32
|
+
function closeNetServer(server) {
|
|
33
|
+
return new Promise((resolve, reject) => {
|
|
34
|
+
server.close((error) => (error ? reject(error) : resolve()));
|
|
35
|
+
server.closeAllConnections?.();
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
function listenEphemeral(server) {
|
|
39
|
+
return new Promise((resolve, reject) => {
|
|
40
|
+
const onError = (error) => reject(error);
|
|
41
|
+
server.once("error", onError);
|
|
42
|
+
server.listen(0, "127.0.0.1", () => {
|
|
43
|
+
server.off("error", onError);
|
|
44
|
+
resolve();
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Transport for a Node server. A server that is already listening is used
|
|
50
|
+
* where it is and left running; one that is not is started on an ephemeral
|
|
51
|
+
* port, unref'd so a forgotten `close()` cannot hold the process open, and
|
|
52
|
+
* closed by the transport.
|
|
53
|
+
*/
|
|
54
|
+
export async function createNodeServerTransport(server) {
|
|
55
|
+
const secure = isTlsServer(server);
|
|
56
|
+
if (server.listening) {
|
|
57
|
+
return createOriginTransport(originOf(server, secure));
|
|
58
|
+
}
|
|
59
|
+
await listenEphemeral(server);
|
|
60
|
+
server.unref();
|
|
61
|
+
return createOriginTransport(originOf(server, secure), () => closeNetServer(server));
|
|
62
|
+
}
|
|
63
|
+
/** Transport for a bare `(req, res)` listener, served on an ephemeral port. */
|
|
64
|
+
export function createListenerTransport(listener) {
|
|
65
|
+
return createNodeServerTransport(createServer((request, response) => {
|
|
66
|
+
Promise.resolve()
|
|
67
|
+
.then(() => listener(request, response))
|
|
68
|
+
.catch(() => {
|
|
69
|
+
if (!response.headersSent) {
|
|
70
|
+
response.statusCode = 500;
|
|
71
|
+
}
|
|
72
|
+
response.end();
|
|
73
|
+
});
|
|
74
|
+
}));
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=httpTestTransport.server.js.map
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @zudojs/testing — HTTP test transport types.
|
|
3
|
+
*
|
|
4
|
+
* A transport turns one fully-built request into the bytes the app answered
|
|
5
|
+
* with. Every target kind (URL, Node server, request listener, fetch handler,
|
|
6
|
+
* `@zudojs/http` server/router/adapter) is reduced to one of these.
|
|
7
|
+
*/
|
|
8
|
+
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
9
|
+
/** A request as it goes on the wire. */
|
|
10
|
+
export interface RawHttpRequest {
|
|
11
|
+
readonly method: string;
|
|
12
|
+
/** Origin-relative target: path plus query string. */
|
|
13
|
+
readonly target: string;
|
|
14
|
+
readonly headers: Headers;
|
|
15
|
+
readonly body: Uint8Array | undefined;
|
|
16
|
+
readonly timeoutMs: number;
|
|
17
|
+
}
|
|
18
|
+
/** A response as it came off the wire. */
|
|
19
|
+
export interface RawHttpResponse {
|
|
20
|
+
readonly status: number;
|
|
21
|
+
readonly statusText: string;
|
|
22
|
+
readonly headers: Headers;
|
|
23
|
+
readonly body: Uint8Array;
|
|
24
|
+
}
|
|
25
|
+
/** Sends requests to one target and releases what it opened. */
|
|
26
|
+
export interface HttpTestTransport {
|
|
27
|
+
/** Where requests go, e.g. `http://127.0.0.1:41234`. */
|
|
28
|
+
readonly origin: string;
|
|
29
|
+
/**
|
|
30
|
+
* Path prepended to every target before it is sent (`/api` for a base URL
|
|
31
|
+
* of `http://host/api`); the cookie jar scopes cookies by the prefixed
|
|
32
|
+
* path, which is the one the server saw. Absent when there is none.
|
|
33
|
+
*/
|
|
34
|
+
readonly pathPrefix?: string;
|
|
35
|
+
readonly send: (request: RawHttpRequest) => Promise<RawHttpResponse>;
|
|
36
|
+
readonly close: () => Promise<void>;
|
|
37
|
+
}
|
|
38
|
+
/** A web-standard handler: `Request` in, `Response` out. */
|
|
39
|
+
export type FetchHandler = (request: Request) => Response | Promise<Response>;
|
|
40
|
+
/** An object exposing a web-standard `fetch` method (Hono, Bun-style apps). */
|
|
41
|
+
export interface FetchApplication {
|
|
42
|
+
readonly fetch: FetchHandler;
|
|
43
|
+
}
|
|
44
|
+
/** A Node `http.createServer` request listener. */
|
|
45
|
+
export type NodeRequestListener = (request: IncomingMessage, response: ServerResponse) => void | Promise<void>;
|
|
46
|
+
//# sourceMappingURL=httpTestTransport.type.d.ts.map
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @zudojs/testing — HTTP test transport types.
|
|
3
|
+
*
|
|
4
|
+
* A transport turns one fully-built request into the bytes the app answered
|
|
5
|
+
* with. Every target kind (URL, Node server, request listener, fetch handler,
|
|
6
|
+
* `@zudojs/http` server/router/adapter) is reduced to one of these.
|
|
7
|
+
*/
|
|
8
|
+
export {};
|
|
9
|
+
//# sourceMappingURL=httpTestTransport.type.js.map
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @zudojs/testing — `@zudojs/http` transport.
|
|
3
|
+
*
|
|
4
|
+
* `@zudojs/http` has no `handle(Request) => Response` entry point, so its
|
|
5
|
+
* targets are served on an ephemeral port through a real `NodeHttpAdapter`:
|
|
6
|
+
* requests pass the adapter's request guard, body limits, handler and default
|
|
7
|
+
* error mapping exactly as in production. A server or adapter that is already
|
|
8
|
+
* running is used where it listens and left running.
|
|
9
|
+
*/
|
|
10
|
+
import { NodeHttpAdapter } from "@zudojs/http";
|
|
11
|
+
import type { HttpHandler, HttpMiddlewarePipeline, HttpRouter, HttpServer, NodeAdapterOptions } from "@zudojs/http";
|
|
12
|
+
import type { HttpTestTransport } from "./httpTestTransport.type.js";
|
|
13
|
+
/** Adapter settings for the `NodeHttpAdapter` the client creates. */
|
|
14
|
+
export type HttpTestAdapterOptions = Omit<NodeAdapterOptions, "handler" | "host" | "port" | "server">;
|
|
15
|
+
/** Transport for a `NodeHttpAdapter`. */
|
|
16
|
+
export declare function createAdapterTransport(adapter: NodeHttpAdapter): Promise<HttpTestTransport>;
|
|
17
|
+
/** Transport for an `HttpServer`. */
|
|
18
|
+
export declare function createHttpServerTransport(server: HttpServer): Promise<HttpTestTransport>;
|
|
19
|
+
/** Transport for an `HttpHandler`, served by a fresh `NodeHttpAdapter`. */
|
|
20
|
+
export declare function createHandlerTransport(handler: HttpHandler, options?: HttpTestAdapterOptions): Promise<HttpTestTransport>;
|
|
21
|
+
/** Transport for an `HttpRouter`. */
|
|
22
|
+
export declare function createRouterTransport(router: HttpRouter, options?: HttpTestAdapterOptions): Promise<HttpTestTransport>;
|
|
23
|
+
/** Transport for an `HttpMiddlewarePipeline`. */
|
|
24
|
+
export declare function createPipelineTransport(pipeline: HttpMiddlewarePipeline, options?: HttpTestAdapterOptions): Promise<HttpTestTransport>;
|
|
25
|
+
//# sourceMappingURL=httpTestTransport.zudo.d.ts.map
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @zudojs/testing — `@zudojs/http` transport.
|
|
3
|
+
*
|
|
4
|
+
* `@zudojs/http` has no `handle(Request) => Response` entry point, so its
|
|
5
|
+
* targets are served on an ephemeral port through a real `NodeHttpAdapter`:
|
|
6
|
+
* requests pass the adapter's request guard, body limits, handler and default
|
|
7
|
+
* error mapping exactly as in production. A server or adapter that is already
|
|
8
|
+
* running is used where it listens and left running.
|
|
9
|
+
*/
|
|
10
|
+
import { createServer } from "node:http";
|
|
11
|
+
import { NodeHttpAdapter, createResponseContext } from "@zudojs/http";
|
|
12
|
+
import { createOriginTransport } from "./httpTestTransport.node.js";
|
|
13
|
+
import { createNodeServerTransport, formatOrigin, } from "./httpTestTransport.server.js";
|
|
14
|
+
/** Serves a (not listening) Node adapter on an ephemeral port. */
|
|
15
|
+
function bridge(adapter) {
|
|
16
|
+
return createNodeServerTransport(createServer((request, response) => {
|
|
17
|
+
adapter.handle({ request, response }).catch(() => {
|
|
18
|
+
if (!response.writableEnded) {
|
|
19
|
+
response.destroy();
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
}));
|
|
23
|
+
}
|
|
24
|
+
function listeningOrigin(address) {
|
|
25
|
+
return address?.port === undefined
|
|
26
|
+
? undefined
|
|
27
|
+
: formatOrigin(address.host ?? "127.0.0.1", address.port);
|
|
28
|
+
}
|
|
29
|
+
/** Transport for a `NodeHttpAdapter`. */
|
|
30
|
+
export function createAdapterTransport(adapter) {
|
|
31
|
+
const origin = adapter.isStarted
|
|
32
|
+
? listeningOrigin(adapter.address)
|
|
33
|
+
: undefined;
|
|
34
|
+
return origin
|
|
35
|
+
? Promise.resolve(createOriginTransport(origin))
|
|
36
|
+
: bridge(adapter);
|
|
37
|
+
}
|
|
38
|
+
/** Transport for an `HttpServer`. */
|
|
39
|
+
export async function createHttpServerTransport(server) {
|
|
40
|
+
const running = server.isRunning
|
|
41
|
+
? listeningOrigin(server.address)
|
|
42
|
+
: undefined;
|
|
43
|
+
if (running) {
|
|
44
|
+
return createOriginTransport(running);
|
|
45
|
+
}
|
|
46
|
+
if (server.adapter instanceof NodeHttpAdapter) {
|
|
47
|
+
return bridge(server.adapter);
|
|
48
|
+
}
|
|
49
|
+
await server.start();
|
|
50
|
+
const origin = listeningOrigin(server.address);
|
|
51
|
+
if (!origin) {
|
|
52
|
+
await server.stop();
|
|
53
|
+
throw new TypeError(`createHttpTestClient started HttpServer "${server.name}" but its adapter "${server.adapter.name}" reports no TCP address to send requests to.`);
|
|
54
|
+
}
|
|
55
|
+
return createOriginTransport(origin, async () => {
|
|
56
|
+
await server.stop({ force: true });
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
/** Transport for an `HttpHandler`, served by a fresh `NodeHttpAdapter`. */
|
|
60
|
+
export function createHandlerTransport(handler, options = {}) {
|
|
61
|
+
return bridge(new NodeHttpAdapter({ ...options, handler }));
|
|
62
|
+
}
|
|
63
|
+
/** Transport for an `HttpRouter`. */
|
|
64
|
+
export function createRouterTransport(router, options = {}) {
|
|
65
|
+
return createHandlerTransport(async (request) => (await router.dispatch(request)).response, options);
|
|
66
|
+
}
|
|
67
|
+
/** Transport for an `HttpMiddlewarePipeline`. */
|
|
68
|
+
export function createPipelineTransport(pipeline, options = {}) {
|
|
69
|
+
return createHandlerTransport((request) => pipeline.execute(request, createResponseContext()), options);
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=httpTestTransport.zudo.js.map
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @zudojs/testing — HTTP test transports.
|
|
3
|
+
*
|
|
4
|
+
* Each target kind the test client accepts is reduced to a transport that
|
|
5
|
+
* sends one request and releases whatever it opened on `close()`: a socket
|
|
6
|
+
* transport for URLs, Node servers and `@zudojs/http` apps, and an in-process
|
|
7
|
+
* transport for web-standard fetch handlers.
|
|
8
|
+
*/
|
|
9
|
+
export type { FetchApplication, FetchHandler, HttpTestTransport, NodeRequestListener, RawHttpRequest, RawHttpResponse, } from "./httpTestTransport.type.js";
|
|
10
|
+
export { createOriginTransport, sendNodeRequest, } from "./httpTestTransport.node.js";
|
|
11
|
+
export { createListenerTransport, createNodeServerTransport, formatOrigin, } from "./httpTestTransport.server.js";
|
|
12
|
+
export { createFetchTransport } from "./httpTestTransport.fetch.js";
|
|
13
|
+
export { createAdapterTransport, createHandlerTransport, createHttpServerTransport, createPipelineTransport, createRouterTransport, } from "./httpTestTransport.zudo.js";
|
|
14
|
+
export type { HttpTestAdapterOptions } from "./httpTestTransport.zudo.js";
|
|
15
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @zudojs/testing — HTTP test transports.
|
|
3
|
+
*
|
|
4
|
+
* Each target kind the test client accepts is reduced to a transport that
|
|
5
|
+
* sends one request and releases whatever it opened on `close()`: a socket
|
|
6
|
+
* transport for URLs, Node servers and `@zudojs/http` apps, and an in-process
|
|
7
|
+
* transport for web-standard fetch handlers.
|
|
8
|
+
*/
|
|
9
|
+
export { createOriginTransport, sendNodeRequest, } from "./httpTestTransport.node.js";
|
|
10
|
+
export { createListenerTransport, createNodeServerTransport, formatOrigin, } from "./httpTestTransport.server.js";
|
|
11
|
+
export { createFetchTransport } from "./httpTestTransport.fetch.js";
|
|
12
|
+
export { createAdapterTransport, createHandlerTransport, createHttpServerTransport, createPipelineTransport, createRouterTransport, } from "./httpTestTransport.zudo.js";
|
|
13
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @zudojs/testing — HTTP test client.
|
|
3
|
+
*
|
|
4
|
+
* `createHttpTestClient(target)` drives a real app over HTTP, supertest
|
|
5
|
+
* style: fluent requests, a cookie jar that persists across requests, and
|
|
6
|
+
* chained expectations with readable failures. Targets can be a base URL, a
|
|
7
|
+
* Node server or listener, a fetch handler, or an `@zudojs/http` server,
|
|
8
|
+
* adapter, router, pipeline or handler.
|
|
9
|
+
*/
|
|
10
|
+
export { createHttpTestClient } from "./httpTestClient.factory.js";
|
|
11
|
+
export { createHttpTestCookieJar } from "./httpTestClient.cookieJar.js";
|
|
12
|
+
export type { HttpTestCookieJar } from "./httpTestClient.cookieJar.js";
|
|
13
|
+
export type { HttpTestClient, HttpTestClientOptions, HttpTestTarget, HttpTestTargetKind, } from "./httpTestClient.type.js";
|
|
14
|
+
export type { HttpTestBody, HttpTestQuery, HttpTestQueryValue, HttpTestRequest, } from "./httpTestRequest/index.js";
|
|
15
|
+
export { findPartialDifference } from "./httpTestResponse/index.js";
|
|
16
|
+
export type { HttpTestExpectation, HttpTestRequestSummary, HttpTestResponse, } from "./httpTestResponse/index.js";
|
|
17
|
+
export type { FetchApplication, FetchHandler, HttpTestAdapterOptions, NodeRequestListener, } from "./httpTestTransport/index.js";
|
|
18
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @zudojs/testing — HTTP test client.
|
|
3
|
+
*
|
|
4
|
+
* `createHttpTestClient(target)` drives a real app over HTTP, supertest
|
|
5
|
+
* style: fluent requests, a cookie jar that persists across requests, and
|
|
6
|
+
* chained expectations with readable failures. Targets can be a base URL, a
|
|
7
|
+
* Node server or listener, a fetch handler, or an `@zudojs/http` server,
|
|
8
|
+
* adapter, router, pipeline or handler.
|
|
9
|
+
*/
|
|
10
|
+
export { createHttpTestClient } from "./httpTestClient.factory.js";
|
|
11
|
+
export { createHttpTestCookieJar } from "./httpTestClient.cookieJar.js";
|
|
12
|
+
export { findPartialDifference } from "./httpTestResponse/index.js";
|
|
13
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @zudojs/testing — HTTP request builder for testing.
|
|
3
|
+
*
|
|
4
|
+
* Provides a fluent API for constructing test HTTP requests.
|
|
5
|
+
*/
|
|
6
|
+
import type { HTTPRequestBuilder } from "./httpRequest.type.js";
|
|
7
|
+
/**
|
|
8
|
+
* Creates a new HTTP request builder.
|
|
9
|
+
*
|
|
10
|
+
* @returns An HTTPRequestBuilder instance.
|
|
11
|
+
*/
|
|
12
|
+
export declare function createTestHTTPRequest(): HTTPRequestBuilder;
|
|
13
|
+
//# sourceMappingURL=httpRequest.builder.d.ts.map
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @zudojs/testing — HTTP request builder for testing.
|
|
3
|
+
*
|
|
4
|
+
* Provides a fluent API for constructing test HTTP requests.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Creates a new HTTP request builder.
|
|
8
|
+
*
|
|
9
|
+
* @returns An HTTPRequestBuilder instance.
|
|
10
|
+
*/
|
|
11
|
+
export function createTestHTTPRequest() {
|
|
12
|
+
let method = "GET";
|
|
13
|
+
let path = "/";
|
|
14
|
+
const headers = new Headers();
|
|
15
|
+
const query = {};
|
|
16
|
+
const params = {};
|
|
17
|
+
let body = undefined;
|
|
18
|
+
const target = (verb) => (p) => {
|
|
19
|
+
method = verb;
|
|
20
|
+
path = p;
|
|
21
|
+
return builder;
|
|
22
|
+
};
|
|
23
|
+
const builder = {
|
|
24
|
+
GET: target("GET"),
|
|
25
|
+
POST: target("POST"),
|
|
26
|
+
PUT: target("PUT"),
|
|
27
|
+
PATCH: target("PATCH"),
|
|
28
|
+
DELETE: target("DELETE"),
|
|
29
|
+
HEAD: target("HEAD"),
|
|
30
|
+
OPTIONS: target("OPTIONS"),
|
|
31
|
+
withHeader: (key, value) => {
|
|
32
|
+
headers.set(key, value);
|
|
33
|
+
return builder;
|
|
34
|
+
},
|
|
35
|
+
withHeaders: (h) => {
|
|
36
|
+
new Headers(h).forEach((value, key) => {
|
|
37
|
+
headers.set(key, value);
|
|
38
|
+
});
|
|
39
|
+
return builder;
|
|
40
|
+
},
|
|
41
|
+
withQuery: (q) => {
|
|
42
|
+
Object.assign(query, q);
|
|
43
|
+
return builder;
|
|
44
|
+
},
|
|
45
|
+
withParam: (key, value) => {
|
|
46
|
+
params[key] = value;
|
|
47
|
+
return builder;
|
|
48
|
+
},
|
|
49
|
+
withBody: (b) => {
|
|
50
|
+
body = b;
|
|
51
|
+
return builder;
|
|
52
|
+
},
|
|
53
|
+
build: () => ({
|
|
54
|
+
method,
|
|
55
|
+
path,
|
|
56
|
+
headers: new Headers(headers),
|
|
57
|
+
query: { ...query },
|
|
58
|
+
body,
|
|
59
|
+
params: { ...params },
|
|
60
|
+
}),
|
|
61
|
+
};
|
|
62
|
+
return builder;
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=httpRequest.builder.js.map
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @zudojs/testing — one-call HTTP request factory.
|
|
3
|
+
*/
|
|
4
|
+
import type { HTTPMethod, HTTPRequestOptions, TestHTTPRequest } from "./httpRequest.type.js";
|
|
5
|
+
/**
|
|
6
|
+
* Creates a simple test HTTP request without the builder pattern.
|
|
7
|
+
*
|
|
8
|
+
* `query` and `params` are copied, so mutating the object passed in after the
|
|
9
|
+
* call does not change the request (the builder already behaved this way).
|
|
10
|
+
*
|
|
11
|
+
* @param method - HTTP method.
|
|
12
|
+
* @param path - Request path.
|
|
13
|
+
* @param options - Optional headers, query, params, and body.
|
|
14
|
+
* @returns A TestHTTPRequest instance.
|
|
15
|
+
*/
|
|
16
|
+
export declare function createHTTPRequest(method: HTTPMethod, path: string, options?: HTTPRequestOptions): TestHTTPRequest;
|
|
17
|
+
//# sourceMappingURL=httpRequest.factory.d.ts.map
|