From 5d1d7fa19ee10b88c4aee080df71b7f0fabc7fd5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Fromme?= Date: Mon, 5 Oct 2026 17:12:42 +0200 Subject: [PATCH] fix: keep the booking tests independent of the current date --- tests/Controller/BookingControllerTest.php | 123 ++++++++++++--------- 1 file changed, 68 insertions(+), 55 deletions(-) diff --git a/tests/Controller/BookingControllerTest.php b/tests/Controller/BookingControllerTest.php index b8df914..063c3fe 100644 --- a/tests/Controller/BookingControllerTest.php +++ b/tests/Controller/BookingControllerTest.php @@ -26,6 +26,7 @@ class BookingControllerTest extends WebTestCase { private KernelBrowser $client; private EntityManagerInterface $em; + private \DateTimeImmutable $monday; protected function setUp(): void { @@ -33,6 +34,10 @@ class BookingControllerTest extends WebTestCase $this->client->disableReboot(); $this->em = self::getContainer()->get(EntityManagerInterface::class); $this->em->getConnection()->beginTransaction(); + // Every day these tests book on is counted from here. Fixed dates fell into the past and + // started being refused; the next Monday is always ahead, always inside the booking + // horizon, and keeps the weekday the series tests count on. + $this->monday = new \DateTimeImmutable('next monday'); } protected function tearDown(): void @@ -45,11 +50,11 @@ class BookingControllerTest extends WebTestCase { $this->client->loginUser($this->user('anna@ep-reisen.de')); - $this->client->request('GET', '/book/desk/day/2026-10-01', server: ['HTTP_HX-Request' => 'true']); + $this->client->request('GET', '/book/desk/day/'.$this->day(), server: ['HTTP_HX-Request' => 'true']); $response = $this->client->getResponse(); self::assertResponseIsSuccessful(); - self::assertSame('/book/desk?date=2026-10-01', $response->headers->get('HX-Push-Url')); + self::assertSame('/book/desk?date='.$this->day(), $response->headers->get('HX-Push-Url')); self::assertSame(2, substr_count((string) $response->getContent(), 'hx-swap-oob="true"')); self::assertStringContainsString('id="board-calendar"', (string) $response->getContent()); self::assertStringContainsString('id="board-floorplan"', (string) $response->getContent()); @@ -59,9 +64,9 @@ class BookingControllerTest extends WebTestCase { $this->client->loginUser($this->user('anna@ep-reisen.de')); - $this->client->request('GET', '/book/desk/day/2026-10-01'); + $this->client->request('GET', '/book/desk/day/'.$this->day()); - self::assertResponseRedirects('/book/desk?date=2026-10-01'); + self::assertResponseRedirects('/book/desk?date='.$this->day()); } /** @@ -73,7 +78,7 @@ class BookingControllerTest extends WebTestCase $this->client->loginUser($this->user('anna@ep-reisen.de')); $desk = $this->resource('D1'); - $response = $this->submitBookingModal($desk, '2026-10-01'); + $response = $this->submitBookingModal($desk, $this->day()); self::assertSame(Response::HTTP_OK, $response->getStatusCode()); // The modal is gone from the response, which is what closes it: an out-of-band-only @@ -91,7 +96,7 @@ class BookingControllerTest extends WebTestCase { $this->client->loginUser($this->user('anna@ep-reisen.de')); - $response = $this->submitBookingModal($this->resource('D1'), '2026-10-01', ['allDay' => '1', 'date' => '2020-01-01']); + $response = $this->submitBookingModal($this->resource('D1'), $this->day(), ['allDay' => '1', 'date' => '2020-01-01']); self::assertSame(Response::HTTP_UNPROCESSABLE_ENTITY, $response->getStatusCode()); self::assertStringContainsString('id="htmx-modal"', (string) $response->getContent()); @@ -104,15 +109,15 @@ class BookingControllerTest extends WebTestCase public function testADeskAlreadyTakenIsReportedInsideTheModal(): void { $desk = $this->resource('D1'); - $this->em->persist(new Booking($this->user('bea@ep-reisen.de'), $desk, new \DateTimeImmutable('2026-10-01'))); + $this->em->persist(new Booking($this->user('bea@ep-reisen.de'), $desk, new \DateTimeImmutable($this->day()))); $this->em->flush(); $this->client->loginUser($this->user('anna@ep-reisen.de')); - $response = $this->submitBookingModal($desk, '2026-10-01'); + $response = $this->submitBookingModal($desk, $this->day()); self::assertSame(Response::HTTP_UNPROCESSABLE_ENTITY, $response->getStatusCode()); - self::assertStringContainsString('ist am 01.10.2026 bereits belegt', (string) $response->getContent()); + self::assertStringContainsString(\sprintf('ist am %s bereits belegt', $this->day(0, 'd.m.Y')), (string) $response->getContent()); } /** @@ -123,12 +128,12 @@ class BookingControllerTest extends WebTestCase public function testASecondDeskAtTheSameTimeIsBookedAndWarnedAbout(): void { $anna = $this->user('anna@ep-reisen.de'); - $this->em->persist(new Booking($anna, $this->resource('D1'), new \DateTimeImmutable('2026-10-01'))); + $this->em->persist(new Booking($anna, $this->resource('D1'), new \DateTimeImmutable($this->day()))); $this->em->flush(); $this->client->loginUser($anna); - $response = $this->submitBookingModal($this->resource('D2'), '2026-10-01'); + $response = $this->submitBookingModal($this->resource('D2'), $this->day()); $content = (string) $response->getContent(); self::assertSame(Response::HTTP_OK, $response->getStatusCode()); @@ -139,13 +144,13 @@ class BookingControllerTest extends WebTestCase public function testEveryClashingDeskIsNamedInTheWarning(): void { $anna = $this->user('anna@ep-reisen.de'); - $this->em->persist(new Booking($anna, $this->resource('D1'), new \DateTimeImmutable('2026-10-01'))); - $this->em->persist(new Booking($anna, $this->resource('D2'), new \DateTimeImmutable('2026-10-01'))); + $this->em->persist(new Booking($anna, $this->resource('D1'), new \DateTimeImmutable($this->day()))); + $this->em->persist(new Booking($anna, $this->resource('D2'), new \DateTimeImmutable($this->day()))); $this->em->flush(); $this->client->loginUser($anna); - $response = $this->submitBookingModal($this->resource('D3'), '2026-10-01'); + $response = $this->submitBookingModal($this->resource('D3'), $this->day()); self::assertSame(Response::HTTP_OK, $response->getStatusCode()); self::assertStringContainsString('bereits 2 weitere Schreibtische gebucht: D1, D2', (string) $response->getContent()); @@ -154,12 +159,12 @@ class BookingControllerTest extends WebTestCase public function testTwoDesksAtDifferentTimesAreNotWarnedAbout(): void { $anna = $this->user('anna@ep-reisen.de'); - $this->em->persist(new Booking($anna, $this->resource('D1'), new \DateTimeImmutable('2026-10-01'), TimeRange::fromMinutes(540, 720))); + $this->em->persist(new Booking($anna, $this->resource('D1'), new \DateTimeImmutable($this->day()), TimeRange::fromMinutes(540, 720))); $this->em->flush(); $this->client->loginUser($anna); - $response = $this->submitBookingModal($this->resource('D2'), '2026-10-01', [ + $response = $this->submitBookingModal($this->resource('D2'), $this->day(), [ 'startTime' => '13:00', 'endTime' => '17:00', ]); @@ -175,12 +180,12 @@ class BookingControllerTest extends WebTestCase public function testADeskAndAParkingSpotOnTheSameDayAreBothAllowedWithoutAWarning(): void { $anna = $this->user('anna@ep-reisen.de'); - $this->em->persist(new Booking($anna, $this->resource('D1'), new \DateTimeImmutable('2026-10-01'))); + $this->em->persist(new Booking($anna, $this->resource('D1'), new \DateTimeImmutable($this->day()))); $this->em->flush(); $this->client->loginUser($anna); - $response = $this->submitBookingModal($this->resource('P1', ResourceType::PARKING), '2026-10-01'); + $response = $this->submitBookingModal($this->resource('P1', ResourceType::PARKING), $this->day()); self::assertSame(Response::HTTP_OK, $response->getStatusCode()); self::assertStringNotContainsString('Hinweis', (string) $response->getContent()); @@ -191,33 +196,33 @@ class BookingControllerTest extends WebTestCase $anna = $this->user('anna@ep-reisen.de'); $other = $this->resource('D2'); // Two of the four Mondays the series lands on, and one Tuesday it does not. - $this->em->persist(new Booking($anna, $other, new \DateTimeImmutable('2026-10-05'), TimeRange::fromMinutes(600, 660))); - $this->em->persist(new Booking($anna, $other, new \DateTimeImmutable('2026-10-19'), TimeRange::fromMinutes(600, 660))); - $this->em->persist(new Booking($anna, $other, new \DateTimeImmutable('2026-10-20'), TimeRange::fromMinutes(600, 660))); + $this->em->persist(new Booking($anna, $other, new \DateTimeImmutable($this->day()), TimeRange::fromMinutes(600, 660))); + $this->em->persist(new Booking($anna, $other, new \DateTimeImmutable($this->day(14)), TimeRange::fromMinutes(600, 660))); + $this->em->persist(new Booking($anna, $other, new \DateTimeImmutable($this->day(15)), TimeRange::fromMinutes(600, 660))); $this->em->flush(); $this->client->loginUser($anna); - $response = $this->submitBookingModal($this->resource('D1'), '2026-10-05', [ + $response = $this->submitBookingModal($this->resource('D1'), $this->day(), [ 'startTime' => '09:00', 'endTime' => '12:00', 'repeat' => 'weekly', - 'until' => '2026-10-30', + 'until' => $this->day(25), ]); $content = (string) $response->getContent(); self::assertSame(Response::HTTP_OK, $response->getStatusCode()); self::assertSame(1, substr_count($content, 'Hinweis:'), 'one flash for the whole series'); self::assertStringContainsString('An 2 von 4 Terminen', $content); - self::assertStringContainsString('(5.10., 19.10.)', $content); + self::assertStringContainsString(\sprintf('(%s, %s)', $this->day(0, 'j.n.'), $this->day(14, 'j.n.')), $content); } public function testMovingABookingOntoAnotherOneWarns(): void { $anna = $this->user('anna@ep-reisen.de'); - $mine = new Booking($anna, $this->resource('D1'), new \DateTimeImmutable('2026-10-01'), TimeRange::fromMinutes(780, 1020)); + $mine = new Booking($anna, $this->resource('D1'), new \DateTimeImmutable($this->day()), TimeRange::fromMinutes(780, 1020)); $this->em->persist($mine); - $this->em->persist(new Booking($anna, $this->resource('D2'), new \DateTimeImmutable('2026-10-01'), TimeRange::fromMinutes(540, 720))); + $this->em->persist(new Booking($anna, $this->resource('D2'), new \DateTimeImmutable($this->day()), TimeRange::fromMinutes(540, 720))); $this->em->flush(); $this->client->loginUser($anna); @@ -240,8 +245,8 @@ class BookingControllerTest extends WebTestCase $this->client->loginUser($this->user('anna@ep-reisen.de')); $desk = $this->resource('D1'); - $this->client->request('POST', \sprintf('/bookings/new/%d/2026-10-01', $desk->getId()), [ - 'booking' => ['date' => '2026-10-01', '_token' => 'nope'], + $this->client->request('POST', \sprintf('/bookings/new/%d/%s', $desk->getId(), $this->day()), [ + 'booking' => ['date' => $this->day(), '_token' => 'nope'], ], server: ['HTTP_HX-Request' => 'true']); self::assertSame(Response::HTTP_UNPROCESSABLE_ENTITY, $this->client->getResponse()->getStatusCode()); @@ -250,7 +255,7 @@ class BookingControllerTest extends WebTestCase public function testReleasingAnotherPersonsBookingIsRefused(): void { - $booking = new Booking($this->user('bea@ep-reisen.de'), $this->resource('D1'), new \DateTimeImmutable('2026-10-01')); + $booking = new Booking($this->user('bea@ep-reisen.de'), $this->resource('D1'), new \DateTimeImmutable($this->day())); $this->em->persist($booking); $this->em->flush(); @@ -264,7 +269,7 @@ class BookingControllerTest extends WebTestCase public function testReleasingOwnBookingSwapsTheBoard(): void { $anna = $this->user('anna@ep-reisen.de'); - $booking = new Booking($anna, $this->resource('D1'), new \DateTimeImmutable('2026-10-01')); + $booking = new Booking($anna, $this->resource('D1'), new \DateTimeImmutable($this->day())); $this->em->persist($booking); $this->em->flush(); $id = $booking->getId(); @@ -291,12 +296,12 @@ class BookingControllerTest extends WebTestCase public function testTheFloorPlanOpensTheEditModalForYourOwnBooking(): void { $anna = $this->user('anna@ep-reisen.de'); - $booking = new Booking($anna, $this->resource('D1'), new \DateTimeImmutable('2026-10-01')); + $booking = new Booking($anna, $this->resource('D1'), new \DateTimeImmutable($this->day())); $this->em->persist($booking); $this->em->flush(); $this->client->loginUser($anna); - $crawler = $this->client->request('GET', '/book/desk?date=2026-10-01'); + $crawler = $this->client->request('GET', '/book/desk?date='.$this->day()); self::assertCount(1, $crawler->filter(\sprintf('[hx-get="/bookings/%d/edit"]', $booking->getId()))); } @@ -312,13 +317,13 @@ class BookingControllerTest extends WebTestCase public function testASecondPersonCanBookTheRestOfADayFromTheDefaultBoard(): void { $desk = $this->resource('D1'); - $this->em->persist(new Booking($this->user('bea@ep-reisen.de'), $desk, new \DateTimeImmutable('2026-10-01'), TimeRange::fromMinutes(540, 780))); + $this->em->persist(new Booking($this->user('bea@ep-reisen.de'), $desk, new \DateTimeImmutable($this->day()), TimeRange::fromMinutes(540, 780))); $this->em->flush(); $this->client->loginUser($this->user('anna@ep-reisen.de')); // 1. The shape is clickable on the all-day board. - $crawler = $this->client->request('GET', '/book/desk?date=2026-10-01'); + $crawler = $this->client->request('GET', '/book/desk?date='.$this->day()); $opener = $crawler->filter(\sprintf('[data-spot][hx-get*="/bookings/new/%d/"]', $desk->getId())); self::assertCount(1, $opener, 'a partly booked desk must still open the booking modal'); @@ -330,7 +335,7 @@ class BookingControllerTest extends WebTestCase self::assertNull($modal->filter('#booking_endTime')->attr('value')); // 3. Submitting it works. - $response = $this->submitBookingModal($desk, '2026-10-01', [ + $response = $this->submitBookingModal($desk, $this->day(), [ 'startTime' => '13:00', 'endTime' => '17:00', ]); @@ -342,11 +347,11 @@ class BookingControllerTest extends WebTestCase public function testAFullyBookedDeskIsNotClickable(): void { $desk = $this->resource('D1'); - $this->em->persist(new Booking($this->user('bea@ep-reisen.de'), $desk, new \DateTimeImmutable('2026-10-01'))); + $this->em->persist(new Booking($this->user('bea@ep-reisen.de'), $desk, new \DateTimeImmutable($this->day()))); $this->em->flush(); $this->client->loginUser($this->user('anna@ep-reisen.de')); - $crawler = $this->client->request('GET', '/book/desk?date=2026-10-01'); + $crawler = $this->client->request('GET', '/book/desk?date='.$this->day()); self::assertCount(0, $crawler->filter(\sprintf('[data-spot][hx-get*="/bookings/new/%d/"]', $desk->getId()))); } @@ -356,7 +361,7 @@ class BookingControllerTest extends WebTestCase $this->client->loginUser($this->user('anna@ep-reisen.de')); $desk = $this->resource('D1'); - $response = $this->submitBookingModal($desk, '2026-10-01', [ + $response = $this->submitBookingModal($desk, $this->day(), [ 'startTime' => '09:00', 'endTime' => '13:00', ]); @@ -372,7 +377,7 @@ class BookingControllerTest extends WebTestCase $this->client->loginUser($this->user('anna@ep-reisen.de')); $desk = $this->resource('D1'); - $this->submitBookingModal($desk, '2026-10-01'); + $this->submitBookingModal($desk, $this->day()); $booking = $this->em->getRepository(Booking::class)->findOneBy(['resource' => $desk]); self::assertNotNull($booking); @@ -383,7 +388,7 @@ class BookingControllerTest extends WebTestCase { $this->client->loginUser($this->user('anna@ep-reisen.de')); - $response = $this->submitBookingModal($this->resource('D1'), '2026-10-01', [ + $response = $this->submitBookingModal($this->resource('D1'), $this->day(), [ 'startTime' => '13:00', 'endTime' => '09:00', ]); @@ -395,12 +400,12 @@ class BookingControllerTest extends WebTestCase public function testAPartDayBookingLeavesTheRestOfTheDayBookable(): void { $desk = $this->resource('D1'); - $this->em->persist(new Booking($this->user('bea@ep-reisen.de'), $desk, new \DateTimeImmutable('2026-10-01'), TimeRange::fromMinutes(540, 780))); + $this->em->persist(new Booking($this->user('bea@ep-reisen.de'), $desk, new \DateTimeImmutable($this->day()), TimeRange::fromMinutes(540, 780))); $this->em->flush(); $this->client->loginUser($this->user('anna@ep-reisen.de')); - $response = $this->submitBookingModal($desk, '2026-10-01', [ + $response = $this->submitBookingModal($desk, $this->day(), [ 'startTime' => '13:00', 'endTime' => '17:00', ]); @@ -413,13 +418,13 @@ class BookingControllerTest extends WebTestCase $this->client->loginUser($this->user('anna@ep-reisen.de')); $desk = $this->resource('D1'); - // 2026-10-05 is a Monday, and the weekday is taken from it rather than asked for -- - // the Mondays up to the 30th are the 5th, 12th, 19th and 26th. - $response = $this->submitBookingModal($desk, '2026-10-05', [ + // day() is a Monday, and the weekday is taken from it rather than asked for -- the + // Mondays up to the Friday three weeks on are four. + $response = $this->submitBookingModal($desk, $this->day(), [ 'startTime' => '09:00', 'endTime' => '12:00', 'repeat' => 'weekly', - 'until' => '2026-10-30', + 'until' => $this->day(25), ]); self::assertSame(Response::HTTP_OK, $response->getStatusCode()); @@ -436,8 +441,8 @@ class BookingControllerTest extends WebTestCase $this->client->loginUser($this->user('anna@ep-reisen.de')); $desk = $this->resource('D1'); - // Relative to today, unlike the rest of this file: the three-month horizon moves with the - // clock, so a fixed end date here would start failing the day it fell outside it. + // The three-month horizon moves with the clock, so a fixed end date here would start + // failing the day it fell outside it. $start = (new \DateTimeImmutable('today'))->modify('next monday'); $until = $start->modify('+56 days'); @@ -459,7 +464,7 @@ class BookingControllerTest extends WebTestCase { $this->client->loginUser($this->user('anna@ep-reisen.de')); - $response = $this->submitBookingModal($this->resource('D1'), '2026-10-05', [ + $response = $this->submitBookingModal($this->resource('D1'), $this->day(), [ 'allDay' => '1', 'repeat' => 'daily', ]); @@ -472,7 +477,7 @@ class BookingControllerTest extends WebTestCase { $this->client->loginUser($this->user('anna@ep-reisen.de')); - $response = $this->submitBookingModal($this->resource('D1'), '2026-10-05', [ + $response = $this->submitBookingModal($this->resource('D1'), $this->day(), [ 'allDay' => '1', 'repeat' => 'daily', 'until' => '2030-01-01', @@ -585,7 +590,7 @@ class BookingControllerTest extends WebTestCase $crawler = $this->client->request( 'GET', - \sprintf('/bookings/new/%d/2026-10-01?from=09:00&to=13:00', $desk->getId()), + \sprintf('/bookings/new/%d/%s?from=09:00&to=13:00', $desk->getId(), $this->day()), server: ['HTTP_HX-Request' => 'true'], ); @@ -598,10 +603,10 @@ class BookingControllerTest extends WebTestCase { $this->client->loginUser($this->user('anna@ep-reisen.de')); - $this->client->request('GET', '/book/desk/day/2026-10-01?from=09:00&to=13:00', server: ['HTTP_HX-Request' => 'true']); + $this->client->request('GET', '/book/desk/day/'.$this->day().'?from=09:00&to=13:00', server: ['HTTP_HX-Request' => 'true']); self::assertResponseIsSuccessful(); - self::assertSame('/book/desk?date=2026-10-01&from=09:00&to=13:00', $this->client->getResponse()->headers->get('HX-Push-Url')); + self::assertSame('/book/desk?date='.$this->day().'&from=09:00&to=13:00', $this->client->getResponse()->headers->get('HX-Push-Url')); } public function testOverridingOneOccurrenceLeavesTheSeriesAlone(): void @@ -610,11 +615,11 @@ class BookingControllerTest extends WebTestCase $this->client->loginUser($anna); $desk = $this->resource('D1'); - $this->submitBookingModal($desk, '2026-10-05', [ + $this->submitBookingModal($desk, $this->day(), [ 'startTime' => '09:00', 'endTime' => '12:00', 'repeat' => 'weekly', - 'until' => '2026-10-26', + 'until' => $this->day(21), ]); $occurrences = $this->em->getRepository(Booking::class)->findBy(['resource' => $desk], ['date' => 'ASC']); @@ -671,6 +676,14 @@ class BookingControllerTest extends WebTestCase return $this->client->getResponse(); } + /** + * A day counted from the next Monday, as the routes and form fields spell it. + */ + private function day(int $offset = 0, string $format = 'Y-m-d'): string + { + return $this->monday->modify(\sprintf('+%d days', $offset))->format($format); + } + /** * Opens a confirmation modal and reads its CSRF token off the confirm button's hx-vals -- * the same round trip a browser makes, rather than reaching into the token manager (which