- aircraft attack orders fly a sortie to the target, bomb once, return to a friendly airport and re-arm (two-day cooldown, charged per aircraft); rebasing is bounded by fuel rather than the 10-tile combat radius - artillery and warships open a sustained hourly bombardment on a standing target, drawn as a red dashed line and ring while either endpoint is selected - a game launched from localhost can enable a testing mode whose train and build rows offer free, instant orders; the server only honours them for a loopback admin in a testing game - an open city panel now unlocks aircraft as soon as an airport finishes
108 lines
4.9 KiB
JavaScript
108 lines
4.9 KiB
JavaScript
import { TestCase } from "./framework/test_case.js";
|
|
import { smallState } from "./framework/helpers.js";
|
|
import { GameServer } from "../server/game_server.js";
|
|
import { isLoopback } from "../server/server.js";
|
|
|
|
const BIG_BUDGET = 1.0e15;
|
|
|
|
function makeServer(testing) {
|
|
const network = { getPeerIds: () => [], send: () => {} };
|
|
const server = new GameServer(network);
|
|
server.configureGame({
|
|
seed: 12345,
|
|
player_civ: 0,
|
|
civilisations: ["france", "britain"],
|
|
testing,
|
|
});
|
|
return server;
|
|
}
|
|
|
|
export class TestingModeTest extends TestCase {
|
|
test_loopback_addresses_are_recognised() {
|
|
this.assertTrue(isLoopback("127.0.0.1"), "v4 loopback");
|
|
this.assertTrue(isLoopback("127.0.1.5"), "any 127/8 address");
|
|
this.assertTrue(isLoopback("::1"), "v6 loopback");
|
|
this.assertTrue(isLoopback("::ffff:127.0.0.1"), "mapped v4 loopback");
|
|
this.assertFalse(isLoopback("10.0.0.1"), "a private address is remote");
|
|
this.assertFalse(isLoopback("::ffff:8.8.8.8"), "a mapped public address is remote");
|
|
this.assertFalse(isLoopback(""), "an empty address is remote");
|
|
this.assertFalse(isLoopback(undefined), "a missing address is remote");
|
|
}
|
|
|
|
test_free_training_spawns_instantly_and_costs_nothing() {
|
|
const state = smallState();
|
|
state.budgets.set(0, BIG_BUDGET);
|
|
const city = state.cities.find((c) => c.civ === 0);
|
|
const artillery = state.protoUnits.findIndex((u) => u.id === "artillery");
|
|
const before = state.units.filter((u) => u.civ === 0).length;
|
|
this.assertTrue(state.requestTrain(city.id, artillery, true), "the free order is accepted");
|
|
this.assertEqual(
|
|
state.units.filter((u) => u.civ === 0).length,
|
|
before + 1,
|
|
"the unit appears at once"
|
|
);
|
|
this.assertEqual(state.getBudget(0), BIG_BUDGET, "nothing is charged");
|
|
this.assertFalse(state.training.has(city.id), "nothing is queued");
|
|
}
|
|
|
|
test_free_build_raises_the_level_instantly_and_costs_nothing() {
|
|
const state = smallState();
|
|
state.budgets.set(0, BIG_BUDGET);
|
|
const city = state.cities.find((c) => c.civ === 0);
|
|
const market = state.protoBuildings.findIndex((b) => b.id === "market");
|
|
this.assertTrue(state.requestBuild(city.id, market, true), "the free build is accepted");
|
|
this.assertEqual(state.getCityBuildingLevel(city, market), 1, "the level rose at once");
|
|
this.assertEqual(state.getBudget(0), BIG_BUDGET, "nothing is charged");
|
|
this.assertFalse(state.training.has(city.id), "nothing is queued");
|
|
}
|
|
|
|
test_free_build_still_respects_placement_rules() {
|
|
const state = smallState();
|
|
const inland = state.cities.find((c) => c.civ === 0 && !state.isCoastalCity(c));
|
|
this.assertNotNull(inland, "the fixture has an inland city");
|
|
const shipyard = state.protoBuildings.findIndex((b) => b.id === "shipyard");
|
|
this.assertFalse(state.requestBuild(inland.id, shipyard, true), "an inland shipyard is refused");
|
|
}
|
|
|
|
test_a_normal_order_still_charges_and_queues() {
|
|
const state = smallState();
|
|
state.budgets.set(0, BIG_BUDGET);
|
|
const city = state.cities.find((c) => c.civ === 0);
|
|
const market = state.protoBuildings.findIndex((b) => b.id === "market");
|
|
this.assertTrue(state.requestBuild(city.id, market), "the paid order is accepted");
|
|
this.assertEqual(state.getCityBuildingLevel(city, market), 0, "it does not rise at once");
|
|
this.assertLess(state.getBudget(0), BIG_BUDGET, "it was charged");
|
|
this.assertTrue(state.training.has(city.id), "it went into the queue");
|
|
}
|
|
|
|
test_server_honours_free_orders_in_a_testing_game() {
|
|
const server = makeServer(true);
|
|
server.peerCiv.set(1, 0);
|
|
const city = server.state.cities.find((c) => c.civ === 0);
|
|
const market = server.state.protoBuildings.findIndex((b) => b.id === "market");
|
|
const budgetBefore = server.state.getBudget(0);
|
|
this.assertTrue(
|
|
server.handleOrder(1, { type: "build", city: city.id, building: market, free: true }),
|
|
"the order is accepted"
|
|
);
|
|
this.assertEqual(server.state.getCityBuildingLevel(city, market), 1, "the building went up");
|
|
this.assertEqual(server.state.getBudget(0), budgetBefore, "it was free");
|
|
this.assertTrue(server.state.testing, "the snapshot flag is set");
|
|
}
|
|
|
|
test_server_ignores_the_free_flag_in_a_normal_game() {
|
|
const server = makeServer(false);
|
|
server.peerCiv.set(1, 0);
|
|
const city = server.state.cities.find((c) => c.civ === 0);
|
|
const market = server.state.protoBuildings.findIndex((b) => b.id === "market");
|
|
const budgetBefore = server.state.getBudget(0);
|
|
this.assertTrue(
|
|
server.handleOrder(1, { type: "build", city: city.id, building: market, free: true }),
|
|
"the order is still processed"
|
|
);
|
|
this.assertEqual(server.state.getCityBuildingLevel(city, market), 0, "it only went into the queue");
|
|
this.assertLess(server.state.getBudget(0), budgetBefore, "the normal cost was charged");
|
|
this.assertFalse(server.state.testing, "the testing flag stays off");
|
|
}
|
|
}
|