import test from 'node:test'; import assert from 'node:assert/strict'; import fs from 'node:fs'; import path from 'node:path'; import {fileURLToPath} from 'node:url'; import * as b from './bonus.mjs'; const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../..'); const DIR = path.join(ROOT, 'resources/grid_fight'); const load = n => JSON.parse(fs.readFileSync(path.join(DIR, n), 'utf8')); test('bonus extractor covers the bonus pools and the Plot Armor recommendations', () => { assert.deepEqual(b.TYPES, [ 'GridFightBasicBonusConfigRow', 'GridFightCombinationBonusConfigRow', 'GridFightRandomBonusPoolConfigRow', 'GridFightVictoryBonusConfigRow', 'GridFightRoleRecomSpotLightConfigRow', 'GridFightSpotLightTagConfigRow' ]); }); test('installed bonus tables: counts, provenance and references', () => { const t = load('bonus.raw.json'), proof = load('bonus-provenance.json'), mech = load('mechanics-provenance.json'); const counts = Object.fromEntries(b.TYPES.map(n => [n, t[n].length])); assert.deepEqual(counts, { GridFightBasicBonusConfigRow: 1116, GridFightCombinationBonusConfigRow: 478, GridFightRandomBonusPoolConfigRow: 110, GridFightVictoryBonusConfigRow: 7, GridFightRoleRecomSpotLightConfigRow: 126, GridFightSpotLightTagConfigRow: 51 }); assert.equal(proof.dump_sha256, mech.dump_sha256); assert.ok(proof.tables.every(x => x.round_trip)); // Every recommended SpotLight is a GridFightSpotLightConfigRow, every role a GridFightRoleConfigRow. const spots = new Set(load('mechanics.json').GridFightSpotLightConfigRow.map(r => r.ID)); const roles = new Set(load('roles.json').map(r => r.id)); for (const r of t.GridFightRoleRecomSpotLightConfigRow) { assert.ok(roles.has(r.RoleID)); for (const id of [...(r.FirstRecommendSpotLightList ?? []), ...(r.SecondRecommendSpotLightList ?? [])]) assert.ok(spots.has(id)); } }); test('Encounter tier bonus rules reference bonus IDs no bonus table defines', () => { // GridFightBonusRuleConfigRow 91c0q (chapter c, quality q) is the only // per-tier reward hook; its ProgressBonusList IDs resolve nowhere, which is // why the server sends no Encounter branch drops. const t = load('bonus.raw.json'), rules = load('battle.json').GridFightBonusRuleConfigRow; const defined = new Set([ ...t.GridFightBasicBonusConfigRow.map(r => r.BonusID), ...t.GridFightCombinationBonusConfigRow.map(r => r.BonusID), ...t.GridFightRandomBonusPoolConfigRow.map(r => r.RandomBonusID) ]); const tiers = rules.filter(r => r.ID >= 91101 && r.ID <= 91314); assert.equal(tiers.length, 36); for (const r of tiers) for (const id of r.ProgressBonusList ?? []) assert.ok(!defined.has(id), `bonus ${id} unexpectedly defined`); });