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>
50 lines
1.7 KiB
TypeScript
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));
|
|
});
|
|
});
|