import assert from "node:assert/strict"; import test from "node:test"; import { validateNativeAlphaResourceCache } from "./native-alpha-cache-validation.mjs"; const sha = "a".repeat(64); const toolchain = { converterSha256: "b".repeat(64), exporterSha256: "c".repeat(64), nativeAlphaPolicySha256: "d".repeat(64), }; function report() { return { sourceModel: "model\\element\\dungeon\\waterfall.ros", sourceResourceSha256: sha, materials: [{ name: "runewaker_material_9", sourceTexture: "texture\\dungeon\\gate_waterfall", sourceTextureChannels: [{ name: "diffuse", texture: "texture\\dungeon\\gate_waterfall" }], alphaBlendParameters: [ { name: "alphaTestMode", value: 0 }, { name: "alphaMode", value: 0 }, { name: "translucent", value: true }, { name: "alpha", value: 1 }, { name: "alphaModifier", value: 1 }, ], }], }; } function input(overrides = {}) { const rawReport = report(); return { manifest: { source: { resource: "model/element/dungeon/waterfall.ros", sha256: sha }, toolchain, }, rawReport, preparedReport: structuredClone(rawReport), expectedResource: "model/element/dungeon/waterfall.ros", expectedSourceSha256: sha, expectedToolchain: toolchain, ...overrides, }; } test("resource cache accepts matching raw/prepared native alpha provenance", () => { const result = validateNativeAlphaResourceCache(input()); assert.deepEqual(result, { valid: true, errors: [], nativeMaterialCount: 1 }); }); test("resource cache rejects old missing toolchain and source hash stamps", () => { const options = input(); delete options.manifest.toolchain; delete options.rawReport.sourceResourceSha256; delete options.preparedReport.sourceResourceSha256; const result = validateNativeAlphaResourceCache(options); assert.equal(result.valid, false); assert.ok(result.errors.some((error) => error.includes("toolchain exporterSha256"))); assert.ok(result.errors.some((error) => error.includes("raw source resource SHA-256"))); assert.ok(result.errors.some((error) => error.includes("prepared source resource SHA-256"))); }); test("resource cache rejects native material rows missing alpha flags or source-channel joins", () => { const options = input(); options.rawReport.materials[0].alphaBlendParameters = []; options.preparedReport.materials[0].alphaBlendParameters = []; options.rawReport.materials[0].sourceTextureChannels = []; options.preparedReport.materials[0].sourceTextureChannels = []; const result = validateNativeAlphaResourceCache(options); assert.equal(result.valid, false); assert.ok(result.errors.some((error) => error.includes("lacks native alpha parameter alphaMode"))); assert.ok(result.errors.some((error) => error.includes("diffuse channel does not match"))); }); test("resource cache rejects raw/prepared report drift even when stamps match", () => { const options = input(); options.preparedReport.materials[0].alphaBlendParameters[2].value = false; const result = validateNativeAlphaResourceCache(options); assert.equal(result.valid, false); assert.ok(result.errors.includes("prepared material rows differ from the raw native exporter report")); }); test("resource cache cannot accept cutout material flags without the source cutoff", () => { const options = input(); for (const report of [options.rawReport, options.preparedReport]) { report.materials[0].alphaBlendParameters[0].value = 1; } assert.equal(validateNativeAlphaResourceCache(options).valid, false); for (const report of [options.rawReport, options.preparedReport]) { report.materials[0].alphaBlendParameters.push({ name: "alphaTestValue", value: 192 }); } assert.equal(validateNativeAlphaResourceCache(options).valid, true); });