feat: add the booking domain layer

This commit is contained in:
fromme committed 2026-09-13 14:54:42 +02:00
1 parent ef8a88ed2a
commit cb407477bf
13 files changed
+693 -6

No files matched your search

+149
View File
@@ -0,0 +1,149 @@
<?php
declare(strict_types=1);
namespace App\Tests\Repository;
use App\Entity\Booking;
use App\Entity\Resource;
use App\Entity\ResourceType;
use App\Entity\User;
use App\Repository\BookingRepository;
use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
/**
* Hits the real database, because the behaviour under test is the database's: the unique
* constraint is what actually prevents double-booking.
*
* Each test runs inside a transaction that is rolled back afterwards.
*/
class BookingRepositoryTest extends KernelTestCase
{
private EntityManagerInterface $em;
private BookingRepository $bookings;
protected function setUp(): void
{
self::bootKernel();
$this->em = self::getContainer()->get(EntityManagerInterface::class);
$this->bookings = self::getContainer()->get(BookingRepository::class);
$this->em->getConnection()->beginTransaction();
}
protected function tearDown(): void
{
$this->em->getConnection()->rollBack();
parent::tearDown();
}
public function testTheSameResourceCannotBeBookedTwiceOnOneDay(): void
{
$desk = $this->resource('D1');
$date = new \DateTimeImmutable('2026-10-01');
$this->persist(new Booking($this->user('[email protected]'), $desk, $date));
$this->em->flush();
$this->persist(new Booking($this->user('[email protected]'), $desk, $date));
// The controller catches exactly this and re-renders the fragment with an inline error,
// rather than pre-checking, which would be racy.
$this->expectException(UniqueConstraintViolationException::class);
$this->em->flush();
}
public function testTheSameResourceOnAnotherDayIsFine(): void
{
$desk = $this->resource('D1');
$user = $this->user('[email protected]');
$this->persist(new Booking($user, $desk, new \DateTimeImmutable('2026-10-01')));
$this->persist(new Booking($user, $desk, new \DateTimeImmutable('2026-10-02')));
$this->em->flush();
self::assertCount(1, $this->bookings->findForDate(new \DateTimeImmutable('2026-10-01')));
}
public function testATimeComponentDoesNotCreateASecondSlotForTheSameDay(): void
{
$desk = $this->resource('D1');
$this->persist(new Booking($this->user('[email protected]'), $desk, new \DateTimeImmutable('2026-10-01 08:00')));
$this->em->flush();
$this->persist(new Booking($this->user('[email protected]'), $desk, new \DateTimeImmutable('2026-10-01 17:30')));
$this->expectException(UniqueConstraintViolationException::class);
$this->em->flush();
}
public function testFindForDateCarriesTheBookingHolderForTheTeamView(): void
{
$this->persist(new Booking($this->user('[email protected]', 'Anna'), $this->resource('D1'), new \DateTimeImmutable('2026-10-01')));
$this->em->flush();
$this->em->clear();
$bookings = $this->bookings->findForDate(new \DateTimeImmutable('2026-10-01'));
self::assertCount(1, $bookings);
self::assertSame('Anna', $bookings[0]->getUser()->getName());
self::assertSame('D1', $bookings[0]->getResource()->getLabel());
}
public function testCountPerDateInRangeIsKeyedByDay(): void
{
$user = $this->user('[email protected]');
$this->persist(new Booking($user, $this->resource('D1'), new \DateTimeImmutable('2026-10-01')));
$this->persist(new Booking($this->user('[email protected]'), $this->resource('D2'), new \DateTimeImmutable('2026-10-01')));
$this->persist(new Booking($user, $this->resource('P1', ResourceType::PARKING), new \DateTimeImmutable('2026-10-03')));
$this->em->flush();
$counts = $this->bookings->countPerDateInRange(new \DateTimeImmutable('2026-10-01'), new \DateTimeImmutable('2026-10-31'));
self::assertSame(['2026-10-01' => 2, '2026-10-03' => 1], $counts);
}
public function testCountPerDateIgnoresDaysOutsideTheWindow(): void
{
$this->persist(new Booking($this->user('[email protected]'), $this->resource('D1'), new \DateTimeImmutable('2026-11-05')));
$this->em->flush();
self::assertSame([], $this->bookings->countPerDateInRange(new \DateTimeImmutable('2026-10-01'), new \DateTimeImmutable('2026-10-31')));
}
public function testFindForUserInRangeOnlyReturnsThatPersonsBookings(): void
{
$anna = $this->user('[email protected]');
$this->persist(new Booking($anna, $this->resource('D1'), new \DateTimeImmutable('2026-10-01')));
$this->persist(new Booking($this->user('[email protected]'), $this->resource('D2'), new \DateTimeImmutable('2026-10-01')));
$this->em->flush();
$mine = $this->bookings->findForUserInRange($anna, new \DateTimeImmutable('2026-10-01'), new \DateTimeImmutable('2026-10-31'));
self::assertCount(1, $mine);
self::assertSame('D1', $mine[0]->getResource()->getLabel());
}
private function persist(object $entity): void
{
$this->em->persist($entity);
}
private function user(string $email, string $name = 'Test'): User
{
$user = new User($email, $name);
$this->em->persist($user);
return $user;
}
private function resource(string $label, ResourceType $type = ResourceType::DESK): Resource
{
$resource = new Resource($type, $label);
$this->em->persist($resource);
return $resource;
}
}