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>
This commit is contained in:
49
apps/api/src/libraries/library-path.test.ts
Normal file
49
apps/api/src/libraries/library-path.test.ts
Normal file
@ -0,0 +1,49 @@
|
||||
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));
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user