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

214 lines
8.5 KiB
PHP

<?php
declare(strict_types=1);
namespace Tests\Unit\Domain\Services;
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 Domain\Models\ValueObjects\SessionId;
use Domain\Ports\Repositories\BasketRepository;
use Domain\Ports\Repositories\ProductRepository;
use Domain\Ports\Services\SessionService;
use Domain\Services\BasketService;
use Mockery;
use Money\Money;
use Ramsey\Uuid\Uuid;
function makeBasketProduct(array $overrides = []): BasketProduct
{
$data = array_merge([
'id' => new ProductId(Uuid::fromString('019e9e37-f2a1-74ef-b9c2-66d222470128')),
'name' => 'Poulet Rôti',
'qty' => 1,
'unitPriceHt' => Money::EUR(1000),
'unitDiscount' => Money::EUR(0),
'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.0),
'priceKgTtc' => Money::EUR(1500),
], $overrides);
return new BasketProduct(...$data);
}
describe('BasketService', function (): void {
it('adds a new product to the basket and saves it', function (): void {
// GIVEN
$productId = new ProductId(Uuid::uuid7());
$qty = 2;
$basket = new Basket(new BasketId(Uuid::uuid7()));
$basketProduct = makeBasketProduct(['id' => $productId, 'qty' => $qty]);
$basketRepository = Mockery::mock(BasketRepository::class);
$sessionService = Mockery::mock(SessionService::class);
$sessionService->shouldReceive('get')->andReturn(new SessionId(Uuid::uuid7()))->byDefault();
$basketRepository->shouldReceive('find')->once()->andReturn($basket);
$basketRepository->shouldReceive('persist')->once()->with(Mockery::on(function (Basket $b) use ($productId) {
return $b->has($productId) && $b->countItems() === 2;
}));
$productRepository = Mockery::mock(ProductRepository::class);
$productRepository->shouldReceive('getBasketProduct')->once()->with($productId, $qty)->andReturn($basketProduct);
$service = new BasketService($basketRepository, $productRepository, $sessionService);
// WHEN
$service->add($productId, $qty);
// THEN
expect($basket->has($productId))->toBeTrue();
});
it('removes a quantity from the basket', function (): void {
// GIVEN
$productId = new ProductId(Uuid::uuid7());
$basket = new Basket(new BasketId(Uuid::uuid7()));
$basket->addOrUpdate(makeBasketProduct(['id' => $productId, 'qty' => 3]));
$basketRepository = Mockery::mock(BasketRepository::class);
$sessionService = Mockery::mock(SessionService::class);
$sessionService->shouldReceive('get')->andReturn(new SessionId(Uuid::uuid7()))->byDefault();
$basketRepository->shouldReceive('find')->once()->andReturn($basket);
$basketRepository->shouldReceive('persist')->once()->with(Mockery::on(function (Basket $b) use ($productId) {
$items = $b->items();
return $items[(string) $productId->value]->qty === 1;
}));
$productRepository = Mockery::mock(ProductRepository::class);
$service = new BasketService($basketRepository, $productRepository, $sessionService);
// WHEN
$service->remove($productId, 2);
// THEN
$items = $basket->items();
expect($items[(string) $productId->value]->qty)->toBe(1);
});
it('removes the entire line when quantity to remove equals current quantity', function (): void {
// GIVEN
$productId = new ProductId(Uuid::uuid7());
$basket = new Basket(new BasketId(Uuid::uuid7()));
$basket->addOrUpdate(makeBasketProduct(['id' => $productId, 'qty' => 3]));
$basketRepository = Mockery::mock(BasketRepository::class);
$sessionService = Mockery::mock(SessionService::class);
$sessionService->shouldReceive('get')->andReturn(new SessionId(Uuid::uuid7()))->byDefault();
$basketRepository->shouldReceive('find')->once()->andReturn($basket);
$basketRepository->shouldReceive('persist')->once()->with(Mockery::on(function (Basket $b) use ($productId) {
return ! $b->has($productId);
}));
$productRepository = Mockery::mock(ProductRepository::class);
$service = new BasketService($basketRepository, $productRepository, $sessionService);
// WHEN
$service->remove($productId, 3);
// THEN
expect($basket->has($productId))->toBeFalse();
});
it('removes an entire line directly via removeLine', function (): void {
// GIVEN
$productId = new ProductId(Uuid::uuid7());
$basket = new Basket(new BasketId(Uuid::uuid7()));
$basket->addOrUpdate(makeBasketProduct(['id' => $productId, 'qty' => 5]));
$basketRepository = Mockery::mock(BasketRepository::class);
$sessionService = Mockery::mock(SessionService::class);
$sessionService->shouldReceive('get')->andReturn(new SessionId(Uuid::uuid7()))->byDefault();
$basketRepository->shouldReceive('find')->once()->andReturn($basket);
$basketRepository->shouldReceive('persist')->once()->with(Mockery::on(function (Basket $b) use ($productId) {
return ! $b->has($productId);
}));
$productRepository = Mockery::mock(ProductRepository::class);
$service = new BasketService($basketRepository, $productRepository, $sessionService);
// WHEN
$service->removeLine($productId);
// THEN
expect($basket->has($productId))->toBeFalse();
});
it('empties the basket by destroying it from repository', function (): void {
// GIVEN
$basketRepository = Mockery::mock(BasketRepository::class);
$sessionService = Mockery::mock(SessionService::class);
$sessionService->shouldReceive('get')->andReturn(new SessionId(Uuid::uuid7()))->byDefault();
$basketRepository->shouldReceive('find')->once()->andReturn(new Basket(new BasketId(Uuid::uuid7())));
$basketRepository->shouldReceive('delete')->once();
$productRepository = Mockery::mock(ProductRepository::class);
$service = new BasketService($basketRepository, $productRepository, $sessionService);
// WHEN
$service->empty();
// THEN
expect(true)->toBeTrue();
});
it('gets the basket from repository', function (): void {
// GIVEN
$basket = new Basket(new BasketId(Uuid::uuid7()));
$basketRepository = Mockery::mock(BasketRepository::class);
$sessionService = Mockery::mock(SessionService::class);
$sessionService->shouldReceive('get')->andReturn(new SessionId(Uuid::uuid7()))->byDefault();
$basketRepository->shouldReceive('find')->once()->andReturn($basket);
$productRepository = Mockery::mock(ProductRepository::class);
$service = new BasketService($basketRepository, $productRepository, $sessionService);
// WHEN
$returnedBasket = $service->getBasket();
// THEN
expect($returnedBasket)->toBe($basket);
});
it('returns a new basket if not found in repository', function (): void {
// GIVEN
$sessionId = new SessionId(Uuid::uuid7());
$basketRepository = Mockery::mock(BasketRepository::class);
$sessionService = Mockery::mock(SessionService::class);
$sessionService->shouldReceive('get')->andReturn($sessionId)->byDefault();
$basketRepository->shouldReceive('find')->once()->andReturn(null);
$productRepository = Mockery::mock(ProductRepository::class);
$service = new BasketService($basketRepository, $productRepository, $sessionService);
// WHEN
$returnedBasket = $service->getBasket();
// THEN
expect($returnedBasket)->toBeInstanceOf(Basket::class)
->and($returnedBasket->id->value)->toBe($sessionId->value)
->and($returnedBasket->countItems())->toBe(0);
});
});