[$line]]); // THEN expect($order)->toBeInstanceOf(Order::class) ->and($order->status)->toBe(OrderStatus::VALIDATED) ->and($order->getTotal()->getAmount())->toBe('3798') ->and($order->getLines())->toHaveCount(1); }); it('throws if total is zero or negative', function (): void { // GIVEN $total = Money::EUR(0); // WHEN $action = fn () => makeOrder(['total' => $total]); // THEN expect($action)->toThrow(\DomainException::class, 'Total amount must be greater than zero'); }); it('throws if lines contain non-OrderLine instances', function (): void { // GIVEN $lines = ['not-an-order-line']; // WHEN $action = fn () => makeOrder(['lines' => $lines]); // THEN expect($action)->toThrow(\DomainException::class, 'All lines must be instances of OrderLine'); }); }); describe('Order - Cancellation', function (): void { it('cancels a validated order', function (): void { // GIVEN $order = makeOrder(['status' => OrderStatus::VALIDATED]); // WHEN $order->cancel(); // THEN expect($order->status)->toBe(OrderStatus::CANCELED); }); it('cancels a waiting order', function (): void { // GIVEN $order = makeOrder(['status' => OrderStatus::WAITING]); // WHEN $order->cancel(); // THEN expect($order->status)->toBe(OrderStatus::CANCELED); }); it('is idempotent when cancelling an already cancelled order', function (): void { // GIVEN $order = makeOrder(['status' => OrderStatus::CANCELED]); // WHEN $order->cancel(); // THEN expect($order->status)->toBe(OrderStatus::CANCELED); }); }); describe('Order - Getters immutability', function (): void { it('returns a clone of total', function (): void { // GIVEN $order = makeOrder(['total' => Money::EUR(5000)]); // WHEN $total1 = $order->getTotal(); $total2 = $order->getTotal(); // THEN expect($total1)->toEqual($total2); }); it('returns clones of lines', function (): void { // GIVEN $order = makeOrder(); // WHEN $lines1 = $order->getLines(); $lines2 = $order->getLines(); // THEN expect($lines1[0])->toEqual($lines2[0]); }); }); describe('Order - id', function (): void { it('returns id', function (): void { // GIVEN $order = makeOrder(); // WHEN $id = $order->id(); // THEN expect($id)->toBeInstanceOf(OrderId::class); }); }); describe('Order - fromBasket factory', function (): void { it('creates an order from a basket with all lines mapped', function (): void { // GIVEN $basketId = new BasketId(Uuid::uuid7()); $basket = new Basket($basketId); $basketProduct = new BasketProduct( id: new ProductId(Uuid::uuid7()), name: 'Poulet Rôti', qty: 2, unitPriceHt: Money::EUR(1000), unitDiscount: Money::EUR(100), taxRate: TaxRate::TVA_5_5, weightGrams: 1200, brand: ProductBrand::marie, incoId: new IncoId(45), orderUnit: OrderUnit::PCE, slug: 'poulet-roti', picture: 'images/poulet.jpg', description: 'Un bon poulet', weightVariable: false, discountRate: DiscountRate::fromRatio(0.1), priceKgTtc: Money::EUR(1500) ); $basket->addOrUpdate($basketProduct); $orderId = new OrderId(Uuid::uuid7()); $reference = new OrderReference(Uuid::uuid7()); $pickupTimeSlot = new PickupTimeSlot( new \DateTimeImmutable('+1 day 10:00'), new \DateInterval('PT10M') ); $shopper = new Shopper( id: new ShopperId(Uuid::uuid7()), email: new Email('jean.dupont@example.com'), matricule: new Matricule('1234567'), nom: 'Dupont', prenom: 'Jean', startDate: CarbonImmutable::now(), endDate: null, etablissement: new ShopperEstablishmentCode('ETAB01'), site: new ShopperSiteCode('SITEA'), service: new ShopperServiceCode('101') ); $shop = new Shop( id: new ShopId(Uuid::uuid7()), name: 'Cocotte Lyon', address: '12 rue de la Paix', postcode: '69001', city: 'Lyon', phone: '0478123456' ); // WHEN $order = Order::fromBasket($orderId, $reference, $basket, $pickupTimeSlot, $shopper, $shop); // THEN expect($order->id())->toBe($orderId) ->and($order->reference)->toBe($reference) ->and($order->status)->toBe(OrderStatus::VALIDATED) ->and($order->employee->firstname)->toBe('Jean') ->and($order->employee->lastname)->toBe('Dupont') ->and($order->deliveryAddress->name)->toBe('Cocotte Lyon') ->and($order->getLines())->toHaveCount(1) ->and($order->getLines()[0]->product->name)->toBe('Poulet Rôti') ->and($order->getLines()[0]->quantity)->toBe(2); }); }); describe('Order - Domain Events', function (): void { it('produces an AnOrderHasBeenPlaced domain event', function (): void { // GIVEN $order = makeOrder(); // WHEN $event = $order->toPlacedEvent(); // THEN expect($event)->toBeInstanceOf(AnOrderHasBeenPlaced::class) ->and($event->orderId)->toBe($order->id()) ->and($event->reference)->toBe($order->reference) ->and($event->totalTtc->getAmount())->toBe($order->getTotal()->getAmount()) ->and($event->byMatricule)->toBe($order->employee->matricule) ->and($event->employee->matricule)->toBe($order->employee->matricule) ->and($event->shop->name)->toBe($order->deliveryAddress->name) ->and($event->lines)->toHaveCount(count($order->getLines())) ->and($event->lines[0]->productName)->toBe($order->getLines()[0]->product->name); }); });