ldc-boutique-cnc-sable/tests/Unit/Domain/Services/OrderTunnelServiceTest.php

726 lines
27 KiB
PHP

<?php
declare(strict_types=1);
namespace Tests\Unit\Domain\Services;
use Carbon\CarbonImmutable;
use DateInterval;
use Domain\Builders\OrderBuilder;
use Domain\Enums\OrderTunnelStep;
use Domain\Enums\TaxRate;
use Domain\Enums\Units\OrderUnit;
use Domain\Exceptions\OrderTunnel\MissingOrderBuilderException;
use Domain\Models\Entities\Basket;
use Domain\Models\Entities\Order;
use Domain\Models\Entities\Shop;
use Domain\Models\Entities\Shopper;
use Domain\Models\ValueObjects\BasketProduct;
use Domain\Models\ValueObjects\DiscountRate;
use Domain\Models\ValueObjects\Email;
use Domain\Models\ValueObjects\Entities\BasketId;
use Domain\Models\ValueObjects\Entities\ProductId;
use Domain\Models\ValueObjects\Entities\ShopId;
use Domain\Models\ValueObjects\Entities\ShopperId;
use Domain\Models\ValueObjects\IncoId;
use Domain\Models\ValueObjects\Matricule;
use Domain\Models\ValueObjects\PickupTimeSlot;
use Domain\Models\ValueObjects\SessionId;
use Domain\Models\ValueObjects\ShopperEstablishmentCode;
use Domain\Models\ValueObjects\ShopperServiceCode;
use Domain\Models\ValueObjects\ShopperSiteCode;
use Domain\Policies\CustomerOrderLimitPolicy;
use Domain\Ports\Repositories\BasketRepository;
use Domain\Ports\Repositories\OrderRepository;
use Domain\Ports\Repositories\PickupExceptionRepository;
use Domain\Ports\Repositories\ProductRepository;
use Domain\Ports\Repositories\ShopperRepository;
use Domain\Ports\Repositories\ShopRepository;
use Domain\Ports\Services\EmployeeStatisticsService;
use Domain\Ports\Services\OrderBuilderSessionManager;
use Domain\Ports\Services\SessionService;
use Domain\Ports\Services\TransactionManager;
use Domain\Services\BasketService;
use Domain\Services\OrderService;
use Domain\Services\OrderTunnelService;
use Domain\Services\PickupSchedulingService;
use Domain\Services\ShopperService;
use Money\Money;
use Ramsey\Uuid\Uuid;
use Shared\DDD\DomainEventDispatcher;
function createTestBasket(int $qty = 0): Basket
{
$items = [];
if ($qty > 0) {
$productId = new ProductId(Uuid::fromString('019e9e37-f2a1-74ef-b9c2-66d222470128'));
$basketProduct = new BasketProduct(
id: $productId,
name: 'Test Product',
qty: $qty,
unitPriceHt: Money::EUR(100),
unitDiscount: Money::EUR(0),
taxRate: TaxRate::TVA_20,
weightGrams: 100,
brand: null,
incoId: new IncoId(123),
orderUnit: OrderUnit::PCE,
slug: 'test-product',
picture: null,
description: null,
weightVariable: false,
discountRate: DiscountRate::fromRatio(0.0),
priceKgTtc: Money::EUR(120)
);
$items[(string) $productId->value] = $basketProduct;
}
$basketId = new BasketId(Uuid::fromString('019e9e37-f2a1-74ef-b9c2-66d222470128'));
return new Basket($basketId, $items);
}
function createTestBuilder(bool $completed = false): OrderBuilder
{
$basket = createTestBasket(1);
$shopperId = new ShopperId(Uuid::uuid7());
$builder = OrderBuilder::fromBasket($basket, $shopperId);
if ($completed) {
$shopId = new ShopId(Uuid::uuid7());
$timeSlot = new PickupTimeSlot(CarbonImmutable::create(2025, 1, 1), new DateInterval('PT1H'));
$builder = $builder->withPickupTimeSlot($shopId, $timeSlot);
}
/** @var OrderBuilder $builder */
return $builder;
}
function createOrderTunnelService(
?BasketRepository $basketRepository = null,
?ProductRepository $productRepository = null,
?ShopRepository $shopRepository = null,
?ShopperRepository $shopperRepository = null,
?PickupExceptionRepository $pickupExceptionRepository = null,
?OrderBuilderSessionManager $orderBuilderSessionManager = null,
?OrderRepository $orderRepository = null,
?TransactionManager $transactionManager = null,
?DomainEventDispatcher $eventDispatcher = null,
): OrderTunnelService {
if ($basketRepository === null) {
$basketRepository = mock(BasketRepository::class);
$basketRepository->shouldReceive('persist')->byDefault();
}
if ($productRepository === null) {
$productRepository = mock(ProductRepository::class);
$productRepository->shouldReceive('getBasketProduct')->andReturnUsing(function ($id, $qty) {
return new BasketProduct(
id: $id,
name: 'Test Product',
qty: $qty,
unitPriceHt: Money::EUR(100),
unitDiscount: Money::EUR(0),
taxRate: TaxRate::TVA_20,
weightGrams: 100,
brand: null,
incoId: new IncoId(123),
orderUnit: OrderUnit::PCE,
slug: 'test-product',
picture: null,
description: null,
weightVariable: false,
discountRate: DiscountRate::fromRatio(0.0),
priceKgTtc: Money::EUR(120)
);
})->byDefault();
}
$shopRepository ??= mock(ShopRepository::class);
$shopperRepository ??= mock(ShopperRepository::class);
$pickupExceptionRepository ??= mock(PickupExceptionRepository::class);
$orderBuilderSessionManager ??= mock(OrderBuilderSessionManager::class);
$orderRepository ??= mock(OrderRepository::class);
$transactionManager ??= mock(TransactionManager::class);
$eventDispatcher ??= mock(DomainEventDispatcher::class);
$sessionService = \Mockery::mock(SessionService::class);
$sessionService->shouldReceive('get')->andReturn(new SessionId(Uuid::uuid7()))->byDefault();
$employeeStatisticsService = \Mockery::mock(EmployeeStatisticsService::class);
$employeeStatisticsService->shouldReceive('getWeeklyTotalForEmployee')->andReturn(Money::EUR(0))->byDefault();
$basketService = new BasketService($basketRepository, $productRepository, $sessionService);
$shopperService = new ShopperService($shopRepository, $shopperRepository);
$pickupService = new PickupSchedulingService($pickupExceptionRepository);
$orderService = new OrderService($orderRepository);
$orderLimitPolicy = new CustomerOrderLimitPolicy(500000, 500000);
return new OrderTunnelService(
$basketService,
$eventDispatcher,
$shopperService,
$orderBuilderSessionManager,
$pickupService,
$orderRepository,
$orderLimitPolicy,
$transactionManager,
$shopRepository,
$shopperRepository,
$orderService,
$employeeStatisticsService
);
}
describe('OrderTunnelService', function (): void {
it('validates basket is valid when it has items', function (): void {
// GIVEN
$basketRepo = mock(BasketRepository::class);
$basketRepo->shouldReceive('find')->andReturn(createTestBasket(2));
$service = createOrderTunnelService(basketRepository: $basketRepo);
// WHEN
$isValid = $service->hasValidBasket();
// THEN
expect($isValid)->toBeTrue();
});
it('invalidates basket when it is empty', function (): void {
// GIVEN
$basketRepo = mock(BasketRepository::class);
$basketRepo->shouldReceive('find')->andReturn(createTestBasket(0));
$service = createOrderTunnelService(basketRepository: $basketRepo);
// WHEN
$isValid = $service->hasValidBasket();
// THEN
expect($isValid)->toBeFalse();
});
it('allows access to REVIEW step when basket is valid', function (): void {
// GIVEN
$basketRepo = mock(BasketRepository::class);
$basketRepo->shouldReceive('find')->andReturn(createTestBasket(1));
$orderBuilderRepository = mock(OrderBuilderSessionManager::class);
$orderBuilderRepository->shouldReceive('get')->andReturn(null);
$service = createOrderTunnelService(
basketRepository: $basketRepo,
orderBuilderSessionManager: $orderBuilderRepository
);
// WHEN
$canAccess = $service->canAccessStep(OrderTunnelStep::REVIEW);
// THEN
expect($canAccess)->toBeTrue();
});
it('denies access to REVIEW step when basket is empty', function (): void {
// GIVEN
$basketRepo = mock(BasketRepository::class);
$basketRepo->shouldReceive('find')->andReturn(createTestBasket(0));
$orderBuilderRepository = mock(OrderBuilderSessionManager::class);
$orderBuilderRepository->shouldReceive('get')->andReturn(null);
$service = createOrderTunnelService(
basketRepository: $basketRepo,
orderBuilderSessionManager: $orderBuilderRepository
);
// WHEN
$canAccess = $service->canAccessStep(OrderTunnelStep::REVIEW);
// THEN
expect($canAccess)->toBeFalse();
});
it('allows access to BOOKING step when builder is from basket and basket is valid', function (): void {
// GIVEN
$basketRepo = mock(BasketRepository::class);
$basketRepo->shouldReceive('find')->andReturn(createTestBasket(1));
$builder = createTestBuilder();
$orderBuilderRepository = mock(OrderBuilderSessionManager::class);
$orderBuilderRepository->shouldReceive('get')->andReturn($builder);
$service = createOrderTunnelService(
basketRepository: $basketRepo,
orderBuilderSessionManager: $orderBuilderRepository
);
// WHEN
$canAccess = $service->canAccessStep(OrderTunnelStep::BOOKING);
// THEN
expect($canAccess)->toBeTrue();
});
it('denies access to BOOKING step when builder is missing', function (): void {
// GIVEN
$basketRepo = mock(BasketRepository::class);
$basketRepo->shouldReceive('find')->andReturn(createTestBasket(1));
$orderBuilderRepository = mock(OrderBuilderSessionManager::class);
$orderBuilderRepository->shouldReceive('get')->andReturn(null);
$service = createOrderTunnelService(
basketRepository: $basketRepo,
orderBuilderSessionManager: $orderBuilderRepository
);
// WHEN
$canAccess = $service->canAccessStep(OrderTunnelStep::BOOKING);
// THEN
expect($canAccess)->toBeFalse();
});
it('allows access to CONFIRMATION step when builder is completed and basket is valid', function (): void {
// GIVEN
$basketRepo = mock(BasketRepository::class);
$basketRepo->shouldReceive('find')->andReturn(createTestBasket(1));
$builder = createTestBuilder(completed: true);
$orderBuilderRepository = mock(OrderBuilderSessionManager::class);
$orderBuilderRepository->shouldReceive('get')->andReturn($builder);
$service = createOrderTunnelService(
basketRepository: $basketRepo,
orderBuilderSessionManager: $orderBuilderRepository
);
// WHEN
$canAccess = $service->canAccessStep(OrderTunnelStep::CONFIRMATION);
// THEN
expect($canAccess)->toBeTrue();
});
it('denies access to CONFIRMATION step when builder is not completed', function (): void {
// GIVEN
$basketRepo = mock(BasketRepository::class);
$basketRepo->shouldReceive('find')->andReturn(createTestBasket(1));
$builder = createTestBuilder(completed: false);
$orderBuilderRepository = mock(OrderBuilderSessionManager::class);
$orderBuilderRepository->shouldReceive('get')->andReturn($builder);
$service = createOrderTunnelService(
basketRepository: $basketRepo,
orderBuilderSessionManager: $orderBuilderRepository
);
// WHEN
$canAccess = $service->canAccessStep(OrderTunnelStep::CONFIRMATION);
// THEN
expect($canAccess)->toBeFalse();
});
it('denies access to CONFIRMATION step when builder is missing', function (): void {
// GIVEN
$basketRepo = mock(BasketRepository::class);
$basketRepo->shouldReceive('find')->andReturn(createTestBasket(1));
$orderBuilderRepository = mock(OrderBuilderSessionManager::class);
$orderBuilderRepository->shouldReceive('get')->andReturn(null);
$service = createOrderTunnelService(
basketRepository: $basketRepo,
orderBuilderSessionManager: $orderBuilderRepository
);
// WHEN
$canAccess = $service->canAccessStep(OrderTunnelStep::CONFIRMATION);
// THEN
expect($canAccess)->toBeFalse();
});
it('gets shop for customer successfully', function (): void {
// GIVEN
$shopperId = new ShopperId(Uuid::uuid7());
$shopper = new Shopper(
id: $shopperId,
email: new Email('test@test.com'),
matricule: new Matricule('1234567'),
nom: 'Doe',
prenom: 'John',
startDate: CarbonImmutable::now()->subYear(),
endDate: null,
etablissement: new ShopperEstablishmentCode('ETAB01'),
site: new ShopperSiteCode('SITEA'),
service: new ShopperServiceCode('101')
);
$shopId = new ShopId(Uuid::uuid7());
$shop = new Shop(
id: $shopId,
name: 'Shop',
address: 'Address',
postcode: '75000',
city: 'Paris'
);
$shopperRepo = mock(ShopperRepository::class);
$shopperRepo->shouldReceive('find')->with($shopperId)->andReturn($shopper);
$shopRepo = mock(ShopRepository::class);
$shopRepo->shouldReceive('findExceptionShopFor')->with($shopper->matricule)->andReturn(null);
$shopRepo->shouldReceive('findShopForSiteAndService')->with($shopper->site, $shopper->service)->andReturn(null);
$shopRepo->shouldReceive('findShopForSiteAndService')->with($shopper->site, null)->andReturn(null);
$shopRepo->shouldReceive('getDefaultShop')->andReturn($shop);
$service = createOrderTunnelService(
shopRepository: $shopRepo,
shopperRepository: $shopperRepo
);
// WHEN
$result = $service->getShopForCustomer($shopperId);
// THEN
expect($result)->toBe($shop);
});
it('throws exception when get shop for customer fails', function (): void {
// GIVEN
$shopperId = new ShopperId(Uuid::uuid7());
$shopperRepo = mock(ShopperRepository::class);
$shopperRepo->shouldReceive('find')->with($shopperId)->andReturn(null);
$service = createOrderTunnelService(shopperRepository: $shopperRepo);
// WHEN
$action = fn () => $service->getShopForCustomer($shopperId);
// THEN
expect($action)->toThrow(\UnexpectedValueException::class);
});
it('gets available time slots', function (): void {
// GIVEN
$shopId = new ShopId(Uuid::uuid7());
$shop = new Shop($shopId, 'Shop', 'Address', '75000', 'Paris');
$pickupExceptionRepo = mock(PickupExceptionRepository::class);
$pickupExceptionRepo->shouldReceive('getExceptionsForShop')->andReturn([]);
$service = createOrderTunnelService(pickupExceptionRepository: $pickupExceptionRepo);
// WHEN
$slots = $service->getAvailableTimeSlots($shop);
// THEN
expect($slots)->toBeArray();
});
it('initializes order from basket', function (): void {
// GIVEN
$basket = createTestBasket(1);
$customerId = new ShopperId(Uuid::uuid7());
$orderBuilderRepo = mock(OrderBuilderSessionManager::class);
$orderBuilderRepo->shouldReceive('save')->once();
$service = createOrderTunnelService(orderBuilderSessionManager: $orderBuilderRepo);
// WHEN
$service->initializeFromBasket($basket, $customerId);
// THEN
expect(true)->toBeTrue();
});
it('gets builder from basket or fail', function (): void {
// GIVEN
$builder = createTestBuilder();
$orderBuilderRepo = mock(OrderBuilderSessionManager::class);
$orderBuilderRepo->shouldReceive('get')->once()->andReturn($builder);
$service = createOrderTunnelService(orderBuilderSessionManager: $orderBuilderRepo);
// WHEN
$result = $service->getBuilderFromBasketOrFail();
// THEN
expect($result)->toBe($builder);
});
it('throws exception if builder not from basket', function (): void {
// GIVEN
$orderBuilderRepo = mock(OrderBuilderSessionManager::class);
$orderBuilderRepo->shouldReceive('get')->once()->andReturn(null);
$service = createOrderTunnelService(orderBuilderSessionManager: $orderBuilderRepo);
// WHEN
$action = fn () => $service->getBuilderFromBasketOrFail();
// THEN
expect($action)->toThrow(MissingOrderBuilderException::class);
});
it('gets completed builder or fail', function (): void {
// GIVEN
$builder = createTestBuilder(completed: true);
$orderBuilderRepo = mock(OrderBuilderSessionManager::class);
$orderBuilderRepo->shouldReceive('get')->once()->andReturn($builder);
$service = createOrderTunnelService(orderBuilderSessionManager: $orderBuilderRepo);
// WHEN
$result = $service->getBuilderCompletedOrFail();
// THEN
expect($result)->toBe($builder);
});
it('throws exception if builder not completed', function (): void {
// GIVEN
$builder = createTestBuilder(completed: false);
$orderBuilderRepo = mock(OrderBuilderSessionManager::class);
$orderBuilderRepo->shouldReceive('get')->once()->andReturn($builder);
$service = createOrderTunnelService(orderBuilderSessionManager: $orderBuilderRepo);
// WHEN
$action = fn () => $service->getBuilderCompletedOrFail();
// THEN
expect($action)->toThrow(MissingOrderBuilderException::class);
});
it('applies time slot', function (): void {
// GIVEN
$builder = createTestBuilder(completed: false);
$shop = new Shop(new ShopId(Uuid::uuid7()), 'Shop', 'Address', '75000', 'Paris');
$selectedDate = CarbonImmutable::create(2025, 1, 1);
$ts1 = new PickupTimeSlot($selectedDate, new DateInterval('PT1H'));
$ts2 = new PickupTimeSlot(CarbonImmutable::create(2025, 1, 2), new DateInterval('PT1H'));
$availableSlots = [$ts1, $ts2];
$orderBuilderRepo = mock(OrderBuilderSessionManager::class);
$orderBuilderRepo->shouldReceive('save')->once();
$service = createOrderTunnelService(orderBuilderSessionManager: $orderBuilderRepo);
// WHEN
$matched = $service->applyTimeSlot($builder, $shop, $selectedDate, $availableSlots);
// THEN
expect($matched)->toBe($ts1);
});
it('returns null if no matching time slot', function (): void {
// GIVEN
$builder = createTestBuilder(completed: false);
$shop = new Shop(new ShopId(Uuid::uuid7()), 'Shop', 'Address', '75000', 'Paris');
$selectedDate = CarbonImmutable::create(2025, 1, 1);
$ts1 = new PickupTimeSlot(CarbonImmutable::create(2025, 1, 2), new DateInterval('PT1H'));
$availableSlots = [$ts1];
$service = createOrderTunnelService();
// WHEN
$matched = $service->applyTimeSlot($builder, $shop, $selectedDate, $availableSlots);
// THEN
expect($matched)->toBeNull();
});
it('finalizes order', function (): void {
// GIVEN
$basketRepo = mock(BasketRepository::class);
$basketRepo->shouldReceive('find')->once()->andReturn(createTestBasket(1));
$basketRepo->shouldReceive('persist')->once();
$basketRepo->shouldReceive('delete')->once();
$builder = createTestBuilder(completed: true);
$shopperId = $builder->getShopperId();
$shopper = new Shopper(
id: $shopperId,
email: new Email('test@test.com'),
matricule: new Matricule('1234567'),
nom: 'Doe',
prenom: 'John',
startDate: CarbonImmutable::now()->subYear(),
endDate: null,
etablissement: new ShopperEstablishmentCode('ETAB01'),
site: new ShopperSiteCode('SITEA'),
service: new ShopperServiceCode('101')
);
$shopperRepo = mock(ShopperRepository::class);
$shopperRepo->shouldReceive('getByIdOrFail')->with(\Mockery::type(ShopperId::class))->andReturn($shopper);
$shopId = clone clone $builder->getShopId();
$shop = new Shop($shopId, 'Shop', 'Address', '75000', 'Paris');
$shopRepo = mock(ShopRepository::class);
$shopRepo->shouldReceive('find')->andReturn($shop); // any with shopId is fine
$orderRepo = mock(OrderRepository::class);
$orderRepo->shouldReceive('getWeeklyTotalForEmployee')->andReturn(Money::EUR(5000));
$orderRepo->shouldReceive('persist')->once();
$transactionManager = mock(TransactionManager::class);
$transactionManager->shouldReceive('execute')->once()->andReturnUsing(function ($callback) {
return $callback();
});
$eventDispatcher = mock(DomainEventDispatcher::class);
$eventDispatcher->shouldReceive('dispatch')->once();
$builderRepo = mock(OrderBuilderSessionManager::class);
$builderRepo->shouldReceive('clear')->once();
$service = createOrderTunnelService(
basketRepository: $basketRepo,
shopRepository: $shopRepo,
shopperRepository: $shopperRepo,
orderBuilderSessionManager: $builderRepo,
orderRepository: $orderRepo,
transactionManager: $transactionManager,
eventDispatcher: $eventDispatcher
);
// WHEN
$order = $service->finalizeOrder($builder);
// THEN
expect($order)->toBeInstanceOf(Order::class);
expect($order->employee->matricule->value)->toBe('1234567');
expect($order->deliveryAddress->name)->toBe('Shop');
});
it('throws exception if shop not found during finalize order', function (): void {
// GIVEN
$builder = createTestBuilder(completed: true);
$shopperId = clone clone $builder->getShopperId();
$shopper = new Shopper(
id: $shopperId,
email: new Email('test@test.com'),
matricule: new Matricule('1234567'),
nom: 'Doe',
prenom: 'John',
startDate: CarbonImmutable::now()->subYear(),
endDate: null,
etablissement: new ShopperEstablishmentCode('ETAB01'),
site: new ShopperSiteCode('SITEA'),
service: new ShopperServiceCode('101')
);
$shopperRepo = mock(ShopperRepository::class);
$shopperRepo->shouldReceive('getByIdOrFail')->with(\Mockery::type(ShopperId::class))->andReturn($shopper);
$shopRepo = mock(ShopRepository::class);
$shopRepo->shouldReceive('find')->andReturn(null);
$orderRepo = mock(OrderRepository::class);
$orderRepo->shouldReceive('getWeeklyTotalForEmployee')->andReturn(Money::EUR(5000));
$transactionManager = mock(TransactionManager::class);
$transactionManager->shouldReceive('execute')->once()->andReturnUsing(function ($callback) {
return $callback();
});
$service = createOrderTunnelService(
shopRepository: $shopRepo,
shopperRepository: $shopperRepo,
orderRepository: $orderRepo,
transactionManager: $transactionManager
);
// WHEN
$action = fn () => $service->finalizeOrder($builder);
// THEN
expect($action)->toThrow(\DomainException::class);
});
it('revalidates basket prices from product repository when finalizing order', function (): void {
// GIVEN
$builder = createTestBuilder(completed: true);
$shopperId = $builder->getShopperId();
$shopper = new Shopper(
id: $shopperId,
email: new Email('test@test.com'),
matricule: new Matricule('1234567'),
nom: 'Doe',
prenom: 'John',
startDate: CarbonImmutable::now()->subYear(),
endDate: null,
etablissement: new ShopperEstablishmentCode('ETAB01'),
site: new ShopperSiteCode('SITEA'),
service: new ShopperServiceCode('101')
);
$shopperRepo = mock(ShopperRepository::class);
$shopperRepo->shouldReceive('getByIdOrFail')->with(\Mockery::type(ShopperId::class))->andReturn($shopper);
$shopId = clone $builder->getShopId();
$shop = new Shop($shopId, 'Shop', 'Address', '75000', 'Paris');
$shopRepo = mock(ShopRepository::class);
$shopRepo->shouldReceive('find')->andReturn($shop);
$orderRepo = mock(OrderRepository::class);
$orderRepo->shouldReceive('getWeeklyTotalForEmployee')->andReturn(Money::EUR(5000));
$orderRepo->shouldReceive('persist')->once();
$transactionManager = mock(TransactionManager::class);
$transactionManager->shouldReceive('execute')->once()->andReturnUsing(function ($callback) {
return $callback();
});
$eventDispatcher = mock(DomainEventDispatcher::class);
$eventDispatcher->shouldReceive('dispatch')->once();
$builderRepo = mock(OrderBuilderSessionManager::class);
$builderRepo->shouldReceive('clear')->once();
$basketRepo = mock(BasketRepository::class);
$basketRepo->shouldReceive('find')->once()->andReturn(createTestBasket(1));
$basketRepo->shouldReceive('persist')->once();
$basketRepo->shouldReceive('delete')->once();
$productRepo = mock(ProductRepository::class);
$productRepo->shouldReceive('getBasketProduct')->once()->andReturnUsing(function ($id, $qty) {
return new BasketProduct(
id: $id,
name: 'Updated Product Name',
qty: $qty,
unitPriceHt: Money::EUR(250),
unitDiscount: Money::EUR(50),
taxRate: TaxRate::TVA_20,
weightGrams: 200,
brand: null,
incoId: new IncoId(123),
orderUnit: OrderUnit::PCE,
slug: 'updated-product',
picture: null,
description: null,
weightVariable: false,
discountRate: DiscountRate::fromRatio(0.0),
priceKgTtc: Money::EUR(240)
);
});
$service = createOrderTunnelService(
basketRepository: $basketRepo,
productRepository: $productRepo,
shopRepository: $shopRepo,
shopperRepository: $shopperRepo,
orderBuilderSessionManager: $builderRepo,
orderRepository: $orderRepo,
transactionManager: $transactionManager,
eventDispatcher: $eventDispatcher
);
// WHEN
$order = $service->finalizeOrder($builder);
// THEN
expect($order)->toBeInstanceOf(Order::class);
});
});