feat: ✨ add validations to entities and value objects
This commit is contained in:
parent
a58d604814
commit
59e4dbf2ee
16 changed files with 439 additions and 110 deletions
|
|
@ -31,6 +31,11 @@ final readonly class Campaign implements AggregateRoot, HasTimestampIdentifier
|
|||
public BrandCode $brand,
|
||||
public ?\DateTimeInterface $updatedAt = null,
|
||||
) {
|
||||
foreach ($this->coupons as $coupon) {
|
||||
if (!$coupon instanceof CampaignCoupon) {
|
||||
throw new \DomainException('Campaign coupons must be CampaignCoupon instances.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function id(): CampaignId
|
||||
|
|
|
|||
|
|
@ -37,14 +37,19 @@ final readonly class Coupon implements AggregateRoot, HasTimestampIdentifier
|
|||
public CouponDiffusion $diffusion,
|
||||
public CouponAvailabilityCalendar $availability,
|
||||
public CouponValidity $validity,
|
||||
/** @var array<int, CouponLegalInformation> $legal */
|
||||
public array $legal,
|
||||
/** @var array<int, CouponLegalInformation> $legalInformation */
|
||||
public array $legalInformation,
|
||||
public ProcessingCode $code,
|
||||
public BrandCode $brandCode,
|
||||
public ActorId $createdBy,
|
||||
public ?GS1 $gtin = null,
|
||||
public ?\DateTimeInterface $updatedAt = null,
|
||||
) {
|
||||
foreach ($this->legalInformation as $legal) {
|
||||
if (!$legal instanceof CouponLegalInformation) {
|
||||
throw new \DomainException('Coupon legal information must be CouponLegalInformation instances.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function id(): CouponId
|
||||
|
|
@ -63,7 +68,7 @@ final readonly class Coupon implements AggregateRoot, HasTimestampIdentifier
|
|||
}
|
||||
|
||||
/**
|
||||
* @param array<int, CouponLegalInformation> $legal
|
||||
* @param array<int, CouponLegalInformation> $legalInformation
|
||||
*/
|
||||
public static function create(
|
||||
CouponName $name,
|
||||
|
|
@ -75,7 +80,7 @@ final readonly class Coupon implements AggregateRoot, HasTimestampIdentifier
|
|||
CouponDiffusion $diffusion,
|
||||
CouponAvailabilityCalendar $availability,
|
||||
CouponValidity $validity,
|
||||
array $legal,
|
||||
array $legalInformation,
|
||||
ProcessingCode $code,
|
||||
BrandCode $brandCode,
|
||||
ActorId $createdBy,
|
||||
|
|
@ -93,7 +98,7 @@ final readonly class Coupon implements AggregateRoot, HasTimestampIdentifier
|
|||
diffusion: $diffusion,
|
||||
availability: $availability,
|
||||
validity: $validity,
|
||||
legal: $legal,
|
||||
legalInformation: $legalInformation,
|
||||
code: $code,
|
||||
brandCode: $brandCode,
|
||||
createdBy: $createdBy,
|
||||
|
|
@ -103,7 +108,7 @@ final readonly class Coupon implements AggregateRoot, HasTimestampIdentifier
|
|||
}
|
||||
|
||||
/**
|
||||
* @param array<int, CouponLegalInformation> $legal
|
||||
* @param array<int, CouponLegalInformation> $legalInformation
|
||||
*/
|
||||
public static function hydrate(
|
||||
CouponId $id,
|
||||
|
|
@ -116,7 +121,7 @@ final readonly class Coupon implements AggregateRoot, HasTimestampIdentifier
|
|||
CouponDiffusion $diffusion,
|
||||
CouponAvailabilityCalendar $availability,
|
||||
CouponValidity $validity,
|
||||
array $legal,
|
||||
array $legalInformation,
|
||||
ProcessingCode $code,
|
||||
BrandCode $brandCode,
|
||||
ActorId $createdBy,
|
||||
|
|
@ -134,7 +139,7 @@ final readonly class Coupon implements AggregateRoot, HasTimestampIdentifier
|
|||
diffusion: $diffusion,
|
||||
availability: $availability,
|
||||
validity: $validity,
|
||||
legal: $legal,
|
||||
legalInformation: $legalInformation,
|
||||
code: $code,
|
||||
brandCode: $brandCode,
|
||||
createdBy: $createdBy,
|
||||
|
|
|
|||
|
|
@ -26,6 +26,11 @@ final readonly class Stock implements AggregateRoot, HasTimestampIdentifier
|
|||
public array $stockAdjustment,
|
||||
public ?\DateTimeInterface $updatedAt = null,
|
||||
) {
|
||||
foreach ($this->stockAdjustment as $adjustment) {
|
||||
if (!$adjustment instanceof StockAdjustment) {
|
||||
throw new \DomainException('Stock adjustment must be StockAdjustment instances.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function id(): StockId
|
||||
|
|
|
|||
|
|
@ -13,5 +13,8 @@ final readonly class CampaignRequestFilters
|
|||
public ?BrandCode $brandCode = null,
|
||||
public ?CampaignStatus $status = null,
|
||||
) {
|
||||
if (null === $this->brandCode && null === $this->status) {
|
||||
throw new \InvalidArgumentException('CampaignRequestFilters require at least a brand code or a status.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,5 +13,11 @@ final readonly class CampaignRequestSortCriteria
|
|||
if ($this->clauses === []) {
|
||||
throw new \DomainException('Campaign Request Sort Criteria requires at least one clause.');
|
||||
}
|
||||
|
||||
foreach ($this->clauses as $clause) {
|
||||
if (!$clause instanceof CampaignRequestSortClause) {
|
||||
throw new \DomainException('CampaignRequestSortCriteria clauses must be CampaignRequestSortClause instances.');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,12 @@ final readonly class CouponAvailabilityCalendar implements Equatable
|
|||
if ($this->periods === []) {
|
||||
throw new \DomainException('Diffusion requires at least one period.');
|
||||
}
|
||||
|
||||
foreach ($this->periods as $period) {
|
||||
if (!$period instanceof Period) {
|
||||
throw new \DomainException('CouponAvailabilityCalendar periods must be Period instances.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function isActiveAt(DateTime $in): bool
|
||||
|
|
|
|||
|
|
@ -15,5 +15,11 @@ final readonly class CouponDiffusion
|
|||
if ($this->periods === []) {
|
||||
throw new \DomainException('Diffusion requires at least one period.');
|
||||
}
|
||||
|
||||
foreach ($this->periods as $period) {
|
||||
if (!$period instanceof Period) {
|
||||
throw new \DomainException('CouponDiffusion periods must be Period instances.');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,5 +10,16 @@ final readonly class PrintRules
|
|||
public ?int $maxPerCustomer = null,
|
||||
public ?int $cooldownDays = null,
|
||||
) {
|
||||
if (null === $this->maxPerCustomer && null === $this->cooldownDays) {
|
||||
throw new \InvalidArgumentException('PrintRules require at least a max per customer or a cooldown days.');
|
||||
}
|
||||
|
||||
if (null !== $this->maxPerCustomer && $this->maxPerCustomer <= 0) {
|
||||
throw new \InvalidArgumentException('Max per customer must be greater than 0.');
|
||||
}
|
||||
|
||||
if (null !== $this->cooldownDays && $this->cooldownDays <= 0) {
|
||||
throw new \InvalidArgumentException('Cooldown days must be greater than 0.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -72,6 +72,34 @@ describe('campaign', function (): void {
|
|||
->and($campaign->updatedAt())->toBeNull();
|
||||
});
|
||||
|
||||
it('creates a campaign without coupon', function (): void {
|
||||
// GIVEN
|
||||
$id = CampaignId::generate();
|
||||
$code = new CampaignCode('Camp100');
|
||||
$name = new CampaignName('Name');
|
||||
$publication = \boolval(0);
|
||||
$printAllAtOnce = \boolval(1);
|
||||
$availableIn = Country::FR;
|
||||
$coupons = [];
|
||||
$brand = new BrandCode('AAA01');
|
||||
|
||||
// WHEN
|
||||
$campaign = Campaign::hydrate(
|
||||
$id,
|
||||
$code,
|
||||
$this->tag,
|
||||
$name,
|
||||
$publication,
|
||||
$printAllAtOnce,
|
||||
$availableIn,
|
||||
$coupons,
|
||||
$brand,
|
||||
);
|
||||
|
||||
// THEN
|
||||
expect($campaign->coupons)->toBe([]);
|
||||
});
|
||||
|
||||
it('creates a coupon with an explicit updatedAt date', function (): void {
|
||||
// GIVEN
|
||||
$id = CampaignId::generate();
|
||||
|
|
@ -197,4 +225,29 @@ describe('campaign', function (): void {
|
|||
// THEN
|
||||
expect($result->format('Y-m-d H:i:s.v'))->toBe($expected->format('Y-m-d H:i:s.v'));
|
||||
});
|
||||
|
||||
it('rejects a campaign with a non-coupon element', function (): void {
|
||||
// GIVEN
|
||||
$id = CampaignId::generate();
|
||||
$code = new CampaignCode('Camp100');
|
||||
$name = new CampaignName('Name');
|
||||
$publication = \boolval(0);
|
||||
$printAllAtOnce = \boolval(1);
|
||||
$availableIn = Country::FR;
|
||||
$coupons = ['PC1'];
|
||||
$brand = new BrandCode('AAA01');
|
||||
|
||||
// WHEN / THEN
|
||||
expect(fn (): Campaign => Campaign::hydrate(
|
||||
$id,
|
||||
$code,
|
||||
$this->tag,
|
||||
$name,
|
||||
$publication,
|
||||
$printAllAtOnce,
|
||||
$availableIn,
|
||||
$coupons,
|
||||
$brand
|
||||
))->toThrow(\DomainException::class, 'Campaign coupons must be CampaignCoupon instances.');
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ use Domain\Models\ValueObjects\CouponPicture;
|
|||
use Domain\Models\ValueObjects\CouponProductName;
|
||||
use Domain\Models\ValueObjects\GS1;
|
||||
use Domain\Models\ValueObjects\MoneyAmount;
|
||||
use Domain\Models\ValueObjects\Period;
|
||||
use Domain\Models\ValueObjects\PicturePath;
|
||||
use Domain\Models\ValueObjects\ProcessingCode;
|
||||
use Ramsey\Uuid\Uuid;
|
||||
|
|
@ -37,21 +38,21 @@ describe('coupon', function (): void {
|
|||
|
||||
it('creates a coupon', function (): void {
|
||||
// GIVEN
|
||||
$id = CouponId::generate();
|
||||
$name = new CouponName('Name');
|
||||
$status = \boolval(0);
|
||||
$productName = new CouponProductName('Lorem ipsum');
|
||||
$amount = new MoneyAmount(new Money(Currency::EUR, 1));
|
||||
$picture = new CouponPicture(new PicturePath('files/product.jpg'), new PicturePath('files/thumbnail.jpg'));
|
||||
$product = new CouponProduct('Lorem ipsum', [], []);
|
||||
$diffusion = new CouponDiffusion(['2026-08-25', '2026-10-31']);
|
||||
$availability = new CouponAvailabilityCalendar(['2026-08-25', '2026-10-31']);
|
||||
$validity = new CouponValidity(30, new DateTime('2026-11-30'));
|
||||
$legal = [new CouponLegalInformation('Lorem ipsum dolor sit amet consectetur adipiscing elit.', Country::FR)];
|
||||
$code = new ProcessingCode('5425003944130');
|
||||
$brandCode = new BrandCode('AAA01');
|
||||
$createdBy = ActorId::generate();
|
||||
$gtin = new GS1('110101010', BarcodeFormat::EAN13);
|
||||
$id = CouponId::generate();
|
||||
$name = new CouponName('Name');
|
||||
$status = \boolval(0);
|
||||
$productName = new CouponProductName('Lorem ipsum');
|
||||
$amount = new MoneyAmount(new Money(Currency::EUR, 1));
|
||||
$picture = new CouponPicture(new PicturePath('files/product.jpg'), new PicturePath('files/thumbnail.jpg'));
|
||||
$product = new CouponProduct('Lorem ipsum', [], []);
|
||||
$diffusion = new CouponDiffusion([new Period(new DateTime('2026-01-01'), new DateTime('2026-02-01'))]);
|
||||
$availability = new CouponAvailabilityCalendar([new Period(new DateTime('2026-08-25'), new DateTime('2026-10-31'))]);
|
||||
$validity = new CouponValidity(30, new DateTime('2026-11-30'));
|
||||
$legalInformation = [new CouponLegalInformation('Lorem ipsum dolor sit amet consectetur adipiscing elit.', Country::FR)];
|
||||
$code = new ProcessingCode('5425003944130');
|
||||
$brandCode = new BrandCode('AAA01');
|
||||
$createdBy = ActorId::generate();
|
||||
$gtin = new GS1('110101010', BarcodeFormat::EAN13);
|
||||
|
||||
// WHEN
|
||||
$coupon = Coupon::hydrate(
|
||||
|
|
@ -65,7 +66,7 @@ describe('coupon', function (): void {
|
|||
$diffusion,
|
||||
$availability,
|
||||
$validity,
|
||||
$legal,
|
||||
$legalInformation,
|
||||
$code,
|
||||
$brandCode,
|
||||
$createdBy,
|
||||
|
|
@ -83,7 +84,7 @@ describe('coupon', function (): void {
|
|||
->and($coupon->diffusion)->toBe($diffusion)
|
||||
->and($coupon->availability)->toBe($availability)
|
||||
->and($coupon->validity)->toBe($validity)
|
||||
->and($coupon->legal)->toBe($legal)
|
||||
->and($coupon->legalInformation)->toBe($legalInformation)
|
||||
->and($coupon->code)->toBe($code)
|
||||
->and($coupon->brandCode)->toBe($brandCode)
|
||||
->and($coupon->createdBy)->toBe($createdBy)
|
||||
|
|
@ -94,20 +95,20 @@ describe('coupon', function (): void {
|
|||
|
||||
it('creates a coupon without gs1', function (): void {
|
||||
// GIVEN
|
||||
$id = CouponId::generate();
|
||||
$name = new CouponName('Name');
|
||||
$status = \boolval(0);
|
||||
$productName = new CouponProductName('Lorem ipsum');
|
||||
$amount = new MoneyAmount(new Money(Currency::EUR, 1));
|
||||
$picture = new CouponPicture(new PicturePath('files/product.jpg'), new PicturePath('files/thumbnail.jpg'));
|
||||
$product = new CouponProduct('Lorem ipsum', [], []);
|
||||
$diffusion = new CouponDiffusion(['2026-08-25', '2026-10-31']);
|
||||
$availability = new CouponAvailabilityCalendar(['2026-08-25', '2026-10-31']);
|
||||
$validity = new CouponValidity(30, new DateTime('2026-11-30'));
|
||||
$legal = [new CouponLegalInformation('Lorem ipsum dolor sit amet consectetur adipiscing elit.', Country::FR)];
|
||||
$code = new ProcessingCode('5425003944130');
|
||||
$brandCode = new BrandCode('AAA01');
|
||||
$createdBy = ActorId::generate();
|
||||
$id = CouponId::generate();
|
||||
$name = new CouponName('Name');
|
||||
$status = \boolval(0);
|
||||
$productName = new CouponProductName('Lorem ipsum');
|
||||
$amount = new MoneyAmount(new Money(Currency::EUR, 1));
|
||||
$picture = new CouponPicture(new PicturePath('files/product.jpg'), new PicturePath('files/thumbnail.jpg'));
|
||||
$product = new CouponProduct('Lorem ipsum', [], []);
|
||||
$diffusion = new CouponDiffusion([new Period(new DateTime('2026-01-01'), new DateTime('2026-02-01'))]);
|
||||
$availability = new CouponAvailabilityCalendar([new Period(new DateTime('2026-08-25'), new DateTime('2026-10-31'))]);
|
||||
$validity = new CouponValidity(30, new DateTime('2026-11-30'));
|
||||
$legalInformation = [new CouponLegalInformation('Lorem ipsum dolor sit amet consectetur adipiscing elit.', Country::FR)];
|
||||
$code = new ProcessingCode('5425003944130');
|
||||
$brandCode = new BrandCode('AAA01');
|
||||
$createdBy = ActorId::generate();
|
||||
|
||||
// WHEN
|
||||
$coupon = Coupon::hydrate(
|
||||
|
|
@ -121,7 +122,7 @@ describe('coupon', function (): void {
|
|||
$diffusion,
|
||||
$availability,
|
||||
$validity,
|
||||
$legal,
|
||||
$legalInformation,
|
||||
$code,
|
||||
$brandCode,
|
||||
$createdBy,
|
||||
|
|
@ -138,7 +139,7 @@ describe('coupon', function (): void {
|
|||
->and($coupon->diffusion)->toBe($diffusion)
|
||||
->and($coupon->availability)->toBe($availability)
|
||||
->and($coupon->validity)->toBe($validity)
|
||||
->and($coupon->legal)->toBe($legal)
|
||||
->and($coupon->legalInformation)->toBe($legalInformation)
|
||||
->and($coupon->code)->toBe($code)
|
||||
->and($coupon->brandCode)->toBe($brandCode)
|
||||
->and($coupon->createdBy)->toBe($createdBy)
|
||||
|
|
@ -148,22 +149,22 @@ describe('coupon', function (): void {
|
|||
|
||||
it('creates a coupon with an explicit updatedAt date', function (): void {
|
||||
// GIVEN
|
||||
$id = CouponId::generate();
|
||||
$name = new CouponName('Name');
|
||||
$status = \boolval(0);
|
||||
$productName = new CouponProductName('Lorem ipsum');
|
||||
$amount = new MoneyAmount(new Money(Currency::EUR, 1));
|
||||
$picture = new CouponPicture(new PicturePath('files/product.jpg'), new PicturePath('files/thumbnail.jpg'));
|
||||
$product = new CouponProduct('Lorem ipsum', [], []);
|
||||
$diffusion = new CouponDiffusion(['2026-08-25', '2026-10-31']);
|
||||
$availability = new CouponAvailabilityCalendar(['2026-08-25', '2026-10-31']);
|
||||
$validity = new CouponValidity(30, new DateTime('2026-11-30'));
|
||||
$legal = [new CouponLegalInformation('Lorem ipsum dolor sit amet consectetur adipiscing elit.', Country::FR)];
|
||||
$code = new ProcessingCode('5425003944130');
|
||||
$brandCode = new BrandCode('AAA01');
|
||||
$gtin = new GS1('110101010', BarcodeFormat::EAN13);
|
||||
$createdBy = ActorId::generate();
|
||||
$updatedAt = new \DateTimeImmutable('2026-09-22 10:00:00', new \DateTimeZone('UTC'));
|
||||
$id = CouponId::generate();
|
||||
$name = new CouponName('Name');
|
||||
$status = \boolval(0);
|
||||
$productName = new CouponProductName('Lorem ipsum');
|
||||
$amount = new MoneyAmount(new Money(Currency::EUR, 1));
|
||||
$picture = new CouponPicture(new PicturePath('files/product.jpg'), new PicturePath('files/thumbnail.jpg'));
|
||||
$product = new CouponProduct('Lorem ipsum', [], []);
|
||||
$diffusion = new CouponDiffusion([new Period(new DateTime('2026-01-01'), new DateTime('2026-02-01'))]);
|
||||
$availability = new CouponAvailabilityCalendar([new Period(new DateTime('2026-08-25'), new DateTime('2026-10-31'))]);
|
||||
$validity = new CouponValidity(30, new DateTime('2026-11-30'));
|
||||
$legalInformation = [new CouponLegalInformation('Lorem ipsum dolor sit amet consectetur adipiscing elit.', Country::FR)];
|
||||
$code = new ProcessingCode('5425003944130');
|
||||
$brandCode = new BrandCode('AAA01');
|
||||
$gtin = new GS1('110101010', BarcodeFormat::EAN13);
|
||||
$createdBy = ActorId::generate();
|
||||
$updatedAt = new \DateTimeImmutable('2026-09-22 10:00:00', new \DateTimeZone('UTC'));
|
||||
|
||||
// WHEN
|
||||
$coupon = Coupon::hydrate(
|
||||
|
|
@ -177,7 +178,7 @@ describe('coupon', function (): void {
|
|||
$diffusion,
|
||||
$availability,
|
||||
$validity,
|
||||
$legal,
|
||||
$legalInformation,
|
||||
$code,
|
||||
$brandCode,
|
||||
$createdBy,
|
||||
|
|
@ -192,20 +193,20 @@ describe('coupon', function (): void {
|
|||
|
||||
it('creates a new coupon with a freshly generated id', function (): void {
|
||||
// GIVEN
|
||||
$name = new CouponName('Name');
|
||||
$status = \boolval(0);
|
||||
$productName = new CouponProductName('Lorem ipsum');
|
||||
$amount = new MoneyAmount(new Money(Currency::EUR, 1));
|
||||
$picture = new CouponPicture(new PicturePath('files/product.jpg'), new PicturePath('files/thumbnail.jpg'));
|
||||
$product = new CouponProduct('Lorem ipsum', [], []);
|
||||
$diffusion = new CouponDiffusion(['2026-08-25', '2026-10-31']);
|
||||
$availability = new CouponAvailabilityCalendar(['2026-08-25', '2026-10-31']);
|
||||
$validity = new CouponValidity(30, new DateTime('2026-11-30'));
|
||||
$legal = [new CouponLegalInformation('Lorem ipsum dolor sit amet consectetur adipiscing elit.', Country::FR)];
|
||||
$code = new ProcessingCode('5425003944130');
|
||||
$brandCode = new BrandCode('AAA01');
|
||||
$createdBy = ActorId::generate();
|
||||
$gtin = new GS1('110101010', BarcodeFormat::EAN13);
|
||||
$name = new CouponName('Name');
|
||||
$status = \boolval(0);
|
||||
$productName = new CouponProductName('Lorem ipsum');
|
||||
$amount = new MoneyAmount(new Money(Currency::EUR, 1));
|
||||
$picture = new CouponPicture(new PicturePath('files/product.jpg'), new PicturePath('files/thumbnail.jpg'));
|
||||
$product = new CouponProduct('Lorem ipsum', [], []);
|
||||
$diffusion = new CouponDiffusion([new Period(new DateTime('2026-01-01'), new DateTime('2026-02-01'))]);
|
||||
$availability = new CouponAvailabilityCalendar([new Period(new DateTime('2026-08-25'), new DateTime('2026-10-31'))]);
|
||||
$validity = new CouponValidity(30, new DateTime('2026-11-30'));
|
||||
$legalInformation = [new CouponLegalInformation('Lorem ipsum dolor sit amet consectetur adipiscing elit.', Country::FR)];
|
||||
$code = new ProcessingCode('5425003944130');
|
||||
$brandCode = new BrandCode('AAA01');
|
||||
$createdBy = ActorId::generate();
|
||||
$gtin = new GS1('110101010', BarcodeFormat::EAN13);
|
||||
|
||||
// WHEN
|
||||
$coupon = Coupon::create(
|
||||
|
|
@ -218,7 +219,7 @@ describe('coupon', function (): void {
|
|||
$diffusion,
|
||||
$availability,
|
||||
$validity,
|
||||
$legal,
|
||||
$legalInformation,
|
||||
$code,
|
||||
$brandCode,
|
||||
$createdBy,
|
||||
|
|
@ -236,7 +237,7 @@ describe('coupon', function (): void {
|
|||
->and($coupon->diffusion)->toBe($diffusion)
|
||||
->and($coupon->availability)->toBe($availability)
|
||||
->and($coupon->validity)->toBe($validity)
|
||||
->and($coupon->legal)->toBe($legal)
|
||||
->and($coupon->legalInformation)->toBe($legalInformation)
|
||||
->and($coupon->code)->toBe($code)
|
||||
->and($coupon->brandCode)->toBe($brandCode)
|
||||
->and($coupon->createdBy)->toBe($createdBy)
|
||||
|
|
@ -247,20 +248,20 @@ describe('coupon', function (): void {
|
|||
|
||||
it('generates a different id for each created coupon', function (): void {
|
||||
// GIVEN
|
||||
$name = new CouponName('Name');
|
||||
$status = \boolval(0);
|
||||
$productName = new CouponProductName('Lorem ipsum');
|
||||
$amount = new MoneyAmount(new Money(Currency::EUR, 1));
|
||||
$picture = new CouponPicture(new PicturePath('files/product.jpg'), new PicturePath('files/thumbnail.jpg'));
|
||||
$product = new CouponProduct('Lorem ipsum', [], []);
|
||||
$diffusion = new CouponDiffusion(['2026-08-25', '2026-10-31']);
|
||||
$availability = new CouponAvailabilityCalendar(['2026-08-25', '2026-10-31']);
|
||||
$validity = new CouponValidity(30, new DateTime('2026-11-30'));
|
||||
$legal = [new CouponLegalInformation('Lorem ipsum dolor sit amet consectetur adipiscing elit.', Country::FR)];
|
||||
$code = new ProcessingCode('5425003944130');
|
||||
$brandCode = new BrandCode('AAA01');
|
||||
$createdBy = ActorId::generate();
|
||||
$gtin = new GS1('110101010', BarcodeFormat::EAN13);
|
||||
$name = new CouponName('Name');
|
||||
$status = \boolval(0);
|
||||
$productName = new CouponProductName('Lorem ipsum');
|
||||
$amount = new MoneyAmount(new Money(Currency::EUR, 1));
|
||||
$picture = new CouponPicture(new PicturePath('files/product.jpg'), new PicturePath('files/thumbnail.jpg'));
|
||||
$product = new CouponProduct('Lorem ipsum', [], []);
|
||||
$diffusion = new CouponDiffusion([new Period(new DateTime('2026-01-01'), new DateTime('2026-02-01'))]);
|
||||
$availability = new CouponAvailabilityCalendar([new Period(new DateTime('2026-08-25'), new DateTime('2026-10-31'))]);
|
||||
$validity = new CouponValidity(30, new DateTime('2026-11-30'));
|
||||
$legalInformation = [new CouponLegalInformation('Lorem ipsum dolor sit amet consectetur adipiscing elit.', Country::FR)];
|
||||
$code = new ProcessingCode('5425003944130');
|
||||
$brandCode = new BrandCode('AAA01');
|
||||
$createdBy = ActorId::generate();
|
||||
$gtin = new GS1('110101010', BarcodeFormat::EAN13);
|
||||
|
||||
// WHEN
|
||||
$firstCoupon = Coupon::create(
|
||||
|
|
@ -273,7 +274,7 @@ describe('coupon', function (): void {
|
|||
$diffusion,
|
||||
$availability,
|
||||
$validity,
|
||||
$legal,
|
||||
$legalInformation,
|
||||
$code,
|
||||
$brandCode,
|
||||
$createdBy,
|
||||
|
|
@ -289,7 +290,7 @@ describe('coupon', function (): void {
|
|||
$diffusion,
|
||||
$availability,
|
||||
$validity,
|
||||
$legal,
|
||||
$legalInformation,
|
||||
$code,
|
||||
$brandCode,
|
||||
$createdBy,
|
||||
|
|
@ -312,8 +313,8 @@ describe('coupon', function (): void {
|
|||
new MoneyAmount(new Money(Currency::EUR, 1)),
|
||||
new CouponPicture(new PicturePath('files/product.jpg'), new PicturePath('files/thumbnail.jpg')),
|
||||
new CouponProduct('Lorem ipsum', [], []),
|
||||
new CouponDiffusion(['2026-08-25', '2026-10-31']),
|
||||
new CouponAvailabilityCalendar(['2026-08-25', '2026-10-31']),
|
||||
new CouponDiffusion([new Period(new DateTime('2026-01-01'), new DateTime('2026-02-01'))]),
|
||||
new CouponAvailabilityCalendar([new Period(new DateTime('2026-08-25'), new DateTime('2026-10-31'))]),
|
||||
new CouponValidity(30, new DateTime('2026-11-30')),
|
||||
[new CouponLegalInformation('Lorem ipsum dolor sit amet consectetur adipiscing elit.', Country::FR)],
|
||||
new ProcessingCode('5425003944130'),
|
||||
|
|
|
|||
|
|
@ -20,9 +20,9 @@ describe('stock', function (): void {
|
|||
// GIVEN
|
||||
$id = StockId::generate();
|
||||
$stockAmount = new StockAmount(10);
|
||||
$stockAdjustment = [new StockAdjustment(5, 'Inventory correction', ActorId::generate())];
|
||||
$campaignCode = new CampaignCode('AAA01');
|
||||
$processingCode = new ProcessingCode('PC1');
|
||||
$stockAdjustment = [new StockAdjustment(5, 'Inventory correction', ActorId::generate())];
|
||||
|
||||
// WHEN
|
||||
$stock = Stock::hydrate($id, $stockAmount, $campaignCode, $processingCode, $stockAdjustment);
|
||||
|
|
@ -30,13 +30,81 @@ describe('stock', function (): void {
|
|||
// THEN
|
||||
expect($stock->id())->toBe($id)
|
||||
->and($stock->stockAmount)->toBe($stockAmount)
|
||||
->and($stock->stockAdjustment)->toBe($stockAdjustment)
|
||||
->and($stock->campaignCode)->toBe($campaignCode)
|
||||
->and($stock->processingCode)->toBe($processingCode)
|
||||
->and($stock->stockAdjustment)->toBe($stockAdjustment)
|
||||
->and($stock->updatedAt)->toBeNull()
|
||||
->and($stock->updatedAt())->toBeNull();
|
||||
});
|
||||
|
||||
it('hydrates a stock with several stock adjustments', function (): void {
|
||||
// GIVEN
|
||||
$id = StockId::generate();
|
||||
$stockAmount = new StockAmount(10);
|
||||
$campaignCode = new CampaignCode('AAA01');
|
||||
$processingCode = new ProcessingCode('PC1');
|
||||
$stockAdjustment = [
|
||||
new StockAdjustment(5, 'Inventory correction', ActorId::generate()),
|
||||
new StockAdjustment(10, 'Inventory correction', ActorId::generate()),
|
||||
];
|
||||
|
||||
// WHEN
|
||||
$stock = Stock::hydrate(
|
||||
$id,
|
||||
$stockAmount,
|
||||
$campaignCode,
|
||||
$processingCode,
|
||||
$stockAdjustment,
|
||||
);
|
||||
|
||||
// THEN
|
||||
expect($stock->id())->toBe($id)
|
||||
->and($stock->stockAmount)->toBe($stockAmount)
|
||||
->and($stock->campaignCode)->toBe($campaignCode)
|
||||
->and($stock->processingCode)->toBe($processingCode)
|
||||
->and($stock->stockAdjustment)->toBe($stockAdjustment)
|
||||
->and($stock->updatedAt)->toBeNull()
|
||||
->and($stock->updatedAt())->toBeNull();
|
||||
});
|
||||
|
||||
it('hydrates a stock without stock adjustement', function (): void {
|
||||
// GIVEN
|
||||
$id = StockId::generate();
|
||||
$stockAmount = new StockAmount(10);
|
||||
$campaignCode = new CampaignCode('AAA01');
|
||||
$processingCode = new ProcessingCode('PC1');
|
||||
|
||||
// WHEN
|
||||
$stock = Stock::hydrate($id, $stockAmount, $campaignCode, $processingCode);
|
||||
|
||||
// THEN
|
||||
expect($stock->id())->toBe($id)
|
||||
->and($stock->stockAmount)->toBe($stockAmount)
|
||||
->and($stock->campaignCode)->toBe($campaignCode)
|
||||
->and($stock->processingCode)->toBe($processingCode)
|
||||
->and($stock->stockAdjustment)->toBe([])
|
||||
->and($stock->updatedAt)->toBeNull()
|
||||
->and($stock->updatedAt())->toBeNull();
|
||||
});
|
||||
|
||||
it('rejects a stock with a non-stock-adjustment element', function (): void {
|
||||
// GIVEN
|
||||
$id = StockId::generate();
|
||||
$stockAmount = new StockAmount(10);
|
||||
$campaignCode = new CampaignCode('AAA01');
|
||||
$processingCode = new ProcessingCode('PC1');
|
||||
$stockAdjustment = [new StockAmount(5)];
|
||||
|
||||
// WHEN / THEN
|
||||
expect(fn (): Stock => Stock::hydrate(
|
||||
$id,
|
||||
$stockAmount,
|
||||
$campaignCode,
|
||||
$processingCode,
|
||||
$stockAdjustment,
|
||||
))->toThrow(\DomainException::class, 'Stock adjustment must be StockAdjustment instances.');
|
||||
});
|
||||
|
||||
it('hydrates a stock with an explicit updatedAt date', function (): void {
|
||||
// GIVEN
|
||||
$updatedAt = new \DateTimeImmutable('2026-06-15 12:00:00', new \DateTimeZone('UTC'));
|
||||
|
|
|
|||
|
|
@ -0,0 +1,56 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Unit\Domain\Models\ValueObjects;
|
||||
|
||||
use Domain\Enums\Campaign\CampaignStatus;
|
||||
use Domain\Models\ValueObjects\Brand\BrandCode;
|
||||
use Domain\Models\ValueObjects\CampaignRequest\CampaignRequestFilters;
|
||||
|
||||
covers(CampaignRequestFilters::class);
|
||||
|
||||
describe('campaign request filters', function (): void {
|
||||
it('creates filters with both fields set', function (): void {
|
||||
// GIVEN
|
||||
$brandCode = new BrandCode('AAA01');
|
||||
$status = CampaignStatus::CREATED;
|
||||
|
||||
// WHEN
|
||||
$filters = new CampaignRequestFilters(brandCode: $brandCode, status: $status);
|
||||
|
||||
// THEN
|
||||
expect($filters->brandCode)->toBe($brandCode)
|
||||
->and($filters->status)->toBe($status);
|
||||
});
|
||||
|
||||
it('creates filters with only brandCode set', function (): void {
|
||||
// GIVEN
|
||||
$brandCode = new BrandCode('AAA01');
|
||||
|
||||
// WHEN
|
||||
$filters = new CampaignRequestFilters(brandCode: $brandCode);
|
||||
|
||||
// THEN
|
||||
expect($filters->brandCode)->toBe($brandCode)
|
||||
->and($filters->status)->toBeNull();
|
||||
});
|
||||
|
||||
it('creates filters with only status set', function (): void {
|
||||
// GIVEN
|
||||
$status = CampaignStatus::CREATED;
|
||||
|
||||
// WHEN
|
||||
$filters = new CampaignRequestFilters(status: $status);
|
||||
|
||||
// THEN
|
||||
expect($filters->brandCode)->toBeNull()
|
||||
->and($filters->status)->toBe($status);
|
||||
});
|
||||
|
||||
it('rejects filters without any field set', function (): void {
|
||||
// WHEN / THEN
|
||||
expect(fn (): CampaignRequestFilters => new CampaignRequestFilters())
|
||||
->toThrow(\InvalidArgumentException::class, 'CampaignRequestFilters require at least a brand code or a status.');
|
||||
});
|
||||
});
|
||||
|
|
@ -11,9 +11,11 @@ use Domain\Models\ValueObjects\CampaignRequest\CampaignRequestSortField;
|
|||
covers(CampaignRequestSortCriteria::class);
|
||||
|
||||
describe('campaign request sort criteria', function (): void {
|
||||
it('creates a valid campaign request sort criteria', function (): void {
|
||||
it('creates a valid campaign request sort criteria with one clause', function (): void {
|
||||
// GIVEN
|
||||
$clauses = [new CampaignRequestSortClause(field: CampaignRequestSortField::BrandCode, direction: \SortDirection::Ascending)];
|
||||
$clauses = [
|
||||
new CampaignRequestSortClause(field: CampaignRequestSortField::BrandCode, direction: \SortDirection::Ascending),
|
||||
];
|
||||
|
||||
// WHEN
|
||||
$campaignRequestSortCriteria = new CampaignRequestSortCriteria($clauses);
|
||||
|
|
@ -22,11 +24,35 @@ describe('campaign request sort criteria', function (): void {
|
|||
expect($campaignRequestSortCriteria->clauses)->toBe($clauses);
|
||||
});
|
||||
|
||||
it('rejects invalid campaign request sort criteria', function (): void {
|
||||
it('creates a valid campaign request sort criteria with several clauses', function (): void {
|
||||
// GIVEN
|
||||
$clauses = [];
|
||||
$clauses = [
|
||||
new CampaignRequestSortClause(field: CampaignRequestSortField::BrandCode, direction: \SortDirection::Ascending),
|
||||
new CampaignRequestSortClause(field: CampaignRequestSortField::Status, direction: \SortDirection::Descending),
|
||||
];
|
||||
|
||||
// GIVEN / THEN
|
||||
expect(fn (): CampaignRequestSortCriteria => new CampaignRequestSortCriteria($clauses))->toThrow(\DomainException::class);
|
||||
// WHEN
|
||||
$campaignRequestSortCriteria = new CampaignRequestSortCriteria($clauses);
|
||||
|
||||
// THEN
|
||||
expect($campaignRequestSortCriteria->clauses)->toBe($clauses);
|
||||
});
|
||||
|
||||
it('rejects a campaign request sort criteria without clause', function (): void {
|
||||
// GIVEN
|
||||
$clauses = [];
|
||||
|
||||
// WHEN / THEN
|
||||
expect(fn (): CampaignRequestSortCriteria => new CampaignRequestSortCriteria($clauses))
|
||||
->toThrow(\DomainException::class, 'Campaign Request Sort Criteria requires at least one clause.');
|
||||
});
|
||||
|
||||
it('rejects a campaign request sort criteria with a non-clause element', function (): void {
|
||||
// GIVEN
|
||||
$clauses = [CampaignRequestSortField::BrandCode];
|
||||
|
||||
// WHEN / THEN
|
||||
expect(fn (): CampaignRequestSortCriteria => new CampaignRequestSortCriteria($clauses))
|
||||
->toThrow(\DomainException::class, 'CampaignRequestSortCriteria clauses must be CampaignRequestSortClause instances.');
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -5,13 +5,17 @@ declare(strict_types=1);
|
|||
namespace Tests\Unit\Domain\Models\ValueObjects\Coupon;
|
||||
|
||||
use Domain\Models\ValueObjects\Coupon\CouponAvailabilityCalendar;
|
||||
use Domain\Models\ValueObjects\Period;
|
||||
use Shared\ValueObjects\DateTime;
|
||||
|
||||
covers(CouponAvailabilityCalendar::class);
|
||||
|
||||
describe('coupon availability calendar', function (): void {
|
||||
it('creates a valid coupon availability calendar', function (): void {
|
||||
it('creates a valid coupon availability calendar with one period', function (): void {
|
||||
// GIVEN
|
||||
$periods = ['2026-08-25', '2026-10-31'];
|
||||
$periods = [
|
||||
new Period(new DateTime('2026-01-01'), new DateTime('2026-02-01')),
|
||||
];
|
||||
|
||||
// WHEN
|
||||
$couponAvailabilityCalendar = new CouponAvailabilityCalendar($periods);
|
||||
|
|
@ -20,11 +24,34 @@ describe('coupon availability calendar', function (): void {
|
|||
expect($couponAvailabilityCalendar->periods)->toBe($periods);
|
||||
});
|
||||
|
||||
it('rejects invalid coupon availability calendar', function (): void {
|
||||
it('creates a valid coupon availability calendar with several periods', function (): void {
|
||||
// GIVEN
|
||||
$periods = [
|
||||
new Period(new DateTime('2026-01-01'), new DateTime('2026-02-01')),
|
||||
new Period(new DateTime('2026-03-01'), new DateTime('2026-04-01')),
|
||||
];
|
||||
|
||||
// WHEN
|
||||
$couponAvailabilityCalendar = new CouponAvailabilityCalendar($periods);
|
||||
|
||||
// THEN
|
||||
expect($couponAvailabilityCalendar->periods)->toBe($periods);
|
||||
});
|
||||
|
||||
it('rejects a coupon availability calendar without period', function (): void {
|
||||
// GIVEN
|
||||
$periods = [];
|
||||
|
||||
// GIVEN / THEN
|
||||
expect(fn (): CouponAvailabilityCalendar => new CouponAvailabilityCalendar($periods))->toThrow(\DomainException::class);
|
||||
});
|
||||
|
||||
it('rejects a coupon availability calendar with a non-period element', function (): void {
|
||||
// GIVEN
|
||||
$periods = ['2026-08-25', '2026-10-31'];
|
||||
|
||||
// WHEN / THEN
|
||||
expect(fn (): CouponAvailabilityCalendar => new CouponAvailabilityCalendar($periods))
|
||||
->toThrow(\DomainException::class, 'CouponAvailabilityCalendar periods must be Period instances.');
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -5,13 +5,17 @@ declare(strict_types=1);
|
|||
namespace Tests\Unit\Domain\Models\ValueObjects\Coupon;
|
||||
|
||||
use Domain\Models\ValueObjects\Coupon\CouponDiffusion;
|
||||
use Domain\Models\ValueObjects\Period;
|
||||
use Shared\ValueObjects\DateTime;
|
||||
|
||||
covers(CouponDiffusion::class);
|
||||
|
||||
describe('coupon diffusion', function (): void {
|
||||
it('creates a valid coupon diffusion', function (): void {
|
||||
it('creates a valid coupon diffusion with one period', function (): void {
|
||||
// GIVEN
|
||||
$periods = ['2026-08-25', '2026-10-31'];
|
||||
$periods = [
|
||||
new Period(new DateTime('2026-01-01'), new DateTime('2026-02-01')),
|
||||
];
|
||||
|
||||
// WHEN
|
||||
$couponDiffusion = new CouponDiffusion($periods);
|
||||
|
|
@ -20,11 +24,34 @@ describe('coupon diffusion', function (): void {
|
|||
expect($couponDiffusion->periods)->toBe($periods);
|
||||
});
|
||||
|
||||
it('rejects invalid coupon diffusion', function (): void {
|
||||
it('creates a valid coupon diffusion with several periods', function (): void {
|
||||
// GIVEN
|
||||
$periods = [
|
||||
new Period(new DateTime('2026-01-01'), new DateTime('2026-02-01')),
|
||||
new Period(new DateTime('2026-03-01'), new DateTime('2026-04-01')),
|
||||
];
|
||||
|
||||
// WHEN
|
||||
$couponDiffusion = new CouponDiffusion($periods);
|
||||
|
||||
// THEN
|
||||
expect($couponDiffusion->periods)->toBe($periods);
|
||||
});
|
||||
|
||||
it('rejects a coupon diffusion without period', function (): void {
|
||||
// GIVEN
|
||||
$periods = [];
|
||||
|
||||
// GIVEN / THEN
|
||||
expect(fn (): CouponDiffusion => new CouponDiffusion($periods))->toThrow(\DomainException::class);
|
||||
});
|
||||
|
||||
it('rejects a coupon diffusion with a non-period element', function (): void {
|
||||
// GIVEN
|
||||
$periods = ['2026-08-25', '2026-10-31'];
|
||||
|
||||
// WHEN / THEN
|
||||
expect(fn (): CouponDiffusion => new CouponDiffusion($periods))
|
||||
->toThrow(\DomainException::class, 'CouponDiffusion periods must be Period instances.');
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -37,12 +37,36 @@ describe('print rules', function (): void {
|
|||
->and($printRules->cooldownDays)->toBe(45);
|
||||
});
|
||||
|
||||
it('creates print rules with both fields null by default', function (): void {
|
||||
it('rejects print rules without any field set', function (): void {
|
||||
// WHEN / THEN
|
||||
expect(fn (): PrintRules => new PrintRules())
|
||||
->toThrow(\InvalidArgumentException::class, 'PrintRules require at least a max per customer or a cooldown days.');
|
||||
});
|
||||
|
||||
it('accepts the minimum value of 1 for both fields', function (): void {
|
||||
// WHEN
|
||||
$printRules = new PrintRules();
|
||||
$printRules = new PrintRules(maxPerCustomer: 1, cooldownDays: 1);
|
||||
|
||||
// THEN
|
||||
expect($printRules->maxPerCustomer)->toBeNull()
|
||||
->and($printRules->cooldownDays)->toBeNull();
|
||||
expect($printRules->maxPerCustomer)->toBe(1)
|
||||
->and($printRules->cooldownDays)->toBe(1);
|
||||
});
|
||||
|
||||
it('rejects a maxPerCustomer equal to zero', function (): void {
|
||||
// GIVEN
|
||||
$maxPerCustomer = 0;
|
||||
|
||||
// WHEN / THEN
|
||||
expect(fn (): PrintRules => new PrintRules(maxPerCustomer: $maxPerCustomer))
|
||||
->toThrow(\InvalidArgumentException::class, 'Max per customer must be greater than 0.');
|
||||
});
|
||||
|
||||
it('rejects a cooldownDays equal to zero', function (): void {
|
||||
// GIVEN
|
||||
$cooldownDays = 0;
|
||||
|
||||
// WHEN / THEN
|
||||
expect(fn (): PrintRules => new PrintRules(cooldownDays: $cooldownDays))
|
||||
->toThrow(\InvalidArgumentException::class, 'Cooldown days must be greater than 0.');
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue