feat: refactored accommodation booking status model

This commit is contained in:
Björn Fromme
2026-08-19 11:46:52 +02:00
parent e8accbb3ed
commit 5a3957e143
42 changed files with 1190 additions and 277 deletions
@@ -8,7 +8,7 @@ use App\Entity\Groups\AccommodationBooking;
use App\Enum\Groups\AccommodationBookingStatus;
use App\Form\Admin\Groups\AccommodationBookingCreateType;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Validator\Constraints\Email;
use Symfony\Component\Validator\Constraints\EmailValidator;
@@ -17,76 +17,74 @@ use Symfony\Component\Validator\Validation;
class AccommodationBookingCreateTypeTest extends TestCase
{
public function testDraftsAreValidatedWithoutTheOfferGroup(): void
public function testCreationIsAlwaysValidatedAsAScratchRecord(): void
{
self::assertSame(['Default'], $this->resolveValidationGroups(AccommodationBookingStatus::Draft));
self::assertSame(['Default'], $this->resolveValidationGroups());
}
public function testOffersAddTheOfferGroup(): void
public function testTheStatusCannotBeChosenOnCreation(): void
{
self::assertSame(['Default', 'offer'], $this->resolveValidationGroups(AccommodationBookingStatus::Open));
// A new record is always an Entwurf and only leaves that status through a named
// action, so offering the choice here would let it start out somewhere it cannot
// have arrived at legitimately.
self::assertNotContains('status', $this->builtFieldNames());
}
public function testDraftCanBeCreatedWithoutAnEmail(): void
public function testANewBookingStartsAsADraft(): void
{
$booking = new AccommodationBooking();
$booking->setStatus(AccommodationBookingStatus::Draft);
self::assertTrue((new AccommodationBooking())->isDraft());
}
$violations = $this->validate($booking, $this->resolveValidationGroups(AccommodationBookingStatus::Draft));
public function testABookingCanBeCreatedWithoutAnEmail(): void
{
// The address is enforced when the booking is actually sent somewhere — see
// SendOfferController and ConfirmController — not while it is still a scratch record.
$violations = $this->validate(new AccommodationBooking(), $this->resolveValidationGroups());
self::assertCount(0, $violations);
}
public function testOfferCannotBeCreatedWithoutAnEmail(): void
public function testAMalformedEmailIsStillRejectedOnceTheBookingIsEdited(): void
{
$booking = new AccommodationBooking();
$booking->setStatus(AccommodationBookingStatus::Open);
$booking->setStatus(AccommodationBookingStatus::Requested);
$booking->setEmail('not-an-address');
$violations = $this->validate($booking, $this->resolveValidationGroups(AccommodationBookingStatus::Open));
$violations = $this->validate($booking, ['edit']);
$properties = array_map(static fn ($violation) => $violation->getPropertyPath(), iterator_to_array($violations));
self::assertSame(['email'], $properties, 'only the address is enforced here — the rest of the contact data belongs to the edit form');
}
public function testOfferRejectsAMalformedEmail(): void
{
$booking = new AccommodationBooking();
$booking->setStatus(AccommodationBookingStatus::Open);
$booking->setEmail('not-an-address');
$violations = $this->validate($booking, $this->resolveValidationGroups(AccommodationBookingStatus::Open));
self::assertCount(1, $violations);
self::assertSame('email', $violations[0]->getPropertyPath());
}
public function testOfferWithAnEmailIsValid(): void
{
$booking = new AccommodationBooking();
$booking->setStatus(AccommodationBookingStatus::Open);
$booking->setEmail('[email protected]');
$violations = $this->validate($booking, $this->resolveValidationGroups(AccommodationBookingStatus::Open));
self::assertCount(0, $violations);
self::assertContains('email', $properties);
}
/**
* @return string[]
*/
private function resolveValidationGroups(AccommodationBookingStatus $status): array
private function resolveValidationGroups(): array
{
$resolver = new OptionsResolver();
(new AccommodationBookingCreateType())->configureOptions($resolver);
$booking = new AccommodationBooking();
$booking->setStatus($status);
return $resolver->resolve()['validation_groups'];
}
$form = $this->createMock(FormInterface::class);
$form->method('getData')->willReturn($booking);
/**
* @return string[]
*/
private function builtFieldNames(): array
{
$names = [];
return ($resolver->resolve()['validation_groups'])($form);
$builder = $this->createMock(FormBuilderInterface::class);
$builder->method('add')->willReturnCallback(static function (string $name) use (&$names, $builder) {
$names[] = $name;
return $builder;
});
(new AccommodationBookingCreateType())->buildForm($builder, ['data' => new AccommodationBooking()]);
return $names;
}
/**