import test from 'node:test'; import assert from 'node:assert/strict'; import fs from 'node:fs'; import {Codec, asset_hash} from './stages.mjs'; import {references, candidates, locate, decodeRecord, ROOT_TYPES} from './level.mjs'; import {sha256} from './extract.mjs'; test('level references deduplicate paths while retaining JSON pointer citations', () => { const p = 'Config/Level/GridFight/PortalBuff/GridFightPortal149.json'; assert.deepEqual(references({'a.json': {'a/b': [p, p], other: 'other.json'}}), [ {path: p, sources: [{file: 'a.json', pointer: '/a~1b/0'}, {file: 'a.json', pointer: '/a~1b/1'}]} ]); assert.throws(() => candidates('Config/Level/GridFight/../secret.json'), /Invalid/); }); test('level lookup distinguishes missing assets from corrupt indexed assets', () => { const path = 'Config/Level/GridFight/SpotLight/GridFightSpotLight100000.json'; const missing = locate({layers: [new Map()], get() { assert.fail('missing index must not read'); }}, path); assert.equal(missing.status, 'not-in-designv'); assert.equal(missing.attempts.length, 5); const asset = 'BakedConfig/' + path.replace('.json', '.bytes'); const present = {layers: [new Map([[asset_hash(asset), {}]])], get(p) { assert.equal(p, asset); throw Error('Truncated indexed asset'); }}; assert.throws(() => locate(present, path), /Truncated/); present.get = () => [Buffer.from([1]), {asset}]; assert.equal(locate(present, path).proof.asset, asset); }); test('whole-record proof rejects trailing bytes, changed encoders, and stripped readers', () => { const codec = new Codec(), type = 'RPG.GameCore.TestLevel'; codec.schemas[type] = [{name: 'Nodes', type: 'int[]'}, {name: 'Count', type: 'int'}]; codec.fields = () => new Map([[16, ['Nodes', 'int[]']], [24, ['Count', 'int']]]); const row = {Nodes: [1, 6, -1], Count: 3}, raw = codec.encode_record(row, type); assert.deepEqual(decodeRecord(codec, raw, type).row, row); assert.throws(() => decodeRecord(codec, Buffer.concat([raw, Buffer.from([0])]), type), /round-trip/); codec.encode_record = () => Buffer.from([0]); assert.throws(() => decodeRecord(codec, raw, type), /round-trip/); codec.fields = () => new Map(); assert.throws(() => decodeRecord(codec, Buffer.from([0]), type), /No serialized client fields/); }); test('installed level audit exposes unknown rules without false round-trip proofs', () => { const dir = new URL('../../resources/grid_fight/', import.meta.url); const read = name => fs.readFileSync(new URL(name, dir)); const data = JSON.parse(read('level-availability.json')), proof = JSON.parse(read('level-provenance.json')); const schemaFile = JSON.parse(read('level-schemas.json')), schemas = schemaFile.client_roots; assert.equal(data.status, 'blocked-no-decoded-rule-assets'); assert.deepEqual(data.decoded_rule_assets, []); assert.deepEqual(Object.values(data.rules), [null, null, null]); assert.equal(data.assets.length, 914); assert.ok(data.assets.every(a => a.status === 'not-in-designv' && a.attempts.length === 5)); assert.deepEqual(data.manifest_gridfight_level_paths, []); assert.equal(proof.rule_asset_round_trips, 0); const links = JSON.parse(read('level-links.raw.json')).GridFightSpecialGoodsConfigRow; assert.equal(links.length, 164); assert.ok(proof.tables.length > 0 && proof.tables.every(t => t.round_trip)); assert.equal(proof.tables.reduce((n, t) => n + t.records, 0), links.length); assert.ok(schemaFile.tables['RPG.GameCore.GridFightSpecialGoodsConfigRow'].some(f => f.name === 'JsonPath')); for (const row of links) assert.ok(data.assets.some(a => a.path === row.JsonPath)); assert.equal(proof.dump_sha256, JSON.parse(read('bonus-provenance.json')).dump_sha256); for (const [name, hash] of Object.entries(proof.outputs)) assert.equal(sha256(read(name)), hash); for (const name of ROOT_TYPES) { const shape = schemas['RPG.GameCore.' + name]; assert.deepEqual(shape.fields, []); assert.equal(shape.status, 'no-serialized-client-fields'); } for (const name of ['GridFightActionTriggerSpotLightSelectConfig', 'GridFightModifierEliteBranchExtraRewardConfig']) { assert.ok(schemas['RPG.GameCore.' + name].methods.some(m => m.immediate_ret)); } assert.ok(!JSON.stringify(proof).includes('/home/')); });