Files
BattleForge/tests/Unit/Domain/ScenarioValidatorTest.php
T

163 lines
5.7 KiB
PHP

<?php
declare(strict_types=1);
namespace BattleForge\Tests\Unit\Domain;
use BattleForge\Domain\Archetype;
use BattleForge\Domain\Battlefield;
use BattleForge\Domain\DeploymentZone;
use BattleForge\Domain\ObjectiveMarker;
use BattleForge\Domain\Position;
use BattleForge\Domain\Scenario;
use BattleForge\Domain\ScenarioValidator;
use BattleForge\Domain\Terrain;
use BattleForge\Domain\UnitState;
use BattleForge\Domain\VictoryCondition;
use PHPUnit\Framework\TestCase;
final class ScenarioValidatorTest extends TestCase
{
public function testItReturnsAnEmptyListForAValidScenario(): void
{
$scenario = $this->validScenario();
self::assertSame([], ScenarioValidator::validate($scenario));
}
public function testItRejectsAnObjectiveOnImpassableTerrain(): void
{
$scenario = $this->validScenario(
terrain: ['4:4' => Terrain::Water],
objectivePosition: new Position(4, 4),
);
$errors = ScenarioValidator::validate($scenario);
self::assertNotEmpty($errors);
self::assertTrue((bool) array_filter($errors, static fn (string $error): bool => str_contains($error, 'impassable')));
}
public function testItRejectsAnObjectiveUnreachableFromAnyDeploymentZone(): void
{
$scenario = $this->validScenario(
terrain: [
'3:4' => Terrain::Blocking,
'4:3' => Terrain::Blocking,
'5:4' => Terrain::Blocking,
'4:5' => Terrain::Blocking,
],
objectivePosition: new Position(4, 4),
);
$errors = ScenarioValidator::validate($scenario);
self::assertTrue((bool) array_filter($errors, static fn (string $error): bool => str_contains($error, 'unreachable')));
}
public function testItRejectsOverlappingDeploymentZones(): void
{
$units = $this->unitSet();
$battlefield = new Battlefield(8, 8);
$scenario = new Scenario(
id: 'demo',
name: 'Demo',
battlefield: $battlefield,
units: $units,
deploymentZones: [
'alpha' => new DeploymentZone('alpha', [new Position(0, 0), new Position(1, 0), new Position(2, 0)]),
'bravo' => new DeploymentZone('bravo', [new Position(1, 0), new Position(2, 0), new Position(7, 7), new Position(6, 7), new Position(5, 7)]),
],
objectives: [],
victoryCondition: VictoryCondition::EliminateAll,
holdRoundsRequired: 1,
);
$errors = ScenarioValidator::validate($scenario);
self::assertTrue((bool) array_filter($errors, static fn (string $error): bool => str_contains($error, 'overlap')));
}
public function testItRejectsDeploymentPositionsOnImpassableTerrain(): void
{
$units = $this->unitSet();
$battlefield = new Battlefield(8, 8, ['0:0' => Terrain::Water]);
$scenario = new Scenario(
id: 'demo',
name: 'Demo',
battlefield: $battlefield,
units: $units,
deploymentZones: [
'alpha' => new DeploymentZone('alpha', [new Position(0, 0), new Position(1, 0), new Position(2, 0)]),
'bravo' => new DeploymentZone('bravo', [new Position(7, 7), new Position(6, 7), new Position(5, 7)]),
],
objectives: [],
victoryCondition: VictoryCondition::EliminateAll,
holdRoundsRequired: 1,
);
$errors = ScenarioValidator::validate($scenario);
self::assertTrue((bool) array_filter($errors, static fn (string $error): bool => str_contains($error, 'impassable')));
}
/**
* @param array<string, Terrain> $terrain
*/
private function validScenario(
array $terrain = [],
?Position $objectivePosition = null,
): Scenario {
$units = $this->unitSet();
$battlefield = new Battlefield(8, 8, $terrain);
$objectives = [];
if ($objectivePosition !== null) {
$objectives = ['objective-1' => new ObjectiveMarker('objective-1', $objectivePosition)];
}
return new Scenario(
id: 'demo',
name: 'Demo',
battlefield: $battlefield,
units: $units,
deploymentZones: $this->zones(),
objectives: $objectives,
victoryCondition: $objectivePosition === null
? VictoryCondition::EliminateAll
: VictoryCondition::HoldObjective,
holdRoundsRequired: 1,
);
}
/** @return array<string, DeploymentZone> */
private function zones(): array
{
return [
'alpha' => new DeploymentZone('alpha', [
new Position(0, 0),
new Position(1, 0),
new Position(2, 0),
]),
'bravo' => new DeploymentZone('bravo', [
new Position(7, 7),
new Position(6, 7),
new Position(5, 7),
]),
];
}
/** @return list<UnitState> */
private function unitSet(): array
{
return [
new UnitState('alpha-1', 'alpha', new Position(0, 0), 10, 10, 4, 3, 2, 2, false, Archetype::Defender),
new UnitState('alpha-2', 'alpha', new Position(1, 0), 8, 8, 5, 2, 3, 2, false, Archetype::Striker),
new UnitState('alpha-3', 'alpha', new Position(2, 0), 7, 7, 2, 2, 3, 2, false, Archetype::Support),
new UnitState('bravo-1', 'bravo', new Position(7, 7), 10, 10, 4, 3, 2, 2, false, Archetype::Defender),
new UnitState('bravo-2', 'bravo', new Position(6, 7), 8, 8, 5, 2, 3, 2, false, Archetype::Striker),
new UnitState('bravo-3', 'bravo', new Position(5, 7), 6, 6, 3, 1, 5, 2, false, Archetype::Scout),
];
}
}