138 lines
4.1 KiB
PHP
138 lines
4.1 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Tests\Model;
|
|
|
|
use App\Entity\Booking;
|
|
use App\Entity\Resource;
|
|
use App\Entity\ResourceGroup;
|
|
use App\Entity\User;
|
|
use App\Enum\ResourceType;
|
|
use App\Model\Spot;
|
|
use App\Model\SpotGroup;
|
|
use App\Model\TimeRange;
|
|
use PHPUnit\Framework\TestCase;
|
|
|
|
class SpotGroupTest extends TestCase
|
|
{
|
|
public function testSpotsArePartitionedByTheirRoom(): void
|
|
{
|
|
$north = $this->room('Raum Nord');
|
|
$south = $this->room('Raum Süd');
|
|
|
|
$groups = SpotGroup::over([
|
|
$this->spot('D1', $north),
|
|
$this->spot('D2', $north),
|
|
$this->spot('D3', $south),
|
|
]);
|
|
|
|
self::assertCount(2, $groups);
|
|
self::assertSame($north, $groups[0]->group);
|
|
self::assertCount(2, $groups[0]->spots);
|
|
self::assertSame($south, $groups[1]->group);
|
|
self::assertCount(1, $groups[1]->spots);
|
|
}
|
|
|
|
/**
|
|
* The repository orders by group position, so the order spots arrive in is already the order
|
|
* they should be drawn in -- grouping must not reshuffle it.
|
|
*/
|
|
public function testTheOrderSpotsArriveInIsKept(): void
|
|
{
|
|
$first = $this->room('Erster Raum');
|
|
$second = $this->room('Zweiter Raum');
|
|
|
|
$groups = SpotGroup::over([$this->spot('D1', $second), $this->spot('D2', $first)]);
|
|
|
|
self::assertSame(['Zweiter Raum', 'Erster Raum'], array_map(
|
|
static fn (SpotGroup $group): string => $group->group?->getLabel() ?? '',
|
|
$groups,
|
|
));
|
|
}
|
|
|
|
/**
|
|
* Two unpersisted groups both have a null id, so grouping cannot key on it.
|
|
*/
|
|
public function testTwoUnsavedRoomsDoNotCollapseIntoOne(): void
|
|
{
|
|
$groups = SpotGroup::over([$this->spot('D1', $this->room('A')), $this->spot('D2', $this->room('B'))]);
|
|
|
|
self::assertCount(2, $groups);
|
|
}
|
|
|
|
public function testUngroupedSpotsCollectIntoOneTrailingGroup(): void
|
|
{
|
|
$room = $this->room('Raum Nord');
|
|
|
|
$groups = SpotGroup::over([$this->spot('D1', null), $this->spot('D2', $room), $this->spot('D3', null)]);
|
|
|
|
self::assertCount(2, $groups);
|
|
self::assertSame($room, $groups[0]->group);
|
|
self::assertNull($groups[1]->group);
|
|
self::assertCount(2, $groups[1]->spots);
|
|
}
|
|
|
|
public function testAPlanWithNoGroupsAtAllIsOneNullGroup(): void
|
|
{
|
|
$groups = SpotGroup::over([$this->spot('P1', null), $this->spot('P2', null)]);
|
|
|
|
self::assertCount(1, $groups);
|
|
self::assertNull($groups[0]->group);
|
|
}
|
|
|
|
public function testNoSpotsMeansNoGroups(): void
|
|
{
|
|
self::assertSame([], SpotGroup::over([]));
|
|
}
|
|
|
|
/**
|
|
* FREE only, deliberately: a partly booked desk is already amber on the plan, and counting
|
|
* it here would make the badge disagree with the colours beside it.
|
|
*/
|
|
public function testOnlyFullyFreeSpotsAreCounted(): void
|
|
{
|
|
$room = $this->room('Raum Nord');
|
|
|
|
$group = SpotGroup::over([
|
|
$this->spot('D1', $room),
|
|
$this->spot('D2', $room, [$this->booking(540, 780)]), // PARTLY
|
|
$this->spot('D3', $room, [$this->booking(0, 1440)]), // TAKEN
|
|
])[0];
|
|
|
|
self::assertCount(3, $group->spots);
|
|
self::assertSame(1, $group->freeCount());
|
|
}
|
|
|
|
/**
|
|
* @param list<Booking> $bookings
|
|
*/
|
|
private function spot(string $label, ?ResourceGroup $group, array $bookings = []): Spot
|
|
{
|
|
$resource = new Resource(ResourceType::DESK, $label);
|
|
$resource->setGroup($group);
|
|
|
|
return Spot::for($resource, $bookings, TimeRange::wholeDay(), $this->viewer());
|
|
}
|
|
|
|
private function booking(int $start, int $end): Booking
|
|
{
|
|
return new Booking(
|
|
new UserWithId('[email protected]', 'Bea', 2),
|
|
new Resource(ResourceType::DESK, 'X'),
|
|
new \DateTimeImmutable('2026-10-01'),
|
|
TimeRange::fromMinutes($start, $end),
|
|
);
|
|
}
|
|
|
|
private function room(string $label): ResourceGroup
|
|
{
|
|
return new ResourceGroup(ResourceType::DESK, $label);
|
|
}
|
|
|
|
private function viewer(): User
|
|
{
|
|
return new UserWithId('[email protected]', 'Anna', 1);
|
|
}
|
|
}
|