Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 52 additions & 6 deletions packages/engine/src/utils/urlDownloader.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,14 +112,40 @@ function isoBmffMediaBytes(marker: string): Buffer {
return Buffer.concat([ftyp, Buffer.from(marker)]);
}

// Fake only Date: downloadToTemp's deadlines read Date.now() around sync work, so a slow
// runner can trip a false timeout with no timer-driven delay. Real timers keep firing, so
// setTimeout-based races are unaffected.
// Fake Date, and hold the 1_000 ms attempt deadline every test passes: on a slow runner one
// attempt (hash, fsync, refetch) can outlast it and throw "Download timeout". A test that
// needs the deadline calls fireAttemptDeadline(). Other timers (cache-lock poll, test
// delays, the 20 ms stalled-body test) stay real.
const ATTEMPT_DEADLINE_MS = 1_000;
const heldDeadlines = new Map<ReturnType<typeof setTimeout>, () => void>();
let realSetTimeout: typeof setTimeout;

function fireAttemptDeadline(): void {
for (const [handle, fire] of [...heldDeadlines]) {
heldDeadlines.delete(handle);
clearTimeout(handle);
fire();
}
}

beforeEach(() => {
vi.useFakeTimers({ toFake: ["Date"] });
realSetTimeout = globalThis.setTimeout;
vi.spyOn(globalThis, "setTimeout").mockImplementation(((
handler: () => void,
ms?: number,
...args: unknown[]
) => {
if (ms !== ATTEMPT_DEADLINE_MS) return realSetTimeout(handler, ms, ...args);
const handle = realSetTimeout(() => undefined, 2 ** 31 - 1);
heldDeadlines.set(handle, handler);
return handle;
}) as typeof setTimeout);
});

afterEach(() => {
vi.mocked(globalThis.setTimeout).mockRestore();
heldDeadlines.clear();
vi.useRealTimers();
vi.unstubAllGlobals();
fsRaceControls.deleteBeforeLstatPath = undefined;
Expand Down Expand Up @@ -307,6 +333,23 @@ describe("fetchPublicHttpsText", () => {
});

describe("downloadToTemp atomic publication and bounded retry", () => {
it("does not race a real wall-clock deadline on a slow attempt", async () => {
vi.stubGlobal(
"fetch",
vi.fn().mockImplementation(
(_url: string, init: RequestInit) =>
new Promise<Response>((resolve, reject) => {
init.signal?.addEventListener("abort", () => reject(new Error("aborted")), {
once: true,
});
setTimeout(() => resolve(new Response("complete")), 1_200);
}),
),
);
const path = await downloadToTemp("https://cdn.example/slow.mp4", makeTempDir(), 1_000);
expect(readFileSync(path, "utf8")).toBe("complete");
});

it("follows a bounded redirect only after validating the next public HTTPS hop", async () => {
const fetchMock = vi
.fn()
Expand Down Expand Up @@ -1485,12 +1528,15 @@ describe("downloadToTemp atomic publication and bounded retry", () => {
1_000,
secondController.signal,
);
firstController.abort();

await expect(first).rejects.toMatchObject({
const firstRejected = expect(first).rejects.toMatchObject({
kind: "cancelled",
retryable: false,
} satisfies Partial<UrlDownloadError>);
firstController.abort();
await vi.waitFor(() => expect(fetchMock).toHaveBeenCalledTimes(1));
fireAttemptDeadline();

await firstRejected;
const path = await second;
expect(readFileSync(path, "utf8")).toBe("complete");
expect(fetchMock).toHaveBeenCalledTimes(2);
Expand Down
Loading