new ProductId(Uuid::fromString('019e9e37-f2a1-74ef-b9c2-66d222470128')), 'name' => 'Poulet Rôti', 'qty' => 2, 'unitPriceHt' => Money::EUR(1000), 'unitDiscount' => Money::EUR(200), '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 rôti', 'weightVariable' => true, 'discountRate' => DiscountRate::fromRatio(0.2), 'priceKgTtc' => Money::EUR(1500), 'options' => ['extra' => 'crispy'], ], $overrides); return new BasketProduct(...$data); } it('can instantiate a basket product with all new typed properties', function (): void { // GIVEN $productId = new ProductId(Uuid::fromString('019e9e37-f2a1-74ef-b9c2-66d222470128')); $brand = ProductBrand::marie; $orderUnit = OrderUnit::PCE; $price = Money::EUR(1000); $discount = Money::EUR(200); $priceKgTtc = Money::EUR(1500); // WHEN $item = new BasketProduct( id: $productId, name: 'Poulet Rôti', qty: 2, unitPriceHt: $price, unitDiscount: $discount, taxRate: TaxRate::TVA_5_5, weightGrams: 1200, brand: $brand, incoId: new IncoId(45), orderUnit: $orderUnit, slug: 'poulet-roti', picture: 'images/poulet.jpg', description: 'Un bon poulet rôti', weightVariable: true, discountRate: DiscountRate::fromRatio(0.2), priceKgTtc: $priceKgTtc, options: ['extra' => 'crispy'] ); // THEN expect((string) $item->id->value)->toBe('019e9e37-f2a1-74ef-b9c2-66d222470128'); expect($item->name)->toBe('Poulet Rôti'); expect($item->qty)->toBe(2); expect($item->unitPriceHt->getAmount())->toBe('1000'); expect($item->unitDiscount->getAmount())->toBe('200'); expect($item->taxRate)->toBe(TaxRate::TVA_5_5); expect($item->weightGrams)->toBe(1200); expect($item->brand)->toBe(ProductBrand::marie); expect($item->incoId->value)->toBe(45); expect($item->orderUnit)->toBe(OrderUnit::PCE); expect($item->slug)->toBe('poulet-roti'); expect($item->picture)->toBe('images/poulet.jpg'); expect($item->description)->toBe('Un bon poulet rôti'); expect($item->weightVariable)->toBeTrue(); expect($item->discountRate->basisPoints())->toBe(2000); expect($item->priceKgTtc->getAmount())->toBe('1500'); expect($item->options)->toBe(['extra' => 'crispy']); }); it('can clone a basket product with new quantity and keep properties', function (): void { // GIVEN $productId = new ProductId(Uuid::fromString('019e9e37-f2a1-74ef-b9c2-66d222470128')); $item = getNewBasketProduct(['id' => $productId, 'qty' => 2]); $basketId = new BasketId(Uuid::fromString('019e9e37-f2a1-74ef-b9c2-66d222470128')); $basket = new Basket($basketId); // WHEN $basket->addOrUpdate($item); $basket->addOrUpdate(getNewBasketProduct([ 'id' => $productId, 'qty' => 3, // Add 3 ])); // THEN expect($basket->countItems())->toBe(5); $updatedItem = $basket->items()[(string) $productId->value]; expect($updatedItem->qty)->toBe(5); expect($updatedItem->brand)->toBe(ProductBrand::marie); expect($updatedItem->incoId->value)->toBe(45); expect($updatedItem->orderUnit)->toBe(OrderUnit::PCE); expect($updatedItem->slug)->toBe('poulet-roti'); expect($updatedItem->picture)->toBe('images/poulet.jpg'); expect($updatedItem->description)->toBe('Un bon poulet rôti'); expect($updatedItem->weightVariable)->toBeTrue(); expect($updatedItem->discountRate->basisPoints())->toBe(2000); expect($updatedItem->priceKgTtc->getAmount())->toBe('1500'); expect($updatedItem->options)->toBe(['extra' => 'crispy']); }); }); describe('Basket additional coverage', function (): void { it('throws if item is not BasketProduct', function (): void { // GIVEN $basketId = new BasketId(Uuid::uuid7()); // WHEN $action = fn () => new Basket($basketId, ['not-an-item']); // THEN expect($action)->toThrow(\DomainException::class); }); it('returns id', function (): void { // GIVEN $basketId = new BasketId(Uuid::uuid7()); $basket = new Basket($basketId); // WHEN $id = $basket->id(); // THEN expect($id)->toBe($basketId); }); it('removes item completely if quantity is greater or equal', function (): void { // GIVEN $basketId = new BasketId(Uuid::uuid7()); $basket = new Basket($basketId); $productId = new ProductId(Uuid::uuid7()); $item = getNewBasketProduct(['id' => $productId, 'qty' => 2]); $basket->addOrUpdate($item); // WHEN $basket->remove($productId, 2); // THEN expect($basket->has($productId))->toBeFalse(); }); it('decreases quantity when removing', function (): void { // GIVEN $basketId = new BasketId(Uuid::uuid7()); $basket = new Basket($basketId); $productId = new ProductId(Uuid::uuid7()); $item = getNewBasketProduct(['id' => $productId, 'qty' => 5]); $basket->addOrUpdate($item); // WHEN $basket->remove($productId, 2); // THEN expect($basket->items()[(string) $productId->value]->qty)->toBe(3); }); it('throws when removing less than 1', function (): void { // GIVEN $basketId = new BasketId(Uuid::uuid7()); $basket = new Basket($basketId); $productId = new ProductId(Uuid::uuid7()); // WHEN $action = fn () => $basket->remove($productId, 0); // THEN expect($action)->toThrow(\DomainException::class); }); it('throws when removing non-existing item', function (): void { // GIVEN $basketId = new BasketId(Uuid::uuid7()); $basket = new Basket($basketId); $productId = new ProductId(Uuid::uuid7()); // WHEN $action = fn () => $basket->remove($productId, 1); // THEN expect($action)->toThrow(\DomainException::class); }); it('removes line directly', function (): void { // GIVEN $basketId = new BasketId(Uuid::uuid7()); $basket = new Basket($basketId); $productId = new ProductId(Uuid::uuid7()); $item = getNewBasketProduct(['id' => $productId, 'qty' => 5]); $basket->addOrUpdate($item); // WHEN $basket->removeLine($productId); // THEN expect($basket->has($productId))->toBeFalse(); }); it('throws when removeLine on non-existing item', function (): void { // GIVEN $basketId = new BasketId(Uuid::uuid7()); $basket = new Basket($basketId); $productId = new ProductId(Uuid::uuid7()); // WHEN $action = fn () => $basket->removeLine($productId); // THEN expect($action)->toThrow(\DomainException::class); }); it('computes totals', function (): void { // GIVEN $basketId = new BasketId(Uuid::uuid7()); $basket = new Basket($basketId); $productId = new ProductId(Uuid::uuid7()); $item = getNewBasketProduct(['id' => $productId, 'qty' => 1]); $basket->addOrUpdate($item); // WHEN $taxInc = $basket->totalTaxIncluded(); $vat = $basket->totalVat(); $raw = $basket->totalRaw(); $discount = $basket->totalDiscount(); $weight = $basket->totalWeight(); // THEN expect($taxInc->getAmount())->toBe('844') // (1000 - 200) * 1.055 = 844 ->and($vat->getAmount())->toBe('44') ->and($raw->getAmount())->toBe('800') ->and($discount->getAmount())->toBe('200') ->and($weight)->toBe(1200); }); });