154 lines
4.9 KiB
PHP
154 lines
4.9 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace BattleForge\Tests\Unit\Domain;
|
|
|
|
use BattleForge\Domain\Position;
|
|
use BattleForge\Domain\UnitState;
|
|
use InvalidArgumentException;
|
|
use PHPUnit\Framework\Attributes\DataProvider;
|
|
use PHPUnit\Framework\TestCase;
|
|
|
|
final class UnitStateTest extends TestCase
|
|
{
|
|
/**
|
|
* @param callable(): UnitState $createUnit
|
|
*/
|
|
#[DataProvider('invalidUnitProvider')]
|
|
public function testItRejectsInvalidConstructorArguments(callable $createUnit): void
|
|
{
|
|
$this->expectException(InvalidArgumentException::class);
|
|
|
|
$createUnit();
|
|
}
|
|
|
|
/**
|
|
* @return iterable<string, array{callable(): UnitState}>
|
|
*/
|
|
public static function invalidUnitProvider(): iterable
|
|
{
|
|
yield 'empty unit id' => [
|
|
static fn (): UnitState => new UnitState('', 'alpha', new Position(0, 0), 10, 10, 4, 2, 4, 2),
|
|
];
|
|
yield 'empty team id' => [
|
|
static fn (): UnitState => new UnitState('unit-1', '', new Position(0, 0), 10, 10, 4, 2, 4, 2),
|
|
];
|
|
yield 'zero maximum health' => [
|
|
static fn (): UnitState => new UnitState('unit-1', 'alpha', new Position(0, 0), 0, 0, 4, 2, 4, 2),
|
|
];
|
|
yield 'negative health' => [
|
|
static fn (): UnitState => new UnitState('unit-1', 'alpha', new Position(0, 0), 10, -1, 4, 2, 4, 2),
|
|
];
|
|
yield 'health above maximum' => [
|
|
static fn (): UnitState => new UnitState('unit-1', 'alpha', new Position(0, 0), 10, 11, 4, 2, 4, 2),
|
|
];
|
|
yield 'negative attack' => [
|
|
static fn (): UnitState => new UnitState('unit-1', 'alpha', new Position(0, 0), 10, 10, -1, 2, 4, 2),
|
|
];
|
|
yield 'negative defense' => [
|
|
static fn (): UnitState => new UnitState('unit-1', 'alpha', new Position(0, 0), 10, 10, 4, -1, 4, 2),
|
|
];
|
|
yield 'zero speed' => [
|
|
static fn (): UnitState => new UnitState('unit-1', 'alpha', new Position(0, 0), 10, 10, 4, 2, 0, 2),
|
|
];
|
|
yield 'negative actions remaining' => [
|
|
static fn (): UnitState => new UnitState('unit-1', 'alpha', new Position(0, 0), 10, 10, 4, 2, 4, -1),
|
|
];
|
|
yield 'too many actions remaining' => [
|
|
static fn (): UnitState => new UnitState('unit-1', 'alpha', new Position(0, 0), 10, 10, 4, 2, 4, 3),
|
|
];
|
|
}
|
|
|
|
public function testItReportsWhetherItIsDefeated(): void
|
|
{
|
|
self::assertTrue($this->unit(health: 0)->isDefeated());
|
|
self::assertFalse($this->unit(health: 1)->isDefeated());
|
|
}
|
|
|
|
public function testMovingReturnsAChangedCopy(): void
|
|
{
|
|
$unit = $this->unit();
|
|
$moved = $unit->moveTo(new Position(2, 3));
|
|
|
|
self::assertNotSame($unit, $moved);
|
|
self::assertSame('0:0', $unit->position->key());
|
|
self::assertSame('2:3', $moved->position->key());
|
|
}
|
|
|
|
public function testSpendingAnActionReturnsAChangedCopyAndRejectsExhaustedUnits(): void
|
|
{
|
|
$unit = $this->unit(actionsRemaining: 1);
|
|
$spent = $unit->spendAction();
|
|
|
|
self::assertSame(1, $unit->actionsRemaining);
|
|
self::assertSame(0, $spent->actionsRemaining);
|
|
|
|
$this->expectException(InvalidArgumentException::class);
|
|
$spent->spendAction();
|
|
}
|
|
|
|
public function testDamageCannotReduceHealthBelowZero(): void
|
|
{
|
|
$unit = $this->unit(health: 3);
|
|
$damaged = $unit->takeDamage(5);
|
|
|
|
self::assertSame(3, $unit->health);
|
|
self::assertSame(0, $damaged->health);
|
|
}
|
|
|
|
public function testNegativeDamageIsANoOp(): void
|
|
{
|
|
$unit = $this->unit(health: 7);
|
|
$damaged = $unit->takeDamage(-3);
|
|
|
|
self::assertNotSame($unit, $damaged);
|
|
self::assertSame(7, $damaged->health);
|
|
}
|
|
|
|
public function testMarkingAttackedReturnsAChangedCopy(): void
|
|
{
|
|
$unit = $this->unit();
|
|
$attacked = $unit->markAttacked();
|
|
|
|
self::assertFalse($unit->hasAttacked);
|
|
self::assertTrue($attacked->hasAttacked);
|
|
}
|
|
|
|
public function testStartingTurnResetsALivingUnit(): void
|
|
{
|
|
$unit = $this->unit(actionsRemaining: 0, hasAttacked: true);
|
|
$started = $unit->startTurn();
|
|
|
|
self::assertNotSame($unit, $started);
|
|
self::assertSame(2, $started->actionsRemaining);
|
|
self::assertFalse($started->hasAttacked);
|
|
}
|
|
|
|
public function testStartingTurnReturnsTheSameDefeatedUnit(): void
|
|
{
|
|
$unit = $this->unit(health: 0, actionsRemaining: 0, hasAttacked: true);
|
|
|
|
self::assertSame($unit, $unit->startTurn());
|
|
}
|
|
|
|
private function unit(
|
|
int $health = 10,
|
|
int $actionsRemaining = 2,
|
|
bool $hasAttacked = false,
|
|
): UnitState {
|
|
return new UnitState(
|
|
'unit-1',
|
|
'alpha',
|
|
new Position(0, 0),
|
|
10,
|
|
$health,
|
|
4,
|
|
2,
|
|
4,
|
|
$actionsRemaining,
|
|
$hasAttacked,
|
|
);
|
|
}
|
|
}
|