443 lines
16 KiB
PHP
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'));
|
|
});
|
|
});
|