feat: upgrade phpunit to 12.5

This commit is contained in:
Björn Fromme
2026-08-31 09:31:00 +02:00
parent 010cfe56b2
commit bfad61f1dd
95 changed files with 1436 additions and 1224 deletions
@@ -141,7 +141,7 @@ class AccommodationBookingBreakdownCalculatorTest extends TestCase
'undersubscription40Eur' => 0,
'undersubscription40Chf' => 0,
]),
$priceRepository ?? $this->createMock(AccommodationPriceRepository::class),
$priceRepository ?? $this->createStub(AccommodationPriceRepository::class),
);
}
}
@@ -178,7 +178,7 @@ class AccommodationBookingLinkSignerTest extends TestCase
private function createSigner(): AccommodationBookingLinkSigner
{
$urlGenerator = $this->createMock(UrlGeneratorInterface::class);
$urlGenerator = $this->createStub(UrlGeneratorInterface::class);
$urlGenerator
->method('generate')
->willReturnCallback(static fn (string $name, array $parameters) => sprintf(
@@ -64,7 +64,7 @@ class AccommodationBookingPdfGeneratorTest extends TestCase
$twig = new Environment(new FilesystemLoader(__DIR__.'/../../templates'));
$twig->addExtension(new IntlExtension());
$calculator = $this->createMock(AccommodationBookingBreakdownCalculator::class);
$calculator = $this->createStub(AccommodationBookingBreakdownCalculator::class);
$calculator->method('compute')->willReturn($breakdown);
return new AccommodationBookingPdfGenerator($twig, $calculator, \dirname(__DIR__, 2));
@@ -25,6 +25,7 @@ use App\Service\AccommodationBookingService;
use App\Service\CmsDataProvider;
use App\Service\PriceTimelineBuilder;
use Doctrine\ORM\EntityManagerInterface;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\TestCase;
use Psr\Log\LoggerInterface;
@@ -153,10 +154,10 @@ class AccommodationBookingServiceTest extends TestCase
$booking->setStatus(AccommodationBookingStatus::Open);
$booking->setAccessLinkIssuedAt(new \DateTimeImmutable());
$linkSigner = $this->createMock(AccommodationBookingLinkSigner::class);
$linkSigner = $this->createStub(AccommodationBookingLinkSigner::class);
$linkSigner->method('sign')->willReturn('https://example.com/offer/signed-link');
$breakdownCalculator = $this->createMock(AccommodationBookingBreakdownCalculator::class);
$breakdownCalculator = $this->createStub(AccommodationBookingBreakdownCalculator::class);
$breakdownCalculator->method('compute')->willReturn(null);
$mailer = $this->createMock(Mailer::class);
@@ -183,7 +184,7 @@ class AccommodationBookingServiceTest extends TestCase
$linkSigner = $this->createMock(AccommodationBookingLinkSigner::class);
$linkSigner->method('sign')->with($booking)->willReturn('https://example.com/offer/signed-link');
$breakdownCalculator = $this->createMock(AccommodationBookingBreakdownCalculator::class);
$breakdownCalculator = $this->createStub(AccommodationBookingBreakdownCalculator::class);
$breakdownCalculator->method('compute')->willReturn(['total' => 1000, 'currency' => 'EUR']);
$mailer = $this->createMock(Mailer::class);
@@ -208,7 +209,7 @@ class AccommodationBookingServiceTest extends TestCase
$booking->setEmail('[email protected]');
$booking->setStatus(AccommodationBookingStatus::Open);
$breakdownCalculator = $this->createMock(AccommodationBookingBreakdownCalculator::class);
$breakdownCalculator = $this->createStub(AccommodationBookingBreakdownCalculator::class);
$breakdownCalculator->method('compute')->willReturn(null);
$mailer = $this->createMock(Mailer::class);
@@ -231,10 +232,10 @@ class AccommodationBookingServiceTest extends TestCase
$booking->setEmail('[email protected]');
$booking->setStatus(AccommodationBookingStatus::Open);
$breakdownCalculator = $this->createMock(AccommodationBookingBreakdownCalculator::class);
$breakdownCalculator = $this->createStub(AccommodationBookingBreakdownCalculator::class);
$breakdownCalculator->method('compute')->willReturn(null);
$mailer = $this->createMock(Mailer::class);
$mailer = $this->createStub(Mailer::class);
$mailer->method('createAndSendEmail')->willThrowException(new \RuntimeException('SMTP down'));
$logger = $this->createMock(LoggerInterface::class);
@@ -258,7 +259,7 @@ class AccommodationBookingServiceTest extends TestCase
$entityManager = $this->createMock(EntityManagerInterface::class);
$entityManager->expects(self::once())->method('flush');
$breakdownCalculator = $this->createMock(AccommodationBookingBreakdownCalculator::class);
$breakdownCalculator = $this->createStub(AccommodationBookingBreakdownCalculator::class);
$breakdownCalculator->method('compute')->willReturn(null);
$mailer = $this->createMock(Mailer::class);
@@ -416,9 +417,7 @@ class AccommodationBookingServiceTest extends TestCase
self::assertSame($issuedAt, $booking->getAccessLinkIssuedAt());
}
/**
* @dataProvider statusesThatAreNotAwaitingAnOffer
*/
#[DataProvider('statusesThatAreNotAwaitingAnOffer')]
public function testSendOfferNoOpsOnceTheOfferIsOut(AccommodationBookingStatus $status): void
{
$entityManager = $this->createMock(EntityManagerInterface::class);
@@ -495,9 +494,7 @@ class AccommodationBookingServiceTest extends TestCase
self::assertSame($issuedAt, $booking->getAccessLinkIssuedAt());
}
/**
* @dataProvider statusesThatAreNotAwaitingAnOffer
*/
#[DataProvider('statusesThatAreNotAwaitingAnOffer')]
public function testGenerateAccessLinkNoOpsOnceTheOfferIsOut(AccommodationBookingStatus $status): void
{
$entityManager = $this->createMock(EntityManagerInterface::class);
@@ -533,9 +530,7 @@ class AccommodationBookingServiceTest extends TestCase
self::assertSame(AccommodationBookingStatus::Discarded, $booking->getStatus());
}
/**
* @dataProvider closedStatuses
*/
#[DataProvider('closedStatuses')]
public function testDiscardBookingNoOpsForAClosedBooking(AccommodationBookingStatus $status): void
{
$entityManager = $this->createMock(EntityManagerInterface::class);
@@ -643,7 +638,7 @@ class AccommodationBookingServiceTest extends TestCase
$booking->setEmail('[email protected]');
$booking->setRemarks('vom Telefonat');
$service = $this->createServiceWithAccommodation(mailer: $this->createMock(Mailer::class));
$service = $this->createServiceWithAccommodation(mailer: $this->createStub(Mailer::class));
$service->acceptBooking($booking, " Bitte Zimmer im EG\nund Frühstück um 8 \n");
@@ -657,7 +652,7 @@ class AccommodationBookingServiceTest extends TestCase
$booking->setEmail('[email protected]');
$booking->setRemarks('vom Telefonat');
$service = $this->createServiceWithAccommodation(mailer: $this->createMock(Mailer::class));
$service = $this->createServiceWithAccommodation(mailer: $this->createStub(Mailer::class));
$service->acceptBooking($booking, ' ');
@@ -671,7 +666,7 @@ class AccommodationBookingServiceTest extends TestCase
$booking->setEmail('[email protected]');
$booking->setRemarks('vom Telefonat');
$service = $this->createServiceWithAccommodation(mailer: $this->createMock(Mailer::class));
$service = $this->createServiceWithAccommodation(mailer: $this->createStub(Mailer::class));
$service->acceptBooking($booking);
@@ -684,7 +679,7 @@ class AccommodationBookingServiceTest extends TestCase
$booking->setStatus(AccommodationBookingStatus::Received);
$booking->setRemarks('vom Telefonat');
$service = $this->createServiceWithAccommodation(mailer: $this->createMock(Mailer::class));
$service = $this->createServiceWithAccommodation(mailer: $this->createStub(Mailer::class));
$service->acceptBooking($booking, 'zu spät');
@@ -693,7 +688,7 @@ class AccommodationBookingServiceTest extends TestCase
public function testConfirmBookingConfirmsReceivedBookingAndNotifiesTheCustomer(): void
{
$entityManager = $this->createMock(EntityManagerInterface::class);
$entityManager = $this->createStub(EntityManagerInterface::class);
$booking = new AccommodationBooking();
$booking->setStatus(AccommodationBookingStatus::Received);
@@ -835,7 +830,7 @@ class AccommodationBookingServiceTest extends TestCase
$booking = new AccommodationBooking();
$booking->setStatus(AccommodationBookingStatus::Received);
$mailer = $this->createMock(Mailer::class);
$mailer = $this->createStub(Mailer::class);
$mailer->method('createAndSendEmail')->willThrowException(new \RuntimeException('SMTP down'));
$logger = $this->createMock(LoggerInterface::class);
@@ -852,7 +847,7 @@ class AccommodationBookingServiceTest extends TestCase
$booking->setEmail('[email protected]');
$booking->setStatus(AccommodationBookingStatus::Confirmed);
$mailer = $this->createMock(Mailer::class);
$mailer = $this->createStub(Mailer::class);
$mailer->method('createAndSendEmail')->willThrowException(new \RuntimeException('SMTP down'));
$logger = $this->createMock(LoggerInterface::class);
@@ -894,7 +889,7 @@ class AccommodationBookingServiceTest extends TestCase
$booking->setEmail('[email protected]');
$booking->setStatus(AccommodationBookingStatus::Confirmed);
$pdfGenerator = $this->createMock(AccommodationBookingPdfGenerator::class);
$pdfGenerator = $this->createStub(AccommodationBookingPdfGenerator::class);
$pdfGenerator->method('createAttachment')->willThrowException(new \RuntimeException('no prices'));
$mailer = $this->createMock(Mailer::class);
@@ -1038,7 +1033,7 @@ class AccommodationBookingServiceTest extends TestCase
->with(['calendarCode' => 'HOTEL'])
->willReturn($accommodation);
$priceRepo = $this->createMock(AccommodationPriceRepository::class);
$priceRepo = $this->createStub(AccommodationPriceRepository::class);
$priceRepo
->method('findByHotelCodeAndDateRange')
->willReturn($prices);
@@ -1046,16 +1041,16 @@ class AccommodationBookingServiceTest extends TestCase
return new AccommodationBookingService(
$accommodationRepo,
$priceRepo,
$this->createMock(AdditionalServiceRepository::class),
$this->createMock(BoardServiceRepository::class),
$this->createStub(AdditionalServiceRepository::class),
$this->createStub(BoardServiceRepository::class),
new PriceTimelineBuilder(),
$entityManager ?? $this->createMock(EntityManagerInterface::class),
$mailer ?? $this->createMock(Mailer::class),
$logger ?? $this->createMock(LoggerInterface::class),
$this->createMock(CmsDataProvider::class),
$linkSigner ?? $this->createMock(AccommodationBookingLinkSigner::class),
$breakdownCalculator ?? $this->createMock(AccommodationBookingBreakdownCalculator::class),
$pdfGenerator ?? $this->createMock(AccommodationBookingPdfGenerator::class),
$entityManager ?? $this->createStub(EntityManagerInterface::class),
$mailer ?? $this->createStub(Mailer::class),
$logger ?? $this->createStub(LoggerInterface::class),
$this->createStub(CmsDataProvider::class),
$linkSigner ?? $this->createStub(AccommodationBookingLinkSigner::class),
$breakdownCalculator ?? $this->createStub(AccommodationBookingBreakdownCalculator::class),
$pdfGenerator ?? $this->createStub(AccommodationBookingPdfGenerator::class),
'[email protected]',
);
}
@@ -13,7 +13,7 @@ class AccommodationPriceCoverageTest extends TestCase
{
public function testCoversOnlyDaysWithinPricePeriod(): void
{
$coverage = new AccommodationPriceCoverage($this->createMock(AccommodationPriceRepository::class));
$coverage = new AccommodationPriceCoverage($this->createStub(AccommodationPriceRepository::class));
$covered = $coverage->coveredDatesFor(
[$this->price('2026-06-01', '2026-06-09')],
@@ -30,7 +30,7 @@ class AccommodationPriceCoverageTest extends TestCase
public function testGapBetweenPricePeriodsStaysUncovered(): void
{
$coverage = new AccommodationPriceCoverage($this->createMock(AccommodationPriceRepository::class));
$coverage = new AccommodationPriceCoverage($this->createStub(AccommodationPriceRepository::class));
$covered = $coverage->coveredDatesFor(
[$this->price('2026-06-01', '2026-06-02'), $this->price('2026-06-05', '2026-06-06')],
@@ -46,7 +46,7 @@ class AccommodationPriceCoverageTest extends TestCase
public function testNoPricesMeansNoCoverage(): void
{
$coverage = new AccommodationPriceCoverage($this->createMock(AccommodationPriceRepository::class));
$coverage = new AccommodationPriceCoverage($this->createStub(AccommodationPriceRepository::class));
$covered = $coverage->coveredDatesFor(
[],
@@ -137,7 +137,7 @@ class ApplicantInsuranceCascadeTest extends TestCase
$cascade = new ApplicantInsuranceCascade(
$insuranceService,
$this->createMock(BookingPriceCalculator::class)
$this->createStub(BookingPriceCalculator::class)
);
$cascade->apply($bookingDto);
@@ -201,11 +201,11 @@ class ApplicantInsuranceCascadeTest extends TestCase
*/
private function createCascade(array $assignments, array $selectable): ApplicantInsuranceCascade
{
$insuranceService = $this->createMock(InsuranceManager::class);
$insuranceService = $this->createStub(InsuranceManager::class);
$insuranceService->method('getSelectableInsurances')->willReturn($selectable);
$insuranceService->method('batchAssignInsuranceToParticipants')->willReturn($assignments);
$priceCalculator = $this->createMock(BookingPriceCalculator::class);
$priceCalculator = $this->createStub(BookingPriceCalculator::class);
$priceCalculator->method('calculateIndividualParticipantPriceExcludingInsurance')->willReturn(0.0);
return new ApplicantInsuranceCascade($insuranceService, $priceCalculator);
+35 -31
View File
@@ -19,23 +19,25 @@ use PHPUnit\Framework\TestCase;
class BookingConfiguratorBabyTest extends TestCase
{
private BookingConfigurator $bookingService;
private ?BookingConfigurator $bookingService = null;
private ParticipantEligibilityChecker $participantEligibilityService;
protected function setUp(): void
{
$travelDataService = $this->createMock(TravelDataProvider::class);
$this->participantEligibilityService = $this->createMock(ParticipantEligibilityChecker::class);
$bookingStatusRuleRegistry = $this->createMock(BookingStatusRuleRegistry::class);
$bookingStatusRuleRegistry->method('evaluateStatus')->willReturn('F');
$agencyLoader = $this->createMock(AgencyLoader::class);
$this->participantEligibilityService = $this->createStub(ParticipantEligibilityChecker::class);
}
$this->bookingService = new BookingConfigurator(
$this->createMock(BookingSessionManager::class),
$travelDataService,
private function bookingService(): BookingConfigurator
{
$bookingStatusRuleRegistry = $this->createStub(BookingStatusRuleRegistry::class);
$bookingStatusRuleRegistry->method('evaluateStatus')->willReturn('F');
return $this->bookingService ??= new BookingConfigurator(
$this->createStub(BookingSessionManager::class),
$this->createStub(TravelDataProvider::class),
$this->participantEligibilityService,
$bookingStatusRuleRegistry,
$agencyLoader,
$this->createStub(AgencyLoader::class),
'F' // default booking status
);
}
@@ -52,7 +54,7 @@ class BookingConfiguratorBabyTest extends TestCase
$participant->additionalServices = [];
$bookingDto->participants[0] = $participant;
$this->bookingService->preselectDefaultServices($bookingDto);
$this->bookingService()->preselectDefaultServices($bookingDto);
$this->assertEmpty($participant->additionalServices, 'Mandatory services should not be preselected for baby participants');
}
@@ -69,7 +71,7 @@ class BookingConfiguratorBabyTest extends TestCase
$participant->additionalServices = [];
$bookingDto->participants[0] = $participant;
$this->bookingService->preselectDefaultServices($bookingDto);
$this->bookingService()->preselectDefaultServices($bookingDto);
$this->assertEmpty($participant->additionalServices, 'Mandatory services should not be preselected for participants at BABY_MAX_AGE');
}
@@ -91,7 +93,7 @@ class BookingConfiguratorBabyTest extends TestCase
->method('isParticipantEligible')
->willReturn(true);
$this->bookingService->preselectDefaultServices($bookingDto);
$this->bookingService()->preselectDefaultServices($bookingDto);
$this->assertNotEmpty($participant->additionalServices, 'Mandatory services should be preselected for non-baby participants');
$this->assertCount(1, $participant->additionalServices);
@@ -115,7 +117,7 @@ class BookingConfiguratorBabyTest extends TestCase
->method('isParticipantEligible')
->willReturn(true);
$this->bookingService->preselectDefaultServices($bookingDto);
$this->bookingService()->preselectDefaultServices($bookingDto);
$this->assertNotEmpty($participant->additionalServices, 'Mandatory services should be preselected for adult participants');
$this->assertCount(1, $participant->additionalServices);
@@ -123,6 +125,8 @@ class BookingConfiguratorBabyTest extends TestCase
public function testPreselectMandatoryServicesHandlesMixedParticipants(): void
{
$this->participantEligibilityService = $this->createMock(ParticipantEligibilityChecker::class);
$travel = $this->createTravelWithMandatoryServices();
$bookingDto = new BookingDto($travel, 1);
@@ -146,7 +150,7 @@ class BookingConfiguratorBabyTest extends TestCase
->with($bookingDto, 1) // Only called for adult
->willReturn(true);
$this->bookingService->preselectDefaultServices($bookingDto);
$this->bookingService()->preselectDefaultServices($bookingDto);
$this->assertEmpty($babyParticipant->additionalServices, 'Baby should not have mandatory services preselected');
$this->assertNotEmpty($adultParticipant->additionalServices, 'Adult should have mandatory services preselected');
@@ -164,7 +168,7 @@ class BookingConfiguratorBabyTest extends TestCase
$participant->additionalServices = [];
$bookingDto->participants[0] = $participant;
$this->bookingService->preselectDefaultServices($bookingDto);
$this->bookingService()->preselectDefaultServices($bookingDto);
$this->assertEmpty($participant->additionalServices, 'Mandatory services should not be preselected for newborn babies');
}
@@ -186,7 +190,7 @@ class BookingConfiguratorBabyTest extends TestCase
$participant->additionalServices = [];
$bookingDto->participants[0] = $participant;
$this->bookingService->preselectDefaultServices($bookingDto);
$this->bookingService()->preselectDefaultServices($bookingDto);
// At age 1, participant is a baby and should be skipped
$this->assertEmpty($participant->additionalServices, 'Age should be calculated at travel date, not current date');
@@ -207,7 +211,7 @@ class BookingConfiguratorBabyTest extends TestCase
->method('isParticipantEligible')
->willReturn(true);
$this->bookingService->preselectDefaultServices($bookingDto);
$this->bookingService()->preselectDefaultServices($bookingDto);
$this->assertCount(1, $participant->additionalServices);
$this->assertSame('Auto Book Service', $participant->additionalServices[0]->label);
@@ -229,7 +233,7 @@ class BookingConfiguratorBabyTest extends TestCase
->method('isParticipantEligible')
->willReturn(true);
$this->bookingService->preselectDefaultServices($bookingDto);
$this->bookingService()->preselectDefaultServices($bookingDto);
$this->assertEmpty($participant->additionalServices);
}
@@ -250,7 +254,7 @@ class BookingConfiguratorBabyTest extends TestCase
->method('isParticipantEligible')
->willReturn(true);
$this->bookingService->preselectDefaultServices($bookingDto);
$this->bookingService()->preselectDefaultServices($bookingDto);
$this->assertCount(1, $participant->additionalServices);
$this->assertSame('Mandatory And Auto Book Service', $participant->additionalServices[0]->label);
@@ -270,7 +274,7 @@ class BookingConfiguratorBabyTest extends TestCase
->method('isParticipantEligible')
->willReturn(true);
$this->bookingService->preselectDefaultServices($bookingDto);
$this->bookingService()->preselectDefaultServices($bookingDto);
$this->assertNotNull($participant->skiPass);
$this->assertSame('Auto Book Ski Pass', $participant->skiPass?->label);
@@ -291,7 +295,7 @@ class BookingConfiguratorBabyTest extends TestCase
->method('isParticipantEligible')
->willReturn(true);
$this->bookingService->preselectDefaultServices($bookingDto);
$this->bookingService()->preselectDefaultServices($bookingDto);
$this->assertNull($participant->skiPass);
}
@@ -311,7 +315,7 @@ class BookingConfiguratorBabyTest extends TestCase
->method('isParticipantEligible')
->willReturn(true);
$this->bookingService->preselectDefaultServices($bookingDto);
$this->bookingService()->preselectDefaultServices($bookingDto);
$this->assertNotNull($participant->skiPass);
$this->assertSame('Mandatory And Auto Book Ski Pass', $participant->skiPass?->label);
@@ -331,7 +335,7 @@ class BookingConfiguratorBabyTest extends TestCase
->method('isParticipantEligible')
->willReturn(true);
$this->bookingService->preselectDefaultServices($bookingDto);
$this->bookingService()->preselectDefaultServices($bookingDto);
$this->assertCount(1, $participant->board);
$this->assertSame('Auto Book Board', $participant->board[0]->label);
@@ -352,7 +356,7 @@ class BookingConfiguratorBabyTest extends TestCase
->method('isParticipantEligible')
->willReturn(true);
$this->bookingService->preselectDefaultServices($bookingDto);
$this->bookingService()->preselectDefaultServices($bookingDto);
$this->assertSame([], $participant->board);
}
@@ -372,7 +376,7 @@ class BookingConfiguratorBabyTest extends TestCase
->method('isParticipantEligible')
->willReturn(true);
$this->bookingService->preselectDefaultServices($bookingDto);
$this->bookingService()->preselectDefaultServices($bookingDto);
$this->assertCount(1, $participant->board);
$this->assertSame('Mandatory And Auto Book Board', $participant->board[0]->label);
@@ -393,7 +397,7 @@ class BookingConfiguratorBabyTest extends TestCase
->method('isParticipantEligible')
->willReturn(true);
$this->bookingService->preselectDefaultServices($bookingDto);
$this->bookingService()->preselectDefaultServices($bookingDto);
$this->assertCount(1, $participant->rentals);
$this->assertSame('Auto Book Rental', $participant->rentals[0]->label);
@@ -415,7 +419,7 @@ class BookingConfiguratorBabyTest extends TestCase
->method('isParticipantEligible')
->willReturn(true);
$this->bookingService->preselectDefaultServices($bookingDto);
$this->bookingService()->preselectDefaultServices($bookingDto);
$this->assertSame([], $participant->rentals);
}
@@ -436,7 +440,7 @@ class BookingConfiguratorBabyTest extends TestCase
->method('isParticipantEligible')
->willReturn(true);
$this->bookingService->preselectDefaultServices($bookingDto);
$this->bookingService()->preselectDefaultServices($bookingDto);
$this->assertCount(1, $participant->rentals);
$this->assertSame('Mandatory And Auto Book Rental', $participant->rentals[0]->label);
@@ -456,7 +460,7 @@ class BookingConfiguratorBabyTest extends TestCase
->method('isParticipantEligible')
->willReturn(true);
$this->bookingService->preselectDefaultServices($bookingDto);
$this->bookingService()->preselectDefaultServices($bookingDto);
$this->assertSame([], $participant->rentals);
}
@@ -480,7 +484,7 @@ class BookingConfiguratorBabyTest extends TestCase
->method('isParticipantEligible')
->willReturn(true);
$this->bookingService->preselectDefaultServices($bookingDto);
$this->bookingService()->preselectDefaultServices($bookingDto);
$this->assertSame([], $participant->rentals);
}
+18 -18
View File
@@ -4,11 +4,9 @@ declare(strict_types=1);
namespace App\Tests\Service;
use App\Form\Model\BookingDto;
use App\Form\Model\ParticipantDto;
use App\BusProNet\Constants;
use App\BusProNet\Model\CrmSelection;
use App\BusProNet\Model\CrmSelectionGroup;
use App\BusProNet\Constants;
use App\BusProNet\Model\Room;
use App\BusProNet\Model\Service;
use App\BusProNet\Model\Travel;
@@ -16,6 +14,8 @@ use App\BusProNet\Service\BookingStatusRuleRegistry;
use App\BusProNet\Service\StatusRule\Selection1473StatusRule;
use App\BusProNet\XmlLoader\AgencyLoader;
use App\Exception\NoRoomsAvailableException;
use App\Form\Model\BookingDto;
use App\Form\Model\ParticipantDto;
use App\Service\BookingConfigurator;
use App\Service\BookingSessionManager;
use App\Service\ParticipantEligibilityChecker;
@@ -32,12 +32,12 @@ class BookingConfiguratorStatusTest extends TestCase
protected function setUp(): void
{
$bookingSessionService = $this->createMock(BookingSessionManager::class);
$this->travelDataService = $this->createMock(TravelDataProvider::class);
$participantEligibility = $this->createMock(ParticipantEligibilityChecker::class);
$bookingStatusRuleRegistry = $this->createMock(BookingStatusRuleRegistry::class);
$bookingSessionService = $this->createStub(BookingSessionManager::class);
$this->travelDataService = $this->createStub(TravelDataProvider::class);
$participantEligibility = $this->createStub(ParticipantEligibilityChecker::class);
$bookingStatusRuleRegistry = $this->createStub(BookingStatusRuleRegistry::class);
$bookingStatusRuleRegistry->method('evaluateStatus')->willReturn('F');
$agencyLoader = $this->createMock(AgencyLoader::class);
$agencyLoader = $this->createStub(AgencyLoader::class);
$this->bookingService = new BookingConfigurator(
$bookingSessionService,
@@ -180,13 +180,13 @@ class BookingConfiguratorStatusTest extends TestCase
public function testStartFreshBookingWithSelection1473UsesOptionStatus(): void
{
$bookingService = new BookingConfigurator(
$this->createMock(BookingSessionManager::class),
$this->createStub(BookingSessionManager::class),
$this->travelDataService,
$this->createMock(ParticipantEligibilityChecker::class),
$this->createStub(ParticipantEligibilityChecker::class),
new BookingStatusRuleRegistry([
new Selection1473StatusRule(),
]),
$this->createMock(AgencyLoader::class),
$this->createStub(AgencyLoader::class),
'F'
);
@@ -230,15 +230,15 @@ class BookingConfiguratorStatusTest extends TestCase
public function testApplyCreateBookingStatusRulesSetsOptionFromRegistry(): void
{
$bookingStatusRuleRegistry = $this->createMock(BookingStatusRuleRegistry::class);
$bookingStatusRuleRegistry = $this->createStub(BookingStatusRuleRegistry::class);
$bookingStatusRuleRegistry->method('evaluateStatus')->willReturn('O');
$bookingService = new BookingConfigurator(
$this->createMock(BookingSessionManager::class),
$this->createStub(BookingSessionManager::class),
$this->travelDataService,
$this->createMock(ParticipantEligibilityChecker::class),
$this->createStub(ParticipantEligibilityChecker::class),
$bookingStatusRuleRegistry,
$this->createMock(AgencyLoader::class),
$this->createStub(AgencyLoader::class),
'F'
);
@@ -265,11 +265,11 @@ class BookingConfiguratorStatusTest extends TestCase
$bookingStatusRuleRegistry->expects($this->never())->method('evaluateStatus');
$bookingService = new BookingConfigurator(
$this->createMock(BookingSessionManager::class),
$this->createStub(BookingSessionManager::class),
$this->travelDataService,
$this->createMock(ParticipantEligibilityChecker::class),
$this->createStub(ParticipantEligibilityChecker::class),
$bookingStatusRuleRegistry,
$this->createMock(AgencyLoader::class),
$this->createStub(AgencyLoader::class),
'F'
);
@@ -44,7 +44,7 @@ class BookingCreateContextFactoryTest extends TestCase
$travel->rooms = [$roomByRoom, $roomByPax];
$bookingDto = new BookingDto($travel, 157047);
$summaryData = $this->createMock(BookingSummaryDto::class);
$summaryData = $this->createStub(BookingSummaryDto::class);
$summaryDataService = $this->createMock(BookingSummaryAssembler::class);
$summaryDataService->expects($this->once())
@@ -63,7 +63,7 @@ class BookingCreateContextFactoryTest extends TestCase
$priceCalculator->expects($this->never())
->method('calculateAllParticipantIndividualPrices');
$familyInsuranceService = $this->createMock(FamilyInsuranceAvailabilityChecker::class);
$familyInsuranceService = $this->createStub(FamilyInsuranceAvailabilityChecker::class);
$familyInsuranceService->method('isFamilyInsuranceUpgradeAvailable')->willReturn(true);
$service = new BookingCreateContextFactory(
@@ -101,7 +101,7 @@ class BookingCreateContextFactoryTest extends TestCase
$travel->rooms = [$room];
$bookingDto = new BookingDto($travel, 157047);
$summaryData = $this->createMock(BookingSummaryDto::class);
$summaryData = $this->createStub(BookingSummaryDto::class);
$cardData = new ParticipantCardDataDto(
name: 'Max Mustermann',
email: '[email protected]',
@@ -126,7 +126,7 @@ class BookingCreateContextFactoryTest extends TestCase
$priceCalculator->expects($this->never())
->method('calculateAllParticipantIndividualPrices');
$familyInsuranceService = $this->createMock(FamilyInsuranceAvailabilityChecker::class);
$familyInsuranceService = $this->createStub(FamilyInsuranceAvailabilityChecker::class);
$familyInsuranceService->method('isFamilyInsuranceUpgradeAvailable')->willReturn(true);
$service = new BookingCreateContextFactory(
@@ -162,7 +162,7 @@ class BookingCreateContextFactoryTest extends TestCase
$travel->rooms = [$room];
$bookingDto = new BookingDto($travel, 157047);
$summaryData = $this->createMock(BookingSummaryDto::class);
$summaryData = $this->createStub(BookingSummaryDto::class);
$summaryDataService = $this->createMock(BookingSummaryAssembler::class);
$summaryDataService->expects($this->once())
@@ -180,7 +180,7 @@ class BookingCreateContextFactoryTest extends TestCase
->with($bookingDto)
->willReturn([123.45]);
$familyInsuranceService = $this->createMock(FamilyInsuranceAvailabilityChecker::class);
$familyInsuranceService = $this->createStub(FamilyInsuranceAvailabilityChecker::class);
$familyInsuranceService->method('isFamilyInsuranceUpgradeAvailable')->willReturn(true);
$service = new BookingCreateContextFactory(
@@ -32,7 +32,7 @@ class BookingEditContextFactoryTest extends TestCase
$bookingData = new Booking();
$bookingData->dateId = 123;
$summaryData = $this->createMock(BookingSummaryDto::class);
$summaryData = $this->createStub(BookingSummaryDto::class);
$participantCardAssembler = $this->createMock(ParticipantCardAssembler::class);
$participantCardAssembler->expects($this->once())
@@ -34,9 +34,9 @@ class BookingEditDraftManagerMutabilityTest extends TestCase
protected function setUp(): void
{
$this->service = new BookingEditDraftManager(
$this->createMock(BookingEditDraftRepository::class),
$this->createMock(EntityManagerInterface::class),
$this->createMock(BookingChangeTracker::class),
$this->createStub(BookingEditDraftRepository::class),
$this->createStub(EntityManagerInterface::class),
$this->createStub(BookingChangeTracker::class),
new BookingEditDraftMerger(),
new NullLogger(),
);
@@ -544,7 +544,7 @@ class BookingEditDraftManagerMutabilityTest extends TestCase
private function createDraft(array $formData): BookingEditDraft
{
$user = $this->createMock(User::class);
$user = $this->createStub(User::class);
return new BookingEditDraft($user, 123, new \DateTimeImmutable(), $formData);
}
@@ -4,20 +4,20 @@ declare(strict_types=1);
namespace App\Tests\Service;
use App\BusProNet\Model\CrmSelection;
use App\BusProNet\Model\CrmSelectionGroup;
use App\BusProNet\Model\Address;
use App\BusProNet\Model\Booking;
use App\BusProNet\Model\CrmSelection;
use App\BusProNet\Model\CrmSelectionGroup;
use App\BusProNet\Model\Service;
use App\BusProNet\Model\Travel;
use App\BusProNet\XmlLoader\AgencyLoader;
use App\Form\Model\BookingDto;
use App\Form\Model\ParticipantDto;
use App\Form\Service\ServiceAgeEvaluator;
use App\Service\BookingEditPreFlightChecker;
use App\Service\BookingPriceCalculator;
use App\Service\ParticipantEligibilityChecker;
use App\Service\VoucherValidator;
use App\Form\Service\ServiceAgeEvaluator;
use App\Validator\Constraints\MandatoryAdditionalServicesSelectedValidator;
use App\Validator\Constraints\PromoVoucherValidator;
use App\Validator\Constraints\PurchaseVoucherValidator;
@@ -248,19 +248,14 @@ class BookingEditPreFlightCheckerTest extends TestCase
private function createValidator(): ValidatorInterface
{
$voucherValidator = $this->createMock(VoucherValidator::class);
$bookingPriceCalculator = $this->createMock(BookingPriceCalculator::class);
$participantEligibilityChecker = $this->createMock(ParticipantEligibilityChecker::class);
$voucherValidator = $this->createStub(VoucherValidator::class);
$bookingPriceCalculator = $this->createStub(BookingPriceCalculator::class);
$participantEligibilityChecker = $this->createStub(ParticipantEligibilityChecker::class);
$participantEligibilityChecker
->method('isParticipantEligible')
->willReturn(false);
$validatorFactory = new class(
$voucherValidator,
$bookingPriceCalculator,
$participantEligibilityChecker,
new ServiceAgeEvaluator(),
) implements ConstraintValidatorFactoryInterface {
$validatorFactory = new class($voucherValidator, $bookingPriceCalculator, $participantEligibilityChecker, new ServiceAgeEvaluator()) implements ConstraintValidatorFactoryInterface {
public function __construct(
private readonly VoucherValidator $voucherValidator,
private readonly BookingPriceCalculator $bookingPriceCalculator,
+9 -11
View File
@@ -19,6 +19,7 @@ use App\Service\BookingEditSubmitGuard;
use App\Service\BookingEditSubmitter;
use App\Service\BookingSessionManager;
use App\Service\TravelDataProvider;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\TestCase;
use Psr\Log\LoggerInterface;
use Symfony\Component\HttpFoundation\Request;
@@ -53,9 +54,7 @@ class BookingEditSubmitterTest extends TestCase
$this->assertFalse($result->immutableChangesReverted);
}
/**
* @dataProvider updateFailureProvider
*/
#[DataProvider('updateFailureProvider')]
public function testHandleSubmissionReturnsRedirectWhenUpdateThrows(
\Throwable $exception,
string $expectedStatus,
@@ -437,13 +436,13 @@ class BookingEditSubmitterTest extends TestCase
?BookingSessionManager $bookingSessionService = null,
): BookingEditSubmitter {
return new BookingEditSubmitter(
$apiClient ?? $this->createMock(\App\BusProNet\ApiClient::class),
$dataLoader ?? $this->createMock(BookingEditDataLoader::class),
$draftService ?? $this->createMock(BookingEditDraftManager::class),
$travelDataService ?? $this->createMock(TravelDataProvider::class),
$submitGuard ?? $this->createMock(BookingEditSubmitGuard::class),
$bookingSessionService ?? $this->createMock(BookingSessionManager::class),
$this->createMock(LoggerInterface::class),
$apiClient ?? $this->createStub(\App\BusProNet\ApiClient::class),
$dataLoader ?? $this->createStub(BookingEditDataLoader::class),
$draftService ?? $this->createStub(BookingEditDraftManager::class),
$travelDataService ?? $this->createStub(TravelDataProvider::class),
$submitGuard ?? $this->createStub(BookingEditSubmitGuard::class),
$bookingSessionService ?? $this->createStub(BookingSessionManager::class),
$this->createStub(LoggerInterface::class),
);
}
@@ -475,5 +474,4 @@ class BookingEditSubmitterTest extends TestCase
return $request;
}
}
+1 -1
View File
@@ -28,7 +28,7 @@ class BookingPriceCalculatorTest extends TestCase
protected function setUp(): void
{
$eligibilityChecker = $this->createMock(ParticipantEligibilityChecker::class);
$eligibilityChecker = $this->createStub(ParticipantEligibilityChecker::class);
$eligibilityChecker->method('isParticipantEligible')->willReturn(true);
$this->roomPricingCalculator = new RoomPricingCalculator();
@@ -19,7 +19,7 @@ class BookingPriceMismatchAnalyzerTest extends TestCase
{
public function testBuildDiagnosticsProvidesDeltaBreakdown(): void
{
$pricingAssembler = $this->createMock(BookingPricingAssembler::class);
$pricingAssembler = $this->createStub(BookingPricingAssembler::class);
$pricingAssembler->method('getPricingBreakdown')->willReturn([
'rooms' => [
['roomId' => 74, 'totalPrice' => 444.0],
@@ -24,7 +24,7 @@ class BookingPricingAssemblerTest extends TestCase
protected function setUp(): void
{
$this->eligibilityChecker = $this->createMock(ParticipantEligibilityChecker::class);
$this->eligibilityChecker = $this->createStub(ParticipantEligibilityChecker::class);
$this->eligibilityChecker->method('isParticipantEligible')->willReturn(true);
$this->assembler = $this->createAssembler($this->eligibilityChecker);
@@ -176,7 +176,7 @@ class BookingPricingAssemblerTest extends TestCase
$bookingDto = new BookingDto($travel, 2);
$bookingDto->participants = [$participant1, $participant2];
$eligibilityChecker = $this->createMock(ParticipantEligibilityChecker::class);
$eligibilityChecker = $this->createStub(ParticipantEligibilityChecker::class);
$eligibilityChecker->method('isParticipantEligible')
->willReturnCallback(fn ($booking, int $index): bool => 0 === $index);
+19 -13
View File
@@ -19,12 +19,16 @@ use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage;
class BookingSessionManagerTest extends TestCase
{
private TravelDataProvider $travelDataService;
private BookingSessionManager $service;
private ?BookingSessionManager $service = null;
protected function setUp(): void
{
$this->travelDataService = $this->createMock(TravelDataProvider::class);
$this->service = new BookingSessionManager($this->travelDataService);
$this->travelDataService = $this->createStub(TravelDataProvider::class);
}
private function service(): BookingSessionManager
{
return $this->service ??= new BookingSessionManager($this->travelDataService);
}
public function testGetOrCreateBookingCreateDtoThrowsWhenSessionMissing(): void
@@ -33,11 +37,13 @@ class BookingSessionManagerTest extends TestCase
$this->expectException(BookingSessionNotFoundException::class);
$this->service->getOrCreateBookingCreateDto($request);
$this->service()->getOrCreateBookingCreateDto($request);
}
public function testSaveAndLoadBookingDtoHydratesTravel(): void
{
$this->travelDataService = $this->createMock(TravelDataProvider::class);
$request = $this->createRequestWithSession();
$storedTravel = new Travel();
@@ -59,8 +65,8 @@ class BookingSessionManagerTest extends TestCase
->with(12, 34)
->willReturn($hydratedTravel);
$this->service->saveBookingDto($request, $bookingDto, BookingDto::MODE_CREATE);
$loadedDto = $this->service->getBookingDto($request, BookingDto::MODE_CREATE);
$this->service()->saveBookingDto($request, $bookingDto, BookingDto::MODE_CREATE);
$loadedDto = $this->service()->getBookingDto($request, BookingDto::MODE_CREATE);
$this->assertNotNull($loadedDto);
$this->assertSame($hydratedTravel, $loadedDto?->travel);
@@ -76,11 +82,11 @@ class BookingSessionManagerTest extends TestCase
$selection->quantity = 2;
$bookingDto->roomSelections = [$selection];
$first = $this->service->getOrCreateBaselineSnapshot($request, $bookingDto);
$first = $this->service()->getOrCreateBaselineSnapshot($request, $bookingDto);
$this->assertSame([[1, 2]], $first);
$selection->quantity = 5;
$second = $this->service->getOrCreateBaselineSnapshot($request, $bookingDto);
$second = $this->service()->getOrCreateBaselineSnapshot($request, $bookingDto);
$this->assertSame($first, $second);
$this->assertSame([[1, 2]], $second);
@@ -90,19 +96,19 @@ class BookingSessionManagerTest extends TestCase
{
$request = $this->createRequestWithSession();
$this->service->storeReturnUrl($request, 'javascript:alert(1)');
$this->service()->storeReturnUrl($request, 'javascript:alert(1)');
$this->assertSame(BookingSessionManager::DEFAULT_RETURN_URL, $this->service->getReturnUrl($request));
$this->assertSame(BookingSessionManager::DEFAULT_RETURN_URL, $this->service()->getReturnUrl($request));
}
public function testClearBookingSessionPreservesReturnUrl(): void
{
$request = $this->createRequestWithSession();
$this->service->storeReturnUrl($request, 'https://example.test/after-booking');
$this->service->clearBookingSession($request);
$this->service()->storeReturnUrl($request, 'https://example.test/after-booking');
$this->service()->clearBookingSession($request);
$this->assertSame('https://example.test/after-booking', $this->service->getReturnUrl($request));
$this->assertSame('https://example.test/after-booking', $this->service()->getReturnUrl($request));
}
private function createRequestWithSession(): Request
@@ -72,10 +72,10 @@ class BookingSummaryAssemblerTest extends TestCase
private function createService(): BookingSummaryAssembler
{
$priceCalculator = $this->createMock(BookingPriceCalculator::class);
$priceCalculator = $this->createStub(BookingPriceCalculator::class);
$priceCalculator->method('calculateAllParticipantIndividualPrices')->willReturn([]);
$pricingAssembler = $this->createMock(BookingPricingAssembler::class);
$pricingAssembler = $this->createStub(BookingPricingAssembler::class);
$pricingAssembler->method('getPricingBreakdown')->willReturn([
'rooms' => [],
'services' => [],
@@ -86,10 +86,10 @@ class BookingSummaryAssemblerTest extends TestCase
return new BookingSummaryAssembler(
$priceCalculator,
$pricingAssembler,
$this->createMock(CmsDataProvider::class),
$this->createMock(HotelLoader::class),
$this->createMock(CountryDataProvider::class),
$this->createMock(CacheInterface::class),
$this->createStub(CmsDataProvider::class),
$this->createStub(HotelLoader::class),
$this->createStub(CountryDataProvider::class),
$this->createStub(CacheInterface::class),
new NullLogger(),
);
}
+19 -10
View File
@@ -17,27 +17,29 @@ use App\Repository\Groups\ContingentDayRepository;
use App\Repository\Groups\ContingentSyncStateRepository;
use App\Service\ContingentSnapshotManager;
use Doctrine\ORM\EntityManagerInterface;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\MockObject\Stub;
use PHPUnit\Framework\TestCase;
use Psr\Log\LoggerInterface;
class ContingentSnapshotManagerTest extends TestCase
{
private ContingentsClient&MockObject $client;
private ContingentDayRepository&MockObject $dayRepository;
private ContingentSyncStateRepository&MockObject $syncStateRepository;
private EntityManagerInterface&MockObject $entityManager;
private ContingentsClient&Stub $client;
private ContingentDayRepository&Stub $dayRepository;
private ContingentSyncStateRepository&Stub $syncStateRepository;
private EntityManagerInterface&Stub $entityManager;
protected function setUp(): void
{
$this->client = $this->createMock(ContingentsClient::class);
$this->dayRepository = $this->createMock(ContingentDayRepository::class);
$this->syncStateRepository = $this->createMock(ContingentSyncStateRepository::class);
$this->entityManager = $this->createMock(EntityManagerInterface::class);
$this->client = $this->createStub(ContingentsClient::class);
$this->dayRepository = $this->createStub(ContingentDayRepository::class);
$this->syncStateRepository = $this->createStub(ContingentSyncStateRepository::class);
$this->entityManager = $this->createStub(EntityManagerInterface::class);
}
public function testAddsMissingDaysAndMarksSnapshotChanged(): void
{
$this->entityManager = $this->createMock(EntityManagerInterface::class);
$this->dayRepository->method('findByAccommodationAndDateRange')->willReturn([]);
$this->dayRepository->method('findStatusFingerprintParts')->willReturn(['2026-07-01:OK', '2026-07-02:BLOCKED']);
$this->client->method('getContingentCalendar')->willReturn($this->response([
@@ -58,6 +60,8 @@ class ContingentSnapshotManagerTest extends TestCase
public function testUpdatesChangedStatusAndRemovesVanishedDays(): void
{
$this->entityManager = $this->createMock(EntityManagerInterface::class);
$existing = [
'2026-07-01' => $this->day('2026-07-01', ContingentStatus::Ok),
'2026-07-02' => $this->day('2026-07-02', ContingentStatus::Ok),
@@ -104,6 +108,9 @@ class ContingentSnapshotManagerTest extends TestCase
public function testUpstreamFailureKeepsSnapshotAndRecordsError(): void
{
$this->dayRepository = $this->createMock(ContingentDayRepository::class);
$this->entityManager = $this->createMock(EntityManagerInterface::class);
$state = new ContingentSyncState($this->accommodation());
$this->syncStateRepository->method('findOneByAccommodation')->willReturn($state);
$this->client->method('getContingentCalendar')->willThrowException(new BpnConnectException('upstream down'));
@@ -121,6 +128,8 @@ class ContingentSnapshotManagerTest extends TestCase
public function testEmptyUpstreamResponseDoesNotWipeAPopulatedRange(): void
{
$this->entityManager = $this->createMock(EntityManagerInterface::class);
$state = new ContingentSyncState($this->accommodation());
$this->syncStateRepository->method('findOneByAccommodation')->willReturn($state);
$this->dayRepository->method('findByAccommodationAndDateRange')->willReturn([
@@ -193,7 +202,7 @@ class ContingentSnapshotManagerTest extends TestCase
$this->dayRepository,
$this->syncStateRepository,
$this->entityManager,
$this->createMock(LoggerInterface::class),
$this->createStub(LoggerInterface::class),
);
return $manager->sync(
+12 -6
View File
@@ -12,23 +12,25 @@ use App\Repository\Groups\ContingentDayRepository;
use App\Repository\Groups\ContingentSyncStateRepository;
use App\Service\ContingentSnapshotReader;
use Carbon\CarbonImmutable;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\MockObject\Stub;
use PHPUnit\Framework\TestCase;
use Psr\Log\LoggerInterface;
class ContingentSnapshotReaderTest extends TestCase
{
private ContingentDayRepository&MockObject $dayRepository;
private ContingentSyncStateRepository&MockObject $syncStateRepository;
private ContingentDayRepository&Stub $dayRepository;
private ContingentSyncStateRepository&Stub $syncStateRepository;
protected function setUp(): void
{
$this->dayRepository = $this->createMock(ContingentDayRepository::class);
$this->syncStateRepository = $this->createMock(ContingentSyncStateRepository::class);
$this->dayRepository = $this->createStub(ContingentDayRepository::class);
$this->syncStateRepository = $this->createStub(ContingentSyncStateRepository::class);
}
public function testReturnsNullWhenNothingHasEverSynced(): void
{
$this->dayRepository = $this->createMock(ContingentDayRepository::class);
$this->syncStateRepository->method('findOneByAccommodation')->willReturn(null);
$this->dayRepository->expects(self::never())->method('findByHotelCodeAndDateRange');
@@ -37,6 +39,8 @@ class ContingentSnapshotReaderTest extends TestCase
public function testReturnsNullWhenTheSnapshotIsStale(): void
{
$this->dayRepository = $this->createMock(ContingentDayRepository::class);
$this->syncStateRepository->method('findOneByAccommodation')->willReturn($this->state('-7 hours'));
$this->dayRepository->expects(self::never())->method('findByHotelCodeAndDateRange');
@@ -67,6 +71,8 @@ class ContingentSnapshotReaderTest extends TestCase
public function testReturnsNullForAnAccommodationWithoutACalendarCode(): void
{
$this->syncStateRepository = $this->createMock(ContingentSyncStateRepository::class);
$this->syncStateRepository->expects(self::never())->method('findOneByAccommodation');
self::assertNull($this->read(new Accommodation()));
@@ -80,7 +86,7 @@ class ContingentSnapshotReaderTest extends TestCase
$reader = new ContingentSnapshotReader(
$this->dayRepository,
$this->syncStateRepository,
$this->createMock(LoggerInterface::class),
$this->createStub(LoggerInterface::class),
);
return $reader->statusesFor(
+1 -1
View File
@@ -18,7 +18,7 @@ class DatabaseAnonymizerTest extends TestCase
public function testAnonymizeEntityMethodsReuseSharedIdentityAndPreserveDraftTimestamps(): void
{
$service = new DatabaseAnonymizer(
$this->createMock(EntityManagerInterface::class),
$this->createStub(EntityManagerInterface::class),
new NullLogger(),
);
@@ -21,8 +21,8 @@ class FamilyInsuranceAvailabilityCheckerTest extends TestCase
protected function setUp(): void
{
$this->insuranceService = $this->createMock(InsuranceManager::class);
$this->priceCalculatorService = $this->createMock(BookingPriceCalculator::class);
$this->insuranceService = $this->createStub(InsuranceManager::class);
$this->priceCalculatorService = $this->createStub(BookingPriceCalculator::class);
$this->insuranceService->method('getSelectableInsurances')
->willReturnCallback(fn (Travel $travel) => $travel->insurances ?? []);
+18 -11
View File
@@ -15,6 +15,7 @@ use App\Repository\NewsletterConsentRepository;
use App\Repository\NewsletterOptInRequestRepository;
use App\Service\NewsletterManager;
use Doctrine\ORM\EntityManagerInterface;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Psr\Log\NullLogger;
@@ -152,10 +153,13 @@ class NewsletterManagerTest extends TestCase
$mailjet->expects(self::never())->method('isSubscribed');
$mailjet->expects(self::once())->method('upsertContact')->with('[email protected]', 'Mia', 'Muster');
$ensureSubscribedCalls = [];
$mailjet
->expects(self::exactly(2))
->method('ensureSubscribed')
->withConsecutive(['[email protected]', 1], ['[email protected]', 2]);
->willReturnCallback(static function (string $email, ?int $listId) use (&$ensureSubscribedCalls): void {
$ensureSubscribedCalls[] = [$email, $listId];
});
$consents->method('findActiveByEmail')->with('[email protected]')->willReturn($existingConsent);
$consents
->method('findOneByEmailAndListId')
@@ -184,6 +188,7 @@ class NewsletterManagerTest extends TestCase
1 => NewsletterSubscriptionRequestResult::LIST_STATE_ALREADY_REGISTERED,
2 => NewsletterSubscriptionRequestResult::LIST_STATE_ALREADY_REGISTERED,
], $result->listStates);
self::assertSame([['[email protected]', 1], ['[email protected]', 2]], $ensureSubscribedCalls);
}
public function testApiRequestRejectsUnknownListIdsBeforeSideEffects(): void
@@ -192,7 +197,7 @@ class NewsletterManagerTest extends TestCase
$consents = $this->createMock(NewsletterConsentRepository::class);
$entityManager = $this->createMock(EntityManagerInterface::class);
$mailjet = $this->createMock(ApiClient::class);
$mailer = $this->createMock(Mailer::class);
$mailer = $this->createStub(Mailer::class);
$mailjet->expects(self::never())->method('isSubscribed');
$mailjet->expects(self::never())->method('ensureSubscribed');
@@ -252,7 +257,7 @@ class NewsletterManagerTest extends TestCase
$consents = $this->createMock(NewsletterConsentRepository::class);
$entityManager = $this->createMock(EntityManagerInterface::class);
$mailjet = $this->createMock(ApiClient::class);
$mailer = $this->createMock(Mailer::class);
$mailer = $this->createStub(Mailer::class);
$mailjet->expects(self::never())->method('isSubscribed');
$mailjet->expects(self::once())->method('upsertContact')->with('[email protected]', 'Mia', 'Muster');
@@ -274,7 +279,7 @@ class NewsletterManagerTest extends TestCase
public function testDefaultRequestCreatesDefaultListPendingConfirmation(): void
{
$repository = $this->createMock(NewsletterOptInRequestRepository::class);
$consents = $this->createMock(NewsletterConsentRepository::class);
$consents = $this->createStub(NewsletterConsentRepository::class);
$entityManager = $this->createMock(EntityManagerInterface::class);
$mailjet = $this->createMock(ApiClient::class);
$mailer = $this->createMock(Mailer::class);
@@ -301,7 +306,7 @@ class NewsletterManagerTest extends TestCase
public function testRequestConfirmationTruncatesNamesBeforePersisting(): void
{
$repository = $this->createMock(NewsletterOptInRequestRepository::class);
$consents = $this->createMock(NewsletterConsentRepository::class);
$consents = $this->createStub(NewsletterConsentRepository::class);
$entityManager = $this->createMock(EntityManagerInterface::class);
$mailjet = $this->createMock(ApiClient::class);
$mailer = $this->createMock(Mailer::class);
@@ -340,7 +345,7 @@ class NewsletterManagerTest extends TestCase
$consents = $this->createMock(NewsletterConsentRepository::class);
$entityManager = $this->createMock(EntityManagerInterface::class);
$mailjet = $this->createMock(ApiClient::class);
$mailer = $this->createMock(Mailer::class);
$mailer = $this->createStub(Mailer::class);
$repository->expects(self::once())->method('findByTokenHash')->with(hash('sha256', $token))->willReturn($confirmation);
$repository->expects(self::never())->method('deletePendingByEmail');
@@ -371,16 +376,19 @@ class NewsletterManagerTest extends TestCase
$consents = $this->createMock(NewsletterConsentRepository::class);
$entityManager = $this->createMock(EntityManagerInterface::class);
$mailjet = $this->createMock(ApiClient::class);
$mailer = $this->createMock(Mailer::class);
$mailer = $this->createStub(Mailer::class);
$repository->expects(self::once())->method('findByTokenHash')->with(hash('sha256', $token))->willReturn($confirmation);
$repository->expects(self::never())->method('deletePendingByEmail');
$consents->method('findOneByEmailAndListId')->willReturn(null);
$consents->method('findByEmailAndListIds')->with('[email protected]', [1, 2])->willReturn([]);
$ensureSubscribedCalls = [];
$mailjet
->expects(self::exactly(2))
->method('ensureSubscribed')
->withConsecutive(['[email protected]', 1], ['[email protected]', 2]);
->willReturnCallback(static function (string $email, ?int $listId) use (&$ensureSubscribedCalls): void {
$ensureSubscribedCalls[] = [$email, $listId];
});
$entityManager->expects(self::exactly(2))->method('persist')->with(self::isInstanceOf(NewsletterConsent::class));
$entityManager->expects(self::once())->method('remove')->with($confirmation);
$entityManager->expects(self::once())->method('flush');
@@ -389,6 +397,7 @@ class NewsletterManagerTest extends TestCase
->confirmToken($token);
self::assertSame(NewsletterConfirmationResult::STATUS_CONFIRMED, $result->status);
self::assertSame([['[email protected]', 1], ['[email protected]', 2]], $ensureSubscribedCalls);
}
public function testConfirmationEntityNormalizesMailjetListIds(): void
@@ -403,9 +412,7 @@ class NewsletterManagerTest extends TestCase
self::assertSame([1, 2], $confirmation->getMailjetListIds());
}
/**
* @dataProvider invalidEntityMailjetListIdProvider
*/
#[DataProvider('invalidEntityMailjetListIdProvider')]
public function testConfirmationEntityRejectsInvalidMailjetListIds(mixed $mailjetListId): void
{
$this->expectException(\InvalidArgumentException::class);
+48 -30
View File
@@ -4,9 +4,6 @@ declare(strict_types=1);
namespace App\Tests\Service;
use Symfony\Component\Validator\ConstraintViolationListInterface;
use Symfony\Component\Validator\ConstraintViolationInterface;
use Symfony\Component\Validator\ConstraintViolationList;
use App\BusProNet\Model\Booking;
use App\BusProNet\Model\Room;
use App\BusProNet\Model\Surcharge;
@@ -18,19 +15,26 @@ use App\Form\Model\ParticipantEditDto;
use App\Service\BookingPriceCalculator;
use App\Service\ParticipantCardAssembler;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Validator\ConstraintViolationInterface;
use Symfony\Component\Validator\ConstraintViolationList;
use Symfony\Component\Validator\ConstraintViolationListInterface;
use Symfony\Component\Validator\Validator\ValidatorInterface;
class ParticipantCardAssemblerTest extends TestCase
{
private ParticipantCardAssembler $service;
private ?ParticipantCardAssembler $service = null;
private BookingPriceCalculator $priceCalculator;
private ValidatorInterface $validator;
protected function setUp(): void
{
$this->priceCalculator = $this->createMock(BookingPriceCalculator::class);
$this->validator = $this->createMock(ValidatorInterface::class);
$this->service = new ParticipantCardAssembler(
$this->priceCalculator = $this->createStub(BookingPriceCalculator::class);
$this->validator = $this->createStub(ValidatorInterface::class);
}
private function service(): ParticipantCardAssembler
{
return $this->service ??= new ParticipantCardAssembler(
$this->priceCalculator,
$this->validator
);
@@ -38,6 +42,8 @@ class ParticipantCardAssemblerTest extends TestCase
public function testGetCardDataWithFullParticipantData(): void
{
$this->priceCalculator = $this->createMock(BookingPriceCalculator::class);
// Create test room
$room = new Room();
$room->id = 1;
@@ -63,7 +69,7 @@ class ParticipantCardAssemblerTest extends TestCase
->with($bookingDto)
->willReturn([450.50]);
$result = $this->service->getCardData($bookingDto, 0);
$result = $this->service()->getCardData($bookingDto, 0);
$this->assertSame('Max Mustermann', $result->name);
$this->assertSame('Doppelzimmer', $result->roomName);
@@ -88,7 +94,7 @@ class ParticipantCardAssemblerTest extends TestCase
->method('calculateAllParticipantIndividualPrices')
->willReturn([0.0]);
$result = $this->service->getCardData($bookingDto, 0);
$result = $this->service()->getCardData($bookingDto, 0);
$this->assertSame('Max', $result->name);
}
@@ -110,7 +116,7 @@ class ParticipantCardAssemblerTest extends TestCase
->method('calculateAllParticipantIndividualPrices')
->willReturn([0.0]);
$result = $this->service->getCardData($bookingDto, 0);
$result = $this->service()->getCardData($bookingDto, 0);
$this->assertSame('Anmelder:in', $result->name);
}
@@ -132,7 +138,7 @@ class ParticipantCardAssemblerTest extends TestCase
->method('calculateAllParticipantIndividualPrices')
->willReturn([0.0]);
$result = $this->service->getCardData($bookingDto, 0);
$result = $this->service()->getCardData($bookingDto, 0);
$this->assertSame('Anmelder:in', $result->name);
}
@@ -154,7 +160,7 @@ class ParticipantCardAssemblerTest extends TestCase
->method('calculateAllParticipantIndividualPrices')
->willReturn([0.0]);
$result = $this->service->getCardData($bookingDto, 0);
$result = $this->service()->getCardData($bookingDto, 0);
$this->assertSame('Kein Zimmer zugewiesen', $result->roomName);
}
@@ -176,7 +182,7 @@ class ParticipantCardAssemblerTest extends TestCase
->method('calculateAllParticipantIndividualPrices')
->willReturn([0.0]);
$result = $this->service->getCardData($bookingDto, 0);
$result = $this->service()->getCardData($bookingDto, 0);
$this->assertSame('Unbekanntes Zimmer', $result->roomName);
}
@@ -198,7 +204,7 @@ class ParticipantCardAssemblerTest extends TestCase
->method('calculateAllParticipantIndividualPrices')
->willReturn([0.0]);
$result = $this->service->getCardData($bookingDto, 0);
$result = $this->service()->getCardData($bookingDto, 0);
// When no room assigned and price is zero, display dash (incomplete configuration)
$this->assertNull($result->price->amount);
@@ -228,7 +234,7 @@ class ParticipantCardAssemblerTest extends TestCase
->method('calculateAllParticipantIndividualPrices')
->willReturn([0.0]);
$result = $this->service->getCardData($bookingDto, 0);
$result = $this->service()->getCardData($bookingDto, 0);
// When room is assigned but price is zero, display formatted zero price
$this->assertSame(0.0, $result->price->amount);
@@ -244,11 +250,13 @@ class ParticipantCardAssemblerTest extends TestCase
$this->expectException(\InvalidArgumentException::class);
$this->expectExceptionMessage('Participant at index 0 does not exist');
$this->service->getCardData($bookingDto, 0);
$this->service()->getCardData($bookingDto, 0);
}
public function testGetAllCardsDataWithMultipleParticipants(): void
{
$this->priceCalculator = $this->createMock(BookingPriceCalculator::class);
// Create test rooms
$room1 = new Room();
$room1->id = 1;
@@ -287,7 +295,7 @@ class ParticipantCardAssemblerTest extends TestCase
->with($bookingDto)
->willReturn([450.0, 500.0, 480.0]);
$result = $this->service->getAllCardsData($bookingDto);
$result = $this->service()->getAllCardsData($bookingDto);
$this->assertCount(3, $result);
@@ -312,6 +320,8 @@ class ParticipantCardAssemblerTest extends TestCase
public function testGetAllCardsDataUsesCanceledSurchargesBeforePrecomputedActivePrices(): void
{
$this->priceCalculator = $this->createMock(BookingPriceCalculator::class);
$room = new Room();
$room->id = 1;
$room->label = 'Doppelzimmer';
@@ -347,7 +357,7 @@ class ParticipantCardAssemblerTest extends TestCase
->with($bookingDto)
->willReturn([0 => 450.0, 1 => 999.0]);
$result = $this->service->getAllCardsData($bookingDto);
$result = $this->service()->getAllCardsData($bookingDto);
$this->assertSame(450.0, $result[0]->price->amount);
$this->assertSame(75.0, $result[1]->price->amount);
@@ -360,7 +370,7 @@ class ParticipantCardAssemblerTest extends TestCase
$bookingDto = new BookingDto($travel, 1);
$bookingDto->participants = [];
$result = $this->service->getAllCardsData($bookingDto);
$result = $this->service()->getAllCardsData($bookingDto);
$this->assertEmpty($result);
}
@@ -381,7 +391,7 @@ class ParticipantCardAssemblerTest extends TestCase
->method('calculateAllParticipantIndividualPrices')
->willReturn([1234.56]);
$result = $this->service->getCardData($bookingDto, 0);
$result = $this->service()->getCardData($bookingDto, 0);
$this->assertSame(1234.56, $result->price->amount);
$this->assertFalse($result->price->showDash);
@@ -403,9 +413,9 @@ class ParticipantCardAssemblerTest extends TestCase
->method('calculateAllParticipantIndividualPrices')
->willReturn([0.0, 0.0, 0.0]);
$result1 = $this->service->getCardData($bookingDto, 0);
$result2 = $this->service->getCardData($bookingDto, 1);
$result3 = $this->service->getCardData($bookingDto, 2);
$result1 = $this->service()->getCardData($bookingDto, 0);
$result2 = $this->service()->getCardData($bookingDto, 1);
$result3 = $this->service()->getCardData($bookingDto, 2);
$this->assertSame('Anmelder:in', $result1->name);
$this->assertSame('Teilnehmer:in', $result2->name);
@@ -414,6 +424,8 @@ class ParticipantCardAssemblerTest extends TestCase
public function testGetCardDataWithValidationReturnsValidCard(): void
{
$this->validator = $this->createMock(ValidatorInterface::class);
$room = new Room();
$room->id = 1;
$room->label = 'Doppelzimmer';
@@ -445,7 +457,7 @@ class ParticipantCardAssemblerTest extends TestCase
->method('validate')
->willReturn($violations);
$result = $this->service->getCardDataWithValidation($bookingDto, 0);
$result = $this->service()->getCardDataWithValidation($bookingDto, 0);
$this->assertSame('Max Mustermann', $result->name);
$this->assertSame('Doppelzimmer', $result->roomName);
@@ -457,6 +469,8 @@ class ParticipantCardAssemblerTest extends TestCase
public function testGetCardDataWithValidationReturnsInvalidCard(): void
{
$this->validator = $this->createMock(ValidatorInterface::class);
$travel = new Travel();
$travel->rooms = [];
@@ -486,7 +500,7 @@ class ParticipantCardAssemblerTest extends TestCase
->method('validate')
->willReturn($violations);
$result = $this->service->getCardDataWithValidation($bookingDto, 0);
$result = $this->service()->getCardDataWithValidation($bookingDto, 0);
$this->assertSame('Max Mustermann', $result->name);
$this->assertFalse($result->isValid);
@@ -496,6 +510,9 @@ class ParticipantCardAssemblerTest extends TestCase
public function testGetAllCardsDataWithValidationReturnsAllCards(): void
{
$this->priceCalculator = $this->createMock(BookingPriceCalculator::class);
$this->validator = $this->createMock(ValidatorInterface::class);
$room = new Room();
$room->id = 1;
$room->label = 'Doppelzimmer';
@@ -534,7 +551,7 @@ class ParticipantCardAssemblerTest extends TestCase
->method('validate')
->willReturn($violations);
$result = $this->service->getAllCardsDataWithValidation($bookingDto);
$result = $this->service()->getAllCardsDataWithValidation($bookingDto);
$this->assertCount(2, $result);
$this->assertTrue($result[0]->isValid);
@@ -545,6 +562,8 @@ class ParticipantCardAssemblerTest extends TestCase
public function testGetCardDataWithValidationCanForceStrictRequiredInEditMode(): void
{
$this->validator = $this->createMock(ValidatorInterface::class);
$travel = new Travel();
$travel->rooms = [];
@@ -556,7 +575,7 @@ class ParticipantCardAssemblerTest extends TestCase
$bookingDto = new BookingDto($travel, 1);
$bookingDto->agencyCode = AgencyLoader::INTERNAL_AGENCY_CODE;
$bookingDto->booking = new \App\BusProNet\Model\Booking();
$bookingDto->booking = new Booking();
$bookingDto->participants = [$participant];
$this->priceCalculator
@@ -574,16 +593,15 @@ class ParticipantCardAssemblerTest extends TestCase
->expects($this->once())
->method('validate')
->with(
$this->isInstanceOf(\App\Form\Model\ParticipantEditDto::class),
$this->isInstanceOf(ParticipantEditDto::class),
null,
['booking_edit', 'strict_required']
)
->willReturn($violations);
$result = $this->service->getCardDataWithValidation($bookingDto, 0, true);
$result = $this->service()->getCardDataWithValidation($bookingDto, 0, true);
$this->assertFalse($result->isValid);
$this->assertSame(['Bitte angeben'], $result->errorMessages);
}
}
+40 -16
View File
@@ -23,15 +23,18 @@ class ParticipantDataPrefillerTest extends TestCase
private ApiClient $apiClient;
private Crypt $crypt;
private LoggerInterface $logger;
private ParticipantDataPrefiller $service;
private ?ParticipantDataPrefiller $service = null;
protected function setUp(): void
{
$this->apiClient = $this->createMock(ApiClient::class);
$this->crypt = $this->createMock(Crypt::class);
$this->logger = $this->createMock(LoggerInterface::class);
$this->apiClient = $this->createStub(ApiClient::class);
$this->crypt = $this->createStub(Crypt::class);
$this->logger = $this->createStub(LoggerInterface::class);
}
$this->service = new ParticipantDataPrefiller(
private function service(): ParticipantDataPrefiller
{
return $this->service ??= new ParticipantDataPrefiller(
$this->apiClient,
$this->crypt,
$this->logger
@@ -40,6 +43,9 @@ class ParticipantDataPrefillerTest extends TestCase
public function testPrepopulateApplicantWithCompletePersonalData(): void
{
$this->apiClient = $this->createMock(ApiClient::class);
$this->crypt = $this->createMock(Crypt::class);
$user = $this->createUser('[email protected]', 'encrypted_password');
$applicant = new ParticipantDto();
$applicant->index = 0;
@@ -56,7 +62,7 @@ class ParticipantDataPrefillerTest extends TestCase
->with('[email protected]', 'decrypted_password')
->willReturn($personalData);
$result = $this->service->prefillApplicantFromUser($user, $applicant);
$result = $this->service()->prefillApplicantFromUser($user, $applicant);
// BPN API IDs for linking to existing records
$this->assertSame(12345, $result->addressId);
@@ -88,6 +94,9 @@ class ParticipantDataPrefillerTest extends TestCase
public function testPrepopulateApplicantWithPartialPersonalData(): void
{
$this->apiClient = $this->createMock(ApiClient::class);
$this->crypt = $this->createMock(Crypt::class);
$user = $this->createUser('[email protected]', 'encrypted_password');
$applicant = new ParticipantDto();
$applicant->index = 0;
@@ -102,7 +111,7 @@ class ParticipantDataPrefillerTest extends TestCase
->method('getPersonalData')
->willReturn($personalData);
$result = $this->service->prefillApplicantFromUser($user, $applicant);
$result = $this->service()->prefillApplicantFromUser($user, $applicant);
// Required fields should be populated
$this->assertSame('Jane', $result->firstName);
@@ -123,6 +132,10 @@ class ParticipantDataPrefillerTest extends TestCase
public function testPrepopulateApplicantWithApiNotificationError(): void
{
$this->apiClient = $this->createMock(ApiClient::class);
$this->crypt = $this->createMock(Crypt::class);
$this->logger = $this->createMock(LoggerInterface::class);
$user = $this->createUser('[email protected]', 'encrypted_password');
$applicant = new ParticipantDto();
$applicant->index = 0;
@@ -148,7 +161,7 @@ class ParticipantDataPrefillerTest extends TestCase
$this->logger->expects($this->never())
->method('info');
$result = $this->service->prefillApplicantFromUser($user, $applicant);
$result = $this->service()->prefillApplicantFromUser($user, $applicant);
// Should return unchanged applicant
$this->assertNull($result->firstName);
@@ -158,6 +171,10 @@ class ParticipantDataPrefillerTest extends TestCase
public function testPrepopulateApplicantWithDecryptionException(): void
{
$this->apiClient = $this->createMock(ApiClient::class);
$this->crypt = $this->createMock(Crypt::class);
$this->logger = $this->createMock(LoggerInterface::class);
$user = $this->createUser('[email protected]', 'encrypted_password');
$applicant = new ParticipantDto();
$applicant->index = 0;
@@ -176,7 +193,7 @@ class ParticipantDataPrefillerTest extends TestCase
'email' => '[email protected]',
]);
$result = $this->service->prefillApplicantFromUser($user, $applicant);
$result = $this->service()->prefillApplicantFromUser($user, $applicant);
// Should return unchanged applicant
$this->assertNull($result->firstName);
@@ -186,6 +203,10 @@ class ParticipantDataPrefillerTest extends TestCase
public function testPrepopulateApplicantWithApiException(): void
{
$this->apiClient = $this->createMock(ApiClient::class);
$this->crypt = $this->createMock(Crypt::class);
$this->logger = $this->createMock(LoggerInterface::class);
$user = $this->createUser('[email protected]', 'encrypted_password');
$applicant = new ParticipantDto();
$applicant->index = 0;
@@ -205,7 +226,7 @@ class ParticipantDataPrefillerTest extends TestCase
'email' => '[email protected]',
]);
$result = $this->service->prefillApplicantFromUser($user, $applicant);
$result = $this->service()->prefillApplicantFromUser($user, $applicant);
// Should return unchanged applicant
$this->assertNull($result->firstName);
@@ -215,6 +236,9 @@ class ParticipantDataPrefillerTest extends TestCase
public function testPrepopulatePreservesBookingSpecificFields(): void
{
$this->apiClient = $this->createMock(ApiClient::class);
$this->crypt = $this->createMock(Crypt::class);
$user = $this->createUser('[email protected]', 'encrypted_password');
$applicant = new ParticipantDto();
$applicant->index = 0;
@@ -231,7 +255,7 @@ class ParticipantDataPrefillerTest extends TestCase
->method('getPersonalData')
->willReturn($personalData);
$result = $this->service->prefillApplicantFromUser($user, $applicant);
$result = $this->service()->prefillApplicantFromUser($user, $applicant);
// Personal data should be updated
$this->assertSame('John', $result->firstName);
@@ -250,8 +274,8 @@ class ParticipantDataPrefillerTest extends TestCase
$filled = new ParticipantDto();
$filled->firstName = 'Already set';
$this->assertTrue($this->service->shouldPrefillApplicant($fresh));
$this->assertFalse($this->service->shouldPrefillApplicant($filled));
$this->assertTrue($this->service()->shouldPrefillApplicant($fresh));
$this->assertFalse($this->service()->shouldPrefillApplicant($filled));
}
public function testDummyTokenMatchesInCreateAndEditModes(): void
@@ -259,8 +283,8 @@ class ParticipantDataPrefillerTest extends TestCase
$participant = new ParticipantDto();
$participant->lastName = ParticipantDataPrefiller::TOKEN;
$this->assertTrue($this->service->isDummyDataFillRequested($participant, BookingDto::MODE_CREATE));
$this->assertTrue($this->service->isDummyDataFillRequested($participant, BookingDto::MODE_EDIT));
$this->assertTrue($this->service()->isDummyDataFillRequested($participant, BookingDto::MODE_CREATE));
$this->assertTrue($this->service()->isDummyDataFillRequested($participant, BookingDto::MODE_EDIT));
}
public function testFillDummyParticipantSetsGeneratedData(): void
@@ -268,7 +292,7 @@ class ParticipantDataPrefillerTest extends TestCase
CarbonImmutable::setTestNow(CarbonImmutable::create(2026, 1, 15, 14, 30));
$participant = new ParticipantDto();
$this->service->fillDummyParticipant($participant, 0);
$this->service()->fillDummyParticipant($participant, 0);
$this->assertSame('Vorname 1', $participant->firstName);
$this->assertSame('Muster 1 14:30', $participant->lastName);
+12 -33
View File
@@ -11,6 +11,7 @@ use App\Form\Model\BookingDto;
use App\Form\Model\ParticipantDto;
use App\Form\Model\RoomSelectionDto;
use App\Service\RoomAssigner;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\TestCase;
/**
@@ -25,9 +26,7 @@ class RoomAssignerTest extends TestCase
$this->service = new RoomAssigner();
}
/**
* @test
*/
#[Test]
public function shouldAutoAssignRoomsReturnsTrueForSingleRoomType(): void
{
$bookingDto = $this->createBookingDtoWithRoomSelections([
@@ -39,9 +38,7 @@ class RoomAssignerTest extends TestCase
self::assertTrue($result, 'Should auto-assign when exactly one room type is selected');
}
/**
* @test
*/
#[Test]
public function shouldAutoAssignRoomsReturnsFalseForMultipleRoomTypes(): void
{
$bookingDto = $this->createBookingDtoWithRoomSelections([
@@ -54,9 +51,7 @@ class RoomAssignerTest extends TestCase
self::assertFalse($result, 'Should not auto-assign when multiple room types are selected');
}
/**
* @test
*/
#[Test]
public function shouldAutoAssignRoomsReturnsFalseForNoRoomSelections(): void
{
$bookingDto = $this->createBookingDtoWithRoomSelections([]);
@@ -66,9 +61,7 @@ class RoomAssignerTest extends TestCase
self::assertFalse($result, 'Should not auto-assign when no rooms are selected');
}
/**
* @test
*/
#[Test]
public function shouldAutoAssignRoomsIgnoresZeroQuantityRooms(): void
{
$bookingDto = $this->createBookingDtoWithRoomSelections([
@@ -82,9 +75,7 @@ class RoomAssignerTest extends TestCase
self::assertTrue($result, 'Should treat zero/null quantity as not selected');
}
/**
* @test
*/
#[Test]
public function assignParticipantsToRoomsWorksWithSingleRoomType(): void
{
$bookingDto = $this->createBookingDtoWithRoomSelections([
@@ -117,9 +108,7 @@ class RoomAssignerTest extends TestCase
self::assertSame(100, $bookingDto->participants[3]->assignedRoomId);
}
/**
* @test
*/
#[Test]
public function assignParticipantsToRoomsSkipsAssignmentWithMultipleRoomTypes(): void
{
$bookingDto = $this->createBookingDtoWithRoomSelections([
@@ -159,9 +148,7 @@ class RoomAssignerTest extends TestCase
self::assertNull($bookingDto->participants[3]->assignedRoomId);
}
/**
* @test
*/
#[Test]
public function assignParticipantsToRoomsHandlesMixedRoomCapacities(): void
{
$bookingDto = $this->createBookingDtoWithRoomSelections([
@@ -190,9 +177,7 @@ class RoomAssignerTest extends TestCase
self::assertSame(100, $bookingDto->participants[1]->assignedRoomId);
}
/**
* @test
*/
#[Test]
public function validateAndResetInvalidAssignmentsResetsOrphanedAssignments(): void
{
// Create DTO with only room 100 selected
@@ -221,9 +206,7 @@ class RoomAssignerTest extends TestCase
self::assertNull($bookingDto->participants[3]->assignedRoomId, 'Invalid assignment should be reset to null');
}
/**
* @test
*/
#[Test]
public function validateAndResetInvalidAssignmentsReturnsFalseWhenAllValid(): void
{
$bookingDto = $this->createBookingDtoWithRoomSelections([
@@ -245,9 +228,7 @@ class RoomAssignerTest extends TestCase
self::assertSame(100, $bookingDto->participants[1]->assignedRoomId);
}
/**
* @test
*/
#[Test]
public function validateAndResetInvalidAssignmentsHandlesNullAssignments(): void
{
$bookingDto = $this->createBookingDtoWithRoomSelections([
@@ -267,9 +248,7 @@ class RoomAssignerTest extends TestCase
self::assertFalse($result, 'Should return false when only null assignments exist');
}
/**
* @test
*/
#[Test]
public function validateAndResetInvalidAssignmentsHandlesEmptySelectedRooms(): void
{
// All rooms have quantity 0 (none selected)
+78 -24
View File
@@ -18,7 +18,7 @@ use Symfony\Contracts\Cache\CacheInterface;
class TravelDataProviderTest extends TestCase
{
private TravelDataProvider $service;
private ?TravelDataProvider $service = null;
private TravelLoader $travelLoader;
private ApiClient $apiClient;
private CacheInterface $cache;
@@ -29,15 +29,18 @@ class TravelDataProviderTest extends TestCase
protected function setUp(): void
{
$this->travelLoader = $this->createMock(TravelLoader::class);
$this->apiClient = $this->createMock(ApiClient::class);
$this->cache = $this->createMock(CacheInterface::class);
$this->logger = $this->createMock(LoggerInterface::class);
$this->travelSnapshotService = $this->createMock(TravelSnapshotManager::class);
$this->travelLookupService = $this->createMock(TravelIndex::class);
$this->travelEnrichmentService = $this->createMock(TravelEnricher::class);
$this->travelLoader = $this->createStub(TravelLoader::class);
$this->apiClient = $this->createStub(ApiClient::class);
$this->cache = $this->createStub(CacheInterface::class);
$this->logger = $this->createStub(LoggerInterface::class);
$this->travelSnapshotService = $this->createStub(TravelSnapshotManager::class);
$this->travelLookupService = $this->createStub(TravelIndex::class);
$this->travelEnrichmentService = $this->createStub(TravelEnricher::class);
}
$this->service = new TravelDataProvider(
private function service(): TravelDataProvider
{
return $this->service ??= new TravelDataProvider(
$this->travelLoader,
$this->apiClient,
$this->cache,
@@ -52,6 +55,10 @@ class TravelDataProviderTest extends TestCase
public function testGetTravelDataFromXmlSuccess(): void
{
$this->travelLoader = $this->createMock(TravelLoader::class);
$this->travelSnapshotService = $this->createMock(TravelSnapshotManager::class);
$this->travelEnrichmentService = $this->createMock(TravelEnricher::class);
$dateId = 12345;
$hotelId = 67890;
$travel = new Travel();
@@ -75,13 +82,17 @@ class TravelDataProviderTest extends TestCase
->method('upsertFromTravel')
->with($travel);
$result = $this->service->getTravelDataFromXml($dateId, $hotelId);
$result = $this->service()->getTravelDataFromXml($dateId, $hotelId);
$this->assertSame($travel, $result);
}
public function testGetTravelDataFromXmlSkipsSnapshotWhenEnrichmentFails(): void
{
$this->travelLoader = $this->createMock(TravelLoader::class);
$this->travelSnapshotService = $this->createMock(TravelSnapshotManager::class);
$this->travelEnrichmentService = $this->createMock(TravelEnricher::class);
$dateId = 12345;
$hotelId = 67890;
$travel = new Travel();
@@ -104,13 +115,16 @@ class TravelDataProviderTest extends TestCase
->expects($this->never())
->method('upsertFromTravel');
$result = $this->service->getTravelDataFromXml($dateId, $hotelId);
$result = $this->service()->getTravelDataFromXml($dateId, $hotelId);
$this->assertSame($travel, $result);
}
public function testGetTravelDataFromXmlNotFound(): void
{
$this->travelLoader = $this->createMock(TravelLoader::class);
$this->travelEnrichmentService = $this->createMock(TravelEnricher::class);
$dateId = 12345;
$hotelId = 67890;
@@ -125,11 +139,16 @@ class TravelDataProviderTest extends TestCase
->method('enrichFromXml');
$this->expectException(TravelNotFoundException::class);
$this->service->getTravelDataFromXml($dateId, $hotelId);
$this->service()->getTravelDataFromXml($dateId, $hotelId);
}
public function testGetTravelDataFromApiSuccess(): void
{
$this->apiClient = $this->createMock(ApiClient::class);
$this->travelSnapshotService = $this->createMock(TravelSnapshotManager::class);
$this->travelLookupService = $this->createMock(TravelIndex::class);
$this->travelEnrichmentService = $this->createMock(TravelEnricher::class);
$dateId = 12345;
$hotelId = 67890;
$productId = 555;
@@ -159,13 +178,16 @@ class TravelDataProviderTest extends TestCase
->method('upsertFromTravel')
->with($travel);
$result = $this->service->getTravelDataFromApi($dateId, $hotelId);
$result = $this->service()->getTravelDataFromApi($dateId, $hotelId);
$this->assertSame($travel, $result);
}
public function testGetTravelDataFromApiSnapshotFailureDoesNotDiscardApiTravel(): void
{
$this->logger = $this->createMock(LoggerInterface::class);
$this->travelSnapshotService = $this->createMock(TravelSnapshotManager::class);
$dateId = 12345;
$hotelId = 67890;
$productId = 555;
@@ -191,7 +213,7 @@ class TravelDataProviderTest extends TestCase
->method('warning')
->with('Failed to persist travel snapshot after API load', $this->arrayHasKey('dateId'));
$result = $this->service->getTravelDataFromApi($dateId, $hotelId);
$result = $this->service()->getTravelDataFromApi($dateId, $hotelId);
$this->assertNotNull($result);
$this->assertSame($travel, $result);
@@ -199,6 +221,9 @@ class TravelDataProviderTest extends TestCase
public function testGetTravelDataFromApiCannotMapDateId(): void
{
$this->apiClient = $this->createMock(ApiClient::class);
$this->travelLookupService = $this->createMock(TravelIndex::class);
$dateId = 12345;
$hotelId = 67890;
@@ -212,13 +237,15 @@ class TravelDataProviderTest extends TestCase
->expects($this->never())
->method('getTravelData');
$result = $this->service->getTravelDataFromApi($dateId, $hotelId);
$result = $this->service()->getTravelDataFromApi($dateId, $hotelId);
$this->assertNull($result);
}
public function testExistsLocallyDelegatesToLookupService(): void
{
$this->travelLookupService = $this->createMock(TravelIndex::class);
$dateId = 12345;
$hotelId = 67890;
@@ -228,11 +255,13 @@ class TravelDataProviderTest extends TestCase
->with($dateId, $hotelId)
->willReturn(true);
$this->assertTrue($this->service->existsLocally($dateId, $hotelId));
$this->assertTrue($this->service()->existsLocally($dateId, $hotelId));
}
public function testGetAvailableSourcesDelegatesToLookupService(): void
{
$this->travelLookupService = $this->createMock(TravelIndex::class);
$dateId = 12345;
$hotelId = 67890;
$expected = ['local' => true, 'remote' => true];
@@ -243,11 +272,13 @@ class TravelDataProviderTest extends TestCase
->with($dateId, $hotelId)
->willReturn($expected);
$this->assertSame($expected, $this->service->getAvailableSources($dateId, $hotelId));
$this->assertSame($expected, $this->service()->getAvailableSources($dateId, $hotelId));
}
public function testGenerateFilesMapDelegatesToLookupService(): void
{
$this->travelLookupService = $this->createMock(TravelIndex::class);
$mapping = [12345 => ['id' => 12345, 'hotels' => []]];
$this->travelLookupService
@@ -255,11 +286,15 @@ class TravelDataProviderTest extends TestCase
->method('generateFilesMap')
->willReturn($mapping);
$this->assertSame($mapping, $this->service->generateFilesMap());
$this->assertSame($mapping, $this->service()->generateFilesMap());
}
public function testGetTravelDataFromLocalPrefersSnapshot(): void
{
$this->travelLoader = $this->createMock(TravelLoader::class);
$this->travelSnapshotService = $this->createMock(TravelSnapshotManager::class);
$this->travelEnrichmentService = $this->createMock(TravelEnricher::class);
$dateId = 12345;
$hotelId = 67890;
$travel = new Travel();
@@ -281,13 +316,17 @@ class TravelDataProviderTest extends TestCase
->method('patchInsurances')
->with($travel);
$result = $this->service->getTravelDataFromLocal($dateId, $hotelId);
$result = $this->service()->getTravelDataFromLocal($dateId, $hotelId);
$this->assertSame($travel, $result);
}
public function testGetTravelDataFromLocalFallsBackToXmlWhenSnapshotMissing(): void
{
$this->travelLoader = $this->createMock(TravelLoader::class);
$this->travelSnapshotService = $this->createMock(TravelSnapshotManager::class);
$this->travelEnrichmentService = $this->createMock(TravelEnricher::class);
$dateId = 12345;
$hotelId = 67890;
$travel = new Travel();
@@ -322,13 +361,16 @@ class TravelDataProviderTest extends TestCase
->method('patchInsurances')
->with($travel);
$result = $this->service->getTravelDataFromLocal($dateId, $hotelId);
$result = $this->service()->getTravelDataFromLocal($dateId, $hotelId);
$this->assertSame($travel, $result);
}
public function testGetTravelDataFromLocalPropagatesTravelNotFoundFromXmlFallback(): void
{
$this->travelLoader = $this->createMock(TravelLoader::class);
$this->travelSnapshotService = $this->createMock(TravelSnapshotManager::class);
$dateId = 12345;
$hotelId = 67890;
@@ -345,11 +387,15 @@ class TravelDataProviderTest extends TestCase
->willThrowException(new TravelNotFoundException($dateId));
$this->expectException(TravelNotFoundException::class);
$this->service->getTravelDataFromLocal($dateId, $hotelId);
$this->service()->getTravelDataFromLocal($dateId, $hotelId);
}
public function testSnapshotLoadFailureFallsBackToXml(): void
{
$this->travelLoader = $this->createMock(TravelLoader::class);
$this->logger = $this->createMock(LoggerInterface::class);
$this->travelSnapshotService = $this->createMock(TravelSnapshotManager::class);
$dateId = 12345;
$hotelId = 67890;
$travel = new Travel();
@@ -373,13 +419,17 @@ class TravelDataProviderTest extends TestCase
->with($dateId, $hotelId)
->willReturn($travel);
$result = $this->service->getTravelDataFromLocal($dateId, $hotelId);
$result = $this->service()->getTravelDataFromLocal($dateId, $hotelId);
$this->assertSame($travel, $result);
}
public function testSnapshotPersistenceFailureDoesNotDiscardXmlTravel(): void
{
$this->travelLoader = $this->createMock(TravelLoader::class);
$this->logger = $this->createMock(LoggerInterface::class);
$this->travelSnapshotService = $this->createMock(TravelSnapshotManager::class);
$dateId = 12345;
$hotelId = 67890;
$travel = new Travel();
@@ -406,7 +456,7 @@ class TravelDataProviderTest extends TestCase
->method('warning')
->with('Failed to persist travel snapshot after XML load', $this->arrayHasKey('dateId'));
$result = $this->service->getTravelDataFromXml($dateId, $hotelId);
$result = $this->service()->getTravelDataFromXml($dateId, $hotelId);
$this->assertNotNull($result);
$this->assertSame($travel, $result);
@@ -414,6 +464,10 @@ class TravelDataProviderTest extends TestCase
public function testGetTravelDataDelegatesToLoadUncached(): void
{
$this->travelLoader = $this->createMock(TravelLoader::class);
$this->cache = $this->createMock(CacheInterface::class);
$this->travelSnapshotService = $this->createMock(TravelSnapshotManager::class);
$dateId = 12345;
$hotelId = 67890;
$travel = new Travel();
@@ -436,7 +490,7 @@ class TravelDataProviderTest extends TestCase
->with($dateId, $hotelId)
->willReturn($travel);
$result = $this->service->getTravelData($dateId, $hotelId, false);
$result = $this->service()->getTravelData($dateId, $hotelId, false);
$this->assertSame($travel, $result);
}
+23 -11
View File
@@ -15,7 +15,7 @@ use Psr\Log\LoggerInterface;
class TravelEnricherTest extends TestCase
{
private TravelEnricher $service;
private ?TravelEnricher $service = null;
private PickupLoader $pickupLoader;
private HotelLoader $hotelLoader;
private InsuranceLoader $insuranceLoader;
@@ -23,12 +23,15 @@ class TravelEnricherTest extends TestCase
protected function setUp(): void
{
$this->pickupLoader = $this->createMock(PickupLoader::class);
$this->hotelLoader = $this->createMock(HotelLoader::class);
$this->insuranceLoader = $this->createMock(InsuranceLoader::class);
$this->logger = $this->createMock(LoggerInterface::class);
$this->pickupLoader = $this->createStub(PickupLoader::class);
$this->hotelLoader = $this->createStub(HotelLoader::class);
$this->insuranceLoader = $this->createStub(InsuranceLoader::class);
$this->logger = $this->createStub(LoggerInterface::class);
}
$this->service = new TravelEnricher(
private function service(): TravelEnricher
{
return $this->service ??= new TravelEnricher(
$this->pickupLoader,
$this->hotelLoader,
$this->insuranceLoader,
@@ -42,6 +45,9 @@ class TravelEnricherTest extends TestCase
public function testEnrichFromXmlReturnsTrueOnSuccess(): void
{
$this->pickupLoader = $this->createMock(PickupLoader::class);
$this->hotelLoader = $this->createMock(HotelLoader::class);
$travel = new Travel();
$travel->id = 100;
@@ -55,11 +61,13 @@ class TravelEnricherTest extends TestCase
->method('patchHotelDetails')
->with($travel);
$this->assertTrue($this->service->enrichFromXml($travel));
$this->assertTrue($this->service()->enrichFromXml($travel));
}
public function testEnrichFromXmlReturnsFalseAndLogsWarningOnFailure(): void
{
$this->logger = $this->createMock(LoggerInterface::class);
$travel = new Travel();
$travel->id = 100;
@@ -72,7 +80,7 @@ class TravelEnricherTest extends TestCase
->method('warning')
->with('Failed to enrich travel data', $this->arrayHasKey('travelId'));
$this->assertFalse($this->service->enrichFromXml($travel));
$this->assertFalse($this->service()->enrichFromXml($travel));
}
// -------------------------------------------------------------------------
@@ -81,6 +89,8 @@ class TravelEnricherTest extends TestCase
public function testPatchInsurancesReplacesExistingInsurances(): void
{
$this->insuranceLoader = $this->createMock(InsuranceLoader::class);
$travel = new Travel();
$travel->id = 100;
@@ -104,7 +114,7 @@ class TravelEnricherTest extends TestCase
->method('loadAll')
->willReturn([$individual, $package]);
$this->service->patchInsurances($travel);
$this->service()->patchInsurances($travel);
$this->assertSame([$individual, $package], $travel->insurances);
}
@@ -126,7 +136,7 @@ class TravelEnricherTest extends TestCase
$this->insuranceLoader->method('loadAll')->willReturn([$individual, $package]);
$this->service->patchInsurances($travel);
$this->service()->patchInsurances($travel);
$this->assertCount(1, $package->containedInsurances);
$this->assertSame($individual, $package->containedInsurances[0]);
@@ -134,6 +144,8 @@ class TravelEnricherTest extends TestCase
public function testPatchInsurancesLogsWarningOnFailureAndDoesNotThrow(): void
{
$this->logger = $this->createMock(LoggerInterface::class);
$travel = new Travel();
$travel->id = 100;
$original = [];
@@ -149,7 +161,7 @@ class TravelEnricherTest extends TestCase
->with('Failed to load insurance data', $this->arrayHasKey('travelId'));
// Must not throw; insurances stay unchanged
$this->service->patchInsurances($travel);
$this->service()->patchInsurances($travel);
$this->assertSame($original, $travel->insurances);
}
+42 -21
View File
@@ -13,7 +13,7 @@ use Psr\Log\LoggerInterface;
class TravelIndexTest extends TestCase
{
private TravelIndex $service;
private ?TravelIndex $service = null;
private TravelLoader $travelLoader;
private HotelLoader $hotelLoader;
private TravelSnapshotManager $travelSnapshotService;
@@ -21,12 +21,15 @@ class TravelIndexTest extends TestCase
protected function setUp(): void
{
$this->travelLoader = $this->createMock(TravelLoader::class);
$this->hotelLoader = $this->createMock(HotelLoader::class);
$this->travelSnapshotService = $this->createMock(TravelSnapshotManager::class);
$this->logger = $this->createMock(LoggerInterface::class);
$this->travelLoader = $this->createStub(TravelLoader::class);
$this->hotelLoader = $this->createStub(HotelLoader::class);
$this->travelSnapshotService = $this->createStub(TravelSnapshotManager::class);
$this->logger = $this->createStub(LoggerInterface::class);
}
$this->service = new TravelIndex(
private function service(): TravelIndex
{
return $this->service ??= new TravelIndex(
$this->travelLoader,
$this->hotelLoader,
$this->travelSnapshotService,
@@ -59,7 +62,7 @@ class TravelIndexTest extends TestCase
$this->travelLoader->method('generateFilesMap')->willReturn($xmlMapping);
$this->travelSnapshotService->method('generateMapping')->willReturn($snapshotMapping);
$result = $this->service->generateFilesMap();
$result = $this->service()->generateFilesMap();
// XML entry is preserved
$this->assertArrayHasKey($dateId, $result);
@@ -73,6 +76,9 @@ class TravelIndexTest extends TestCase
public function testGenerateFilesMapIsMemoizedWithinRequest(): void
{
$this->travelLoader = $this->createMock(TravelLoader::class);
$this->travelSnapshotService = $this->createMock(TravelSnapshotManager::class);
$mapping = [100 => ['id' => 100, 'code' => 'A', 'hotels' => []]];
$this->travelLoader
@@ -86,14 +92,16 @@ class TravelIndexTest extends TestCase
->willReturn([]);
// Two calls — loaders invoked only once
$this->service->generateFilesMap();
$result = $this->service->generateFilesMap();
$this->service()->generateFilesMap();
$result = $this->service()->generateFilesMap();
$this->assertSame($mapping, $result);
}
public function testGenerateFilesMapFallsBackToSnapshotsOnXmlFailure(): void
{
$this->logger = $this->createMock(LoggerInterface::class);
$snapshotMapping = [100 => ['id' => 100, 'code' => 'A', 'hotels' => []]];
$this->travelLoader
@@ -109,7 +117,7 @@ class TravelIndexTest extends TestCase
->method('warning')
->with('Failed to generate XML files mapping, fallback to snapshots only', $this->arrayHasKey('error'));
$result = $this->service->generateFilesMap();
$result = $this->service()->generateFilesMap();
$this->assertSame($snapshotMapping, $result);
}
@@ -125,7 +133,7 @@ class TravelIndexTest extends TestCase
]);
$this->travelSnapshotService->method('generateMapping')->willReturn([]);
$result = $this->service->mapDateCodeToId('WI25');
$result = $this->service()->mapDateCodeToId('WI25');
$this->assertSame(42, $result);
}
@@ -135,7 +143,7 @@ class TravelIndexTest extends TestCase
$this->travelLoader->method('generateFilesMap')->willReturn([]);
$this->travelSnapshotService->method('generateMapping')->willReturn([]);
$result = $this->service->mapDateCodeToId('UNKNOWN');
$result = $this->service()->mapDateCodeToId('UNKNOWN');
$this->assertNull($result);
}
@@ -146,13 +154,15 @@ class TravelIndexTest extends TestCase
public function testMapHotelCodeToIdDelegatesToHotelLoader(): void
{
$this->hotelLoader = $this->createMock(HotelLoader::class);
$this->hotelLoader
->expects($this->once())
->method('mapCodeToId')
->with('HTL001')
->willReturn(77);
$result = $this->service->mapHotelCodeToId('HTL001');
$result = $this->service()->mapHotelCodeToId('HTL001');
$this->assertSame(77, $result);
}
@@ -163,7 +173,7 @@ class TravelIndexTest extends TestCase
->method('mapCodeToId')
->willThrowException(new \RuntimeException('Not found'));
$result = $this->service->mapHotelCodeToId('MISSING');
$result = $this->service()->mapHotelCodeToId('MISSING');
$this->assertNull($result);
}
@@ -174,6 +184,9 @@ class TravelIndexTest extends TestCase
public function testMapDateIdToProductIdPrefersSnapshot(): void
{
$this->travelLoader = $this->createMock(TravelLoader::class);
$this->travelSnapshotService = $this->createMock(TravelSnapshotManager::class);
$this->travelSnapshotService
->expects($this->once())
->method('findProductIdByDateId')
@@ -184,13 +197,16 @@ class TravelIndexTest extends TestCase
->expects($this->never())
->method('mapDateIdToProductId');
$result = $this->service->mapDateIdToProductId(100);
$result = $this->service()->mapDateIdToProductId(100);
$this->assertSame(999, $result);
}
public function testMapDateIdToProductIdFallsBackToLoaderWhenSnapshotReturnsNull(): void
{
$this->travelLoader = $this->createMock(TravelLoader::class);
$this->travelSnapshotService = $this->createMock(TravelSnapshotManager::class);
$this->travelSnapshotService
->expects($this->once())
->method('findProductIdByDateId')
@@ -203,7 +219,7 @@ class TravelIndexTest extends TestCase
->with(100)
->willReturn(42);
$result = $this->service->mapDateIdToProductId(100);
$result = $this->service()->mapDateIdToProductId(100);
$this->assertSame(42, $result);
}
@@ -214,6 +230,9 @@ class TravelIndexTest extends TestCase
public function testExistsLocallyTrueFromSnapshot(): void
{
$this->travelLoader = $this->createMock(TravelLoader::class);
$this->travelSnapshotService = $this->createMock(TravelSnapshotManager::class);
$dateId = 12345;
$hotelId = 67890;
@@ -227,7 +246,7 @@ class TravelIndexTest extends TestCase
->expects($this->never())
->method('generateFilesMap');
$this->assertTrue($this->service->existsLocally($dateId, $hotelId));
$this->assertTrue($this->service()->existsLocally($dateId, $hotelId));
}
public function testExistsLocallyTrueFromXmlMap(): void
@@ -245,7 +264,7 @@ class TravelIndexTest extends TestCase
$this->travelLoader->method('generateFilesMap')->willReturn($mapping);
$this->travelSnapshotService->method('generateMapping')->willReturn([]);
$this->assertTrue($this->service->existsLocally($dateId, $hotelId));
$this->assertTrue($this->service()->existsLocally($dateId, $hotelId));
}
public function testExistsLocallyFalseNoTravel(): void
@@ -254,7 +273,7 @@ class TravelIndexTest extends TestCase
$this->travelLoader->method('generateFilesMap')->willReturn([]);
$this->travelSnapshotService->method('generateMapping')->willReturn([]);
$this->assertFalse($this->service->existsLocally(12345, 67890));
$this->assertFalse($this->service()->existsLocally(12345, 67890));
}
public function testExistsLocallyFalseNoHotel(): void
@@ -269,7 +288,7 @@ class TravelIndexTest extends TestCase
$this->travelLoader->method('generateFilesMap')->willReturn($mapping);
$this->travelSnapshotService->method('generateMapping')->willReturn([]);
$this->assertFalse($this->service->existsLocally($dateId, $hotelId));
$this->assertFalse($this->service()->existsLocally($dateId, $hotelId));
}
// -------------------------------------------------------------------------
@@ -278,6 +297,8 @@ class TravelIndexTest extends TestCase
public function testGetAvailableSourcesReturnsBothFlags(): void
{
$this->travelLoader = $this->createMock(TravelLoader::class);
$dateId = 12345;
$hotelId = 67890;
$mapping = [
@@ -290,7 +311,7 @@ class TravelIndexTest extends TestCase
$this->travelLoader->method('generateFilesMap')->willReturn($mapping);
$this->travelLoader->method('mapDateIdToProductId')->with($dateId)->willReturn(555);
$result = $this->service->getAvailableSources($dateId, $hotelId);
$result = $this->service()->getAvailableSources($dateId, $hotelId);
$this->assertEquals(['local' => true, 'remote' => true], $result);
}
+91 -27
View File
@@ -24,17 +24,20 @@ class TravelSnapshotManagerTest extends TestCase
private ApiClient $apiClient;
private LoggerInterface $logger;
private SerializerInterface $serializer;
private TravelSnapshotManager $service;
private ?TravelSnapshotManager $service = null;
protected function setUp(): void
{
$this->snapshotRepository = $this->createMock(TravelSnapshotRepository::class);
$this->entityManager = $this->createMock(EntityManagerInterface::class);
$this->apiClient = $this->createMock(ApiClient::class);
$this->logger = $this->createMock(LoggerInterface::class);
$this->serializer = $this->createMock(SerializerInterface::class);
$this->snapshotRepository = $this->createStub(TravelSnapshotRepository::class);
$this->entityManager = $this->createStub(EntityManagerInterface::class);
$this->apiClient = $this->createStub(ApiClient::class);
$this->logger = $this->createStub(LoggerInterface::class);
$this->serializer = $this->createStub(SerializerInterface::class);
}
$this->service = new TravelSnapshotManager(
private function service(): TravelSnapshotManager
{
return $this->service ??= new TravelSnapshotManager(
$this->snapshotRepository,
$this->entityManager,
$this->apiClient,
@@ -46,6 +49,10 @@ class TravelSnapshotManagerTest extends TestCase
public function testUpsertCreatesNewSnapshot(): void
{
$this->snapshotRepository = $this->createMock(TravelSnapshotRepository::class);
$this->entityManager = $this->createMock(EntityManagerInterface::class);
$this->serializer = $this->createMock(SerializerInterface::class);
$travel = new Travel();
$travel->id = 100;
$travel->hotelId = 200;
@@ -71,11 +78,15 @@ class TravelSnapshotManagerTest extends TestCase
->expects($this->once())
->method('flush');
$this->service->upsertFromTravel($travel);
$this->service()->upsertFromTravel($travel);
}
public function testUpsertSkipsWhenHashUnchanged(): void
{
$this->snapshotRepository = $this->createMock(TravelSnapshotRepository::class);
$this->entityManager = $this->createMock(EntityManagerInterface::class);
$this->serializer = $this->createMock(SerializerInterface::class);
$travel = new Travel();
$travel->id = 100;
$travel->hotelId = 200;
@@ -100,11 +111,15 @@ class TravelSnapshotManagerTest extends TestCase
->expects($this->never())
->method('flush');
$this->service->upsertFromTravel($travel);
$this->service()->upsertFromTravel($travel);
}
public function testUpsertUpdatesWhenHashChanged(): void
{
$this->snapshotRepository = $this->createMock(TravelSnapshotRepository::class);
$this->entityManager = $this->createMock(EntityManagerInterface::class);
$this->serializer = $this->createMock(SerializerInterface::class);
$travel = new Travel();
$travel->id = 100;
$travel->hotelId = 200;
@@ -131,24 +146,30 @@ class TravelSnapshotManagerTest extends TestCase
->expects($this->once())
->method('flush');
$this->service->upsertFromTravel($travel);
$this->service()->upsertFromTravel($travel);
}
public function testLoadTravelReturnsNullWhenNoSnapshot(): void
{
$this->snapshotRepository = $this->createMock(TravelSnapshotRepository::class);
$this->snapshotRepository
->expects($this->once())
->method('findByDateAndHotel')
->with(100, 200)
->willReturn(null);
$result = $this->service->loadTravel(100, 200);
$result = $this->service()->loadTravel(100, 200);
$this->assertNull($result);
}
public function testLoadTravelReturnsNullOnDeserializationError(): void
{
$this->snapshotRepository = $this->createMock(TravelSnapshotRepository::class);
$this->logger = $this->createMock(LoggerInterface::class);
$this->serializer = $this->createMock(SerializerInterface::class);
$snapshot = new TravelSnapshot(100, 200, 'invalid-json', hash('sha256', 'invalid-json'));
$this->snapshotRepository
@@ -167,13 +188,15 @@ class TravelSnapshotManagerTest extends TestCase
->method('warning')
->with('Snapshot payload cannot be deserialized to Travel', $this->arrayHasKey('snapshotId'));
$result = $this->service->loadTravel(100, 200);
$result = $this->service()->loadTravel(100, 200);
$this->assertNull($result);
}
public function testExistsReturnsTrueWhenSnapshotFound(): void
{
$this->snapshotRepository = $this->createMock(TravelSnapshotRepository::class);
$snapshot = new TravelSnapshot(100, 200, '{}', hash('sha256', '{}'));
$this->snapshotRepository
@@ -182,22 +205,27 @@ class TravelSnapshotManagerTest extends TestCase
->with(100, 200)
->willReturn($snapshot);
$this->assertTrue($this->service->exists(100, 200));
$this->assertTrue($this->service()->exists(100, 200));
}
public function testExistsReturnsFalseWhenNoSnapshot(): void
{
$this->snapshotRepository = $this->createMock(TravelSnapshotRepository::class);
$this->snapshotRepository
->expects($this->once())
->method('findByDateAndHotel')
->with(100, 200)
->willReturn(null);
$this->assertFalse($this->service->exists(100, 200));
$this->assertFalse($this->service()->exists(100, 200));
}
public function testLoadTravelReturnsDeserializedTravel(): void
{
$this->snapshotRepository = $this->createMock(TravelSnapshotRepository::class);
$this->serializer = $this->createMock(SerializerInterface::class);
$travel = new Travel();
$travel->id = 100;
$travel->hotelId = 200;
@@ -217,7 +245,7 @@ class TravelSnapshotManagerTest extends TestCase
->with($payload, Travel::class, 'json')
->willReturn($travel);
$result = $this->service->loadTravel(100, 200);
$result = $this->service()->loadTravel(100, 200);
$this->assertInstanceOf(Travel::class, $result);
$this->assertSame($travel, $result);
@@ -225,6 +253,9 @@ class TravelSnapshotManagerTest extends TestCase
public function testUpsertFromTravelSkipsWhenTravelIsIncomplete(): void
{
$this->entityManager = $this->createMock(EntityManagerInterface::class);
$this->serializer = $this->createMock(SerializerInterface::class);
$travelNoId = new Travel();
$travelNoId->id = null;
$travelNoId->hotelId = 200;
@@ -236,25 +267,29 @@ class TravelSnapshotManagerTest extends TestCase
$this->serializer->expects($this->never())->method('serialize');
$this->entityManager->expects($this->never())->method('flush');
$this->service->upsertFromTravel($travelNoId);
$this->service->upsertFromTravel($travelNoHotel);
$this->service()->upsertFromTravel($travelNoId);
$this->service()->upsertFromTravel($travelNoHotel);
}
public function testFindProductIdByDateIdDelegatesToRepository(): void
{
$this->snapshotRepository = $this->createMock(TravelSnapshotRepository::class);
$this->snapshotRepository
->expects($this->once())
->method('findProductIdByDateId')
->with(42)
->willReturn(365);
$result = $this->service->findProductIdByDateId(42);
$result = $this->service()->findProductIdByDateId(42);
$this->assertSame(365, $result);
}
public function testPurgeExpiredSnapshotsDelegatesWithCorrectDate(): void
{
$this->snapshotRepository = $this->createMock(TravelSnapshotRepository::class);
$this->snapshotRepository
->expects($this->once())
->method('deleteExpiredSnapshots')
@@ -265,13 +300,18 @@ class TravelSnapshotManagerTest extends TestCase
}))
->willReturn(7);
$result = $this->service->purgeExpiredSnapshots();
$result = $this->service()->purgeExpiredSnapshots();
$this->assertSame(7, $result);
}
public function testRefreshExtendedSnapshotsSuccessPath(): void
{
$this->snapshotRepository = $this->createMock(TravelSnapshotRepository::class);
$this->entityManager = $this->createMock(EntityManagerInterface::class);
$this->apiClient = $this->createMock(ApiClient::class);
$this->serializer = $this->createMock(SerializerInterface::class);
$travel = new Travel();
$travel->id = 100;
$travel->hotelId = 200;
@@ -310,13 +350,19 @@ class TravelSnapshotManagerTest extends TestCase
->expects($this->once())
->method('flush');
$result = $this->service->refreshExtendedSnapshots(500, false, 360, []);
$result = $this->service()->refreshExtendedSnapshots(500, false, 360, []);
$this->assertSame(['processed' => 1, 'updated' => 1, 'failed' => 0], $result);
}
public function testRefreshExtendedSnapshotsApiFailureIncrementsFailedCount(): void
{
$this->snapshotRepository = $this->createMock(TravelSnapshotRepository::class);
$this->entityManager = $this->createMock(EntityManagerInterface::class);
$this->apiClient = $this->createMock(ApiClient::class);
$this->logger = $this->createMock(LoggerInterface::class);
$this->serializer = $this->createMock(SerializerInterface::class);
$travel = new Travel();
$travel->id = 100;
$travel->hotelId = 200;
@@ -348,13 +394,18 @@ class TravelSnapshotManagerTest extends TestCase
->expects($this->never())
->method('flush');
$result = $this->service->refreshExtendedSnapshots(500, false, 360, []);
$result = $this->service()->refreshExtendedSnapshots(500, false, 360, []);
$this->assertSame(['processed' => 1, 'updated' => 0, 'failed' => 1], $result);
}
public function testRefreshExtendedSnapshotsDeserializationFailureIncrementsFailedCount(): void
{
$this->snapshotRepository = $this->createMock(TravelSnapshotRepository::class);
$this->entityManager = $this->createMock(EntityManagerInterface::class);
$this->logger = $this->createMock(LoggerInterface::class);
$this->serializer = $this->createMock(SerializerInterface::class);
$payload = '{"id":100}';
$snapshot = new TravelSnapshot(100, 200, $payload, hash('sha256', $payload));
@@ -377,13 +428,17 @@ class TravelSnapshotManagerTest extends TestCase
->expects($this->never())
->method('flush');
$result = $this->service->refreshExtendedSnapshots(500, false, 360, []);
$result = $this->service()->refreshExtendedSnapshots(500, false, 360, []);
$this->assertSame(['processed' => 1, 'updated' => 0, 'failed' => 1], $result);
}
public function testRefreshSkipsAllCandidatesWhenXmlDateIdsIsNull(): void
{
$this->snapshotRepository = $this->createMock(TravelSnapshotRepository::class);
$this->entityManager = $this->createMock(EntityManagerInterface::class);
$this->apiClient = $this->createMock(ApiClient::class);
$payload = '{"id":100}';
$snapshot = new TravelSnapshot(100, 200, $payload, hash('sha256', $payload));
@@ -400,13 +455,16 @@ class TravelSnapshotManagerTest extends TestCase
->expects($this->never())
->method('flush');
$result = $this->service->refreshExtendedSnapshots(500, false, 360, null);
$result = $this->service()->refreshExtendedSnapshots(500, false, 360, null);
$this->assertSame(['processed' => 0, 'updated' => 0, 'failed' => 0], $result);
}
public function testRefreshSkipsSnapshotsWithXmlAvailable(): void
{
$this->snapshotRepository = $this->createMock(TravelSnapshotRepository::class);
$this->apiClient = $this->createMock(ApiClient::class);
$payload = '{"id":100}';
$snapshot = new TravelSnapshot(100, 200, $payload, hash('sha256', $payload));
@@ -419,26 +477,30 @@ class TravelSnapshotManagerTest extends TestCase
->expects($this->never())
->method('getAvailabilitiesExtended');
$result = $this->service->refreshExtendedSnapshots(500, false, 360, [100]);
$result = $this->service()->refreshExtendedSnapshots(500, false, 360, [100]);
$this->assertSame(['processed' => 0, 'updated' => 0, 'failed' => 0], $result);
}
public function testPurgeOrphanedFutureSnapshotsReturnsZeroForEmptyList(): void
{
$this->snapshotRepository = $this->createMock(TravelSnapshotRepository::class);
$this->snapshotRepository
->expects($this->once())
->method('deleteOrphanedFutureSnapshots')
->with([], $this->isInstanceOf(\DateTimeImmutable::class))
->willReturn(0);
$result = $this->service->purgeOrphanedFutureSnapshots([]);
$result = $this->service()->purgeOrphanedFutureSnapshots([]);
$this->assertSame(0, $result);
}
public function testPurgeOrphanedFutureSnapshotsDelegatesWithCorrectArguments(): void
{
$this->snapshotRepository = $this->createMock(TravelSnapshotRepository::class);
$activeIds = [100, 200, 300];
$this->snapshotRepository
@@ -454,13 +516,15 @@ class TravelSnapshotManagerTest extends TestCase
)
->willReturn(3);
$result = $this->service->purgeOrphanedFutureSnapshots($activeIds);
$result = $this->service()->purgeOrphanedFutureSnapshots($activeIds);
$this->assertSame(3, $result);
}
public function testGenerateMappingBuildsCorrectStructure(): void
{
$this->snapshotRepository = $this->createMock(TravelSnapshotRepository::class);
$hotel = new Hotel();
$hotel->code = 'HTL1';
$hotel->name = 'Hotel One';
@@ -498,7 +562,7 @@ class TravelSnapshotManagerTest extends TestCase
->method('findAllForMapping')
->willReturn([$snapshot1, $snapshot2]);
$mapping = $this->service->generateMapping();
$mapping = $this->service()->generateMapping();
$this->assertArrayHasKey(10, $mapping);
$this->assertSame('TRIP1', $mapping[10]['code']);