refactor: ♻️ centralize test variables in beforeEach

This commit is contained in:
Corentin Huet 2026-10-01 12:04:26 +02:00
parent 7265d2fa5f
commit 7136ce9ee6
33 changed files with 902 additions and 769 deletions

View file

@ -12,17 +12,23 @@ use Ramsey\Uuid\Uuid;
covers(Actor::class);
describe('actor', function (): void {
beforeEach(function (): void {
$this->displayName = new ActorDisplayName('John Doe');
});
it('hydrates an actor', function (): void {
// GIVEN
$id = ActorId::generate();
$displayName = new ActorDisplayName('John Doe');
// WHEN
$actor = Actor::hydrate($id, $displayName);
$actor = Actor::hydrate(
$id,
$this->displayName
);
// THEN
expect($actor->id())->toBe($id)
->and($actor->displayName)->toBe($displayName)
->and($actor->displayName)->toBe($this->displayName)
->and($actor->updatedAt)->toBeNull()
->and($actor->updatedAt())->toBeNull();
});
@ -32,7 +38,11 @@ describe('actor', function (): void {
$updatedAt = new \DateTimeImmutable('2026-06-15 12:00:00', new \DateTimeZone('UTC'));
// WHEN
$actor = Actor::hydrate(ActorId::generate(), new ActorDisplayName('John Doe'), $updatedAt);
$actor = Actor::hydrate(
ActorId::generate(),
$this->displayName,
$updatedAt
);
// THEN
expect($actor->updatedAt)->toBe($updatedAt)
@ -43,7 +53,10 @@ describe('actor', function (): void {
// GIVEN
$expected = new \DateTimeImmutable('2024-06-15 12:34:56.789000', new \DateTimeZone('UTC'));
$id = new ActorId(Uuid::uuid7($expected));
$actor = Actor::hydrate($id, new ActorDisplayName('John Doe'));
$actor = Actor::hydrate(
$id,
$this->displayName
);
// WHEN
$result = $actor->createdAt();

View file

@ -17,29 +17,39 @@ use Shared\ValueObjects\Rcs;
covers(Brand::class);
describe('brand', function (): void {
beforeEach(function (): void {
$this->code = new BrandCode('AAA01');
$this->name = new BrandName('Name');
$this->logo = new PicturePath('files/picture.jpg');
$this->rcs = new Rcs('RCS PARIS 111 222 333');
$this->contact = new Email('contact@example.com');
});
afterEach(function (): void {
CarbonImmutable::setTestNow();
});
it('creates a brand', function (): void {
// GIVEN
$id = BrandId::generate();
$code = new BrandCode('AAA01');
$name = new BrandName('Name');
$logo = new PicturePath('files/picture.jpg');
$rcs = new Rcs('RCS PARIS 111 222 333');
$contact = new Email('contact@example.com');
$id = BrandId::generate();
// WHEN
$brand = Brand::hydrate($id, $code, $name, $logo, $rcs, $contact);
$brand = Brand::hydrate(
$id,
$this->code,
$this->name,
$this->logo,
$this->rcs,
$this->contact
);
// THEN
expect($brand->id())->toBe($id)
->and($brand->code)->toBe($code)
->and($brand->name)->toBe($name)
->and($brand->logo)->toBe($logo)
->and($brand->rcs)->toBe($rcs)
->and($brand->contact)->toBe($contact)
->and($brand->code)->toBe($this->code)
->and($brand->name)->toBe($this->name)
->and($brand->logo)->toBe($this->logo)
->and($brand->rcs)->toBe($this->rcs)
->and($brand->contact)->toBe($this->contact)
->and($brand->updatedAt)->toBeNull()
->and($brand->updatedAt())->toBeNull();
});
@ -47,15 +57,18 @@ describe('brand', function (): void {
it('creates a brand with an explicit updatedAt date', function (): void {
// GIVEN
$id = BrandId::generate();
$code = new BrandCode('AAA01');
$name = new BrandName('Name');
$logo = new PicturePath('files/picture.jpg');
$rcs = new Rcs('RCS PARIS 111 222 333');
$contact = new Email('contact@example.com');
$updatedAt = new \DateTimeImmutable('2026-06-15 12:00:00', new \DateTimeZone('UTC'));
// WHEN
$brand = Brand::hydrate($id, $code, $name, $logo, $rcs, $contact, $updatedAt);
$brand = Brand::hydrate(
$id,
$this->code,
$this->name,
$this->logo,
$this->rcs,
$this->contact,
$updatedAt
);
// THEN
expect($brand->updatedAt)->toBe($updatedAt)
@ -63,38 +76,42 @@ describe('brand', function (): void {
});
it('creates a new brand with a freshly generated id', function (): void {
// GIVEN
$code = new BrandCode('AAA01');
$name = new BrandName('Name');
$logo = new PicturePath('files/picture.jpg');
$rcs = new Rcs('RCS PARIS 111 222 333');
$contact = new Email('contact@example.com');
// WHEN
$brand = Brand::create($code, $name, $logo, $rcs, $contact);
// GIVEN / WHEN
$brand = Brand::create(
$this->code,
$this->name,
$this->logo,
$this->rcs,
$this->contact
);
// THEN
expect($brand->id())->toBeInstanceOf(BrandId::class)
->and($brand->code)->toBe($code)
->and($brand->name)->toBe($name)
->and($brand->logo)->toBe($logo)
->and($brand->rcs)->toBe($rcs)
->and($brand->contact)->toBe($contact)
->and($brand->code)->toBe($this->code)
->and($brand->name)->toBe($this->name)
->and($brand->logo)->toBe($this->logo)
->and($brand->rcs)->toBe($this->rcs)
->and($brand->contact)->toBe($this->contact)
->and($brand->updatedAt)->toBeNull()
->and($brand->updatedAt())->toBeNull();
});
it('generates a different id for each created brand', function (): void {
// GIVEN
$code = new BrandCode('AAA01');
$name = new BrandName('Name');
$logo = new PicturePath('files/picture.jpg');
$rcs = new Rcs('RCS PARIS 111 222 333');
$contact = new Email('contact@example.com');
// WHEN
$first = Brand::create($code, $name, $logo, $rcs, $contact);
$second = Brand::create($code, $name, $logo, $rcs, $contact);
// GIVEN / WHEN
$first = Brand::create(
$this->code,
$this->name,
$this->logo,
$this->rcs,
$this->contact
);
$second = Brand::create(
$this->code,
$this->name,
$this->logo,
$this->rcs,
$this->contact
);
// THEN
expect($first->id()->equals($second->id()))->toBeFalse();
@ -104,24 +121,27 @@ describe('brand', function (): void {
// GIVEN
CarbonImmutable::setTestNow('2026-06-15 12:00:00');
$id = BrandId::generate();
$code = new BrandCode('AAA01');
$name = new BrandName('Name');
$logoOld = new PicturePath('files/picture-old.jpg');
$logoNew = new PicturePath('files/picture-new.jpg');
$rcs = new Rcs('RCS PARIS 111 222 333');
$contact = new Email('contact@example.com');
$brand = Brand::hydrate($id, $code, $name, $logoOld, $rcs, $contact);
$brand = Brand::hydrate(
$id,
$this->code,
$this->name,
$logoOld,
$this->rcs,
$this->contact
);
// WHEN
$brand = $brand->changeLogo($logoNew);
// THEN
expect($brand->id())->toBe($id)
->and($brand->code)->toBe($code)
->and($brand->name)->toBe($name)
->and($brand->code)->toBe($this->code)
->and($brand->name)->toBe($this->name)
->and($brand->logo)->toBe($logoNew)
->and($brand->rcs)->toBe($rcs)
->and($brand->contact)->toBe($contact)
->and($brand->rcs)->toBe($this->rcs)
->and($brand->contact)->toBe($this->contact)
->and($brand->updatedAt)->not->toBeNull()
->and($brand->updatedAt()?->format('Y-m-d H:i:s'))->toBe('2026-06-15 12:00:00');
});
@ -130,24 +150,27 @@ describe('brand', function (): void {
// GIVEN
CarbonImmutable::setTestNow('2026-06-15 14:30:00');
$id = BrandId::generate();
$code = new BrandCode('AAA01');
$name = new BrandName('Name');
$logo = new PicturePath('files/picture.jpg');
$rcsOld = new Rcs('RCS PARIS 111 222 333');
$rcsNew = new Rcs('RCS NANTES 444 555 666');
$contact = new Email('contact@example.com');
$brand = Brand::hydrate($id, $code, $name, $logo, $rcsOld, $contact);
$brand = Brand::hydrate(
$id,
$this->code,
$this->name,
$this->logo,
$rcsOld,
$this->contact
);
// WHEN
$brand = $brand->changeRcs($rcsNew);
// THEN
expect($brand->id())->toBe($id)
->and($brand->code)->toBe($code)
->and($brand->name)->toBe($name)
->and($brand->logo)->toBe($logo)
->and($brand->code)->toBe($this->code)
->and($brand->name)->toBe($this->name)
->and($brand->logo)->toBe($this->logo)
->and($brand->rcs)->toBe($rcsNew)
->and($brand->contact)->toBe($contact)
->and($brand->contact)->toBe($this->contact)
->and($brand->updatedAt)->not->toBeNull()
->and($brand->updatedAt()?->format('Y-m-d H:i:s'))->toBe('2026-06-15 14:30:00');
});
@ -156,23 +179,26 @@ describe('brand', function (): void {
// GIVEN
CarbonImmutable::setTestNow('2026-06-15 16:45:00');
$id = BrandId::generate();
$code = new BrandCode('AAA01');
$name = new BrandName('Name');
$logo = new PicturePath('files/picture.jpg');
$rcs = new Rcs('RCS PARIS 111 222 333');
$contactOld = new Email('contact-old@example.com');
$contactNew = new Email('contact-new@example.com');
$brand = Brand::hydrate($id, $code, $name, $logo, $rcs, $contactOld);
$brand = Brand::hydrate(
$id,
$this->code,
$this->name,
$this->logo,
$this->rcs,
$contactOld
);
// WHEN
$brand = $brand->changeContact($contactNew);
// THEN
expect($brand->id())->toBe($id)
->and($brand->code)->toBe($code)
->and($brand->name)->toBe($name)
->and($brand->logo)->toBe($logo)
->and($brand->rcs)->toBe($rcs)
->and($brand->code)->toBe($this->code)
->and($brand->name)->toBe($this->name)
->and($brand->logo)->toBe($this->logo)
->and($brand->rcs)->toBe($this->rcs)
->and($brand->contact)->toBe($contactNew)
->and($brand->updatedAt)->not->toBeNull()
->and($brand->updatedAt()?->format('Y-m-d H:i:s'))->toBe('2026-06-15 16:45:00');
@ -184,11 +210,11 @@ describe('brand', function (): void {
$id = new BrandId(Uuid::uuid7($expected));
$brand = Brand::hydrate(
$id,
new BrandCode('AAA01'),
new BrandName('Name'),
new PicturePath('files/picture.jpg'),
new Rcs('RCS PARIS 111 222 333'),
new Email('contact@example.com'),
$this->code,
$this->name,
$this->logo,
$this->rcs,
$this->contact
);
// WHEN

View file

@ -38,12 +38,8 @@ use Shared\ValueObjects\Money;
covers(CampaignRequest::class);
describe('campaign request', function (): void {
afterEach(function (): void {
CarbonImmutable::setTestNow();
});
beforeEach(function (): void {
$this->requestedCoupon = new RequestedCoupon(
$this->requestedCoupon = new RequestedCoupon(
new CouponName('Coupon A'),
new RequestedCouponQuantity(100),
new Period(new DateTime('2026-01-01'), new DateTime('2026-02-01')),
@ -61,37 +57,44 @@ describe('campaign request', function (): void {
new GS1('01234567890128', BarcodeFormat::EAN13),
new WarningThresholdQuantity(10)
);
$this->code = new BrandCode('AAA01');
$this->name = new CampaignName('Campaign A');
$this->applicant = new Applicant('John', 'Doe', new Email('john.doe@example.com'));
$this->campaignType = CampaignRequestType::CLUB;
$this->requestedCoupon = $this->requestedCoupon;
$this->status = CampaignRequestStatus::CREATED;
$this->transformedStatus = CampaignRequestStatus::TRANSFORMED;
$this->rejectedStatus = CampaignRequestStatus::REJECTED;
$this->rejectionReason = new RejectionReason('Missing legal documents.');
});
afterEach(function (): void {
CarbonImmutable::setTestNow();
});
it('hydrates a campaign request with all its fields', function (): void {
// GIVEN
$id = CampaignRequestId::generate();
$code = new BrandCode('AAA01');
$name = new CampaignName('Campaign A');
$applicant = new Applicant('John', 'Doe', new Email('john.doe@example.com'));
$campaignType = CampaignRequestType::CLUB;
$requestedCoupon = $this->requestedCoupon;
$status = CampaignRequestStatus::CREATED;
// WHEN
$campaignRequest = CampaignRequest::hydrate(
$id,
$code,
$name,
$applicant,
$campaignType,
$requestedCoupon,
$status,
$this->code,
$this->name,
$this->applicant,
$this->campaignType,
$this->requestedCoupon,
$this->status,
);
// THEN
expect($campaignRequest->id())->toBe($id)
->and($campaignRequest->brandCode)->toBe($code)
->and($campaignRequest->name)->toBe($name)
->and($campaignRequest->applicant)->toBe($applicant)
->and($campaignRequest->campaignType)->toBe($campaignType)
->and($campaignRequest->requestedCoupon)->toBe($requestedCoupon)
->and($campaignRequest->status)->toBe($status)
->and($campaignRequest->brandCode)->toBe($this->code)
->and($campaignRequest->name)->toBe($this->name)
->and($campaignRequest->applicant)->toBe($this->applicant)
->and($campaignRequest->campaignType)->toBe($this->campaignType)
->and($campaignRequest->requestedCoupon)->toBe($this->requestedCoupon)
->and($campaignRequest->status)->toBe($this->status)
->and($campaignRequest->updatedAt)->toBeNull()
->and($campaignRequest->updatedAt())->toBeNull();
});
@ -103,12 +106,12 @@ describe('campaign request', function (): void {
// WHEN
$campaignRequest = CampaignRequest::hydrate(
CampaignRequestId::generate(),
new BrandCode('AAA01'),
new CampaignName('Campaign A'),
new Applicant('John', 'Doe', new Email('john.doe@example.com')),
CampaignRequestType::CLUB,
$this->code,
$this->name,
$this->applicant,
$this->campaignType,
$this->requestedCoupon,
CampaignRequestStatus::CREATED,
$this->status,
null,
$updatedAt,
);
@ -119,36 +122,33 @@ describe('campaign request', function (): void {
});
it('hydrates a rejected campaign request with a rejection reason', function (): void {
// GIVEN
$rejectionReason = new RejectionReason('Missing legal documents.');
// WHEN
// GIVEN / WHEN
$campaignRequest = CampaignRequest::hydrate(
CampaignRequestId::generate(),
new BrandCode('AAA01'),
new CampaignName('Campaign A'),
new Applicant('John', 'Doe', new Email('john.doe@example.com')),
CampaignRequestType::CLUB,
$this->code,
$this->name,
$this->applicant,
$this->campaignType,
$this->requestedCoupon,
CampaignRequestStatus::REJECTED,
$rejectionReason,
$this->rejectedStatus,
$this->rejectionReason,
);
// THEN
expect($campaignRequest->status)->toBe(CampaignRequestStatus::REJECTED)
->and($campaignRequest->rejectionReason)->toBe($rejectionReason);
expect($campaignRequest->status)->toBe($this->rejectedStatus)
->and($campaignRequest->rejectionReason)->toBe($this->rejectionReason);
});
it('rejects a rejected campaign request without a rejection reason', function (): void {
// WHEN
// GIVEN / WHEN
$action = fn (): CampaignRequest => CampaignRequest::hydrate(
CampaignRequestId::generate(),
new BrandCode('AAA01'),
new CampaignName('Campaign A'),
new Applicant('John', 'Doe', new Email('john.doe@example.com')),
CampaignRequestType::CLUB,
$this->code,
$this->name,
$this->applicant,
$this->campaignType,
$this->requestedCoupon,
CampaignRequestStatus::REJECTED,
$this->rejectedStatus,
);
// THEN
@ -162,13 +162,13 @@ describe('campaign request', function (): void {
// WHEN
$action = fn (): CampaignRequest => CampaignRequest::hydrate(
CampaignRequestId::generate(),
new BrandCode('AAA01'),
new CampaignName('Campaign A'),
new Applicant('John', 'Doe', new Email('john.doe@example.com')),
CampaignRequestType::CLUB,
$this->code,
$this->name,
$this->applicant,
$this->campaignType,
$this->requestedCoupon,
CampaignRequestStatus::CREATED,
new RejectionReason('Missing legal documents.'),
$this->status,
$this->rejectionReason,
);
// THEN
@ -183,21 +183,20 @@ describe('campaign request', function (): void {
CarbonImmutable::setTestNow('2026-06-15 12:00:00');
$campaignRequest = CampaignRequest::hydrate(
CampaignRequestId::generate(),
new BrandCode('AAA01'),
new CampaignName('Campaign A'),
new Applicant('John', 'Doe', new Email('john.doe@example.com')),
CampaignRequestType::CLUB,
$this->code,
$this->name,
$this->applicant,
$this->campaignType,
$this->requestedCoupon,
CampaignRequestStatus::CREATED,
$this->status,
);
$reason = new RejectionReason('Missing legal documents.');
// WHEN
$rejected = $campaignRequest->reject($reason);
$rejected = $campaignRequest->reject($this->rejectionReason);
// THEN
expect($rejected->status)->toBe(CampaignRequestStatus::REJECTED)
->and($rejected->rejectionReason)->toBe($reason)
->and($rejected->rejectionReason)->toBe($this->rejectionReason)
->and($rejected->id())->toBe($campaignRequest->id())
->and($rejected->brandCode)->toBe($campaignRequest->brandCode)
->and($rejected->name)->toBe($campaignRequest->name)
@ -212,16 +211,16 @@ describe('campaign request', function (): void {
// GIVEN
$campaignRequest = CampaignRequest::hydrate(
CampaignRequestId::generate(),
new BrandCode('AAA01'),
new CampaignName('Campaign A'),
new Applicant('John', 'Doe', new Email('john.doe@example.com')),
CampaignRequestType::CLUB,
$this->code,
$this->name,
$this->applicant,
$this->campaignType,
$this->requestedCoupon,
CampaignRequestStatus::TRANSFORMED,
$this->transformedStatus,
);
// WHEN
$action = fn (): CampaignRequest => $campaignRequest->reject(new RejectionReason('Missing legal documents.'));
$action = fn (): CampaignRequest => $campaignRequest->reject($this->rejectionReason);
// THEN
expect($action)->toThrow(\DomainException::class, 'Only a created campaign request can be rejected.');
@ -231,12 +230,12 @@ describe('campaign request', function (): void {
// GIVEN
$campaignRequest = CampaignRequest::hydrate(
CampaignRequestId::generate(),
new BrandCode('AAA01'),
new CampaignName('Campaign A'),
new Applicant('John', 'Doe', new Email('john.doe@example.com')),
CampaignRequestType::CLUB,
$this->code,
$this->name,
$this->applicant,
$this->campaignType,
$this->requestedCoupon,
CampaignRequestStatus::REJECTED,
$this->rejectedStatus,
new RejectionReason('First reason.'),
);
@ -253,12 +252,12 @@ describe('campaign request', function (): void {
$id = new CampaignRequestId(Uuid::uuid7($expected));
$campaignRequest = CampaignRequest::hydrate(
$id,
new BrandCode('AAA01'),
new CampaignName('Campaign A'),
new Applicant('John', 'Doe', new Email('john.doe@example.com')),
CampaignRequestType::CLUB,
$this->code,
$this->name,
$this->applicant,
$this->campaignType,
$this->requestedCoupon,
CampaignRequestStatus::CREATED,
$this->status,
);
// WHEN

View file

@ -21,18 +21,12 @@ use Ramsey\Uuid\Uuid;
covers(Campaign::class);
describe('campaign', function (): void {
afterEach(function (): void {
CarbonImmutable::setTestNow();
});
it('creates a campaign', function (): void {
// GIVEN
$id = CampaignId::generate();
$code = new CampaignCode('Camp100');
$name = new CampaignName('Name');
$publication = \boolval(0);
$printAllAtOnce = \boolval(1);
$coupons = [
beforeEach(function (): void {
$this->code = new CampaignCode('Camp100');
$this->name = new CampaignName('Name');
$this->publication = \boolval(0);
$this->printAllAtOnce = \boolval(1);
$this->coupons = [
new CampaignCoupon(
new ProcessingCode('PC1'),
new CouponQuantity(100),
@ -41,50 +35,53 @@ describe('campaign', function (): void {
new PrintRules(maxPerCustomer: 3)
)
];
$brandCode = new BrandCode('AAA01');
$this->brandCode = new BrandCode('AAA01');
});
afterEach(function (): void {
CarbonImmutable::setTestNow();
});
it('creates a campaign', function (): void {
// GIVEN
$id = CampaignId::generate();
// WHEN
$campaign = Campaign::hydrate(
$id,
$code,
$name,
$publication,
$printAllAtOnce,
$coupons,
$brandCode,
$this->code,
$this->name,
$this->publication,
$this->printAllAtOnce,
$this->coupons,
$this->brandCode,
);
// THEN
expect($campaign->id())->toBe($id)
->and($campaign->code)->toBe($code)
->and($campaign->name)->toBe($name)
->and($campaign->publication)->toBe($publication)
->and($campaign->printAllAtOnce)->toBe($printAllAtOnce)
->and($campaign->coupons)->toBe($coupons)
->and($campaign->brandCode)->toBe($brandCode)
->and($campaign->code)->toBe($this->code)
->and($campaign->name)->toBe($this->name)
->and($campaign->publication)->toBe($this->publication)
->and($campaign->printAllAtOnce)->toBe($this->printAllAtOnce)
->and($campaign->coupons)->toBe($this->coupons)
->and($campaign->brandCode)->toBe($this->brandCode)
->and($campaign->updatedAt)->toBeNull()
->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);
$coupons = [];
$brandCode = new BrandCode('AAA01');
$id = CampaignId::generate();
// WHEN
$campaign = Campaign::hydrate(
$id,
$code,
$name,
$publication,
$printAllAtOnce,
$coupons,
$brandCode,
$this->code,
$this->name,
$this->publication,
$this->printAllAtOnce,
[],
$this->brandCode,
);
// THEN
@ -93,32 +90,18 @@ describe('campaign', function (): void {
it('creates a coupon with an explicit updatedAt date', function (): void {
// GIVEN
$id = CampaignId::generate();
$code = new CampaignCode('Camp100');
$name = new CampaignName('Name');
$publication = \boolval(0);
$printAllAtOnce = \boolval(1);
$coupons = [
new CampaignCoupon(
new ProcessingCode('PC1'),
new CouponQuantity(100),
Country::FRANCE,
new WarningThresholdQuantity(100),
new PrintRules(maxPerCustomer: 3)
)
];
$brandCode = new BrandCode('AAA01');
$updatedAt = new \DateTimeImmutable('2026-09-22 10:00:00', new \DateTimeZone('UTC'));
$id = CampaignId::generate();
$updatedAt = new \DateTimeImmutable('2026-09-22 10:00:00', new \DateTimeZone('UTC'));
// WHEN
$campaign = Campaign::hydrate(
$id,
$code,
$name,
$publication,
$printAllAtOnce,
$coupons,
$brandCode,
$this->code,
$this->name,
$this->publication,
$this->printAllAtOnce,
$this->coupons,
$this->brandCode,
$updatedAt
);
@ -128,77 +111,45 @@ describe('campaign', function (): void {
});
it('creates a new campaign with a freshly generated id', function (): void {
// GIVEN
$code = new CampaignCode('Camp100');
$name = new CampaignName('Name');
$publication = \boolval(0);
$printAllAtOnce = \boolval(1);
$coupons = [
new CampaignCoupon(
new ProcessingCode('PC1'),
new CouponQuantity(100),
Country::FRANCE,
new WarningThresholdQuantity(100),
new PrintRules(maxPerCustomer: 3)
)
];
$brandCode = new BrandCode('AAA01');
// WHEN
// GIVEN / WHEN
$campaign = Campaign::create(
$code,
$name,
$publication,
$printAllAtOnce,
$coupons,
$brandCode
$this->code,
$this->name,
$this->publication,
$this->printAllAtOnce,
$this->coupons,
$this->brandCode
);
// THEN
expect($campaign->id())->toBeInstanceOf(CampaignId::class)
->and($campaign->code)->toBe($code)
->and($campaign->name)->toBe($name)
->and($campaign->publication)->toBe($publication)
->and($campaign->printAllAtOnce)->toBe($printAllAtOnce)
->and($campaign->coupons)->toBe($coupons)
->and($campaign->brandCode)->toBe($brandCode)
->and($campaign->code)->toBe($this->code)
->and($campaign->name)->toBe($this->name)
->and($campaign->publication)->toBe($this->publication)
->and($campaign->printAllAtOnce)->toBe($this->printAllAtOnce)
->and($campaign->coupons)->toBe($this->coupons)
->and($campaign->brandCode)->toBe($this->brandCode)
->and($campaign->updatedAt)->toBeNull()
->and($campaign->updatedAt())->toBeNull();
});
it('generates a different id for each created campaign', function (): void {
// GIVEN
$code = new CampaignCode('Camp100');
$name = new CampaignName('Name');
$publication = \boolval(0);
$printAllAtOnce = \boolval(1);
$coupons = [
new CampaignCoupon(
new ProcessingCode('PC1'),
new CouponQuantity(100),
Country::FRANCE,
new WarningThresholdQuantity(100),
new PrintRules(maxPerCustomer: 3)
)
];
$brandCode = new BrandCode('AAA01');
// WHEN
// GIVEN / WHEN
$firstCampaign = Campaign::create(
$code,
$name,
$publication,
$printAllAtOnce,
$coupons,
$brandCode
$this->code,
$this->name,
$this->publication,
$this->printAllAtOnce,
$this->coupons,
$this->brandCode
);
$secondCampaign = Campaign::create(
$code,
$name,
$publication,
$printAllAtOnce,
$coupons,
$brandCode
$this->code,
$this->name,
$this->publication,
$this->printAllAtOnce,
$this->coupons,
$this->brandCode
);
// THEN
@ -207,24 +158,16 @@ describe('campaign', function (): void {
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 CampaignId(Uuid::uuid7($expected));
$campaign = Campaign::hydrate(
$expected = new \DateTimeImmutable('2024-06-15 12:34:56.789000', new \DateTimeZone('UTC'));
$id = new CampaignId(Uuid::uuid7($expected));
$campaign = Campaign::hydrate(
$id,
new CampaignCode('Camp100'),
new CampaignName('Name'),
\boolval(0),
\boolval(1),
[
new CampaignCoupon(
new ProcessingCode('PC1'),
new CouponQuantity(100),
Country::FRANCE,
new WarningThresholdQuantity(100),
new PrintRules(maxPerCustomer: 3)
)
],
new BrandCode('AAA01')
$this->code,
$this->name,
$this->publication,
$this->printAllAtOnce,
$this->coupons,
$this->brandCode
);
// WHEN
@ -236,23 +179,18 @@ describe('campaign', function (): void {
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);
$coupons = ['PC1'];
$brandCode = new BrandCode('AAA01');
$id = CampaignId::generate();
$coupons = ['PC1'];
// WHEN / THEN
expect(fn (): Campaign => Campaign::hydrate(
$id,
$code,
$name,
$publication,
$printAllAtOnce,
$this->code,
$this->name,
$this->publication,
$this->printAllAtOnce,
$coupons,
$brandCode
$this->brandCode
))->toThrow(\DomainException::class, 'Campaign coupons must be CampaignCoupon instances.');
});
});

View file

@ -414,24 +414,20 @@ describe('coupon', function (): void {
$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),
$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,
);
// WHEN

View file

@ -16,23 +16,32 @@ use Ramsey\Uuid\Uuid;
covers(Stock::class);
describe('stock', function (): void {
beforeEach(function (): void {
$this->stockAmount = new StockAmount(10);
$this->campaignCode = new CampaignCode('AAA01');
$this->processingCode = new ProcessingCode('PC1');
$this->stockAdjustment = [new StockAdjustment(5, 'Inventory correction', ActorId::generate())];
});
it('hydrates a stock', 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())];
$id = StockId::generate();
// WHEN
$stock = Stock::hydrate($id, $stockAmount, $campaignCode, $processingCode, $stockAdjustment);
$stock = Stock::hydrate(
$id,
$this->stockAmount,
$this->campaignCode,
$this->processingCode,
$this->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->stockAmount)->toBe($this->stockAmount)
->and($stock->campaignCode)->toBe($this->campaignCode)
->and($stock->processingCode)->toBe($this->processingCode)
->and($stock->stockAdjustment)->toBe($this->stockAdjustment)
->and($stock->updatedAt)->toBeNull()
->and($stock->updatedAt())->toBeNull();
});
@ -40,9 +49,6 @@ describe('stock', function (): void {
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()),
@ -51,17 +57,17 @@ describe('stock', function (): void {
// WHEN
$stock = Stock::hydrate(
$id,
$stockAmount,
$campaignCode,
$processingCode,
$this->stockAmount,
$this->campaignCode,
$this->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->stockAmount)->toBe($this->stockAmount)
->and($stock->campaignCode)->toBe($this->campaignCode)
->and($stock->processingCode)->toBe($this->processingCode)
->and($stock->stockAdjustment)->toBe($stockAdjustment)
->and($stock->updatedAt)->toBeNull()
->and($stock->updatedAt())->toBeNull();
@ -69,19 +75,21 @@ describe('stock', function (): void {
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');
$id = StockId::generate();
// WHEN
$stock = Stock::hydrate($id, $stockAmount, $campaignCode, $processingCode);
$stock = Stock::hydrate(
$id,
$this->stockAmount,
$this->campaignCode,
$this->processingCode
);
// THEN
expect($stock->id())->toBe($id)
->and($stock->stockAmount)->toBe($stockAmount)
->and($stock->campaignCode)->toBe($campaignCode)
->and($stock->processingCode)->toBe($processingCode)
->and($stock->stockAmount)->toBe($this->stockAmount)
->and($stock->campaignCode)->toBe($this->campaignCode)
->and($stock->processingCode)->toBe($this->processingCode)
->and($stock->stockAdjustment)->toBe([])
->and($stock->updatedAt)->toBeNull()
->and($stock->updatedAt())->toBeNull();
@ -90,17 +98,14 @@ describe('stock', function (): void {
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,
$this->stockAmount,
$this->campaignCode,
$this->processingCode,
$stockAdjustment,
))->toThrow(\DomainException::class, 'Stock adjustment must be StockAdjustment instances.');
});
@ -112,10 +117,10 @@ describe('stock', function (): void {
// WHEN
$stock = Stock::hydrate(
StockId::generate(),
new StockAmount(10),
new CampaignCode('AAA01'),
new ProcessingCode('PC1'),
[new StockAdjustment(5, 'Inventory correction', ActorId::generate())],
$this->stockAmount,
$this->campaignCode,
$this->processingCode,
$this->stockAdjustment,
$updatedAt,
);
@ -130,10 +135,10 @@ describe('stock', function (): void {
$id = new StockId(Uuid::uuid7($expected));
$stock = Stock::hydrate(
$id,
new StockAmount(10),
new CampaignCode('AAA01'),
new ProcessingCode('PC1'),
[new StockAdjustment(5, 'Inventory correction', ActorId::generate())],
$this->stockAmount,
$this->campaignCode,
$this->processingCode,
$this->stockAdjustment
);
// WHEN

View file

@ -13,6 +13,10 @@ use Ramsey\Uuid\Uuid;
covers(Tag::class);
describe('tag', function (): void {
beforeEach(function (): void {
$this->value = new TagName('Name');
});
afterEach(function (): void {
CarbonImmutable::setTestNow();
});
@ -20,17 +24,16 @@ describe('tag', function (): void {
it('creates a tag', function (): void {
// GIVEN
$id = TagId::generate();
$value = new TagName('Name');
// WHEN
$tag = Tag::hydrate(
$id,
$value,
$this->value,
);
// THEN
expect($tag->id())->toBe($id)
->and($tag->value)->toBe($value)
->and($tag->value)->toBe($this->value)
->and($tag->updatedAt)->toBeNull()
->and($tag->updatedAt())->toBeNull();
});
@ -38,13 +41,12 @@ describe('tag', function (): void {
it('creates a tag with an explicit updatedAt date', function (): void {
// GIVEN
$id = TagId::generate();
$name = new TagName('Name');
$updatedAt = new \DateTimeImmutable('2026-09-22 10:00:00', new \DateTimeZone('UTC'));
// WHEN
$tag = Tag::hydrate(
$id,
$name,
$this->value,
$updatedAt
);
@ -54,31 +56,25 @@ describe('tag', function (): void {
});
it('creates a new tag with a freshly generated id', function (): void {
// GIVEN
$value = new TagName('Name');
// WHEN
// GIVEN / WHEN
$tag = Tag::create(
$value,
$this->value,
);
// THEN
expect($tag->id())->toBeInstanceOf(TagId::class)
->and($tag->value)->toBe($value)
->and($tag->value)->toBe($this->value)
->and($tag->updatedAt)->toBeNull()
->and($tag->updatedAt())->toBeNull();
});
it('generates a different id for each created tag', function (): void {
// GIVEN
$value = new TagName('Name');
// WHEN
// GIVEN / WHEN
$firstTag = Tag::create(
$value
$this->value
);
$secondTag = Tag::create(
$value
$this->value
);
// THEN
@ -91,7 +87,7 @@ describe('tag', function (): void {
$id = new TagId(Uuid::uuid7($expected));
$tag = Tag::hydrate(
$id,
new TagName('Name'),
$this->value,
);
// WHEN

View file

@ -9,22 +9,25 @@ use Domain\Models\ValueObjects\Actor\ActorDisplayName;
covers(ActorDisplayName::class);
describe('actor displayname', function (): void {
it('creates a valid actor displayname', function (): void {
// GIVEN
$value = 'Johnny';
beforeEach(function (): void {
$this->value = 'Johnny';
$this->emptyValue = '';
});
// WHEN
$name = new ActorDisplayName($value);
it('creates a valid actor displayname', function (): void {
// GIVEN / WHEN
$name = new ActorDisplayName(
$this->value
);
// THEN
expect($name->value)->toBe($value);
expect($name->value)->toBe($this->value);
});
it('rejects invalid actor displayname', function (): void {
// GIVEN
$value = '';
// GIVEN / THEN
expect(fn (): ActorDisplayName => new ActorDisplayName($value))->toThrow(\DomainException::class);
// GIVEN / WHEN / THEN
expect(fn (): ActorDisplayName => new ActorDisplayName(
$this->emptyValue
))->toThrow(\DomainException::class);
});
});

View file

@ -9,23 +9,26 @@ use Domain\Models\ValueObjects\Brand\BrandName;
covers(BrandName::class);
describe('brand name', function (): void {
it('creates a valid brand name', function (): void {
// GIVEN
$value = 'Name';
beforeEach(function (): void {
$this->value = 'Name';
$this->emptyValue = '';
});
// WHEN
$name = new BrandName($value);
it('creates a valid brand name', function (): void {
// GIVEN / WHEN
$name = new BrandName(
$this->value
);
// THEN
expect($name->value)->toBe('Name');
expect($name->value)->toBe($this->value);
});
it('rejects invalid brand name', function (): void {
// GIVEN
$value = '';
// GIVEN / THEN
expect(fn (): BrandName => new BrandName($value))->toThrow(\DomainException::class);
// GIVEN / WHEN / THEN
expect(fn (): BrandName => new BrandName(
$this->emptyValue
))->toThrow(\DomainException::class);
});
it('rejects a name that exceeds the maximum allowed length', function (): void {

View file

@ -9,22 +9,23 @@ use Domain\Models\ValueObjects\Campaign\CampaignCode;
covers(CampaignCode::class);
describe('campaign code', function (): void {
it('creates a valid campaign code', function (): void {
// GIVEN
$code = 'Name';
beforeEach(function (): void {
$this->code = 'Name';
$this->emptyCode = '';
});
// WHEN
$name = new CampaignCode($code);
it('creates a valid campaign code', function (): void {
// GIVEN / WHEN
$name = new CampaignCode($this->code);
// THEN
expect($name->code)->toBe($code);
expect($name->code)->toBe($this->code);
});
it('rejects invalid campaign code', function (): void {
// GIVEN
$code = '';
// GIVEN / THEN
expect(fn (): CampaignCode => new CampaignCode($code))->toThrow(\DomainException::class);
// GIVEN / WHEN / THEN
expect(fn (): CampaignCode => new CampaignCode(
$this->emptyCode
))->toThrow(\DomainException::class);
});
});

View file

@ -14,81 +14,75 @@ use Shared\Enums\Country;
covers(CampaignCoupon::class);
describe('campaign coupon', function (): void {
it('creates a valid campaign coupon', function (): void {
// GIVEN
$processingCode = new ProcessingCode('PC1');
$quantity = new CouponQuantity(10);
$availableIn = Country::FRANCE;
$warningThresholdQuantity = new WarningThresholdQuantity(100);
$printRules = new PrintRules(maxPerCustomer: 3);
beforeEach(function (): void {
$this->processingCode = new ProcessingCode('PC1');
$this->quantity = new CouponQuantity(10);
$this->availableIn = Country::FRANCE;
$this->warningThresholdQuantity = new WarningThresholdQuantity(100);
$this->printRules = new PrintRules(maxPerCustomer: 3);
});
// WHEN
$campaignCoupon = new CampaignCoupon($processingCode, $quantity, $availableIn, $warningThresholdQuantity, $printRules);
it('creates a valid campaign coupon', function (): void {
// GIVEN / WHEN
$campaignCoupon = new CampaignCoupon(
$this->processingCode,
$this->quantity,
$this->availableIn,
$this->warningThresholdQuantity,
$this->printRules
);
// THEN
expect($campaignCoupon->processingCode)->toBe($processingCode)
->and($campaignCoupon->quantity)->toBe($quantity)
->and($campaignCoupon->availableIn)->toBe($availableIn)
->and($campaignCoupon->warningThresholdQuantity)->toBe($warningThresholdQuantity)
->and($campaignCoupon->printRules)->toBe($printRules);
expect($campaignCoupon->processingCode)->toBe($this->processingCode)
->and($campaignCoupon->quantity)->toBe($this->quantity)
->and($campaignCoupon->availableIn)->toBe($this->availableIn)
->and($campaignCoupon->warningThresholdQuantity)->toBe($this->warningThresholdQuantity)
->and($campaignCoupon->printRules)->toBe($this->printRules);
});
it('creates a valid campaign coupon whitout warning threshold quantity', function (): void {
// GIVEN
$processingCode = new ProcessingCode('PC1');
$quantity = new CouponQuantity(10);
$availableIn = Country::FRANCE;
$printRules = new PrintRules(maxPerCustomer: 3);
// WHEN
// GIVEN / WHEN
$campaignCoupon = new CampaignCoupon(
processingCode: $processingCode,
quantity: $quantity,
availableIn: $availableIn,
printRules: $printRules
processingCode: $this->processingCode,
quantity: $this->quantity,
availableIn: $this->availableIn,
printRules: $this->printRules
);
// THEN
expect($campaignCoupon->processingCode)->toBe($processingCode)
->and($campaignCoupon->quantity)->toBe($quantity)
->and($campaignCoupon->availableIn)->toBe($availableIn)
->and($campaignCoupon->printRules)->toBe($printRules);
expect($campaignCoupon->processingCode)->toBe($this->processingCode)
->and($campaignCoupon->quantity)->toBe($this->quantity)
->and($campaignCoupon->availableIn)->toBe($this->availableIn)
->and($campaignCoupon->printRules)->toBe($this->printRules);
});
it('creates a valid campaign coupon whitout print rules', function (): void {
// GIVEN
$processingCode = new ProcessingCode('PC1');
$quantity = new CouponQuantity(10);
$availableIn = Country::FRANCE;
$warningThresholdQuantity = new WarningThresholdQuantity(100);
// WHEN
// GIVEN / WHEN
$campaignCoupon = new CampaignCoupon(
processingCode: $processingCode,
quantity: $quantity,
availableIn: $availableIn,
warningThresholdQuantity: $warningThresholdQuantity
processingCode: $this->processingCode,
quantity: $this->quantity,
availableIn: $this->availableIn,
warningThresholdQuantity: $this->warningThresholdQuantity
);
// THEN
expect($campaignCoupon->processingCode)->toBe($processingCode)
->and($campaignCoupon->quantity)->toBe($quantity)
->and($campaignCoupon->availableIn)->toBe($availableIn)
->and($campaignCoupon->warningThresholdQuantity)->toBe($warningThresholdQuantity);
expect($campaignCoupon->processingCode)->toBe($this->processingCode)
->and($campaignCoupon->quantity)->toBe($this->quantity)
->and($campaignCoupon->availableIn)->toBe($this->availableIn)
->and($campaignCoupon->warningThresholdQuantity)->toBe($this->warningThresholdQuantity);
});
it('creates a valid campaign coupon whitout warning threshold quantity and print rules', function (): void {
// GIVEN
$processingCode = new ProcessingCode('PC1');
$quantity = new CouponQuantity(10);
$availableIn = Country::FRANCE;
// WHEN
$campaignCoupon = new CampaignCoupon(processingCode: $processingCode, quantity: $quantity, availableIn: $availableIn);
// GIVEN / WHEN
$campaignCoupon = new CampaignCoupon(
processingCode: $this->processingCode,
quantity: $this->quantity,
availableIn: $this->availableIn
);
// THEN
expect($campaignCoupon->processingCode)->toBe($processingCode)
->and($campaignCoupon->quantity)->toBe($quantity)
->and($campaignCoupon->availableIn)->toBe($availableIn);
expect($campaignCoupon->processingCode)->toBe($this->processingCode)
->and($campaignCoupon->quantity)->toBe($this->quantity)
->and($campaignCoupon->availableIn)->toBe($this->availableIn);
});
});

View file

@ -9,22 +9,23 @@ use Domain\Models\ValueObjects\Campaign\CampaignName;
covers(CampaignName::class);
describe('campaign name', function (): void {
it('creates a valid campaign name', function (): void {
// GIVEN
$value = 'Name';
beforeEach(function (): void {
$this->value = 'Name';
$this->emptyValue = '';
});
// WHEN
$name = new CampaignName($value);
it('creates a valid campaign name', function (): void {
// GIVEN / WHEN
$name = new CampaignName($this->value);
// THEN
expect($name->value)->toBe($value);
expect($name->value)->toBe($this->value);
});
it('rejects invalid campaign name', function (): void {
// GIVEN
$value = '';
// GIVEN / THEN
expect(fn (): CampaignName => new CampaignName($value))->toThrow(\DomainException::class);
// GIVEN / WHEN / THEN
expect(fn (): CampaignName => new CampaignName(
$this->emptyValue
))->toThrow(\DomainException::class);
});
});

View file

@ -10,25 +10,34 @@ use Shared\ValueObjects\Email;
covers(Applicant::class);
describe('applicant', function (): void {
it('creates a valid applicant', function (): void {
// GIVEN
$firstname = 'John';
$lastname = 'Doe';
$email = new Email('john.doe@example.com');
beforeEach(function (): void {
$this->firstname = 'John';
$this->lastname = 'Doe';
$this->email = new Email('john.doe@example.com');
});
// WHEN
$applicant = new Applicant($firstname, $lastname, $email);
it('creates a valid applicant', function (): void {
// GIVEN / WHEN
$applicant = new Applicant(
$this->firstname,
$this->lastname,
$this->email
);
// THEN
expect($applicant)->toBeInstanceOf(Applicant::class)
->and($applicant->firstname)->toBe($firstname)
->and($applicant->lastname)->toBe($lastname)
->and($applicant->email)->toBe($email);
->and($applicant->firstname)->toBe($this->firstname)
->and($applicant->lastname)->toBe($this->lastname)
->and($applicant->email)->toBe($this->email);
});
it('rejects an empty or whitespace-only firstname', function (string $firstname): void {
// WHEN
$action = fn (): Applicant => new Applicant($firstname, 'Doe', new Email('john.doe@example.com'));
$action = fn (): Applicant => new Applicant(
$firstname,
$this->lastname,
$this->email
);
// THEN
expect($action)->toThrow(\DomainException::class, 'Applicant firstname cannot be empty.');
@ -42,7 +51,11 @@ describe('applicant', function (): void {
$firstname = str_repeat('a', 201);
// WHEN
$action = fn (): Applicant => new Applicant($firstname, 'Doe', new Email('john.doe@example.com'));
$action = fn (): Applicant => new Applicant(
$firstname,
$this->lastname,
$this->email
);
// THEN
expect($action)->toThrow(\DomainException::class, 'Applicant firstname exceeds the maximum allowed length.');
@ -53,7 +66,11 @@ describe('applicant', function (): void {
$firstname = str_repeat('a', 200);
// WHEN
$applicant = new Applicant($firstname, 'Doe', new Email('john.doe@example.com'));
$applicant = new Applicant(
$firstname,
$this->lastname,
$this->email
);
// THEN
expect(mb_strlen($applicant->firstname))->toBe(200);
@ -61,7 +78,11 @@ describe('applicant', function (): void {
it('rejects an empty or whitespace-only lastname', function (string $lastname): void {
// WHEN
$action = fn (): Applicant => new Applicant('John', $lastname, new Email('john.doe@example.com'));
$action = fn (): Applicant => new Applicant(
$this->firstname,
$lastname,
$this->email
);
// THEN
expect($action)->toThrow(\DomainException::class, 'Applicant lastname cannot be empty.');
@ -75,7 +96,11 @@ describe('applicant', function (): void {
$lastname = str_repeat('a', 201);
// WHEN
$action = fn (): Applicant => new Applicant('John', $lastname, new Email('john.doe@example.com'));
$action = fn (): Applicant => new Applicant(
$this->firstname,
$lastname,
$this->email
);
// THEN
expect($action)->toThrow(\DomainException::class, 'Applicant lastname exceeds the maximum allowed length.');
@ -86,7 +111,11 @@ describe('applicant', function (): void {
$lastname = str_repeat('a', 200);
// WHEN
$applicant = new Applicant('John', $lastname, new Email('john.doe@example.com'));
$applicant = new Applicant(
$this->firstname,
$lastname,
$this->email
);
// THEN
expect(mb_strlen($applicant->lastname))->toBe(200);

View file

@ -11,41 +11,43 @@ use Domain\Models\ValueObjects\CampaignRequest\CampaignRequestStatus;
covers(CampaignRequestFilters::class);
describe('campaign request filters', function (): void {
it('creates filters with both fields set', function (): void {
// GIVEN
$brandCode = new BrandCode('AAA01');
$status = CampaignRequestStatus::CREATED;
beforeEach(function (): void {
$this->brandCode = new BrandCode('AAA01');
$this->status = CampaignRequestStatus::CREATED;
});
// WHEN
$filters = new CampaignRequestFilters(brandCode: $brandCode, status: $status);
it('creates filters with both fields set', function (): void {
// GIVEN / WHEN
$filters = new CampaignRequestFilters(
brandCode: $this->brandCode,
status: $this->status
);
// THEN
expect($filters->brandCode)->toBe($brandCode)
->and($filters->status)->toBe($status);
expect($filters->brandCode)->toBe($this->brandCode)
->and($filters->status)->toBe($this->status);
});
it('creates filters with only brandCode set', function (): void {
// GIVEN
$brandCode = new BrandCode('AAA01');
// WHEN
$filters = new CampaignRequestFilters(brandCode: $brandCode);
// GIVEN / WHEN
$filters = new CampaignRequestFilters(
brandCode: $this->brandCode
);
// THEN
expect($filters->brandCode)->toBe($brandCode)
expect($filters->brandCode)->toBe($this->brandCode)
->and($filters->status)->toBeNull();
});
it('creates filters with only status set', function (): void {
// GIVEN
$status = CampaignRequestStatus::CREATED;
// WHEN
$filters = new CampaignRequestFilters(status: $status);
// GIVEN / WHEN
$filters = new CampaignRequestFilters(
status: $this->status
);
// THEN
expect($filters->brandCode)->toBeNull()
->and($filters->status)->toBe($status);
->and($filters->status)->toBe($this->status);
});
it('rejects filters without any field set', function (): void {

View file

@ -11,48 +11,48 @@ use Domain\Models\ValueObjects\CampaignRequest\CampaignRequestSortField;
covers(CampaignRequestSortCriteria::class);
describe('campaign request sort criteria', function (): void {
it('creates a valid campaign request sort criteria with one clause', function (): void {
// GIVEN
$clauses = [
beforeEach(function (): void {
$this->singleClauses = [
new CampaignRequestSortClause(field: CampaignRequestSortField::BrandCode, direction: \SortDirection::Ascending),
];
// WHEN
$campaignRequestSortCriteria = new CampaignRequestSortCriteria($clauses);
// THEN
expect($campaignRequestSortCriteria->clauses)->toBe($clauses);
});
it('creates a valid campaign request sort criteria with several clauses', function (): void {
// GIVEN
$clauses = [
$this->clauses = [
new CampaignRequestSortClause(field: CampaignRequestSortField::BrandCode, direction: \SortDirection::Ascending),
new CampaignRequestSortClause(field: CampaignRequestSortField::Status, direction: \SortDirection::Descending),
];
$this->invalidClauses = [CampaignRequestSortField::BrandCode];
$this->emptyClauses = [];
});
// WHEN
$campaignRequestSortCriteria = new CampaignRequestSortCriteria($clauses);
it('creates a valid campaign request sort criteria with one clause', function (): void {
// GIVEN / WHEN
$campaignRequestSortCriteria = new CampaignRequestSortCriteria(
$this->singleClauses
);
// THEN
expect($campaignRequestSortCriteria->clauses)->toBe($clauses);
expect($campaignRequestSortCriteria->clauses)->toBe($this->singleClauses);
});
it('creates a valid campaign request sort criteria with several clauses', function (): void {
// GIVEN / WHEN
$campaignRequestSortCriteria = new CampaignRequestSortCriteria(
$this->clauses
);
// THEN
expect($campaignRequestSortCriteria->clauses)->toBe($this->clauses);
});
it('rejects a campaign request sort criteria without clause', function (): void {
// GIVEN
$clauses = [];
// WHEN / THEN
expect(fn (): CampaignRequestSortCriteria => new CampaignRequestSortCriteria($clauses))
// GIVEN / WHEN / THEN
expect(fn (): CampaignRequestSortCriteria => new CampaignRequestSortCriteria($this->emptyClauses))
->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))
// GIVEN / WHEN / THEN
expect(fn (): CampaignRequestSortCriteria => new CampaignRequestSortCriteria($this->invalidClauses))
->toThrow(\DomainException::class, 'CampaignRequestSortCriteria clauses must be CampaignRequestSortClause instances.');
});
});

View file

@ -9,16 +9,17 @@ use Domain\Models\ValueObjects\CampaignRequest\RejectionReason;
covers(RejectionReason::class);
describe('rejection reason', function (): void {
it('creates a valid rejection reason', function (): void {
// GIVEN
$value = 'Missing legal documents.';
beforeEach(function (): void {
$this->value = 'Missing legal documents.';
});
// WHEN
$reason = new RejectionReason($value);
it('creates a valid rejection reason', function (): void {
// GIVEN / WHEN
$reason = new RejectionReason($this->value);
// THEN
expect($reason)->toBeInstanceOf(RejectionReason::class)
->and($reason->value)->toBe($value);
->and($reason->value)->toBe($this->value);
});
it('rejects an empty or whitespace-only reason', function (string $value): void {

View file

@ -11,47 +11,49 @@ use DateTime;
covers(CouponAvailabilityCalendar::class);
describe('coupon availability calendar', function (): void {
it('creates a valid coupon availability calendar with one period', function (): void {
// GIVEN
$periods = [
beforeEach(function (): void {
$this->singlePeriods = [
new Period(new DateTime('2026-01-01'), new DateTime('2026-02-01')),
];
// WHEN
$couponAvailabilityCalendar = new CouponAvailabilityCalendar($periods);
// THEN
expect($couponAvailabilityCalendar->periods)->toBe($periods);
});
it('creates a valid coupon availability calendar with several periods', function (): void {
// GIVEN
$periods = [
$this->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')),
];
$this->invalidPeriods = ['2026-08-25', '2026-10-31'];
$this->emptyPeriods = [];
});
// WHEN
$couponAvailabilityCalendar = new CouponAvailabilityCalendar($periods);
it('creates a valid coupon availability calendar with one period', function (): void {
// GIVEN / WHEN
$couponAvailabilityCalendar = new CouponAvailabilityCalendar(
$this->singlePeriods
);
// THEN
expect($couponAvailabilityCalendar->periods)->toBe($periods);
expect($couponAvailabilityCalendar->periods)->toBe($this->singlePeriods);
});
it('creates a valid coupon availability calendar with several periods', function (): void {
// GIVEN / WHEN
$couponAvailabilityCalendar = new CouponAvailabilityCalendar(
$this->periods
);
// THEN
expect($couponAvailabilityCalendar->periods)->toBe($this->periods);
});
it('rejects a coupon availability calendar without period', function (): void {
// GIVEN
$periods = [];
// GIVEN / THEN
expect(fn (): CouponAvailabilityCalendar => new CouponAvailabilityCalendar($periods))->toThrow(\DomainException::class);
// GIVEN / WHEN / THEN
expect(fn (): CouponAvailabilityCalendar => new CouponAvailabilityCalendar(
$this->emptyPeriods
))->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.');
// GIVEN / WHEN / THEN
expect(fn (): CouponAvailabilityCalendar => new CouponAvailabilityCalendar(
$this->invalidPeriods
))->toThrow(\DomainException::class, 'CouponAvailabilityCalendar periods must be Period instances.');
});
});

View file

@ -10,25 +10,29 @@ use Domain\Models\ValueObjects\Coupon\CouponLegalInformation;
covers(CouponLegalInformation::class);
describe('coupon legal information', function (): void {
it('creates a valid coupon legal information', function (): void {
// GIVEN
$legalText = 'Lorem ipsum dolor sit amet consectetur adipiscing elit.';
$forLocale = Country::FRANCE;
beforeEach(function (): void {
$this->legalText = 'Lorem ipsum dolor sit amet consectetur adipiscing elit.';
$this->emptyLegalText = '';
$this->forLocale = Country::FRANCE;
});
// WHEN
$couponLegalInformation = new CouponLegalInformation($legalText, $forLocale);
it('creates a valid coupon legal information', function (): void {
// GIVEN / WHEN
$couponLegalInformation = new CouponLegalInformation(
$this->legalText,
$this->forLocale
);
// THEN
expect($couponLegalInformation->legalText)->toBe($legalText)
->and($couponLegalInformation->forLocale)->toBe($forLocale);
expect($couponLegalInformation->legalText)->toBe($this->legalText)
->and($couponLegalInformation->forLocale)->toBe($this->forLocale);
});
it('rejects invalid coupon legal information', function (): void {
// GIVEN
$legalText = '';
$forLocale = Country::FRANCE;
// GIVEN / THEN
expect(fn (): CouponLegalInformation => new CouponLegalInformation($legalText, $forLocale))->toThrow(\DomainException::class);
// GIVEN / WHEN / THEN
expect(fn (): CouponLegalInformation => new CouponLegalInformation(
$this->emptyLegalText,
$this->forLocale
))->toThrow(\DomainException::class);
});
});

View file

@ -9,21 +9,28 @@ use Domain\Models\ValueObjects\Coupon\CouponQuantity;
covers(CouponQuantity::class);
describe('coupon quantity', function (): void {
it('creates a valid coupon quantity', function (): void {
// GIVEN
$quantity = 10;
beforeEach(function (): void {
$this->quantity = 10;
$this->zeroQuantity = 0;
$this->negativeQuantity = -1;
});
// WHEN
$couponQuantity = new CouponQuantity($quantity);
it('creates a valid coupon quantity', function (): void {
// GIVEN / WHEN
$couponQuantity = new CouponQuantity(
$this->quantity
);
// THEN
expect($couponQuantity)->toBeInstanceOf(CouponQuantity::class)
->and($couponQuantity->quantity)->toBe($quantity);
->and($couponQuantity->quantity)->toBe($this->quantity);
});
it('rejects a quantity of zero', function (): void {
// WHEN
$action = fn (): CouponQuantity => new CouponQuantity(0);
$action = fn (): CouponQuantity => new CouponQuantity(
$this->zeroQuantity
);
// THEN
expect($action)->toThrow(\DomainException::class, 'CouponQuantity quantity must be positive.');
@ -31,7 +38,9 @@ describe('coupon quantity', function (): void {
it('rejects a negative quantity', function (): void {
// WHEN
$action = fn (): CouponQuantity => new CouponQuantity(-1);
$action = fn (): CouponQuantity => new CouponQuantity(
$this->negativeQuantity
);
// THEN
expect($action)->toThrow(\DomainException::class, 'CouponQuantity quantity must be positive.');

View file

@ -10,25 +10,29 @@ use Shared\Enums\Country;
covers(CouponValidityInformation::class);
describe('coupon validity information', function (): void {
it('creates a valid coupon validity information', function (): void {
// GIVEN
$validityText = 'Lorem ipsum dolor sit amet consectetur adipiscing elit.';
$forLocale = Country::FRANCE;
beforeEach(function (): void {
$this->validityText = 'Lorem ipsum dolor sit amet consectetur adipiscing elit.';
$this->emptyValidityText = '';
$this->forLocale = Country::FRANCE;
});
// WHEN
$couponValidityInformation = new CouponValidityInformation($validityText, $forLocale);
it('creates a valid coupon validity information', function (): void {
// GIVEN / WHEN
$couponValidityInformation = new CouponValidityInformation(
$this->validityText,
$this->forLocale
);
// THEN
expect($couponValidityInformation->validityText)->toBe($validityText)
->and($couponValidityInformation->forLocale)->toBe($forLocale);
expect($couponValidityInformation->validityText)->toBe($this->validityText)
->and($couponValidityInformation->forLocale)->toBe($this->forLocale);
});
it('rejects invalid coupon validity information', function (): void {
// GIVEN
$validityText = '';
$forLocale = Country::FRANCE;
// GIVEN / THEN
expect(fn (): CouponValidityInformation => new CouponValidityInformation($validityText, $forLocale))->toThrow(\DomainException::class);
// GIVEN / WHEN / THEN
expect(fn (): CouponValidityInformation => new CouponValidityInformation(
$this->emptyValidityText,
$this->forLocale
))->toThrow(\DomainException::class);
});
});

View file

@ -10,25 +10,29 @@ use DateTime;
covers(CouponValidity::class);
describe('coupon validity', function (): void {
it('creates a valid coupon validity', function (): void {
// GIVEN
$days = 30;
$until = new DateTime('2026-11-30');
beforeEach(function (): void {
$this->days = 30;
$this->zeroDays = 0;
$this->until = new DateTime('2026-11-30');
});
// WHEN
$couponValidity = new CouponValidity($days, $until);
it('creates a valid coupon validity', function (): void {
// GIVEN / WHEN
$couponValidity = new CouponValidity(
$this->days,
$this->until
);
// THEN
expect($couponValidity->days)->toBe($days)
->and($couponValidity->until)->toBe($until);
expect($couponValidity->days)->toBe($this->days)
->and($couponValidity->until)->toBe($this->until);
});
it('rejects invalid coupon validity', function (): void {
// GIVEN
$days = 0;
$until = new DateTime('2026-11-30');
// GIVEN / THEN
expect(fn (): CouponValidity => new CouponValidity($days, $until))->toThrow(\DomainException::class);
// GIVEN / WHEN / THEN
expect(fn (): CouponValidity => new CouponValidity(
$this->zeroDays,
$this->until
))->toThrow(\DomainException::class);
});
});

View file

@ -9,22 +9,25 @@ use Domain\Models\ValueObjects\CouponName;
covers(CouponName::class);
describe('coupon name', function (): void {
it('creates a valid coupon name', function (): void {
// GIVEN
$value = 'Name';
beforeEach(function (): void {
$this->value = 'Name';
$this->emptyValue = '';
});
// WHEN
$name = new CouponName($value);
it('creates a valid coupon name', function (): void {
// GIVEN / WHEN
$name = new CouponName(
$this->value
);
// THEN
expect($name->value)->toBe($value);
expect($name->value)->toBe($this->value);
});
it('rejects invalid coupon name', function (): void {
// GIVEN
$value = '';
// GIVEN / THEN
expect(fn (): CouponName => new CouponName($value))->toThrow(\DomainException::class);
// GIVEN / WHEN / THEN
expect(fn (): CouponName => new CouponName(
$this->emptyValue
))->toThrow(\DomainException::class);
});
});

View file

@ -10,25 +10,29 @@ use Domain\Models\ValueObjects\GS1;
covers(GS1::class);
describe('gs1', function (): void {
it('creates a valid gs1', function (): void {
// GIVEN
$value = '1010100101';
$format = BarcodeFormat::EAN13;
beforeEach(function (): void {
$this->value = '1010100101';
$this->emptyValue = '';
$this->format = BarcodeFormat::EAN13;
});
it('creates a valid gs1', function (): void {
// WHEN
$GS1 = new GS1($value, $format);
$GS1 = new GS1(
$this->value,
$this->format
);
// THEN
expect($GS1->value)->toBe($value)
->and($GS1->format)->toBe($format);
expect($GS1->value)->toBe($this->value)
->and($GS1->format)->toBe($this->format);
});
it('rejects invalid gs1', function (): void {
// GIVEN
$value = '';
$format = BarcodeFormat::EAN13;
// GIVEN / THEN
expect(fn (): GS1 => new GS1($value, $format))->toThrow(\DomainException::class);
// GIVEN / WHEN / THEN
expect(fn (): GS1 => new GS1(
$this->emptyValue,
$this->format
))->toThrow(\DomainException::class);
});
});

View file

@ -9,22 +9,25 @@ use Domain\Models\ValueObjects\IncoCode;
covers(IncoCode::class);
describe('inco code', function (): void {
it('creates a valid inco code', function (): void {
// GIVEN
$value = 1010100101;
beforeEach(function (): void {
$this->value = 1010100101;
$this->zeroValue = 0;
});
// WHEN
$incoCode = new IncoCode($value);
it('creates a valid inco code', function (): void {
// GIVEN / WHEN
$incoCode = new IncoCode(
$this->value
);
// THEN
expect($incoCode->value)->toBe($value);
expect($incoCode->value)->toBe($this->value);
});
it('rejects invalid inco code', function (): void {
// GIVEN
$value = 0;
// GIVEN / THEN
expect(fn (): IncoCode => new IncoCode($value))->toThrow(\DomainException::class);
// GIVEN / WHEN / THEN
expect(fn (): IncoCode => new IncoCode(
$this->zeroValue
))->toThrow(\DomainException::class);
});
});

View file

@ -11,23 +11,33 @@ use Shared\ValueObjects\Money;
covers(MoneyAmount::class);
describe('money amount', function (): void {
it('wraps a Money value', function (): void {
// GIVEN
$money = new Money(Currency::EUR, 300);
beforeEach(function (): void {
$this->money = new Money(Currency::EUR, 300);
$this->secondMoney = new Money(Currency::EUR, 500);
});
// WHEN
$amount = new MoneyAmount($money);
it('wraps a Money value', function (): void {
// GIVEN / WHEN
$amount = new MoneyAmount(
$this->money
);
// THEN
expect($amount)->toBeInstanceOf(MoneyAmount::class)
->and($amount->value)->toBe($money);
->and($amount->value)->toBe($this->money);
});
it('implements equals by delegating to the wrapped Money', function (): void {
// GIVEN
$amount1 = new MoneyAmount(new Money(Currency::EUR, 300));
$amount2 = new MoneyAmount(new Money(Currency::EUR, 300));
$amount3 = new MoneyAmount(new Money(Currency::EUR, 500));
// GIVEN / WHEN
$amount1 = new MoneyAmount(
$this->money
);
$amount2 = new MoneyAmount(
$this->money
);
$amount3 = new MoneyAmount(
$this->secondMoney
);
// THEN
expect($amount1->equals($amount2))->toBeTrue()
@ -36,8 +46,10 @@ describe('money amount', function (): void {
});
it('converts to string by delegating to the wrapped Money', function (): void {
// GIVEN
$amount = new MoneyAmount(new Money(Currency::EUR, 300));
// GIVEN / WHEN
$amount = new MoneyAmount(
$this->money
);
// THEN
expect((string) $amount)->toBe((string) $amount->value);

View file

@ -9,32 +9,48 @@ use Domain\Models\ValueObjects\PrintRules;
covers(PrintRules::class);
describe('print rules', function (): void {
beforeEach(function (): void {
$this->maxPerCustomer = 3;
$this->minMaxPerCustomer = 1;
$this->zeroMaxPerCustomer = 0;
$this->cooldownDays = 30;
$this->minCooldownDays = 1;
$this->zeroCooldownDays = 0;
});
it('creates print rules with both fields set', function (): void {
// WHEN
$printRules = new PrintRules(maxPerCustomer: 3, cooldownDays: 30);
// GIVEN / WHEN
$printRules = new PrintRules(
maxPerCustomer: $this->maxPerCustomer,
cooldownDays: $this->cooldownDays
);
// THEN
expect($printRules)->toBeInstanceOf(PrintRules::class)
->and($printRules->maxPerCustomer)->toBe(3)
->and($printRules->cooldownDays)->toBe(30);
->and($printRules->maxPerCustomer)->toBe($this->maxPerCustomer)
->and($printRules->cooldownDays)->toBe($this->cooldownDays);
});
it('creates print rules with only maxPerCustomer set', function (): void {
// WHEN
$printRules = new PrintRules(maxPerCustomer: 5);
// GIVEN / WHEN
$printRules = new PrintRules(
maxPerCustomer: $this->maxPerCustomer
);
// THEN
expect($printRules->maxPerCustomer)->toBe(5)
expect($printRules->maxPerCustomer)->toBe($this->maxPerCustomer)
->and($printRules->cooldownDays)->toBeNull();
});
it('creates print rules with only cooldownDays set', function (): void {
// WHEN
$printRules = new PrintRules(cooldownDays: 45);
// GIVEN / WHEN
$printRules = new PrintRules(
cooldownDays: $this->cooldownDays
);
// THEN
expect($printRules->maxPerCustomer)->toBeNull()
->and($printRules->cooldownDays)->toBe(45);
->and($printRules->cooldownDays)->toBe($this->cooldownDays);
});
it('rejects print rules without any field set', function (): void {
@ -44,29 +60,28 @@ describe('print rules', function (): void {
});
it('accepts the minimum value of 1 for both fields', function (): void {
// WHEN
$printRules = new PrintRules(maxPerCustomer: 1, cooldownDays: 1);
// GIVEN / WHEN
$printRules = new PrintRules(
maxPerCustomer: $this->minMaxPerCustomer,
cooldownDays: $this->minCooldownDays
);
// THEN
expect($printRules->maxPerCustomer)->toBe(1)
->and($printRules->cooldownDays)->toBe(1);
expect($printRules->maxPerCustomer)->toBe($this->minMaxPerCustomer)
->and($printRules->cooldownDays)->toBe($this->minCooldownDays);
});
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.');
// GIVEN / WHEN / THEN
expect(fn (): PrintRules => new PrintRules(
maxPerCustomer: $this->zeroMaxPerCustomer
))->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.');
// GIVEN / WHEN / THEN
expect(fn (): PrintRules => new PrintRules(
cooldownDays: $this->zeroCooldownDays
))->toThrow(\InvalidArgumentException::class, 'Cooldown days must be greater than 0.');
});
});

View file

@ -9,22 +9,25 @@ use Domain\Models\ValueObjects\ProcessingCode;
covers(ProcessingCode::class);
describe('processing code', function (): void {
it('creates a valid processing code', function (): void {
// GIVEN
$value = '1010100101';
beforeEach(function (): void {
$this->value = '1010100101';
$this->emptyValue = '';
});
// WHEN
$processingCode = new ProcessingCode($value);
it('creates a valid processing code', function (): void {
// GIVEN / WHEN
$processingCode = new ProcessingCode(
$this->value
);
// THEN
expect($processingCode->value)->toBe($value);
expect($processingCode->value)->toBe($this->value);
});
it('rejects invalid processing code', function (): void {
// GIVEN
$value = '';
// GIVEN / THEN
expect(fn (): ProcessingCode => new ProcessingCode($value))->toThrow(\DomainException::class);
// GIVEN / WHEN / THEN
expect(fn (): ProcessingCode => new ProcessingCode(
$this->emptyValue
))->toThrow(\DomainException::class);
});
});

View file

@ -10,17 +10,21 @@ use DateTime;
covers(Period::class);
describe('period', function (): void {
it('creates a valid period', function (): void {
// GIVEN
$start = new DateTime('2026-01-01');
$end = new DateTime('2026-01-31');
beforeEach(function (): void {
$this->start = new DateTime('2026-01-01');
$this->end = new DateTime('2026-01-31');
});
// WHEN
$period = new Period($start, $end);
it('creates a valid period', function (): void {
// GIVEN / WHEN
$period = new Period(
$this->start,
$this->end
);
// THEN
expect($period)->toBeInstanceOf(Period::class)
->and($period->start)->toBe($start)
->and($period->end)->toBe($end);
->and($period->start)->toBe($this->start)
->and($period->end)->toBe($this->end);
});
});

View file

@ -9,29 +9,38 @@ use Domain\Models\ValueObjects\RequestedCoupon\RequestedCouponQuantity;
covers(RequestedCouponQuantity::class);
describe('quantity', function (): void {
it('creates a valid quantity', function (): void {
// GIVEN
$value = 10;
beforeEach(function (): void {
$this->value = 10;
$this->zeroValue = 0;
$this->negativeValue = -1;
});
// WHEN
$quantity = new RequestedCouponQuantity($value);
it('creates a valid quantity', function (): void {
// GIVEN / WHEN
$quantity = new RequestedCouponQuantity(
$this->value
);
// THEN
expect($quantity)->toBeInstanceOf(RequestedCouponQuantity::class)
->and($quantity->value)->toBe($value);
->and($quantity->value)->toBe($this->value);
});
it('rejects a quantity of zero', function (): void {
// WHEN
$action = fn (): RequestedCouponQuantity => new RequestedCouponQuantity(0);
// GIVEN / WHEN
$action = fn (): RequestedCouponQuantity => new RequestedCouponQuantity(
$this->zeroValue
);
// THEN
expect($action)->toThrow(\DomainException::class, 'Quantity should be more than 0.');
});
it('rejects a negative quantity', function (): void {
// WHEN
$action = fn (): RequestedCouponQuantity => new RequestedCouponQuantity(-1);
// GIVEN / WHEN
$action = fn (): RequestedCouponQuantity => new RequestedCouponQuantity(
$this->negativeValue
);
// THEN
expect($action)->toThrow(\DomainException::class, 'Quantity should be more than 0.');

View file

@ -10,45 +10,74 @@ use Domain\Models\ValueObjects\Stock\StockAdjustment;
covers(StockAdjustment::class);
describe('stock adjustment', function (): void {
beforeEach(function (): void {
$this->quantity = 10;
$this->zeroQuantity = 0;
$this->negativeQuantity = -5;
$this->reason = 'Inventory correction';
});
it('creates a valid stock adjustment', function (): void {
// GIVEN
$quantity = 10;
$reason = 'Inventory correction';
$actorId = ActorId::generate();
$actorId = ActorId::generate();
// WHEN
$adjustment = new StockAdjustment($quantity, $reason, $actorId);
$adjustment = new StockAdjustment(
$this->quantity,
$this->reason,
$actorId
);
// THEN
expect($adjustment)->toBeInstanceOf(StockAdjustment::class)
->and($adjustment->quantity)->toBe($quantity)
->and($adjustment->reason)->toBe($reason)
->and($adjustment->quantity)->toBe($this->quantity)
->and($adjustment->reason)->toBe($this->reason)
->and($adjustment->actorId)->toBe($actorId)
->and($adjustment->createdAt)->toBeNull();
});
it('creates a stock adjustment with an explicit createdAt date', function (): void {
// GIVEN
$actorId = ActorId::generate();
$createdAt = new \DateTimeImmutable('2026-06-15 12:00:00', new \DateTimeZone('UTC'));
// WHEN
$adjustment = new StockAdjustment(10, 'Inventory correction', ActorId::generate(), $createdAt);
$adjustment = new StockAdjustment(
$this->quantity,
$this->reason,
$actorId,
$createdAt
);
// THEN
expect($adjustment->createdAt)->toBe($createdAt);
});
it('rejects a quantity of zero', function (): void {
// GIVEN
$actorId = ActorId::generate();
// WHEN
$action = fn (): StockAdjustment => new StockAdjustment(0, 'Inventory correction', ActorId::generate());
$action = fn (): StockAdjustment => new StockAdjustment(
$this->zeroQuantity,
$this->reason,
$actorId
);
// THEN
expect($action)->toThrow(\DomainException::class, 'Stock Amount should be more than 0.');
});
it('rejects a negative quantity', function (): void {
// GIVEN
$actorId = ActorId::generate();
// WHEN
$action = fn (): StockAdjustment => new StockAdjustment(-5, 'Inventory correction', ActorId::generate());
$action = fn (): StockAdjustment => new StockAdjustment(
$this->negativeQuantity,
$this->reason,
$actorId
);
// THEN
expect($action)->toThrow(\DomainException::class, 'Stock Amount should be more than 0.');

View file

@ -9,29 +9,38 @@ use Domain\Models\ValueObjects\Stock\StockAmount;
covers(StockAmount::class);
describe('stock amount', function (): void {
it('creates a valid stock amount', function (): void {
// GIVEN
$value = 10;
beforeEach(function (): void {
$this->value = 10;
$this->zeroValue = 0;
$this->negativeValue = -1;
});
// WHEN
$amount = new StockAmount($value);
it('creates a valid stock amount', function (): void {
// GIVEN / WHEN
$amount = new StockAmount(
$this->value
);
// THEN
expect($amount)->toBeInstanceOf(StockAmount::class)
->and($amount->value)->toBe($value);
->and($amount->value)->toBe($this->value);
});
it('accepts a zero value', function (): void {
// WHEN
$amount = new StockAmount(0);
// GIVEN / WHEN
$amount = new StockAmount(
$this->zeroValue
);
// THEN
expect($amount->value)->toBe(0);
expect($amount->value)->toBe($this->zeroValue);
});
it('rejects a negative value', function (): void {
// WHEN
$action = fn (): StockAmount => new StockAmount(-1);
// GIVEN / WHEN
$action = fn (): StockAmount => new StockAmount(
$this->negativeValue
);
// THEN
expect($action)->toThrow(\DomainException::class, 'Stock Amount should be positive.');

View file

@ -9,22 +9,25 @@ use Domain\Models\ValueObjects\Tag\TagName;
covers(TagName::class);
describe('tag name', function (): void {
it('creates a valid tag name', function (): void {
// GIVEN
$value = 'Name';
beforeEach(function (): void {
$this->value = 'Name';
$this->emptyValue = '';
});
// WHEN
$name = new TagName($value);
it('creates a valid tag name', function (): void {
// GIVEN / WHEN
$name = new TagName(
$this->value
);
// THEN
expect($name->value)->toBe($value);
expect($name->value)->toBe($this->value);
});
it('rejects invalid tag name', function (): void {
// GIVEN
$value = '';
// GIVEN / THEN
expect(fn (): TagName => new TagName($value))->toThrow(\DomainException::class);
// GIVEN / WHEN / THEN
expect(fn (): TagName => new TagName(
$this->emptyValue
))->toThrow(\DomainException::class);
});
});

View file

@ -9,29 +9,38 @@ use Domain\Models\ValueObjects\WarningThresholdQuantity;
covers(WarningThresholdQuantity::class);
describe('warning threshold quantity', function (): void {
it('creates a valid warning threshold quantity', function (): void {
// GIVEN
$value = 10;
beforeEach(function (): void {
$this->value = 10;
$this->zeroValue = 0;
$this->negativeValue = -1;
});
// WHEN
$threshold = new WarningThresholdQuantity($value);
it('creates a valid warning threshold quantity', function (): void {
// GIVEN / WHEN
$warningThresholdQuantity = new WarningThresholdQuantity(
$this->value
);
// THEN
expect($threshold)->toBeInstanceOf(WarningThresholdQuantity::class)
->and($threshold->value)->toBe($value);
expect($warningThresholdQuantity)->toBeInstanceOf(WarningThresholdQuantity::class)
->and($warningThresholdQuantity->value)->toBe($this->value);
});
it('accepts a zero value, meaning no warning', function (): void {
// WHEN
$threshold = new WarningThresholdQuantity(0);
// GIVEN / WHEN
$warningThresholdQuantity = new WarningThresholdQuantity(
$this->zeroValue
);
// THEN
expect($threshold->value)->toBe(0);
expect($warningThresholdQuantity->value)->toBe($this->zeroValue);
});
it('rejects a negative value', function (): void {
// WHEN
$action = fn (): WarningThresholdQuantity => new WarningThresholdQuantity(-1);
// GIVEN / WHEN
$action = fn (): WarningThresholdQuantity => new WarningThresholdQuantity(
$this->negativeValue
);
// THEN
expect($action)->toThrow(\DomainException::class, 'Warning threshold quantity cannot be negative.');