import { TestCase } from "./framework/test_case.js"; import { smallState } from "./framework/helpers.js"; import { PROTO_UNITS, TRAINABLE_UNIT_IDS, SPY_OPERATIONS, SPY_SABOTAGE, SPY_CITY, SPY_COMMS, technologyById, } from "../shared/data.js"; import { key, parseKey } from "../shared/hex.js"; export class SpyTest extends TestCase { test_the_spy_is_a_gated_trainable_unit() { const proto = PROTO_UNITS.find((u) => u.id === "spy"); this.assertNotNull(proto, "the spy exists"); this.assertTrue(proto.spy, "it is marked a spy"); this.assertFalse(proto.military, "it does not fight"); this.assertEqual(proto.vision, 1, "it watches a single tile out"); this.assertEqual(proto.requiresTechnology, "intelligence_agencies", "it needs the agency"); this.assertTrue(TRAINABLE_UNIT_IDS.includes("spy"), "it can be trained"); this.assertTrue(proto.icon.endsWith(".svg"), "it carries an icon"); this.assertNotNull(technologyById("intelligence_agencies"), "the agency technology exists"); this.assertNotNull(technologyById("counter_intelligence"), "the counter-intelligence technology exists"); } test_the_covert_operations_are_well_formed() { this.assertNotEmpty(SPY_OPERATIONS); const ids = new Set(); for (const operation of SPY_OPERATIONS) { this.assertTrue(operation.id.length > 0, "every operation has an id"); this.assertFalse(ids.has(operation.id), `the id ${operation.id} is unique`); ids.add(operation.id); this.assertTrue(operation.name.length > 0, `name of ${operation.id}`); this.assertTrue(operation.description.length > 0, `description of ${operation.id}`); } this.assertTrue(ids.has(SPY_SABOTAGE), "sabotage"); this.assertTrue(ids.has(SPY_CITY), "city spying"); this.assertTrue(ids.has(SPY_COMMS), "comms spying"); } test_an_enemy_spy_is_hidden_from_every_other_nation() { const state = smallState(); const home = state.cities.find((city) => city.civ === 1); const spy = state._spawnUnit(home.coords, 1, state.protoUnits.find((u) => u.id === "spy")); const seenByEnemy = (state.snapshot(0).units || []).map((unit) => unit.id); this.assertFalse(seenByEnemy.includes(spy.id), "the enemy never sees the spy"); const seenByOwner = (state.snapshot(1).units || []).map((unit) => unit.id); this.assertTrue(seenByOwner.includes(spy.id), "its own nation always sees it"); } test_a_spy_slips_through_foreign_territory() { const state = smallState(); const home = state.cities.find((city) => city.civ === 0); const spy = state._spawnUnit(home.coords, 0, state.protoUnits.find((u) => u.id === "spy")); const infantry = state._spawnUnit(home.coords, 0, state.protoUnits.find((u) => u.id === "modern_infantry")); // A quiet, unowned British land tile, explored by the spy's nation. let foreign = null; for (const [k, civ] of state.territory) { if (civ !== 1) continue; const coords = parseKey(k); if (state._cityByCoords.has(k)) continue; if (state.unitsAt(coords).length > 0) continue; const tile = state.tiles[k]; if (!tile || tile.terrainClass !== "Land") continue; foreign = coords; break; } this.assertNotNull(foreign, "the fixture has a British land tile"); state.explored.get(0).add(key(foreign.x, foreign.y)); this.assertTrue( state._canUnitEnter(spy, foreign, false), "a spy may cross foreign land on the way through" ); this.assertFalse( state._canUnitEnter(infantry, foreign, false), "a soldier still treats foreign land as a wall" ); } }