fix(qa-channel): settle aborted bus polls

This commit is contained in:
Vincent Koc
2026-05-04 09:07:21 -07:00
parent 6c2573e37a
commit 9008031e96
2 changed files with 46 additions and 1 deletions

View File

@@ -1,4 +1,5 @@
import { createServer } from "node:http";
import { setTimeout as sleep } from "node:timers/promises";
import { afterEach, describe, expect, it } from "vitest";
import { buildQaTarget, getQaBusState, parseQaTarget, pollQaBus } from "./bus-client.js";
@@ -69,6 +70,48 @@ describe("qa-bus client", () => {
).rejects.toThrow(SyntaxError);
});
it("rejects immediately when a poll request is aborted", async () => {
const server = createServer((_req, _res) => {
// Keep the request open so the client abort path owns the outcome.
});
await new Promise<void>((resolve, reject) => {
server.once("error", reject);
server.listen(0, "127.0.0.1", () => resolve());
});
const address = server.address();
if (!address || typeof address === "string") {
throw new Error("test server failed to bind");
}
stops.push(async () => {
server.closeAllConnections?.();
await new Promise<void>((resolve, reject) => {
server.close((error) => (error ? reject(error) : resolve()));
});
});
const abort = new AbortController();
const request = pollQaBus({
baseUrl: `http://127.0.0.1:${address.port}`,
accountId: "acct-a",
cursor: 0,
timeoutMs: 30_000,
signal: abort.signal,
});
abort.abort();
await expect(
Promise.race([
request,
sleep(500).then(() => {
throw new Error("poll abort did not settle");
}),
]),
).rejects.toMatchObject({ name: "AbortError" });
});
it("preserves baseUrl path prefixes when composing bus URLs", async () => {
const server = await startJsonServer((req) => ({
statusCode: req.url === "/qa-bus/v1/state" ? 200 : 404,

View File

@@ -95,7 +95,9 @@ async function postJson<T>(
);
const onAbort = () => {
request.destroy(abortError());
const error = abortError();
request.destroy(error);
reject(error);
};
signal?.addEventListener("abort", onAbort, { once: true });
request.on("error", (error) => {