154 lines
6.5 KiB
JavaScript
154 lines
6.5 KiB
JavaScript
import { TestCase } from "./framework/test_case.js";
|
|
import { smallState, cityOf, gdpBuilding } from "./framework/helpers.js";
|
|
import { ECONOMY, EFFECT_BUDGET_INCOME, EFFECT_RESEARCH } from "../shared/data.js";
|
|
|
|
const BIG_BUDGET = 1.0e15;
|
|
|
|
export class GameStateBuildingsTest extends TestCase {
|
|
addBuilding(state, proto) {
|
|
state.protoBuildings.push(proto);
|
|
return state.protoBuildings.length - 1;
|
|
}
|
|
|
|
test_request_build_charges_budget_and_raises_the_level() {
|
|
const state = smallState();
|
|
state.budgets.set(0, BIG_BUDGET);
|
|
const index = this.addBuilding(state, gdpBuilding());
|
|
const city = cityOf(state, 0);
|
|
const beforeBudget = state.getBudget(0);
|
|
const beforeGdp = state.getPlayerGdp(0);
|
|
this.assertTrue(state.requestBuild(city.id, index));
|
|
this.assertEqual(state.getCityBuildingLevel(city, index), 1);
|
|
this.assertLess(state.getBudget(0), beforeBudget, "cost was paid");
|
|
this.assertGreater(state.getPlayerGdp(0), beforeGdp, "GDP bonus applied");
|
|
}
|
|
|
|
test_request_build_rejects_when_unaffordable() {
|
|
const state = smallState();
|
|
state.budgets.set(0, 0);
|
|
const index = this.addBuilding(state, gdpBuilding());
|
|
this.assertFalse(state.requestBuild(cityOf(state, 0).id, index));
|
|
}
|
|
|
|
test_request_build_respects_max_level() {
|
|
const state = smallState();
|
|
state.budgets.set(0, BIG_BUDGET);
|
|
const index = this.addBuilding(state, gdpBuilding({ maxLevel: 1 }));
|
|
const cityId = cityOf(state, 0).id;
|
|
this.assertTrue(state.requestBuild(cityId, index));
|
|
this.assertFalse(state.requestBuild(cityId, index), "already at max level");
|
|
}
|
|
|
|
test_request_build_rejects_unknown_targets() {
|
|
const state = smallState();
|
|
state.budgets.set(0, BIG_BUDGET);
|
|
this.assertFalse(state.requestBuild(99999, 0));
|
|
this.assertFalse(state.requestBuild(cityOf(state, 0).id, 9999));
|
|
}
|
|
|
|
test_request_demolish_refunds_and_removes_the_building() {
|
|
const state = smallState();
|
|
state.budgets.set(0, BIG_BUDGET);
|
|
const index = this.addBuilding(state, gdpBuilding());
|
|
const city = cityOf(state, 0);
|
|
this.assertTrue(state.requestBuild(city.id, index));
|
|
this.assertTrue(state.requestBuild(city.id, index));
|
|
const before = state.getBudget(0);
|
|
this.assertTrue(state.requestDemolish(city.id, index));
|
|
this.assertEqual(state.getCityBuildingLevel(city, index), 1);
|
|
this.assertGreater(state.getBudget(0), before, "refunded");
|
|
this.assertTrue(state.requestDemolish(city.id, index));
|
|
this.assertEqual(state.getCityBuildingLevel(city, index), 0);
|
|
this.assertFalse(state.requestDemolish(city.id, index), "nothing left to demolish");
|
|
}
|
|
|
|
test_building_upkeep_is_added_to_player_upkeep() {
|
|
const state = smallState();
|
|
state.budgets.set(0, BIG_BUDGET);
|
|
const index = this.addBuilding(
|
|
state,
|
|
gdpBuilding({ baseCost: 0, costGdpFraction: 0, baseUpkeep: 100, upkeepAlpha: 1.0 })
|
|
);
|
|
const before = state.getPlayerUpkeep(0);
|
|
this.assertTrue(state.requestBuild(cityOf(state, 0).id, index));
|
|
this.assertApprox(state.getPlayerUpkeep(0), before + 100);
|
|
}
|
|
|
|
test_research_buildings_accumulate_points_each_hour() {
|
|
const state = smallState();
|
|
state.budgets.set(0, BIG_BUDGET);
|
|
const building = gdpBuilding({ baseCost: 0, costGdpFraction: 0, effects: [] });
|
|
building.effects.push({ stat: EFFECT_RESEARCH, baseValue: 2.0, exponent: 1.0 });
|
|
const index = this.addBuilding(state, building);
|
|
this.assertTrue(state.requestBuild(cityOf(state, 0).id, index));
|
|
this.assertEqual(state.getResearch(0), 0);
|
|
state.advanceHour();
|
|
const first = state.getResearch(0);
|
|
const governmentRate = state.getCivGovernmentModifiers(0)[EFFECT_RESEARCH] || 0;
|
|
this.assertApprox(first - governmentRate, 2.0);
|
|
state.advanceHour();
|
|
this.assertApprox(state.getResearch(0) - first - governmentRate, 2.0);
|
|
}
|
|
|
|
test_viewer_stats_split_income_and_upkeep() {
|
|
const state = smallState();
|
|
state.budgets.set(0, BIG_BUDGET);
|
|
const index = this.addBuilding(
|
|
state,
|
|
gdpBuilding({ baseCost: 0, costGdpFraction: 0, baseUpkeep: 100, upkeepAlpha: 1.0, effects: [] })
|
|
);
|
|
this.assertTrue(state.requestBuild(cityOf(state, 0).id, index));
|
|
const breakdown = state.viewerStats(0).breakdown;
|
|
this.assertHas(breakdown, "income");
|
|
this.assertHas(breakdown, "buildingUpkeep");
|
|
this.assertHas(breakdown, "unitUpkeep");
|
|
this.assertApprox(breakdown.buildingUpkeep.total, 100);
|
|
this.assertApprox(
|
|
breakdown.unitUpkeep.total,
|
|
state.getPlayerUpkeep(0) - breakdown.buildingUpkeep.total
|
|
);
|
|
const expected = state.getPlayerGdp(0) * ECONOMY.budgetGdpRate * (1 + breakdown.income.modifier);
|
|
this.assertApprox(breakdown.income.total, expected);
|
|
}
|
|
|
|
test_budget_breakdown_names_income_sources() {
|
|
const state = smallState();
|
|
state.budgets.set(0, BIG_BUDGET);
|
|
const effect = { stat: EFFECT_BUDGET_INCOME, baseValue: 0.05, exponent: 1.0 };
|
|
const index = this.addBuilding(
|
|
state,
|
|
gdpBuilding({ baseCost: 0, costGdpFraction: 0, baseUpkeep: 0, effects: [effect] })
|
|
);
|
|
this.assertTrue(state.requestBuild(cityOf(state, 0).id, index));
|
|
const breakdown = state.viewerStats(0).breakdown;
|
|
const source = breakdown.income.sources.find((s) => s.label === "Test market");
|
|
this.assertTrue(source, "the building appears as an income source");
|
|
this.assertEqual(source.kind, "building");
|
|
this.assertApprox(source.effect, 0.05);
|
|
this.assertApprox(breakdown.income.modifier, state.getCivModifiers(0)[EFFECT_BUDGET_INCOME] || 0);
|
|
this.assertApprox(source.amount, breakdown.income.base * 0.05);
|
|
this.assertApprox(breakdown.income.total, breakdown.income.base * (1 + breakdown.income.modifier));
|
|
}
|
|
|
|
test_snapshot_exposes_buildings_and_resources() {
|
|
const state = smallState();
|
|
const snapshot = state.snapshot(0);
|
|
this.assertSize(snapshot.buildings, state.protoBuildings.length);
|
|
this.assertHas(snapshot, "research");
|
|
this.assertHas(snapshot, "culture");
|
|
for (const city of snapshot.cities) this.assertHas(city, "buildings");
|
|
this.assertHas(snapshot.viewerStats, "research");
|
|
this.assertHas(snapshot.viewerStats, "culture");
|
|
}
|
|
|
|
test_snapshot_serialises_city_building_levels() {
|
|
const state = smallState();
|
|
state.budgets.set(0, BIG_BUDGET);
|
|
const index = this.addBuilding(state, gdpBuilding());
|
|
const cityId = cityOf(state, 0).id;
|
|
this.assertTrue(state.requestBuild(cityId, index));
|
|
const city = state.snapshot(0).cities.find((c) => c.id === cityId);
|
|
this.assertEqual(city.buildings[index], 1);
|
|
}
|
|
}
|