255 lines
9 KiB
PHP
255 lines
9 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Tests\Unit\Domain\Models\Entities;
|
|
|
|
use Domain\Enums\ProductBrand;
|
|
use Domain\Enums\TaxRate;
|
|
use Domain\Enums\Units\OrderUnit;
|
|
use Domain\Models\Entities\Basket;
|
|
use Domain\Models\ValueObjects\BasketProduct;
|
|
use Domain\Models\ValueObjects\DiscountRate;
|
|
use Domain\Models\ValueObjects\Entities\BasketId;
|
|
use Domain\Models\ValueObjects\Entities\ProductId;
|
|
use Domain\Models\ValueObjects\IncoId;
|
|
use Money\Money;
|
|
use Ramsey\Uuid\Uuid;
|
|
|
|
describe('Basket and BasketProduct', function (): void {
|
|
|
|
function getNewBasketProduct(array $overrides = []): BasketProduct
|
|
{
|
|
$data = array_merge([
|
|
'id' => 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);
|
|
});
|
|
});
|