fix: enforce executable movement state

This commit is contained in:
Keith Solomon
2026-07-04 15:47:29 -05:00
parent 1550e2920b
commit 3eba823e52
4 changed files with 259 additions and 8 deletions
+74 -4
View File
@@ -7,8 +7,10 @@ namespace BattleForge\Tests\Unit\Domain;
use BattleForge\Domain\Battlefield;
use BattleForge\Domain\MatchState;
use BattleForge\Domain\Position;
use BattleForge\Domain\Terrain;
use BattleForge\Domain\UnitState;
use InvalidArgumentException;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\TestCase;
final class MatchStateTest extends TestCase
@@ -24,6 +26,67 @@ final class MatchStateTest extends TestCase
);
}
public function testItRejectsAUnitOutsideTheBattlefield(): void
{
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('Unit alpha-1 is outside the battlefield.');
new MatchState(
new Battlefield(8, 8),
[$this->unit('alpha-1', 'alpha', new Position(8, 0))],
'alpha',
);
}
#[DataProvider('impassableTerrainProvider')]
public function testItRejectsALivingUnitOnImpassableTerrain(Terrain $terrain): void
{
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('Living unit alpha-1 must stand on passable terrain.');
new MatchState(
new Battlefield(8, 8, ['0:0' => $terrain]),
[$this->unit('alpha-1', 'alpha')],
'alpha',
);
}
/**
* @return iterable<string, array{Terrain}>
*/
public static function impassableTerrainProvider(): iterable
{
yield 'water' => [Terrain::Water];
yield 'blocking' => [Terrain::Blocking];
}
public function testItRejectsLivingUnitsAtTheSamePosition(): void
{
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('Living units cannot share position 0:0.');
new MatchState(
new Battlefield(8, 8),
[$this->unit('alpha-1', 'alpha'), $this->unit('bravo-1', 'bravo')],
'alpha',
);
}
public function testItAcceptsLivingAndDefeatedUnitsAtTheSamePosition(): void
{
$match = new MatchState(
new Battlefield(8, 8),
[
$this->unit('alpha-1', 'alpha'),
$this->unit('bravo-1', 'bravo', health: 0),
],
'alpha',
);
self::assertSame('0:0', $match->unit('alpha-1')->position->key());
self::assertSame('0:0', $match->unit('bravo-1')->position->key());
}
public function testItRejectsAnUnknownUnitLookup(): void
{
$match = $this->match();
@@ -169,14 +232,21 @@ final class MatchStateTest extends TestCase
{
return new MatchState(
new Battlefield(8, 8),
[$this->unit('alpha-1', 'alpha'), $this->unit('bravo-1', 'bravo')],
[
$this->unit('alpha-1', 'alpha'),
$this->unit('bravo-1', 'bravo', new Position(7, 7)),
],
'alpha',
actionLog: $actionLog,
);
}
private function unit(string $id, string $teamId): UnitState
{
return new UnitState($id, $teamId, new Position(0, 0), 10, 10, 4, 2, 4, 2);
private function unit(
string $id,
string $teamId,
?Position $position = null,
int $health = 10,
): UnitState {
return new UnitState($id, $teamId, $position ?? new Position(0, 0), 10, $health, 4, 2, 4, 2);
}
}