client = static::createClient(server: ['HTTPS' => true]); $this->entityManager = self::getContainer()->get(EntityManagerInterface::class); $this->entityManager->createQuery(sprintf('DELETE FROM %s', ApplicationFormSubmission::class))->execute(); $this->entityManager->createQuery(sprintf('DELETE FROM %s', User::class))->execute(); $this->client->loginUser($this->reviewer()); } public function testTheDetailPageOffersTheProvisioningButton(): void { $submission = $this->submission(); $crawler = $this->client->request( 'GET', sprintf('/admin/application-form-submission/detail/%s', $submission->getUuid()), ['r' => rawurlencode('/admin/application-form-submission')] ); $this->assertSame(Response::HTTP_OK, $this->client->getResponse()->getStatusCode()); $button = $crawler->filter('button[hx-get*="create-teamer"]'); $this->assertCount(1, $button); // Like every other action on that page: it hands on the return url it was given, so "zurück" // keeps pointing at the list. $this->assertStringContainsString('r='.rawurlencode('/admin/application-form-submission'), (string) $button->attr('hx-get')); } /** * The three writes cannot be undone from here, so the confirmation names what goes to BusPro * rather than asking "are you sure". */ public function testTheConfirmationNamesWhatGoesToBusPro(): void { $crawler = $this->client->request('GET', $this->url($this->submission())); $this->assertSame(Response::HTTP_OK, $this->client->getResponse()->getStatusCode()); $modal = $crawler->filter('#htmx-confirmation-modal')->text(); $this->assertStringContainsString('Anna Muster', $modal); $this->assertStringContainsString('01.01.1990', $modal); $this->assertStringContainsString('Musterstraße 1, 10115 Berlin', $modal); } /** * A row from before the tightened endpoint contract. The confirmation says so before the * reviewer clicks, because BusPro would answer the same thing only after the attempt. */ public function testTheConfirmationFlagsAnIncompleteSubmission(): void { $crawler = $this->client->request('GET', $this->url($this->incompleteSubmission())); $modal = $crawler->filter('#htmx-confirmation-modal')->text(); $this->assertStringContainsString('ohne Geburtsdatum', $modal); $this->assertStringContainsString('ohne Straße, PLZ und Ort', $modal); } /** * The only POST a test may make: TeamerProvisioner refuses an unprovisionable submission before * it touches the api client, so nothing here reaches BusPro. It covers what the reviewer * actually sees when provisioning fails - the refusal's own wording, not "es ist ein Fehler * aufgetreten". */ public function testAnUnprovisionableSubmissionFailsWithoutWritingAnything(): void { $submission = $this->incompleteSubmission(); $this->client->request('POST', $this->url($submission)); $response = $this->client->getResponse(); $this->assertSame(Response::HTTP_OK, $response->getStatusCode()); $this->assertStringContainsString('detail', (string) $response->headers->get('HX-Redirect')); $flashes = $this->client->getRequest()->getSession()->getFlashBag()->peekAll(); $this->assertArrayHasKey('error', $flashes); $this->assertStringContainsString('Geburtsdatum', $flashes['error'][0]); $this->entityManager->clear(); $stored = $this->entityManager->getRepository(ApplicationFormSubmission::class)->find($submission->getId()); $this->assertNotNull($stored); $this->assertFalse($stored->isProvisioned()); $this->assertNull($stored->getBusProAddressId()); $this->assertNull($stored->getUser()); } private function url(ApplicationFormSubmission $submission): string { return sprintf('/admin/application-form-submission/create-teamer/%s', $submission->getUuid()); } private function submission(): ApplicationFormSubmission { return $this->persist( $this->newSubmission() ->setBirthDate('1990-01-01') ->setStreet('Musterstraße 1') ->setZipCode('10115') ->setCity('Berlin') ); } /** * Everything the contract now requires, missing - which only a row stored before it can be. */ private function incompleteSubmission(): ApplicationFormSubmission { return $this->persist($this->newSubmission()->setBirthDate('01.01.1990')); } private function newSubmission(): ApplicationFormSubmission { return (new ApplicationFormSubmission( 'applicationform', 'Bewerbungsformular', new \DateTimeImmutable('2026-09-22T12:30:00+02:00'), 'F' )) ->setFirstName('Anna') ->setLastName('Muster') ->setEmail('anna@example.com') ; } private function persist(ApplicationFormSubmission $submission): ApplicationFormSubmission { $this->entityManager->persist($submission); $this->entityManager->flush(); return $submission; } private function reviewer(): User { $user = (new User()) ->setFirstName('Rita') ->setLastName('Reviewer') ->setEmail('rita.reviewer@example.com') ->setRoles(['ROLE_TEAM_ADMIN']) ->setBusProAddressId(1) ->setBusProPersonId(1) ; $this->entityManager->persist($user); $this->entityManager->flush(); return $user; } }