Files
dota_factory/src/test/UnlockGroupTest.cpp
Malte Langkabel 3c549a160c share one loadTestConfig() helper across the tests
19 test translation units each defined an identical local loadConfig().
They now include src/test/TestConfig.h, which lives off the lib/ui/app
include path like SimulationTestAccess.h.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GH8ZMRY3vhxxXcaUxBqxkk
2026-08-02 20:53:21 +02:00

114 lines
4.2 KiB
C++

// Unlock groups as the unit of loot (REQ-LOCK-EXPLICIT, REQ-LOCK-BUILDING,
// REQ-DEF-SCHEMATIC-DROP): buildings can be locked, and one drop can grant a
// building and a module together.
#include <algorithm>
#include <string>
#include <utility>
#include <vector>
#include "catch.hpp"
#include "BuildingType.h"
#include "ConfigLoader.h"
#include "FactionComponent.h"
#include "GameConfig.h"
#include "HealthComponent.h"
#include "SchematicChoiceOption.h"
#include "Simulation.h"
#include "SimulationTestAccess.h"
#include "StationBodyComponent.h"
#include "UnlocksConfig.h"
#include "TestConfig.h"
namespace
{
void killEnemyStations(Simulation& sim)
{
sim.getAdmin().forEach<StationBodyComponent, FactionComponent, HealthComponent>(
[](entt::entity, StationBodyComponent&, FactionComponent& faction, HealthComponent& health)
{
if (faction.isEnemy) { health.hp = 0.0f; }
});
sim.tick();
}
// A config whose only unlock group grants the salvager module and the salvage
// bay together, gated to the given station level.
GameConfig configWithSalvageGroup(int stationLevel)
{
GameConfig cfg = loadTestConfig();
cfg.unlocks.groups.clear();
cfg.unlocks.groups.push_back(
UnlockGroupDef{"salvage_operations", stationLevel, {}, {}, {"salvager"}, {"salvage_bay"}, {}});
return cfg;
}
} // namespace
TEST_CASE("UnlockGroup: a building granted by a group is locked at game start", "[unlock_group]")
{
const Simulation sim(configWithSalvageGroup(0));
CHECK_FALSE(sim.isBuildingUnlocked(BuildingType::SalvageBay));
CHECK_FALSE(sim.isModuleSchematicUnlocked("salvager"));
// A building in no unlock group is available from the start.
CHECK(sim.isBuildingUnlocked(BuildingType::Miner));
}
TEST_CASE("UnlockGroup: the drop option lists every granted item", "[unlock_group]")
{
Simulation sim(configWithSalvageGroup(0));
killEnemyStations(sim); // level 0
REQUIRE(sim.hasSchematicChoicesPending());
const std::vector<SchematicChoiceOption>& options = sim.getPendingSchematicChoices();
const auto it = std::find_if(options.begin(), options.end(),
[](const SchematicChoiceOption& o) { return o.unlockGroupId == "salvage_operations"; });
REQUIRE(it != options.end());
const bool grantsModule = std::any_of(it->grantedItems.begin(), it->grantedItems.end(),
[](const GrantedSchematic& g) { return g.type == SchematicType::Module && g.id == "salvager"; });
const bool grantsBuilding = std::any_of(it->grantedItems.begin(), it->grantedItems.end(),
[](const GrantedSchematic& g) { return g.type == SchematicType::Building && g.id == "salvage_bay"; });
CHECK(grantsModule);
CHECK(grantsBuilding);
}
TEST_CASE("UnlockGroup: awarding a group unlocks the building and module at once", "[unlock_group]")
{
Simulation sim(configWithSalvageGroup(0));
killEnemyStations(sim); // level 0
REQUIRE(sim.hasSchematicChoicesPending());
// The only eligible group is salvage_operations, so option 0 grants it.
REQUIRE(sim.getPendingSchematicChoices()[0].unlockGroupId == "salvage_operations");
SimulationTestAccess::applySchematicChoice(sim, 0);
CHECK(sim.isBuildingUnlocked(BuildingType::SalvageBay));
CHECK(sim.isModuleSchematicUnlocked("salvager"));
}
TEST_CASE("UnlockGroup: a group above the destroyed station level is not offered", "[unlock_group]")
{
Simulation sim(configWithSalvageGroup(5)); // eligible only at level >= 5
killEnemyStations(sim); // level 0 push
// Pool is empty at level 0, so no choice dialog opens at all.
CHECK_FALSE(sim.hasSchematicChoicesPending());
CHECK_FALSE(sim.isBuildingUnlocked(BuildingType::SalvageBay));
}
TEST_CASE("UnlockGroup: reset re-locks a previously awarded building", "[unlock_group]")
{
Simulation sim(configWithSalvageGroup(0));
killEnemyStations(sim);
REQUIRE(sim.getPendingSchematicChoices()[0].unlockGroupId == "salvage_operations");
SimulationTestAccess::applySchematicChoice(sim, 0);
REQUIRE(sim.isBuildingUnlocked(BuildingType::SalvageBay));
sim.reset();
CHECK_FALSE(sim.isBuildingUnlocked(BuildingType::SalvageBay));
CHECK_FALSE(sim.isModuleSchematicUnlocked("salvager"));
}