feat: improved contingents api performance with db backed snapshots
This commit is contained in:
@@ -4,16 +4,20 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Tests\Controller\Api;
|
||||
|
||||
use App\BpnConnect\ContingentsClient;
|
||||
use App\BpnConnect\Model\ContingentStatus;
|
||||
use App\Controller\Api\ContingentController;
|
||||
use App\Entity\Groups\Accommodation;
|
||||
use App\Entity\Groups\AccommodationPrice;
|
||||
use App\Model\ContingentCalendarQuery;
|
||||
use App\Repository\Groups\AccommodationPriceRepository;
|
||||
use App\Repository\Groups\AccommodationRepository;
|
||||
use App\Service\AccommodationPriceCoverage;
|
||||
use App\Service\ContingentSnapshotReader;
|
||||
use App\Service\PriceTimelineBuilder;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Symfony\Contracts\Cache\CacheInterface;
|
||||
use Symfony\Component\DependencyInjection\Container;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
class ContingentControllerTest extends TestCase
|
||||
{
|
||||
@@ -44,13 +48,75 @@ class ContingentControllerTest extends TestCase
|
||||
self::assertNull($result['minNights']);
|
||||
}
|
||||
|
||||
public function testCalendarFailsLoudlyWhenThereIsNoUsableSnapshot(): void
|
||||
{
|
||||
$response = $this->callCalendar(null);
|
||||
|
||||
self::assertSame(Response::HTTP_BAD_GATEWAY, $response->getStatusCode());
|
||||
self::assertSame('{"error":"Failed to fetch contingent data."}', $response->getContent());
|
||||
}
|
||||
|
||||
public function testCalendarServesTheSnapshotStatuses(): void
|
||||
{
|
||||
$response = $this->callCalendar([
|
||||
'2026-09-01' => ContingentStatus::Ok,
|
||||
'2026-09-02' => ContingentStatus::OnRequest,
|
||||
]);
|
||||
|
||||
self::assertSame(Response::HTTP_OK, $response->getStatusCode());
|
||||
|
||||
$data = json_decode((string) $response->getContent(), true);
|
||||
|
||||
// One entry per requested day, and the format is unchanged from the upstream-backed version.
|
||||
self::assertCount(3, $data);
|
||||
self::assertSame(
|
||||
['date', 'status', 'type', 'pricePerNight', 'defaultPricePerNight', 'priceAdditionalPerson', 'defaultPriceAdditionalPerson', 'currency', 'includedPax', 'minNights'],
|
||||
array_keys($data[0]),
|
||||
);
|
||||
// No price covers these days, so the "not sold without a price" rule blocks them all.
|
||||
self::assertSame(['BLOCKED', 'BLOCKED', 'BLOCKED'], array_column($data, 'status'));
|
||||
}
|
||||
|
||||
public function testCalendarBlocksDaysMissingFromTheSnapshot(): void
|
||||
{
|
||||
$data = json_decode((string) $this->callCalendar([])->getContent(), true);
|
||||
|
||||
self::assertSame(['BLOCKED', 'BLOCKED', 'BLOCKED'], array_column($data, 'status'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, ContingentStatus>|null $statuses
|
||||
*/
|
||||
private function callCalendar(?array $statuses): Response
|
||||
{
|
||||
$accommodationRepository = $this->createMock(AccommodationRepository::class);
|
||||
$accommodationRepository->method('findOneBy')->willReturn((new Accommodation())->setCalendarCode('HOTEL1')->setCurrency('EUR'));
|
||||
|
||||
$snapshotReader = $this->createMock(ContingentSnapshotReader::class);
|
||||
$snapshotReader->method('statusesFor')->willReturn($statuses);
|
||||
|
||||
$controller = new ContingentController(
|
||||
$accommodationRepository,
|
||||
$this->createMock(AccommodationPriceRepository::class),
|
||||
$snapshotReader,
|
||||
new PriceTimelineBuilder(),
|
||||
new AccommodationPriceCoverage($this->createMock(AccommodationPriceRepository::class)),
|
||||
$this->createMock(LoggerInterface::class),
|
||||
);
|
||||
|
||||
// No 'serializer' service registered, so AbstractController::json() falls back to
|
||||
// JsonResponse — which is what this endpoint produces in production anyway.
|
||||
$controller->setContainer(new Container());
|
||||
|
||||
return $controller->calendar(new ContingentCalendarQuery('HOTEL1', '2026-09-01', '2026-09-03'));
|
||||
}
|
||||
|
||||
private function createController(): ContingentController
|
||||
{
|
||||
return new ContingentController(
|
||||
$this->createMock(ContingentsClient::class),
|
||||
$this->createMock(AccommodationRepository::class),
|
||||
$this->createMock(AccommodationPriceRepository::class),
|
||||
$this->createMock(CacheInterface::class),
|
||||
$this->createMock(ContingentSnapshotReader::class),
|
||||
new PriceTimelineBuilder(),
|
||||
new AccommodationPriceCoverage($this->createMock(AccommodationPriceRepository::class)),
|
||||
$this->createMock(LoggerInterface::class),
|
||||
|
||||
Reference in New Issue
Block a user