ldc-mesbr-app/tests/Unit/Domain/Models/Entities/CouponTest.php
2026-10-02 12:48:00 +02:00

443 lines
16 KiB
PHP

<?php
declare(strict_types=1);
namespace Tests\Unit\Domain\Models\Entities;
use Carbon\CarbonImmutable;
use DateTime;
use Domain\Enums\BarcodeFormat;
use Shared\Enums\Country;
use Domain\Models\Entities\Coupon;
use Domain\Models\ValueObjects\Actor\ActorId;
use Domain\Models\ValueObjects\Brand\BrandCode;
use Domain\Models\ValueObjects\Coupon\CouponAvailabilityCalendar;
use Domain\Models\ValueObjects\Coupon\CouponCode;
use Domain\Models\ValueObjects\Coupon\CouponDiffusion;
use Domain\Models\ValueObjects\Coupon\CouponId;
use Domain\Models\ValueObjects\Coupon\CouponLegalInformation;
use Domain\Models\ValueObjects\Coupon\CouponValidity;
use Domain\Models\ValueObjects\Coupon\CouponValidityInformation;
use Domain\Models\ValueObjects\CouponName;
use Domain\Models\ValueObjects\CouponPicture;
use Domain\Models\ValueObjects\GS1;
use Domain\Models\ValueObjects\IncoCode;
use Domain\Models\ValueObjects\MoneyAmount;
use Domain\Models\ValueObjects\Period;
use Domain\Models\ValueObjects\PicturePath;
use Domain\Models\ValueObjects\ProcessingCode;
use Ramsey\Uuid\Uuid;
use Shared\Enums\Currency;
use Shared\ValueObjects\Money;
covers(Coupon::class);
describe('coupon', function (): void {
beforeEach(function (): void {
$this->code = new CouponCode('COU01');
$this->name = new CouponName('Name');
$this->amount = new MoneyAmount(new Money(Currency::EUR, 1));
$this->picture = new CouponPicture(
new PicturePath('files/product.jpg'),
new PicturePath('files/thumbnail.jpg'),
new PicturePath('files/inactive_thumbnail.jpg')
);
$this->diffusion = new CouponDiffusion([new Period(new DateTime('2026-01-01'), new DateTime('2026-02-01'))]);
$this->validity = new CouponValidity(30, new DateTime('2026-02-01'));
$this->couponValidityInformation = [
new CouponValidityInformation(
'Lorem ipsum dolor sit amet consectetur adipiscing elit.',
Country::FRANCE
)
];
$this->couponLegalInformation = [
new CouponLegalInformation(
'Lorem ipsum dolor sit amet consectetur adipiscing elit.',
Country::FRANCE
)
];
$this->processingCode = new ProcessingCode('5425003944130');
$this->brandCode = new BrandCode('AAA01');
$this->createdBy = ActorId::generate();
$this->products = [new IncoCode(35784), new IncoCode(15522)];
$this->availabilityCalendar = new CouponAvailabilityCalendar([new Period(new DateTime('2026-08-25'), new DateTime('2026-10-31'))]);
$this->gtin = new GS1('110101010', BarcodeFormat::EAN13);
});
afterEach(function (): void {
CarbonImmutable::setTestNow();
});
it('creates a coupon', function (): void {
// GIVEN
$id = CouponId::generate();
// WHEN
$coupon = Coupon::hydrate(
$id,
$this->code,
$this->name,
$this->amount,
$this->picture,
$this->diffusion,
$this->validity,
$this->couponValidityInformation,
$this->couponLegalInformation,
$this->processingCode,
$this->brandCode,
$this->createdBy,
$this->products,
$this->availabilityCalendar,
$this->gtin,
);
// THEN
expect($coupon->id())->toBe($id)
->and($coupon->code)->toBe($this->code)
->and($coupon->name)->toBe($this->name)
->and($coupon->amount)->toBe($this->amount)
->and($coupon->picture)->toBe($this->picture)
->and($coupon->diffusion)->toBe($this->diffusion)
->and($coupon->validity)->toBe($this->validity)
->and($coupon->couponValidityInformation)->toBe($this->couponValidityInformation)
->and($coupon->couponLegalInformation)->toBe($this->couponLegalInformation)
->and($coupon->processingCode)->toBe($this->processingCode)
->and($coupon->brandCode)->toBe($this->brandCode)
->and($coupon->createdBy)->toBe($this->createdBy)
->and($coupon->products)->toBe($this->products)
->and($coupon->availabilityCalendar)->toBe($this->availabilityCalendar)
->and($coupon->gtin)->toBe($this->gtin)
->and($coupon->updatedAt)->toBeNull()
->and($coupon->updatedAt())->toBeNull();
});
it('hydrates a coupon without gs1', function (): void {
// GIVEN
$id = CouponId::generate();
// WHEN
$coupon = Coupon::hydrate(
id: $id,
code: $this->code,
name: $this->name,
amount: $this->amount,
picture: $this->picture,
diffusion: $this->diffusion,
validity: $this->validity,
couponValidityInformation: $this->couponValidityInformation,
couponLegalInformation: $this->couponLegalInformation,
processingCode: $this->processingCode,
brandCode: $this->brandCode,
createdBy: $this->createdBy,
products: $this->products,
availabilityCalendar: $this->availabilityCalendar
);
// THEN
expect($coupon->products)->toBe($this->products)
->and($coupon->availabilityCalendar)->toBe($this->availabilityCalendar)
->and($coupon->gtin)->toBeNull();
});
it('hydrates a coupon without availability calendar', function (): void {
// GIVEN
$id = CouponId::generate();
// WHEN
$coupon = Coupon::hydrate(
id: $id,
code: $this->code,
name: $this->name,
amount: $this->amount,
picture: $this->picture,
diffusion: $this->diffusion,
validity: $this->validity,
couponValidityInformation: $this->couponValidityInformation,
couponLegalInformation: $this->couponLegalInformation,
processingCode: $this->processingCode,
brandCode: $this->brandCode,
createdBy: $this->createdBy,
products: $this->products,
gtin: $this->gtin
);
// THEN
expect($coupon->availabilityCalendar)->toBeNull()
->and($coupon->products)->toBe($this->products)
->and($coupon->gtin)->toBe($this->gtin);
});
it('hydrates a coupon without products', function (): void {
// GIVEN
$id = CouponId::generate();
// WHEN
$coupon = Coupon::hydrate(
id: $id,
code: $this->code,
name: $this->name,
amount: $this->amount,
picture: $this->picture,
diffusion: $this->diffusion,
validity: $this->validity,
couponValidityInformation: $this->couponValidityInformation,
couponLegalInformation: $this->couponLegalInformation,
processingCode: $this->processingCode,
brandCode: $this->brandCode,
createdBy: $this->createdBy,
availabilityCalendar: $this->availabilityCalendar,
gtin: $this->gtin
);
// THEN
expect($coupon->products)->toBe([])
->and($coupon->availabilityCalendar)->toBe($this->availabilityCalendar)
->and($coupon->gtin)->toBe($this->gtin);
});
it('hydrates a coupon with only required fields', function (): void {
// GIVEN
$id = CouponId::generate();
// WHEN
$coupon = Coupon::hydrate(
id: $id,
code: $this->code,
name: $this->name,
amount: $this->amount,
picture: $this->picture,
diffusion: $this->diffusion,
validity: $this->validity,
couponValidityInformation: $this->couponValidityInformation,
couponLegalInformation: $this->couponLegalInformation,
processingCode: $this->processingCode,
brandCode: $this->brandCode,
createdBy: $this->createdBy,
);
// THEN
expect($coupon->id())->toBe($id)
->and($coupon->products)->toBe([])
->and($coupon->availabilityCalendar)->toBeNull()
->and($coupon->gtin)->toBeNull()
->and($coupon->updatedAt)->toBeNull()
->and($coupon->updatedAt())->toBeNull();
});
it('accepts empty validity and legal information lists', function (): void {
// GIVEN
$id = CouponId::generate();
// WHEN
$coupon = Coupon::hydrate(
$id,
$this->code,
$this->name,
$this->amount,
$this->picture,
$this->diffusion,
$this->validity,
[],
[],
$this->processingCode,
$this->brandCode,
$this->createdBy,
$this->products,
$this->availabilityCalendar,
$this->gtin,
);
// THEN
expect($coupon->couponValidityInformation)->toBe([])
->and($coupon->couponLegalInformation)->toBe([]);
});
it('throws when validity information contains an invalid item', function (): void {
// GIVEN
$id = CouponId::generate();
// WHEN / THEN
expect(fn () => Coupon::hydrate(
$id,
$this->code,
$this->name,
$this->amount,
$this->picture,
$this->diffusion,
$this->validity,
['Not a CouponValidityInformation'],
$this->couponLegalInformation,
$this->processingCode,
$this->brandCode,
$this->createdBy,
$this->products,
$this->availabilityCalendar,
$this->gtin,
))->toThrow(\DomainException::class, 'Coupon validity information must be CouponValidityInformation instances.');
});
it('throws when legal information contains an invalid item', function (): void {
// GIVEN
$id = CouponId::generate();
// WHEN / THEN
expect(fn () => Coupon::hydrate(
$id,
$this->code,
$this->name,
$this->amount,
$this->picture,
$this->diffusion,
$this->validity,
$this->couponValidityInformation,
['Not a CouponLegalInformation'],
$this->processingCode,
$this->brandCode,
$this->createdBy,
$this->products,
$this->availabilityCalendar,
$this->gtin,
))->toThrow(\DomainException::class, 'Coupon legal information must be CouponLegalInformation instances.');
});
it('hydrates a coupon with an explicit updatedAt date', function (): void {
// GIVEN
$id = CouponId::generate();
$updatedAt = new \DateTimeImmutable('2026-09-22 10:00:00', new \DateTimeZone('UTC'));
// WHEN
$coupon = Coupon::hydrate(
$id,
$this->code,
$this->name,
$this->amount,
$this->picture,
$this->diffusion,
$this->validity,
$this->couponValidityInformation,
$this->couponLegalInformation,
$this->processingCode,
$this->brandCode,
$this->createdBy,
$this->products,
$this->availabilityCalendar,
$this->gtin,
$updatedAt
);
// THEN
expect($coupon->updatedAt)->toBe($updatedAt)
->and($coupon->updatedAt())->toBe($updatedAt);
});
it('creates a new coupon with a freshly generated id', function (): void {
// GIVEN / WHEN
$coupon = Coupon::create(
$this->code,
$this->name,
$this->amount,
$this->picture,
$this->diffusion,
$this->validity,
$this->couponValidityInformation,
$this->couponLegalInformation,
$this->processingCode,
$this->brandCode,
$this->createdBy,
$this->products,
$this->availabilityCalendar,
$this->gtin,
);
// THEN
expect($coupon->id())->toBeInstanceOf(CouponId::class)
->and($coupon->code)->toBe($this->code)
->and($coupon->name)->toBe($this->name)
->and($coupon->amount)->toBe($this->amount)
->and($coupon->picture)->toBe($this->picture)
->and($coupon->diffusion)->toBe($this->diffusion)
->and($coupon->validity)->toBe($this->validity)
->and($coupon->couponValidityInformation)->toBe($this->couponValidityInformation)
->and($coupon->couponLegalInformation)->toBe($this->couponLegalInformation)
->and($coupon->processingCode)->toBe($this->processingCode)
->and($coupon->brandCode)->toBe($this->brandCode)
->and($coupon->createdBy)->toBe($this->createdBy)
->and($coupon->products)->toBe($this->products)
->and($coupon->availabilityCalendar)->toBe($this->availabilityCalendar)
->and($coupon->gtin)->toBe($this->gtin)
->and($coupon->updatedAt)->toBeNull()
->and($coupon->updatedAt())->toBeNull();
});
it('generates a different id for each created coupon', function (): void {
// GIVEN / WHEN
$firstCoupon = Coupon::create(
$this->code,
$this->name,
$this->amount,
$this->picture,
$this->diffusion,
$this->validity,
$this->couponValidityInformation,
$this->couponLegalInformation,
$this->processingCode,
$this->brandCode,
$this->createdBy,
$this->products,
$this->availabilityCalendar,
$this->gtin,
);
$secondCoupon = Coupon::create(
$this->code,
$this->name,
$this->amount,
$this->picture,
$this->diffusion,
$this->validity,
$this->couponValidityInformation,
$this->couponLegalInformation,
$this->processingCode,
$this->brandCode,
$this->createdBy,
$this->products,
$this->availabilityCalendar,
$this->gtin,
);
// THEN
expect($firstCoupon->id()->equals($secondCoupon->id()))->toBeFalse();
});
it('returns the creation date extracted from the id', function (): void {
// GIVEN
$expected = new \DateTimeImmutable('2024-06-15 12:34:56.789000', new \DateTimeZone('UTC'));
$id = new CouponId(Uuid::uuid7($expected));
$coupon = Coupon::hydrate(
$id,
new CouponCode('COU01'),
new CouponName('Name'),
new MoneyAmount(new Money(Currency::EUR, 1)),
new CouponPicture(
new PicturePath('files/product.jpg'),
new PicturePath('files/thumbnail.jpg'),
new PicturePath('files/inactive_thumbnail.jpg')
),
new CouponDiffusion([new Period(new DateTime('2026-01-01'), new DateTime('2026-02-01'))]),
new CouponValidity(30, new DateTime('2026-02-01')),
[new CouponValidityInformation('Lorem ipsum dolor sit amet consectetur adipiscing elit.', Country::FRANCE)],
[new CouponLegalInformation('Lorem ipsum dolor sit amet consectetur adipiscing elit.', Country::FRANCE)],
new ProcessingCode('5425003944130'),
new BrandCode('AAA01'),
ActorId::generate(),
[new IncoCode(35784), new IncoCode(15522)],
new CouponAvailabilityCalendar([new Period(new DateTime('2026-08-25'), new DateTime('2026-10-31'))]),
new GS1('110101010', BarcodeFormat::EAN13),
);
// WHEN
$result = $coupon->createdAt();
// THEN
expect($result->format('Y-m-d H:i:s.v'))->toBe($expected->format('Y-m-d H:i:s.v'));
});
});