MiniSearch / server /downloadFileFromHuggingFaceRepository.test.ts
github-actions[bot]
Sync from https://github.com/felladrin/MiniSearch
6ace587
Raw
History Blame Contribute Delete
4.17 kB
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import { downloadFile, fileDownloadInfo } from "@huggingface/hub";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { downloadFileFromHuggingFaceRepository } from "./downloadFileFromHuggingFaceRepository";
vi.mock("@huggingface/hub", () => ({
downloadFile: vi.fn(),
fileDownloadInfo: vi.fn(),
}));
const REPO = "jinaai/jina-reranker-v1-tiny-en";
const REPO_FILE = "onnx/model.onnx";
const REMOTE_CONTENT = "complete model bytes";
let temporaryDirectory: string;
let localFilePath: string;
function serveRemoteFile(content = REMOTE_CONTENT) {
vi.mocked(fileDownloadInfo).mockResolvedValue({
size: Buffer.byteLength(REMOTE_CONTENT),
etag: "etag",
url: `https://huggingface.co/${REPO}/resolve/main/${REPO_FILE}`,
});
vi.mocked(downloadFile).mockImplementation(async () => new Blob([content]));
}
function download(filePath = localFilePath) {
return downloadFileFromHuggingFaceRepository(REPO, REPO_FILE, filePath);
}
function listDirectory(directory = path.dirname(localFilePath)) {
return fs.existsSync(directory) ? fs.readdirSync(directory).sort() : [];
}
beforeEach(() => {
vi.clearAllMocks();
temporaryDirectory = fs.mkdtempSync(path.join(os.tmpdir(), "hf-download-"));
localFilePath = path.join(temporaryDirectory, "onnx", "model.onnx");
});
afterEach(() => {
vi.restoreAllMocks();
fs.rmSync(temporaryDirectory, { recursive: true, force: true });
});
describe("downloadFileFromHuggingFaceRepository", () => {
it("downloads a file that is not cached yet", async () => {
serveRemoteFile();
await download();
expect(fs.readFileSync(localFilePath, "utf8")).toBe(REMOTE_CONTENT);
expect(listDirectory()).toEqual(["model.onnx"]);
});
it("keeps a cached file whose size matches the repository", async () => {
serveRemoteFile();
fs.mkdirSync(path.dirname(localFilePath), { recursive: true });
fs.writeFileSync(localFilePath, REMOTE_CONTENT);
await download();
expect(downloadFile).not.toHaveBeenCalled();
});
it("only downloads the missing file when a sibling is already cached", async () => {
serveRemoteFile();
const cachedFilePath = path.join(temporaryDirectory, "tokenizer.json");
fs.writeFileSync(cachedFilePath, REMOTE_CONTENT);
await download(cachedFilePath);
await download();
expect(downloadFile).toHaveBeenCalledTimes(1);
expect(fs.readFileSync(localFilePath, "utf8")).toBe(REMOTE_CONTENT);
});
it("replaces a truncated cached file instead of trusting it", async () => {
serveRemoteFile();
fs.mkdirSync(path.dirname(localFilePath), { recursive: true });
fs.writeFileSync(localFilePath, REMOTE_CONTENT.slice(0, 5));
await download();
expect(downloadFile).toHaveBeenCalledTimes(1);
expect(fs.readFileSync(localFilePath, "utf8")).toBe(REMOTE_CONTENT);
});
it("keeps a cached file when the repository metadata is unreachable", async () => {
vi.mocked(fileDownloadInfo).mockRejectedValue(new Error("offline"));
fs.mkdirSync(path.dirname(localFilePath), { recursive: true });
fs.writeFileSync(localFilePath, REMOTE_CONTENT.slice(0, 5));
await download();
expect(downloadFile).not.toHaveBeenCalled();
expect(fs.readFileSync(localFilePath, "utf8")).toBe(
REMOTE_CONTENT.slice(0, 5),
);
});
it("writes nothing when the response is shorter than the expected size", async () => {
serveRemoteFile("truncated");
await expect(download()).rejects.toThrow(/9 bytes instead of 20/);
expect(listDirectory()).toEqual([]);
});
it("leaves no partial file behind when the disk fills up mid-write", async () => {
serveRemoteFile();
vi.spyOn(fs, "writeFileSync").mockImplementation((filePath) => {
const fileDescriptor = fs.openSync(filePath as string, "w");
fs.writeSync(fileDescriptor, REMOTE_CONTENT.slice(0, 5));
fs.closeSync(fileDescriptor);
throw new Error("ENOSPC: no space left on device");
});
await expect(download()).rejects.toThrow("ENOSPC");
expect(listDirectory()).toEqual([]);
});
});