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 p from './portal_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('installed portal-bonus.json equals the derivation from the raw tables', () => { const built = p.build(load('catalog.raw.json'), load('bonus.raw.json'), load('portals.json')); assert.deepEqual(load('portal-bonus.json'), built); assert.equal(Object.keys(built.portals).length, 74); }); test('every Index Fund bundle resolves to season-2 cards of its own Bond and a basic component', () => { const bonus = load('bonus.raw.json'), roles = load('roles.json'), wiki = load('wiki-4654.json'), table = load('portal-bonus.json'); const season = load('season.json').season_id; const basic = id => bonus.GridFightBasicBonusConfigRow.find(r => r.BonusID === id); const sold = row => row.raw.IsInPool && row.raw.SeasonID === season && !roles.some(o => o.avatar_id === row.avatar_id && o.id !== row.id && o.raw.IsInPool && o.raw.SeasonID === season && o.cost < row.cost); const cheapest = avatar => roles.filter(r => r.avatar_id === avatar && sold(r)).sort((a, b) => a.cost - b.cost)[0]; for (const portal of wiki.portals.filter(x => x.id >= 2201 && x.id <= 2214)) { const [bundle, trait, boost] = portal.params; assert.equal(boost, 40); assert.deepEqual(table.portals[portal.id].show, [bundle]); assert.deepEqual(table.portals[portal.id].traits, [trait]); const combo = bonus.GridFightCombinationBonusConfigRow.find(r => r.BonusID === bundle); assert.equal(combo.CombinationBonusList[0], 1); // Gold Coins assert.equal(combo.CombinationBonusList.at(-1), 101); // two random 1-cost cards (OrbConfig 200001) assert.equal(combo.BonusNumberList.at(-1), 20000); const rows = combo.CombinationBonusList.map(basic); const cards = rows.filter(r => r.BonusType === 5).map(r => cheapest(r.BonusTypeParamList[0])); assert.equal(cards.length, 2, 'portal ' + portal.id); for (const card of cards) assert.ok(card && card.traits.includes(trait), `portal ${portal.id}: ${card && card.id} lacks trait ${trait}`); const items = rows.filter(r => r.BonusType === 7).map(r => r.BonusTypeParamList[0]); assert.equal(items.length, 1); assert.ok(items[0] >= 350201 && items[0] <= 350208); // a basic component } }); test('Invitation, weapon-box and delayed bundles match the portal params', () => { const bonus = load('bonus.raw.json'), wiki = load('wiki-4654.json'), table = load('portal-bonus.json').portals; const item = id => bonus.GridFightBasicBonusConfigRow.find(r => r.BonusID === id).BonusTypeParamList[0]; for (const portal of wiki.portals.filter(x => x.id >= 2101 && x.id <= 2119)) assert.equal(item(table[portal.id].show[0]), portal.params[0]); // 101: two Basic Weapon Boxes (99999) = params[0]; 102: Privilege box (99997) later; 104: Wealth Gem; 106: Staff Projector. const combo = bonus.GridFightCombinationBonusConfigRow.find(r => r.BonusID === table[101].show[0]); assert.equal(combo.BonusNumberList[0] / 10000, wiki.portals.find(x => x.id === 101).params[0]); assert.equal(item(209), 99997); assert.deepEqual(table[102].delayed, [209]); assert.equal(item(table[104].delayed[0]), 350701); assert.equal(item(205), 350105); assert.deepEqual(table[106].delayed, [205]); // 103: orbs 140021..140023 hold 6/8/12 coins (BonusID 1 x N). for (const [i, coins] of [6, 8, 12].entries()) { const c = bonus.GridFightCombinationBonusConfigRow.find(r => r.BonusID === 40021 + i); assert.deepEqual(c.CombinationBonusList, [1]); assert.equal(c.BonusNumberList[0] / 10000, coins); assert.equal(wiki.portals.find(x => x.id === 103).params[i], coins); } });