forked from Mike/freno-dev
94 lines
2.9 KiB
TypeScript
94 lines
2.9 KiB
TypeScript
/**
|
|
* Unit tests for the shared `downloadAsset` helper.
|
|
*
|
|
* The helper is a pure function over an injected `DownloadApi` + redirect sink,
|
|
* so these tests verify the tRPC call shape, redirect, and error handling
|
|
* without importing `~/lib/api` (which pulls in solid-js / CSRF cookie code).
|
|
*/
|
|
import { describe, it, expect, mock } from "bun:test";
|
|
import { downloadAsset, type DownloadApi } from "~/lib/download-asset";
|
|
|
|
function makeFakeApi(
|
|
url: string,
|
|
seen: { input: { asset_name: string } }[] = []
|
|
): DownloadApi {
|
|
return {
|
|
downloads: {
|
|
getDownloadUrl: {
|
|
query: async (input: { asset_name: string }) => {
|
|
seen.push({ input });
|
|
return { downloadURL: url };
|
|
}
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
describe("downloadAsset", () => {
|
|
it("queries getDownloadUrl with the provided asset_name", async () => {
|
|
const seen: { input: { asset_name: string } }[] = [];
|
|
const api = makeFakeApi("https://s3/gaze.dmg", seen);
|
|
await downloadAsset({ api, assetName: "gaze", redirect: () => {} });
|
|
expect(seen).toHaveLength(1);
|
|
expect(seen[0]!.input.asset_name).toBe("gaze");
|
|
});
|
|
|
|
it("redirects to the returned signed URL", async () => {
|
|
const api = makeFakeApi("https://s3/gaze.dmg");
|
|
const sink = mock((u: string) => {});
|
|
await downloadAsset({ api, assetName: "gaze", redirect: sink });
|
|
expect(sink).toHaveBeenCalledTimes(1);
|
|
expect(sink).toHaveBeenCalledWith("https://s3/gaze.dmg");
|
|
});
|
|
|
|
it("routes failures to onError without throwing", async () => {
|
|
const api: DownloadApi = {
|
|
downloads: {
|
|
getDownloadUrl: {
|
|
query: async () => {
|
|
throw new Error("boom");
|
|
}
|
|
}
|
|
}
|
|
};
|
|
const sink = mock((u: string) => {});
|
|
const errSink = mock((e: unknown) => {});
|
|
await expect(
|
|
downloadAsset({
|
|
api,
|
|
assetName: "gaze",
|
|
redirect: sink,
|
|
onError: errSink
|
|
})
|
|
).resolves.toBeUndefined();
|
|
expect(sink).not.toHaveBeenCalled();
|
|
expect(errSink).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
it("swallows errors silently when no onError is provided", async () => {
|
|
const api: DownloadApi = {
|
|
downloads: {
|
|
getDownloadUrl: {
|
|
query: async () => {
|
|
throw new Error("boom");
|
|
}
|
|
}
|
|
}
|
|
};
|
|
const sink = mock((u: string) => {});
|
|
await expect(
|
|
downloadAsset({ api, assetName: "gaze", redirect: sink })
|
|
).resolves.toBeUndefined();
|
|
expect(sink).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("works for any asset name (not hard-coded to gaze)", async () => {
|
|
const seen: { input: { asset_name: string } }[] = [];
|
|
const api = makeFakeApi("https://s3/inputhalo.dmg", seen);
|
|
const sink = mock((u: string) => {});
|
|
await downloadAsset({ api, assetName: "inputhalo", redirect: sink });
|
|
expect(seen[0]!.input.asset_name).toBe("inputhalo");
|
|
expect(sink).toHaveBeenCalledWith("https://s3/inputhalo.dmg");
|
|
});
|
|
});
|