import assert from "node:assert/strict"; import test from "node:test"; import { buildOriginNativeMaterialProvenance } from "./origin-native-material-provenance.mjs"; const resources = [ "model\\dungeon\\the_origin\\act_precipice_01.ros", "model\\dungeon\\the_origin\\act_precipice_02.ros", "model\\element\\wailing_mountains\\prop\\waterfall\\act_waterfall02.ros", "model\\element\\elves_island\\vegetation\\gaint_tree_a.ros", "model\\element\\weeping_coast\\vegetation\\n_tree_02.ros", "model\\dungeon\\the_origin\\tree_01_01.ros", "model\\dungeon\\the_origin\\act_cocoon_01.ros", ]; function fixture() { const conversionReports = new Map(); const stagedOverrides = resources.map((resource, index) => { const sha256 = String(index + 1).padStart(64, "0"); conversionReports.set(resource.toLowerCase(), { source: { model: resource, resourceSha256: sha256 }, visual: { materials: { nativeAlpha: { sourceModel: resource, sourceResourceSha256: sha256, materials: [{ sourceModel: resource, sourceResourceSha256: sha256, materialIndex: 1 }], }, }, }, }); return { resource, file: `staging/${index}/visual.glb`, placements: index + 1 }; }); return { conversionReports, assembly: { sourceWdbSha256: "a".repeat(64), sourceWdbReportSha256: "b".repeat(64), primaryResource: "model\\dungeon\\the_origin\\cathedral_01_b.ros", primarySourceSha256: "c".repeat(64), staticPlacementCount: 693, uniqueResourceCount: 220, dynamicPlacementCount: 0, resources: stagedOverrides.map((override, index) => ({ resource: override.resource.replaceAll("\\", "/"), placementCount: override.placements, sourceSha256: String(index + 1).padStart(64, "0"), })), stagedOverrides, assembledVisual: { sha256: "d".repeat(64) }, }, semantics: { khrMaterialsUnlit: [{ sourceModel: resources[2], sourceResourceSha256: String(3).padStart(64, "0") }], nativeAdditiveMaterials: [{ sourceModel: resources[0], sourceResourceSha256: String(1).padStart(64, "0"), alphaMode: "BLEND" }], materialCounts: { total: 2 }, sharedMapLightingPair: { note: "same map, different native lighting identity" }, }, metadata: { title: "The Origin", entry: { x: 1, y: 2, z: 3 }, sourceTerrain: { resource: "terrain/source.wdb", chunkCount: 4 }, nativeTerrain: { report: "terrain-report.json", sourceSha256: "e".repeat(64) }, assets: [{ role: "visual", sha256: "old-visual-hash" }], nativeMaterialAlpha: { report: "old-report.json", resources: [] }, }, }; } test("joins staged Origin overrides by authoritative native ROS identity while retaining current metadata", () => { const data = fixture(); const originalEntry = structuredClone(data.metadata.entry); const originalSourceTerrain = structuredClone(data.metadata.sourceTerrain); const originalNativeTerrain = structuredClone(data.metadata.nativeTerrain); const originalAssets = structuredClone(data.metadata.assets); const output = buildOriginNativeMaterialProvenance(data); assert.equal(output.alphaReport.resources.length, resources.length); assert.equal(output.alphaReport.resources[2].nativeAlpha.materials[0].sourceModel, resources[2]); assert.equal(output.metadata.nativeMaterialAlpha.resources[6].placements, 7); assert.equal(output.metadata.nativeMaterialSemantics.unlitMaterialCount, 1); assert.equal(output.metadata.nativeMaterialSemantics.additiveMaterialCount, 1); assert.deepEqual(output.metadata.entry, originalEntry); assert.deepEqual(output.metadata.sourceTerrain, originalSourceTerrain); assert.deepEqual(output.metadata.nativeTerrain, originalNativeTerrain); assert.deepEqual(output.metadata.assets, originalAssets); assert.equal(data.metadata.nativeMaterialAlpha.report, "old-report.json"); }); test("rejects conversion evidence whose ROS identity or source hash does not match its placement override", () => { const data = fixture(); const first = data.assembly.stagedOverrides[0].resource.toLowerCase(); data.conversionReports.get(first).visual.materials.nativeAlpha.sourceResourceSha256 = "not-a-sha"; assert.throws(() => buildOriginNativeMaterialProvenance(data), /Invalid native source hash/); }); test("rejects staged source hashes that disagree with authoritative assembly resource rows", () => { const data = fixture(); data.assembly.resources[0].sourceSha256 = "f".repeat(64); assert.throws(() => buildOriginNativeMaterialProvenance(data), /does not match the authoritative assembly row/); });