Files
ReadaBook/apps/api/src/libraries/library-path.test.ts
Git Agent 7b72cc0d83 fix(api): chemins de bibliothèques — validation dédiée, alias et unicité
Validation des chemins externalisée (library-path) avec codes d'erreur
explicites, support d'alias LIBRARY_PATH_ALIASES pour traduire un chemin
hôte vers le montage conteneur, rejet des doublons de chemin entre
bibliothèques, documentation README/.env.example et docker-compose
paramétrable via READABOOK_LIBRARY_HOST_PATH.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-23 16:23:57 +02:00

50 lines
1.7 KiB
TypeScript

import { closeSync, existsSync, mkdtempSync, openSync, rmSync } from "node:fs";
import { tmpdir } from "node:os";
import { join, resolve } from "node:path";
import { afterEach, describe, expect, it } from "vitest";
import { LibraryPathValidationError, resolveLibraryPath } from "./library-path.js";
const tempDirs: string[] = [];
const realBooksPath = "/home/anthony/Documents/Projects/ReadaBook/Books";
afterEach(() => {
for (const dir of tempDirs.splice(0)) {
rmSync(dir, { recursive: true, force: true });
}
});
describe("library path resolution", () => {
it("resolves a readable directory", () => {
const dir = mkdtempSync(join(tmpdir(), "readabook-library-"));
tempDirs.push(dir);
expect(resolveLibraryPath(dir)).toBe(resolve(dir));
});
it("maps a host path alias to the mounted container path", () => {
const hostRoot = "/host/project/Books";
const mountedRoot = mkdtempSync(join(tmpdir(), "readabook-mounted-books-"));
tempDirs.push(mountedRoot);
expect(resolveLibraryPath(hostRoot, [{ from: hostRoot, to: mountedRoot }])).toBe(resolve(mountedRoot));
});
it("rejects regular files with a stable code", () => {
const dir = mkdtempSync(join(tmpdir(), "readabook-library-"));
tempDirs.push(dir);
const file = join(dir, "book.epub");
closeSync(openSync(file, "w"));
expect(() => resolveLibraryPath(file)).toThrowError(LibraryPathValidationError);
try {
resolveLibraryPath(file);
} catch (error) {
expect(error).toMatchObject({ code: "LIBRARY_PATH_NOT_DIRECTORY" });
}
});
it.runIf(existsSync(realBooksPath))("accepts the real Books corpus path used by QA", () => {
expect(resolveLibraryPath(realBooksPath)).toBe(resolve(realBooksPath));
});
});