import { strict as assert } from "node:assert"; import { mkdtempSync, rmSync, existsSync, readFileSync, readdirSync } from "node:fs"; import { join } from "node:path"; import { tmpdir } from "node:os"; import { test } from "node:test"; import { LIBRARY_LUT_OFFLINE_CODE, RESOLVABLE_PRESET_IDS, freezeLibraryLut, matchColorLook, readBundledLutIndex, } from "./lut-preset-provider.mjs"; import { buildCube } from "./cube-build.mjs"; import { validateCube, validateCubeFile } from "./cube-validate.mjs"; const REPO_ROOT = join(import.meta.dirname, "..", "..", "..", ".."); function corePresetIdsFromSource() { const src = readFileSync(join(REPO_ROOT, "packages/core/src/colorGrading.ts"), "utf8"); const match = src.match(/export type HfColorGradingPresetId =([\s\S]*?);/); assert.ok(match, "core preset union should be readable"); return [...match[1].matchAll(/"([^"]+)"/g)].map((m) => m[1]); } test("warm daylight and warm natural light resolve to the core warm-daylight preset", () => { assert.deepEqual(matchColorLook("warm daylight"), { kind: "preset", preset: "warm-daylight", score: 2, }); assert.equal(matchColorLook("warm natural light").preset, "warm-daylight"); }); test("high contrast punchy resolves to deep-contrast", () => { assert.equal(matchColorLook("high contrast punchy").preset, "deep-contrast"); }); test("removed preset phrases resolve to surviving looks", () => { assert.equal(matchColorLook("natural lift").preset, "soft-boost"); assert.equal(matchColorLook("fresh pop").preset, "bright-pop"); assert.equal(matchColorLook("warm clean").preset, "warm-daylight"); assert.equal(matchColorLook("cool clean").preset, "clean-studio"); }); test("complete-filter intent aliases resolve deterministically", () => { assert.equal(matchColorLook("analog tape").preset, "vhs-playback"); assert.equal(matchColorLook("creator video").preset, "creator-camcorder"); }); test("library look freezes a validated cube from params offline (--local-only)", async () => { const projectDir = mkdtempSync(join(tmpdir(), "mu-lut-provider-")); try { const match = matchColorLook("teal orange blockbuster"); assert.equal(match.kind, "library"); // localOnly forces the deterministic params path (no network); online, the // same look downloads its .cube from the CDN url (via "url"). const frozen = await freezeLibraryLut(match, { projectDir, type: "grade", localOnly: true }); assert.match(frozen.localPath, /^\.media\/luts\/grade_001\.cube$/); assert.ok(existsSync(join(projectDir, frozen.localPath))); assert.equal(validateCubeFile(join(projectDir, frozen.localPath)).ok, true); assert.equal(frozen.lut.src, frozen.localPath); assert.equal(frozen.metadata.provenance.via, "params-fallback"); } finally { rmSync(projectDir, { recursive: true, force: true }); } }); test("every resolver preset exists in packages/core/src/colorGrading.ts", () => { const corePresetIds = corePresetIdsFromSource(); assert.deepEqual( RESOLVABLE_PRESET_IDS.filter((id) => !corePresetIds.includes(id)), [], ); for (const id of RESOLVABLE_PRESET_IDS) { const match = matchColorLook(id); assert.equal(match.kind, "preset"); assert.equal(match.preset, id); } }); test("zero-overlap intent returns no preset or library match", () => { assert.equal(matchColorLook("zqxv imaginary neutron look"), null); }); test("bundled LUT index entries resolve from params or url", () => { for (const entry of readBundledLutIndex()) { assert.ok(entry.id); assert.ok(entry.description); assert.ok(entry.params || entry.url, `${entry.id} should define params or url`); if (entry.params) { assert.equal(typeof entry.params, "object"); assert.equal(validateCube(buildCube(entry.params)).ok, true, `${entry.id} params validate`); } if (entry.url) assert.equal(typeof entry.url, "string"); } }); test("url library entries respect localOnly and freeze through fetch", async () => { const projectDir = mkdtempSync(join(tmpdir(), "mu-lut-url-provider-")); const match = { kind: "library", id: "cdn-look", description: "CDN-hosted look", tags: ["cdn"], intensity: 0.7, url: "https://example.invalid/look.cube", }; const originalFetch = globalThis.fetch; let fetchCalls = 0; try { globalThis.fetch = async () => { fetchCalls++; throw new Error("network should be skipped under localOnly"); }; await assert.rejects( freezeLibraryLut(match, { projectDir, type: "lut", localOnly: true }), (err) => { assert.equal(err.code, LIBRARY_LUT_OFFLINE_CODE); assert.match(err.message, /--local-only/); return true; }, ); assert.equal(fetchCalls, 0); const cube = buildCube({ contrast: 0.1 }); const body = Buffer.from(cube); globalThis.fetch = async (url) => { fetchCalls++; assert.equal(url, match.url); return { ok: true, headers: { get: () => String(body.length) }, body: [body], }; }; const frozen = await freezeLibraryLut(match, { projectDir, type: "lut" }); assert.equal(fetchCalls, 1); assert.match(frozen.localPath, /^\.media\/luts\/lut_001\.cube$/); assert.equal(validateCubeFile(join(projectDir, frozen.localPath)).ok, true); assert.equal(frozen.metadata.provenance.via, "url"); } finally { globalThis.fetch = originalFetch; rmSync(projectDir, { recursive: true, force: true }); } }); test("failed URL library freeze releases its reservation", async () => { const projectDir = mkdtempSync(join(tmpdir(), "mu-lut-failure-")); const originalFetch = globalThis.fetch; try { globalThis.fetch = async () => ({ ok: true, headers: { get: () => "12" }, body: [Buffer.from("not a cube\n")], }); await assert.rejects( freezeLibraryLut( { kind: "library", id: "broken-cdn-look", description: "Broken CDN look", tags: ["broken"], intensity: 1, url: "https://example.com/broken.cube", }, { projectDir, type: "lut" }, ), /failed to freeze library LUT/, ); assert.deepStrictEqual(readdirSync(join(projectDir, ".media/luts")), []); } finally { globalThis.fetch = originalFetch; rmSync(projectDir, { recursive: true, force: true }); } });