diff --git a/extensions/qa-matrix/src/substrate/request.test.ts b/extensions/qa-matrix/src/substrate/request.test.ts new file mode 100644 index 000000000000..4bc55b3f29c4 --- /dev/null +++ b/extensions/qa-matrix/src/substrate/request.test.ts @@ -0,0 +1,27 @@ +import { afterEach, describe, expect, it, vi } from "vitest"; +import { requestMatrixJson, type MatrixQaFetchLike } from "./request.js"; + +const MAX_TIMER_TIMEOUT_MS = 2_147_000_000; + +describe("requestMatrixJson", () => { + afterEach(() => { + vi.restoreAllMocks(); + }); + + it("caps oversized request timeouts before creating the abort signal", async () => { + const signal = AbortSignal.abort(); + const timeoutSpy = vi.spyOn(AbortSignal, "timeout").mockReturnValue(signal); + const fetchImpl = vi.fn(async () => Response.json({ ok: true })); + + await requestMatrixJson({ + baseUrl: "https://matrix.example.test", + endpoint: "/_matrix/client/v3/account/whoami", + fetchImpl, + method: "GET", + timeoutMs: MAX_TIMER_TIMEOUT_MS + 1_000_000, + }); + + expect(timeoutSpy).toHaveBeenCalledWith(MAX_TIMER_TIMEOUT_MS); + expect(fetchImpl).toHaveBeenCalledWith(expect.any(URL), expect.objectContaining({ signal })); + }); +}); diff --git a/extensions/qa-matrix/src/substrate/request.ts b/extensions/qa-matrix/src/substrate/request.ts index f8990c385ee9..5adf728eb104 100644 --- a/extensions/qa-matrix/src/substrate/request.ts +++ b/extensions/qa-matrix/src/substrate/request.ts @@ -1,3 +1,5 @@ +import { resolveTimerTimeoutMs } from "openclaw/plugin-sdk/number-runtime"; + export type MatrixQaFetchLike = typeof fetch; type MatrixQaRequestResult = { @@ -30,7 +32,7 @@ export async function requestMatrixJson(params: { ...(params.accessToken ? { authorization: `Bearer ${params.accessToken}` } : {}), }, ...(params.body !== undefined ? { body: JSON.stringify(params.body) } : {}), - signal: AbortSignal.timeout(params.timeoutMs ?? 20_000), + signal: AbortSignal.timeout(resolveTimerTimeoutMs(params.timeoutMs, 20_000)), }); let body: unknown = {}; try {