import assert from "node:assert/strict"; import crypto from "node:crypto"; import fs from "node:fs"; import os from "node:os"; import path from "node:path"; import { execFileSync } from "node:child_process"; import { fileURLToPath } from "node:url"; const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../.."); const manifestPath = path.join(root, "tests/golden/M12-01B/manifest.json"); const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf8")); const blender = process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender"); const sha256 = (file) => crypto.createHash("sha256").update(fs.readFileSync(file)).digest("hex"); const artifact = (name) => path.join(root, manifest.artifacts[name].path); assert.equal(manifest.schemaVersion, 1); assert.equal(manifest.task, "M12-01B"); assert.equal(manifest.enablingTask, true); assert.equal(manifest.parityStateChange, false); assert.equal(manifest.nextTask, "M12-01C"); assert.match(execFileSync(blender, ["--version"], { encoding: "utf8" }), /^Blender 5\.2\.0 LTS/m); for (const name of ["generator", "exporter", "checker", "fixture", "catalogDefinition", "canonicalReport"]) { assert.equal(sha256(artifact(name)), manifest.artifacts[name].sha256, `${name} hash drifted`); } const expected = JSON.parse(fs.readFileSync(artifact("canonicalReport"), "utf8")); assert.equal(expected.schemaVersion, 1); assert.equal(expected.blenderVersion, "5.2.0"); assert.equal(expected.catalogDefinition.version, 1); assert.equal(expected.catalogDefinition.records.length, 3); assert.deepEqual(expected.catalogDefinition.records.map((item) => item.path), ["Characters", "Characters/Heroes", "Materials/Metal"]); assert.deepEqual(expected.catalogDefinition.records.map((item) => item.parentPath), [null, "Characters", "Materials"]); assert.equal(expected.assets.length, 3); assert.deepEqual(expected.assets.map((item) => `${item.idType}:${item.name}`), [ "MATERIAL:M12 Brushed Metal", "OBJECT:M12 Hero", "WORLD:M12 Uncataloged World", ]); const hero = expected.assets.find((item) => item.name === "M12 Hero"); assert.deepEqual(hero.tags, ["character", "hero", "rig-ready"]); assert.equal(hero.activeTag, 1); assert.equal(hero.catalogId, "22222222-2222-4222-8222-222222222222"); assert.equal(hero.catalogSimpleName, ""); assert.equal(hero.usePreferredImportMethod, true); assert.equal(hero.preferredImportMethod, "APPEND"); assert.deepEqual(hero.customProperties.map((item) => item.name), ["approved", "dimensions", "rating", "source"]); assert.deepEqual(hero.customProperties.find((item) => item.name === "dimensions").value, [2, 2, 0]); const material = expected.assets.find((item) => item.name === "M12 Brushed Metal"); assert.equal(material.author, ""); assert.equal(material.license, ""); const world = expected.assets.find((item) => item.name === "M12 Uncataloged World"); assert.equal(world.catalogId, "00000000-0000-0000-0000-000000000000"); const temporary = fs.mkdtempSync(path.join(os.tmpdir(), "m12-asset-catalog-v1-")); try { execFileSync(blender, [ "--background", "--factory-startup", "--python", artifact("generator"), "--", temporary, ], { cwd: root, stdio: "pipe" }); const generatedFixture = path.join(temporary, "m12_asset_catalog_v1.blend"); const generatedCatalog = path.join(temporary, "blender_assets.cats.txt"); const generatedReport = path.join(temporary, "canonical.json"); assert.ok(fs.statSync(generatedFixture).size > 0); assert.equal(fs.readFileSync(generatedCatalog, "utf8"), fs.readFileSync(artifact("catalogDefinition"), "utf8")); execFileSync(blender, [ "--background", generatedFixture, "--python", artifact("exporter"), "--", generatedCatalog, generatedReport, ], { cwd: root, stdio: "pipe" }); assert.deepEqual(JSON.parse(fs.readFileSync(generatedReport, "utf8")), expected); const reopenedReport = path.join(temporary, "checked-in-canonical.json"); execFileSync(blender, [ "--background", artifact("fixture"), "--python", artifact("exporter"), "--", artifact("catalogDefinition"), reopenedReport, ], { cwd: root, stdio: "pipe" }); assert.deepEqual(JSON.parse(fs.readFileSync(reopenedReport, "utf8")), expected); } finally { fs.rmSync(temporary, { recursive: true, force: true }); } process.stdout.write(`asset-catalog-v1-fixture-ok catalogs=3 assets=3 metadata=complete next=${manifest.nextTask}\n`);