-
-
Notifications
You must be signed in to change notification settings - Fork 40
Expand file tree
/
Copy pathnode-handler.test.ts
More file actions
115 lines (104 loc) · 3.59 KB
/
node-handler.test.ts
File metadata and controls
115 lines (104 loc) · 3.59 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
import { once } from "node:events";
import { getRandomPort } from "get-port-please";
import { afterEach, beforeEach, expect, test } from "vitest";
import { WebSocketServer } from "ws";
import WebSocket from "ws";
import { fromNodeUpgradeHandler } from "../src/node-handler.ts";
import { serve } from "../src/server/node.ts";
type ServeReturn = ReturnType<typeof serve>;
let currentServer: ServeReturn | undefined;
let currentWss: WebSocketServer | undefined;
let unhandled: unknown[] = [];
function onUnhandled(err: unknown) {
unhandled.push(err);
}
beforeEach(() => {
unhandled = [];
process.on("unhandledRejection", onUnhandled);
process.on("uncaughtException", onUnhandled);
});
afterEach(async () => {
process.off("unhandledRejection", onUnhandled);
process.off("uncaughtException", onUnhandled);
currentWss?.close();
await currentServer?.close(true);
currentServer = undefined;
currentWss = undefined;
// Give any stray async errors a tick to surface before asserting.
await new Promise((r) => setImmediate(r));
if (unhandled.length > 0) {
throw new AggregateError(
unhandled as Error[],
`Unexpected unhandled errors during test: ${unhandled
.map((e) => (e as Error)?.message ?? String(e))
.join("; ")}`,
);
}
});
test("fromNodeUpgradeHandler delegates upgrade to a ws.WebSocketServer", async () => {
const wss = new WebSocketServer({ noServer: true });
currentWss = wss;
const receivedOnUpstream: string[] = [];
wss.on("connection", (ws) => {
ws.on("message", (data) => {
receivedOnUpstream.push(data.toString());
ws.send(`echo:${data.toString()}`);
});
});
const port = await getRandomPort("localhost");
const server = serve({
port,
hostname: "127.0.0.1",
fetch: () => new Response("ok"),
websocket: fromNodeUpgradeHandler((req, socket, head) => {
wss.handleUpgrade(req, socket, head, (ws) => {
wss.emit("connection", ws, req);
});
}),
});
currentServer = server;
await server.ready();
const client = new WebSocket(`ws://127.0.0.1:${port}/`);
await once(client, "open");
client.send("hello");
const [reply] = await once(client, "message");
expect(reply.toString()).toBe("echo:hello");
expect(receivedOnUpstream).toEqual(["hello"]);
client.close();
await once(client, "close");
});
test("fromNodeUpgradeHandler does not invoke node adapter's own handleUpgrade", async () => {
// If the handoff sentinel is ignored, the node adapter would try to run
// ws.handleUpgrade on a socket that the user's handler has already taken
// over — the client would see a connection failure or duplicate upgrade.
// This test catches that regression by asserting the upstream handler is
// the *only* thing that opens the WebSocket.
const wss = new WebSocketServer({ noServer: true });
currentWss = wss;
let upstreamOpens = 0;
wss.on("connection", (ws) => {
upstreamOpens++;
ws.on("message", (data) => ws.send(data));
});
const port = await getRandomPort("localhost");
const server = serve({
port,
hostname: "127.0.0.1",
fetch: () => new Response("ok"),
websocket: fromNodeUpgradeHandler((req, socket, head) => {
wss.handleUpgrade(req, socket, head, (ws) => {
wss.emit("connection", ws, req);
});
}),
});
currentServer = server;
await server.ready();
const client = new WebSocket(`ws://127.0.0.1:${port}/`);
await once(client, "open");
client.send("ping");
const [reply] = await once(client, "message");
expect(reply.toString()).toBe("ping");
expect(upstreamOpens).toBe(1);
client.close();
await once(client, "close");
});