On a Windows host a burst of test traffic can drop a loopback connect now and then (every port, any program — measured on the dev desktop). The browser resends it and only waits; Node (libuv) sends a loopback SYN once and fails at ~300 ms with "connect ETIMEDOUT". `e2e/retryConnect.ts` wraps Playwright's request context so that one error is tried again (0.5 / 1 / 2 / 4 / 8 s, the system's own retry waits) while every other failure goes straight to the test; global.setup's health check, login and /api/me and speed.measure's share probe go through it. Unit-tested; e2e 61/61.
42 lines
1.7 KiB
TypeScript
42 lines
1.7 KiB
TypeScript
// The e2e suite's request wrapper (e2e/retryConnect.ts): a connect that never came about is tried
|
|
// again, as every TCP stack would; any other failure, or an answer, goes straight to the test.
|
|
import { describe, expect, it, vi } from "vitest";
|
|
import { retryConnect } from "../../e2e/retryConnect";
|
|
|
|
const noConnection = () => new Error("apiRequestContext.get: connect ETIMEDOUT 127.0.0.1:8080");
|
|
|
|
describe("retryConnect", () => {
|
|
it("tries a connect that timed out again, and hands back the answer that came", async () => {
|
|
vi.useFakeTimers();
|
|
const get = vi
|
|
.fn()
|
|
.mockRejectedValueOnce(noConnection())
|
|
.mockRejectedValueOnce(noConnection())
|
|
.mockResolvedValue("ok");
|
|
const api = retryConnect({ get });
|
|
const answer = api.get("/healthz");
|
|
await vi.runAllTimersAsync();
|
|
await expect(answer).resolves.toBe("ok");
|
|
expect(get).toHaveBeenCalledTimes(3);
|
|
expect(get).toHaveBeenLastCalledWith("/healthz");
|
|
vi.useRealTimers();
|
|
});
|
|
|
|
it("passes any other failure straight on, untried", async () => {
|
|
const post = vi.fn().mockRejectedValue(new Error("read ECONNRESET"));
|
|
await expect(retryConnect({ post }).post("/auth/password-login")).rejects.toThrow("ECONNRESET");
|
|
expect(post).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
it("gives up after its last wait, with the connect error", async () => {
|
|
vi.useFakeTimers();
|
|
const get = vi.fn().mockRejectedValue(noConnection());
|
|
const answer = retryConnect({ get }).get("/api/me");
|
|
const settled = expect(answer).rejects.toThrow("ETIMEDOUT");
|
|
await vi.runAllTimersAsync();
|
|
await settled;
|
|
expect(get).toHaveBeenCalledTimes(6); // the first try and five more
|
|
vi.useRealTimers();
|
|
});
|
|
});
|