From 37d5320f6bdb9a56e4ccbb377ddea75e16ef0a1f Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sat, 21 Feb 2026 23:00:23 +0000 Subject: [PATCH] test: tighten canvas host websocket watchdog timeouts --- src/canvas-host/server.test.ts | 112 ++++++++++++++++++--------------- 1 file changed, 63 insertions(+), 49 deletions(-) diff --git a/src/canvas-host/server.test.ts b/src/canvas-host/server.test.ts index 616c6a902b7..db4dc13354f 100644 --- a/src/canvas-host/server.test.ts +++ b/src/canvas-host/server.test.ts @@ -18,6 +18,10 @@ const chokidarMockState = vi.hoisted(() => ({ }>, })); +const CANVAS_WS_OPEN_TIMEOUT_MS = 2_000; +const CANVAS_RELOAD_TIMEOUT_MS = 4_000; +const CANVAS_RELOAD_TEST_TIMEOUT_MS = 12_000; + // Tests: avoid chokidar polling/fsevents; trigger "all" events manually. vi.mock("chokidar", () => { const createWatcher = () => { @@ -194,59 +198,69 @@ describe("canvas host", () => { } }); - it("serves HTML with injection and broadcasts reload on file changes", async () => { - const dir = await createCaseDir(); - const index = path.join(dir, "index.html"); - await fs.writeFile(index, "v1", "utf8"); + it( + "serves HTML with injection and broadcasts reload on file changes", + async () => { + const dir = await createCaseDir(); + const index = path.join(dir, "index.html"); + await fs.writeFile(index, "v1", "utf8"); - const watcherStart = chokidarMockState.watchers.length; - const server = await startCanvasHost({ - runtime: quietRuntime, - rootDir: dir, - port: 0, - listenHost: "127.0.0.1", - allowInTests: true, - }); - - try { - const watcher = chokidarMockState.watchers[watcherStart]; - expect(watcher).toBeTruthy(); - - const res = await fetch(`http://127.0.0.1:${server.port}${CANVAS_HOST_PATH}/`); - const html = await res.text(); - expect(res.status).toBe(200); - expect(html).toContain("v1"); - expect(html).toContain(CANVAS_WS_PATH); - - const ws = new WebSocket(`ws://127.0.0.1:${server.port}${CANVAS_WS_PATH}`); - await new Promise((resolve, reject) => { - const timer = setTimeout(() => reject(new Error("ws open timeout")), 5000); - ws.on("open", () => { - clearTimeout(timer); - resolve(); - }); - ws.on("error", (err) => { - clearTimeout(timer); - reject(err); - }); + const watcherStart = chokidarMockState.watchers.length; + const server = await startCanvasHost({ + runtime: quietRuntime, + rootDir: dir, + port: 0, + listenHost: "127.0.0.1", + allowInTests: true, }); - const msg = new Promise((resolve, reject) => { - const timer = setTimeout(() => reject(new Error("reload timeout")), 10_000); - ws.on("message", (data) => { - clearTimeout(timer); - resolve(rawDataToString(data)); - }); - }); + try { + const watcher = chokidarMockState.watchers[watcherStart]; + expect(watcher).toBeTruthy(); - await fs.writeFile(index, "v2", "utf8"); - watcher.__emit("all", "change", index); - expect(await msg).toBe("reload"); - ws.close(); - } finally { - await server.close(); - } - }, 20_000); + const res = await fetch(`http://127.0.0.1:${server.port}${CANVAS_HOST_PATH}/`); + const html = await res.text(); + expect(res.status).toBe(200); + expect(html).toContain("v1"); + expect(html).toContain(CANVAS_WS_PATH); + + const ws = new WebSocket(`ws://127.0.0.1:${server.port}${CANVAS_WS_PATH}`); + await new Promise((resolve, reject) => { + const timer = setTimeout( + () => reject(new Error("ws open timeout")), + CANVAS_WS_OPEN_TIMEOUT_MS, + ); + ws.on("open", () => { + clearTimeout(timer); + resolve(); + }); + ws.on("error", (err) => { + clearTimeout(timer); + reject(err); + }); + }); + + const msg = new Promise((resolve, reject) => { + const timer = setTimeout( + () => reject(new Error("reload timeout")), + CANVAS_RELOAD_TIMEOUT_MS, + ); + ws.on("message", (data) => { + clearTimeout(timer); + resolve(rawDataToString(data)); + }); + }); + + await fs.writeFile(index, "v2", "utf8"); + watcher.__emit("all", "change", index); + expect(await msg).toBe("reload"); + ws.close(); + } finally { + await server.close(); + } + }, + CANVAS_RELOAD_TEST_TIMEOUT_MS, + ); it("serves A2UI scaffold and blocks traversal/symlink escapes", async () => { const dir = await createCaseDir();