From 94a90fcb858b5b8e3bf34a9152765ac74b1dfd90 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sat, 11 Apr 2026 02:45:08 +0100 Subject: [PATCH] test(ci): retry canvas auth reset fetches --- src/gateway/server.canvas-auth.test.ts | 82 +++++++++++++++++++------- 1 file changed, 61 insertions(+), 21 deletions(-) diff --git a/src/gateway/server.canvas-auth.test.ts b/src/gateway/server.canvas-auth.test.ts index 6e99bdb6938..a56276859ca 100644 --- a/src/gateway/server.canvas-auth.test.ts +++ b/src/gateway/server.canvas-auth.test.ts @@ -13,6 +13,33 @@ import { withTempConfig } from "./test-temp-config.js"; const WS_REJECT_TIMEOUT_MS = 2_000; const WS_CONNECT_TIMEOUT_MS = 2_000; + +function isConnectionReset(value: unknown): boolean { + let current: unknown = value; + for (let depth = 0; depth < 4; depth += 1) { + if (!current || typeof current !== "object") { + return false; + } + const record = current as { code?: unknown; cause?: unknown }; + if (record.code === "ECONNRESET") { + return true; + } + current = record.cause; + } + return false; +} + +async function fetchCanvas(input: string, init?: RequestInit): Promise { + try { + return await fetch(input, init); + } catch (err) { + if (isConnectionReset(err)) { + return await fetch(input, init); + } + throw err; + } +} + async function listen( server: ReturnType, host = "127.0.0.1", @@ -225,10 +252,12 @@ describe("gateway canvas host auth", () => { const activeCanvasPath = scopedCanvasPath(activeNodeCapability, `${CANVAS_HOST_PATH}/`); const activeWsPath = scopedCanvasPath(activeNodeCapability, CANVAS_WS_PATH); - const unauthCanvas = await fetch(`http://${host}:${listener.port}${CANVAS_HOST_PATH}/`); + const unauthCanvas = await fetchCanvas( + `http://${host}:${listener.port}${CANVAS_HOST_PATH}/`, + ); expect(unauthCanvas.status).toBe(401); - const malformedScoped = await fetch( + const malformedScoped = await fetchCanvas( `http://${host}:${listener.port}${CANVAS_CAPABILITY_PATH_PREFIX}/broken`, ); expect(malformedScoped.status).toBe(401); @@ -244,7 +273,7 @@ describe("gateway canvas host auth", () => { }), ); - const operatorCapabilityBlocked = await fetch( + const operatorCapabilityBlocked = await fetchCanvas( `http://${host}:${listener.port}${scopedCanvasPath(operatorOnlyCapability, `${CANVAS_HOST_PATH}/`)}`, ); expect(operatorCapabilityBlocked.status).toBe(401); @@ -260,7 +289,7 @@ describe("gateway canvas host auth", () => { }), ); - const expiredCapabilityBlocked = await fetch( + const expiredCapabilityBlocked = await fetchCanvas( `http://${host}:${listener.port}${scopedCanvasPath(expiredNodeCapability, `${CANVAS_HOST_PATH}/`)}`, ); expect(expiredCapabilityBlocked.status).toBe(401); @@ -275,11 +304,13 @@ describe("gateway canvas host auth", () => { }); clients.add(activeNodeClient); - const scopedCanvas = await fetch(`http://${host}:${listener.port}${activeCanvasPath}`); + const scopedCanvas = await fetchCanvas( + `http://${host}:${listener.port}${activeCanvasPath}`, + ); expect(scopedCanvas.status).toBe(200); expect(await scopedCanvas.text()).toBe("ok"); - const scopedA2ui = await fetch( + const scopedA2ui = await fetchCanvas( `http://${host}:${listener.port}${scopedCanvasPath(activeNodeCapability, `${A2UI_PATH}/`)}`, ); expect([200, 503]).toContain(scopedA2ui.status); @@ -288,7 +319,7 @@ describe("gateway canvas host auth", () => { clients.delete(activeNodeClient); - const disconnectedNodeBlocked = await fetch( + const disconnectedNodeBlocked = await fetchCanvas( `http://${host}:${listener.port}${activeCanvasPath}`, ); expect(disconnectedNodeBlocked.status).toBe(401); @@ -315,7 +346,7 @@ describe("gateway canvas host auth", () => { }), ); - const res = await fetch(`http://127.0.0.1:${listener.port}${CANVAS_HOST_PATH}/`); + const res = await fetchCanvas(`http://127.0.0.1:${listener.port}${CANVAS_HOST_PATH}/`); expect(res.status).toBe(401); await expectWsRejected(`ws://127.0.0.1:${listener.port}${CANVAS_WS_PATH}`, {}); @@ -329,10 +360,10 @@ describe("gateway canvas host auth", () => { resolvedAuth: tokenResolvedAuth, handleHttpRequest: allowCanvasHostHttp, run: async ({ listener }) => { - const res = await fetch(`http://127.0.0.1:${listener.port}${CANVAS_HOST_PATH}/`); + const res = await fetchCanvas(`http://127.0.0.1:${listener.port}${CANVAS_HOST_PATH}/`); expect(res.status).toBe(401); - const a2ui = await fetch(`http://127.0.0.1:${listener.port}${A2UI_PATH}/`); + const a2ui = await fetchCanvas(`http://127.0.0.1:${listener.port}${A2UI_PATH}/`); expect(a2ui.status).toBe(401); await expectWsRejected(`ws://127.0.0.1:${listener.port}${CANVAS_WS_PATH}`, {}); @@ -368,7 +399,7 @@ describe("gateway canvas host auth", () => { const canvasPath = scopedCanvasPath(capability, `${CANVAS_HOST_PATH}/`); const wsPath = scopedCanvasPath(capability, CANVAS_WS_PATH); - const scopedCanvas = await fetch(`http://[::1]:${listener.port}${canvasPath}`); + const scopedCanvas = await fetchCanvas(`http://[::1]:${listener.port}${canvasPath}`); expect(scopedCanvas.status).toBe(200); await expectWsConnected(`ws://[::1]:${listener.port}${wsPath}`); @@ -402,14 +433,17 @@ describe("gateway canvas host auth", () => { authorization: "Bearer wrong", "x-forwarded-for": "203.0.113.99", }; - const first = await fetch(`http://127.0.0.1:${listener.port}${CANVAS_HOST_PATH}/`, { + const first = await fetchCanvas(`http://127.0.0.1:${listener.port}${CANVAS_HOST_PATH}/`, { headers, }); expect(first.status).toBe(401); - const second = await fetch(`http://127.0.0.1:${listener.port}${CANVAS_HOST_PATH}/`, { - headers, - }); + const second = await fetchCanvas( + `http://127.0.0.1:${listener.port}${CANVAS_HOST_PATH}/`, + { + headers, + }, + ); expect(second.status).toBe(429); expect(second.headers.get("retry-after")).toBeTruthy(); @@ -444,14 +478,20 @@ describe("gateway canvas host auth", () => { host: "localhost", "x-forwarded-for": "127.0.0.1, 203.0.113.24", }; - const first = await fetch(`http://127.0.0.1:${listener.port}${CANVAS_HOST_PATH}/`, { - headers, - }); + const first = await fetchCanvas( + `http://127.0.0.1:${listener.port}${CANVAS_HOST_PATH}/`, + { + headers, + }, + ); expect(first.status).toBe(401); - const second = await fetch(`http://127.0.0.1:${listener.port}${CANVAS_HOST_PATH}/`, { - headers, - }); + const second = await fetchCanvas( + `http://127.0.0.1:${listener.port}${CANVAS_HOST_PATH}/`, + { + headers, + }, + ); expect(second.status).toBe(429); }, });