137 lines
5.2 KiB
PHP
137 lines
5.2 KiB
PHP
<?php
|
||
|
||
declare(strict_types=1);
|
||
|
||
namespace App\Tests\Controller\Groups\Booking;
|
||
|
||
use App\BpnConnect\Model\ContingentStatus;
|
||
use App\Controller\Groups\Booking\Step1Controller;
|
||
use App\Entity\Groups\Accommodation;
|
||
use App\Entity\Groups\AccommodationPrice;
|
||
use App\Repository\Groups\AccommodationPriceRepository;
|
||
use App\Service\AccommodationBookingService;
|
||
use App\Service\AccommodationPriceCoverage;
|
||
use App\Service\AccommodationSessionManager;
|
||
use App\Service\CalendarGridBuilder;
|
||
use App\Service\ContingentSnapshotReader;
|
||
use App\Service\GroupsPriceCalculator;
|
||
use App\Service\PriceTimelineBuilder;
|
||
use PHPUnit\Framework\TestCase;
|
||
|
||
class Step1CalendarDataTest extends TestCase
|
||
{
|
||
public function testDaysWithoutPriceAreBlockedEvenWhenContingentIsOk(): void
|
||
{
|
||
// Contingent says OK for the whole week, prices only cover 03.–05.
|
||
$price = new AccommodationPrice();
|
||
$price->setDateFrom(new \DateTimeImmutable('2026-06-03'));
|
||
$price->setDateTo(new \DateTimeImmutable('2026-06-05'));
|
||
$price->setMinNights(2);
|
||
|
||
$enriched = $this->buildEnrichedDayData(
|
||
[
|
||
'2026-06-01' => ContingentStatus::Ok,
|
||
'2026-06-02' => ContingentStatus::Ok,
|
||
'2026-06-03' => ContingentStatus::Ok,
|
||
'2026-06-04' => ContingentStatus::Ok,
|
||
'2026-06-05' => ContingentStatus::Ok,
|
||
'2026-06-06' => ContingentStatus::Ok,
|
||
'2026-06-07' => ContingentStatus::Ok,
|
||
],
|
||
[$price],
|
||
'2026-06-01',
|
||
'2026-06-07',
|
||
);
|
||
|
||
self::assertSame('blocked', $enriched['2026-06-01']['status']);
|
||
self::assertSame('blocked', $enriched['2026-06-02']['status']);
|
||
// First priced night — arrival only
|
||
self::assertSame('blocked-to-ok', $enriched['2026-06-03']['status']);
|
||
self::assertSame('ok', $enriched['2026-06-04']['status']);
|
||
self::assertSame('ok', $enriched['2026-06-05']['status']);
|
||
// Day after the last priced night — still valid as checkout
|
||
self::assertSame('checkout-only', $enriched['2026-06-06']['status']);
|
||
self::assertSame('blocked', $enriched['2026-06-07']['status']);
|
||
|
||
self::assertSame(2, $enriched['2026-06-04']['minNights']);
|
||
self::assertSame(0, $enriched['2026-06-07']['minNights']);
|
||
}
|
||
|
||
public function testBlockedContingentWinsOverExistingPrice(): void
|
||
{
|
||
$price = new AccommodationPrice();
|
||
$price->setDateFrom(new \DateTimeImmutable('2026-06-01'));
|
||
$price->setDateTo(new \DateTimeImmutable('2026-06-03'));
|
||
$price->setMinNights(1);
|
||
|
||
$enriched = $this->buildEnrichedDayData(
|
||
[
|
||
'2026-06-01' => ContingentStatus::Ok,
|
||
'2026-06-02' => ContingentStatus::Blocked,
|
||
'2026-06-03' => ContingentStatus::Ok,
|
||
],
|
||
[$price],
|
||
'2026-06-01',
|
||
'2026-06-03',
|
||
);
|
||
|
||
self::assertSame('blocked-to-ok', $enriched['2026-06-01']['status']);
|
||
self::assertSame('checkout-only', $enriched['2026-06-02']['status']);
|
||
self::assertSame('blocked-to-ok', $enriched['2026-06-03']['status']);
|
||
}
|
||
|
||
public function testPriceCoverageStillAppliesWhenThereIsNoUsableSnapshot(): void
|
||
{
|
||
$price = new AccommodationPrice();
|
||
$price->setDateFrom(new \DateTimeImmutable('2026-06-01'));
|
||
$price->setDateTo(new \DateTimeImmutable('2026-06-02'));
|
||
$price->setMinNights(1);
|
||
|
||
$enriched = $this->buildEnrichedDayData(null, [$price], '2026-06-01', '2026-06-03');
|
||
|
||
self::assertSame('blocked-to-ok', $enriched['2026-06-01']['status']);
|
||
self::assertSame('ok', $enriched['2026-06-02']['status']);
|
||
self::assertSame('checkout-only', $enriched['2026-06-03']['status']);
|
||
}
|
||
|
||
/**
|
||
* @param array<string, ContingentStatus>|null $statuses null = no usable snapshot
|
||
* @param AccommodationPrice[] $prices
|
||
*
|
||
* @return array<string, array{status: string, minNights: int}>
|
||
*/
|
||
private function buildEnrichedDayData(
|
||
?array $statuses,
|
||
array $prices,
|
||
string $dateFrom,
|
||
string $dateTo,
|
||
): array {
|
||
$snapshotReader = $this->createStub(ContingentSnapshotReader::class);
|
||
$snapshotReader->method('statusesFor')->willReturn($statuses);
|
||
|
||
$priceRepository = $this->createStub(AccommodationPriceRepository::class);
|
||
$priceRepository->method('findByHotelCodeAndDateRange')->willReturn($prices);
|
||
|
||
$controller = new Step1Controller(
|
||
$this->createStub(AccommodationBookingService::class),
|
||
$this->createStub(AccommodationSessionManager::class),
|
||
$priceRepository,
|
||
new PriceTimelineBuilder(),
|
||
$snapshotReader,
|
||
$this->createStub(CalendarGridBuilder::class),
|
||
$this->createStub(GroupsPriceCalculator::class),
|
||
new AccommodationPriceCoverage($priceRepository),
|
||
);
|
||
|
||
$method = new \ReflectionMethod($controller, 'buildEnrichedDayData');
|
||
|
||
return $method->invoke(
|
||
$controller,
|
||
(new Accommodation())->setCalendarCode('HOTEL'),
|
||
'HOTEL',
|
||
new \DateTimeImmutable($dateFrom),
|
||
new \DateTimeImmutable($dateTo),
|
||
);
|
||
}
|
||
}
|