import type { FileDiffMetadata } from "@pierre/diffs";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { getPatchStream, startPatchStream } from "#lib/api/patchStream.ts";
const streamPatchInWorker = vi.hoisted(() => vi.fn());
vi.mock("#lib/components/repo/diffRpc.ts", () => ({ streamPatchInWorker }));
const file = (name: string, prevName?: string) =>
({ name, prevName, hunks: [] }) as unknown as FileDiffMetadata;
describe("startPatchStream", () => {
beforeEach(() => {
streamPatchInWorker.mockReset();
});
it("indexes every landed file by its name and by the name it had before", async () => {
streamPatchInWorker.mockImplementation(
async (_url: string, land: (files: FileDiffMetadata[]) => void) => {
land([file("src/new.ts", "src/old.ts")]);
}
);
const stream = startPatchStream("/patch");
await expect(stream.done).resolves.toBe(true);
expect(stream.byName.get("src/new.ts")).toBe(stream.byName.get("src/old.ts"));
});
it("reports a failed patch instead of throwing at whoever waits for it", async () => {
streamPatchInWorker.mockRejectedValue(new Error("mirror is down"));
await expect(startPatchStream("/patch").done).resolves.toBe(false);
});
it("drops batches that land after the reader walked away", async () => {
let land: (files: FileDiffMetadata[]) => void = () => undefined;
streamPatchInWorker.mockImplementation(
(_url: string, listener: (files: FileDiffMetadata[]) => void) => {
land = listener;
return new Promise<void>(() => undefined);
}
);
const stream = startPatchStream("/patch");
const seen: FileDiffMetadata[][] = [];
stream.subscribe((files) => seen.push(files));
stream.stop();
land([file("src/late.ts")]);
expect(seen).toEqual([]);
expect(stream.byName.size).toBe(0);
});
});
describe("getPatchStream", () => {
beforeEach(() => {
streamPatchInWorker.mockReset();
streamPatchInWorker.mockImplementation(() => new Promise<void>(() => undefined));
});
it("shares one download between every caller asking for the same patch", () => {
expect(getPatchStream("/shared")).toBe(getPatchStream("/shared"));
});
it("keeps the last couple of patches and stops the ones before them", () => {
const first = getPatchStream("/first");
const stopped = vi.spyOn(first, "stop");
getPatchStream("/second");
getPatchStream("/third");
expect(stopped).toHaveBeenCalled();
expect(getPatchStream("/first")).not.toBe(first);
});
it("drops an oversized patch once a newer one is requested, unless a view listens to it", async () => {
const sized = (name: string, lines: number) =>
({ ...file(name), unifiedLineCount: lines }) as FileDiffMetadata;
let landHuge: (files: FileDiffMetadata[]) => void = () => undefined;
streamPatchInWorker.mockImplementationOnce(
(_url: string, land: (files: FileDiffMetadata[]) => void) => {
landHuge = land;
return new Promise<void>(() => undefined);
}
);
const huge = getPatchStream("/huge");
landHuge([sized("big.lock", 50_000)]);
const listened = getPatchStream("/listened");
listened.subscribe(() => undefined);
getPatchStream("/small");
expect(getPatchStream("/huge")).not.toBe(huge);
expect(getPatchStream("/listened")).toBe(listened);
});
});