ldc-boutique-cnc-sable/tests/Unit/Infra/Adapters/Serializers/BasketSerializerTest.php

205 lines
8.2 KiB
PHP

<?php
declare(strict_types=1);
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 Infrastructure\Adapters\Serializers\BasketSerializer;
use Money\Currency;
use Money\Money;
use Ramsey\Uuid\Uuid;
it('serializes and deserializes basket correctly', function () {
describe('BasketSerializer', function () {
// GIVEN
$serializer = new BasketSerializer();
$basketIdStr = \Symfony\Component\Uid\Uuid::v7()->toRfc4122();
$productIdStr = \Symfony\Component\Uid\Uuid::v7()->toRfc4122();
$basketProduct = new BasketProduct(
id: new ProductId(Uuid::fromString($productIdStr)),
name: 'Product 1',
qty: 2,
unitPriceHt: new Money(1000, new Currency('EUR')),
unitDiscount: new Money(100, new Currency('EUR')),
taxRate: TaxRate::TVA_20,
weightGrams: 500,
brand: ProductBrand::loue,
incoId: new IncoId(12345),
orderUnit: OrderUnit::PCE,
slug: 'product-1',
picture: 'pic.png',
description: 'desc',
weightVariable: true,
discountRate: DiscountRate::fromRatio(0.1),
priceKgTtc: new Money(2000, new Currency('EUR')),
options: ['color' => 'red']
);
$basket = new Basket(
id: new BasketId(Uuid::fromString($basketIdStr)),
items: [$productIdStr => $basketProduct]
);
// WHEN
$array = $serializer->toArray($basket);
$array = json_decode(json_encode($array), true);
$deserializedBasket = $serializer->fromArray($array);
// THEN
expect($array)->toBeArray()
->and($array['id'])->toBe($basketIdStr)
->and($array['items'][0]['id'])->toBe($productIdStr)
->and($array['items'][0]['name'])->toBe('Product 1')
->and($array['items'][0]['taxRate'])->toBe(TaxRate::TVA_20->value)
->and($deserializedBasket->id->value->toString())->toBe($basketIdStr)
->and($deserializedBasket->items())->toHaveCount(1)
->and($deserializedBasket->items()[$productIdStr]->name)->toBe('Product 1')
->and($deserializedBasket->items()[$productIdStr]->weightVariable)->toBeTrue()
->and($deserializedBasket->items()[$productIdStr]->options)->toBe(['color' => 'red'])
->and($array['items'][0]['discountRateBp'])->toBe(1000)
->and($deserializedBasket->items()[$productIdStr]->discountRate->basisPoints())->toBe(1000);
});
it('hydrates the discount rate from the legacy float ratio stored in older sessions', function (float $legacyRatio, int $expectedBasisPoints) {
// GIVEN
$serializer = new BasketSerializer();
$productIdStr = \Symfony\Component\Uid\Uuid::v7()->toRfc4122();
$data = [
'id' => \Symfony\Component\Uid\Uuid::v7()->toRfc4122(),
'items' => [
[
'id' => $productIdStr,
'name' => 'Test',
'qty' => 1,
'unitPriceHt' => ['amount' => 1000, 'currency' => 'EUR'],
'unitDiscount' => ['amount' => 100, 'currency' => 'EUR'],
'taxRate' => TaxRate::TVA_20->value,
'weightGrams' => 500,
'orderUnit' => OrderUnit::PCE->value,
'slug' => 'test',
'weightVariable' => false,
'discountRate' => $legacyRatio,
'priceKgTtc' => ['amount' => 2000, 'currency' => 'EUR'],
],
],
];
// WHEN
$basket = $serializer->fromArray($data);
// THEN
expect($basket->items()[$productIdStr]->discountRate->basisPoints())->toBe($expectedBasisPoints);
})->with([
'regular ratio' => [0.15, 1500],
'no discount' => [0.0, 0],
'out of range value is clamped' => [20.0, 10000],
]);
it('throws exception if currency is missing during deserialization', function () {
// GIVEN
$serializer = new BasketSerializer();
$productIdStr = \Symfony\Component\Uid\Uuid::v7()->toRfc4122();
$data = [
'id' => \Symfony\Component\Uid\Uuid::v7()->toRfc4122(),
'items' => [
[
'id' => $productIdStr,
'name' => 'Test',
'qty' => 1,
'unitPriceHt' => ['amount' => 1000, 'currency' => ''],
'unitDiscount' => ['amount' => 100, 'currency' => 'EUR'],
'taxRate' => TaxRate::TVA_20->value,
'weightGrams' => 500,
'orderUnit' => OrderUnit::PCE->value,
'slug' => 'test',
'weightVariable' => false,
'priceKgTtc' => ['amount' => 2000, 'currency' => 'EUR'],
],
],
];
// WHEN
$action = fn () => $serializer->fromArray($data);
// THEN
expect($action)->toThrow(
\DomainException::class,
'Currency is required for unitPriceHt of item with id ' . $productIdStr
);
});
it('throws exception if currency is missing for unitDiscount', function () {
// GIVEN
$serializer = new BasketSerializer();
$productIdStr = \Symfony\Component\Uid\Uuid::v7()->toRfc4122();
$data = [
'id' => \Symfony\Component\Uid\Uuid::v7()->toRfc4122(),
'items' => [
[
'id' => $productIdStr,
'name' => 'Test',
'qty' => 1,
'unitPriceHt' => ['amount' => 1000, 'currency' => 'EUR'],
'unitDiscount' => ['amount' => 100, 'currency' => ''],
'taxRate' => TaxRate::TVA_20->value,
'weightGrams' => 500,
'orderUnit' => OrderUnit::PCE->value,
'slug' => 'test',
'weightVariable' => false,
'priceKgTtc' => ['amount' => 2000, 'currency' => 'EUR'],
],
],
];
// WHEN
$action = fn () => $serializer->fromArray($data);
// THEN
expect($action)->toThrow(
\DomainException::class,
'Currency is required for unitDiscount of item with id ' . $productIdStr
);
});
it('throws exception if currency is missing for priceKgTtc', function () {
// GIVEN
$serializer = new BasketSerializer();
$productIdStr = \Symfony\Component\Uid\Uuid::v7()->toRfc4122();
$data = [
'id' => \Symfony\Component\Uid\Uuid::v7()->toRfc4122(),
'items' => [
[
'id' => $productIdStr,
'name' => 'Test',
'qty' => 1,
'unitPriceHt' => ['amount' => 1000, 'currency' => 'EUR'],
'unitDiscount' => ['amount' => 100, 'currency' => 'EUR'],
'taxRate' => TaxRate::TVA_20->value,
'weightGrams' => 500,
'orderUnit' => OrderUnit::PCE->value,
'slug' => 'test',
'weightVariable' => false,
'priceKgTtc' => ['amount' => 2000, 'currency' => ''],
],
],
];
// WHEN
$action = fn () => $serializer->fromArray($data);
// THEN
expect($action)->toThrow(
\DomainException::class,
'Currency is required for priceKgTtc of item with id ' . $productIdStr
);
});
});