From 680c71ea87cfa63a4f73e061fffd7f48eb8c537e Mon Sep 17 00:00:00 2001 From: Alexis Gousseau Date: Mon, 12 Oct 2026 17:08:07 +0200 Subject: [PATCH] =?UTF-8?q?test:=20=E2=9C=85=20update=20on=20coupon=20Enti?= =?UTF-8?q?ty=20and=20related=20VO?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../UseCases/Brand/Create/CreateBrandTest.php | 3 +- .../UseCases/Brand/Update/UpdateBrandTest.php | 4 +- .../Create/CreateCampaignRequestTest.php | 66 +- .../Find/FindCampaignRequestTest.php | 133 ++-- .../List/ListCampaignsRequestTest.php | 192 ++--- .../Reject/RejectCampaignRequestTest.php | 82 +-- tests/Unit/Domain/Enums/BarcodeFormatTest.php | 23 - .../BrandCodeAlreadyExistsExceptionTest.php | 24 - .../Unit/Domain/Models/Entities/ActorTest.php | 20 +- .../Unit/Domain/Models/Entities/BrandTest.php | 160 ++--- .../Models/Entities/CampaignRequestTest.php | 279 +++++--- .../Domain/Models/Entities/CampaignTest.php | 232 +++--- .../Domain/Models/Entities/CouponTest.php | 672 ++++++++++-------- .../Unit/Domain/Models/Entities/StockTest.php | 100 ++- .../Actor/ActorDisplayNameTest.php | 19 +- .../ValueObjects/Brand/BrandNameTest.php | 21 +- .../Brand/BrandSortCriteriaTest.php | 34 + .../Campaign/CampaignCodeTest.php | 19 +- .../Campaign/CampaignCouponArrayTest.php | 57 ++ .../Campaign/CampaignCouponTest.php | 66 +- .../Campaign/CampaignNameTest.php | 19 +- .../CampaignRequest/ApplicantTest.php | 78 +- .../CampaignRequestFiltersTest.php | 28 +- .../CampaignRequestSortClauseTest.php | 25 + .../CampaignRequestSortCriteriaTest.php | 44 +- .../CampaignRequest/RejectionReasonTest.php | 14 +- .../Coupon/CouponAvailabilityCalendarTest.php | 50 +- .../Coupon/CouponDiffusionTest.php | 10 +- .../Coupon/CouponIncoCodeTest.php | 28 + .../CouponLegalInformationArrayTest.php | 88 +++ .../Coupon/CouponLegalInformationTest.php | 36 +- .../Coupon/CouponMovementArrayTest.php | 61 ++ .../Coupon/CouponMovementTest.php | 47 ++ .../ValueObjects/Coupon/CouponNameTest.php | 28 + .../Coupon/CouponProcessingCode.php | 45 ++ .../Coupon/CouponProcessingCodeArrayTest.php | 88 +++ .../Coupon/CouponProcessingCodeTest.php | 93 +++ .../Coupon/CouponProductArrayTest.php | 67 ++ .../Coupon/CouponQuantityTest.php | 28 +- .../CouponValidityInformationArrayTest.php | 88 +++ .../Coupon/CouponValidityInformationTest.php | 24 +- .../Coupon/CouponValidityTest.php | 38 - .../Models/ValueObjects/CouponNameTest.php | 33 - .../Domain/Models/ValueObjects/GS1Test.php | 38 - .../Models/ValueObjects/IncoCodeTest.php | 33 - .../Models/ValueObjects/MoneyAmountTest.php | 60 +- .../Models/ValueObjects/PrintRulesTest.php | 54 +- .../ValueObjects/ProcessingCodeTest.php | 33 - .../Models/ValueObjects/RawPictureTest.php | 2 +- .../RequestedCoupon/PeriodTest.php | 19 +- .../RequestedCouponQuantityTest.php | 28 +- .../RequestedCoupon/RequestedCouponTest.php | 406 ++++++----- .../Stock/StockAdjustmentTest.php | 46 +- .../ValueObjects/Stock/StockAmountTest.php | 30 +- .../{Domain => Shared}/Enums/CountryTest.php | 11 +- tests/Unit/Shared/Helpers/DatesHelperTest.php | 4 +- tests/Unit/Shared/Helpers/UuidHelperTest.php | 16 +- .../Pagination/PageNumberTest.php | 2 +- .../ValueObjects/Pagination/PageSizeTest.php | 4 +- 59 files changed, 2351 insertions(+), 1701 deletions(-) delete mode 100644 tests/Unit/Domain/Enums/BarcodeFormatTest.php delete mode 100644 tests/Unit/Domain/Exceptions/BrandCodeAlreadyExistsExceptionTest.php create mode 100644 tests/Unit/Domain/Models/ValueObjects/Brand/BrandSortCriteriaTest.php create mode 100644 tests/Unit/Domain/Models/ValueObjects/Campaign/CampaignCouponArrayTest.php create mode 100644 tests/Unit/Domain/Models/ValueObjects/CampaignRequest/CampaignRequestSortClauseTest.php create mode 100644 tests/Unit/Domain/Models/ValueObjects/Coupon/CouponIncoCodeTest.php create mode 100644 tests/Unit/Domain/Models/ValueObjects/Coupon/CouponLegalInformationArrayTest.php create mode 100644 tests/Unit/Domain/Models/ValueObjects/Coupon/CouponMovementArrayTest.php create mode 100644 tests/Unit/Domain/Models/ValueObjects/Coupon/CouponMovementTest.php create mode 100644 tests/Unit/Domain/Models/ValueObjects/Coupon/CouponNameTest.php create mode 100644 tests/Unit/Domain/Models/ValueObjects/Coupon/CouponProcessingCode.php create mode 100644 tests/Unit/Domain/Models/ValueObjects/Coupon/CouponProcessingCodeArrayTest.php create mode 100644 tests/Unit/Domain/Models/ValueObjects/Coupon/CouponProcessingCodeTest.php create mode 100644 tests/Unit/Domain/Models/ValueObjects/Coupon/CouponProductArrayTest.php create mode 100644 tests/Unit/Domain/Models/ValueObjects/Coupon/CouponValidityInformationArrayTest.php delete mode 100644 tests/Unit/Domain/Models/ValueObjects/Coupon/CouponValidityTest.php delete mode 100644 tests/Unit/Domain/Models/ValueObjects/CouponNameTest.php delete mode 100644 tests/Unit/Domain/Models/ValueObjects/GS1Test.php delete mode 100644 tests/Unit/Domain/Models/ValueObjects/IncoCodeTest.php delete mode 100644 tests/Unit/Domain/Models/ValueObjects/ProcessingCodeTest.php rename tests/Unit/{Domain => Shared}/Enums/CountryTest.php (57%) diff --git a/tests/Unit/Application/UseCases/Brand/Create/CreateBrandTest.php b/tests/Unit/Application/UseCases/Brand/Create/CreateBrandTest.php index aeaa717..d7e81b6 100644 --- a/tests/Unit/Application/UseCases/Brand/Create/CreateBrandTest.php +++ b/tests/Unit/Application/UseCases/Brand/Create/CreateBrandTest.php @@ -7,7 +7,6 @@ namespace Tests\Unit\Application\UseCases\Brand\Create; use Application\UseCases\Brand\Create\CreateBrand; use Application\UseCases\Brand\Create\Input; use Application\UseCases\Brand\Create\Output; -use Domain\Exceptions\BrandCodeAlreadyExistsException; use Domain\Models\Entities\Brand; use Domain\Models\ValueObjects\Brand\BrandCode; use Domain\Models\ValueObjects\Brand\BrandName; @@ -77,7 +76,7 @@ describe('create brand use case validation rules', function (): void { $action = fn (): Output => $useCase->execute($input); // THEN - expect($action)->toThrow(BrandCodeAlreadyExistsException::class); + expect($action)->toThrow(\DomainException::class); }); }); diff --git a/tests/Unit/Application/UseCases/Brand/Update/UpdateBrandTest.php b/tests/Unit/Application/UseCases/Brand/Update/UpdateBrandTest.php index e18077e..c2bbeb4 100644 --- a/tests/Unit/Application/UseCases/Brand/Update/UpdateBrandTest.php +++ b/tests/Unit/Application/UseCases/Brand/Update/UpdateBrandTest.php @@ -48,7 +48,7 @@ describe('update brand use case', function () use ($existingBrand): void { && $saved->contact === $newContact && $saved->code === $brand->code && $saved->name === $brand->name - && $saved->updatedAt !== null, + && $saved->updatedAt instanceof \DateTimeInterface, )); $useCase = new UpdateBrand($repository); @@ -77,7 +77,7 @@ describe('update brand use case', function () use ($existingBrand): void { && $saved->rcs === $brand->rcs && $saved->code === $brand->code && $saved->name === $brand->name - && $saved->updatedAt !== null, + && $saved->updatedAt instanceof \DateTimeInterface, )); $useCase = new UpdateBrand($repository); diff --git a/tests/Unit/Application/UseCases/CampaignRequest/Create/CreateCampaignRequestTest.php b/tests/Unit/Application/UseCases/CampaignRequest/Create/CreateCampaignRequestTest.php index efd146e..99d8350 100644 --- a/tests/Unit/Application/UseCases/CampaignRequest/Create/CreateCampaignRequestTest.php +++ b/tests/Unit/Application/UseCases/CampaignRequest/Create/CreateCampaignRequestTest.php @@ -8,21 +8,26 @@ use Application\UseCases\CampaignRequest\Create\CreateCampaignRequest; use Application\UseCases\CampaignRequest\Create\Input; use Application\UseCases\CampaignRequest\Create\Output; use DateTime; -use Domain\Exceptions\BrandNotFoundException; use Domain\Models\Entities\CampaignRequest; use Domain\Models\ValueObjects\Brand\BrandCode; use Domain\Models\ValueObjects\Campaign\CampaignName; use Domain\Models\ValueObjects\CampaignRequest\Applicant; use Domain\Models\ValueObjects\CampaignRequest\CampaignRequestStatus; use Domain\Models\ValueObjects\CampaignRequest\CampaignRequestType; -use Domain\Models\ValueObjects\Coupon\CouponValidity; +use Domain\Models\ValueObjects\Coupon\CouponIncoCode; +use Domain\Models\ValueObjects\Coupon\CouponLegalInformation; +use Domain\Models\ValueObjects\Coupon\CouponLegalInformationArray; +use Domain\Models\ValueObjects\Coupon\CouponName; +use Domain\Models\ValueObjects\Coupon\CouponPicture; +use Domain\Models\ValueObjects\Coupon\CouponProcessingCode; +use Domain\Models\ValueObjects\Coupon\CouponProcessingCodeArray; +use Domain\Models\ValueObjects\Coupon\CouponProductArray; use Domain\Models\ValueObjects\Coupon\CouponValidityInformation; -use Domain\Models\ValueObjects\CouponName; -use Domain\Models\ValueObjects\IncoCode; +use Domain\Models\ValueObjects\Coupon\CouponValidityInformationArray; use Domain\Models\ValueObjects\MoneyAmount; use Domain\Models\ValueObjects\Period; +use Domain\Models\ValueObjects\PicturePath; use Domain\Models\ValueObjects\PrintRules; -use Domain\Models\ValueObjects\ProcessingCode; use Domain\Models\ValueObjects\RequestedCoupon\RequestedCoupon; use Domain\Models\ValueObjects\RequestedCoupon\RequestedCouponQuantity; use Domain\Ports\Repositories\BrandRepository; @@ -34,33 +39,44 @@ use Shared\ValueObjects\Money; covers(CreateCampaignRequest::class, Input::class, Output::class); -beforeEach(function (): void { - $this->input = new Input( +describe('create campaign request use case', function (): void { + $makeInput = fn (): Input => new Input( brandCode: new BrandCode('AAA01'), name: new CampaignName('Campaign name'), applicant: new Applicant('John', 'Doe', new Email('john.doe@example.com')), campaignType: CampaignRequestType::CLUB, requestedCoupon: new RequestedCoupon( - name: new CouponName('Coupon A'), + couponName: new CouponName('Coupon A'), quantity: new RequestedCouponQuantity(100), diffusion: new Period(new DateTime('2026-01-01'), new DateTime('2026-02-01')), moneyAmount: new MoneyAmount(new Money(Currency::EUR, 300)), - processingCode: new ProcessingCode('PC1'), - couponValidity: new CouponValidity(30, new DateTime('2026-02-01')), - couponValidityInformation: [ + couponPicture: new CouponPicture( + new PicturePath('files/product.jpg'), + new PicturePath('files/thumbnail.jpg'), + new PicturePath('files/mobileThumbnail.jpg') + ), + couponProcessingCodes: new CouponProcessingCodeArray([ + new CouponProcessingCode('ABC1234567890', Country::FRANCE), + ]), + couponValidityInformations: new CouponValidityInformationArray([ new CouponValidityInformation('Lorem ipsum dolor sit amet consectetur adipiscing elit.', Country::FRANCE), - ], - products: [new IncoCode(35784), new IncoCode(15522)], + new CouponValidityInformation('Lorem ipsum dolor sit amet consectetur adipiscing elit.', Country::BENELUX) + ]), + couponLegalInformations: new CouponLegalInformationArray([ + new CouponLegalInformation('Lorem ipsum dolor sit amet consectetur adipiscing elit.', Country::FRANCE), + new CouponLegalInformation('Lorem ipsum dolor sit amet consectetur adipiscing elit.', Country::BENELUX) + ]), + couponProducts: new CouponProductArray([new CouponIncoCode(35784), new CouponIncoCode(15522)]), printRules: new PrintRules(maxPerCustomer: 3) ), ); -}); -describe('create campaign request use case', function (): void { - it('creates a campaign request when the brand exists', function (): void { + it('creates a campaign request when the brand exists', function () use ($makeInput): void { // GIVEN + $input = $makeInput(); + $brandRepository = \Mockery::mock(BrandRepository::class); - $brandRepository->shouldReceive('existsByCode')->once()->with($this->input->brandCode)->andReturn(true); + $brandRepository->shouldReceive('existsByCode')->once()->with($input->brandCode)->andReturn(true); $campaignRequestRepository = \Mockery::mock(CampaignRequestRepository::class); $campaignRequestRepository->shouldReceive('save')->once()->with(\Mockery::type(CampaignRequest::class)); @@ -68,20 +84,20 @@ describe('create campaign request use case', function (): void { $useCase = new CreateCampaignRequest($campaignRequestRepository, $brandRepository); // WHEN - $result = $useCase->execute($this->input); + $result = $useCase->execute($input); // THEN expect($result)->toBeInstanceOf(Output::class) ->and($result->status)->toBe(CampaignRequestStatus::CREATED) - ->and($result->brandCode)->toBe($this->input->brandCode); + ->and($result->brandCode)->toBe($input->brandCode); }); -}); -describe('create campaign request use case validation rules', function (): void { - it('throws when the brand does not exist', function (): void { + it('throws when the brand does not exist', function () use ($makeInput): void { // GIVEN + $input = $makeInput(); + $brandRepository = \Mockery::mock(BrandRepository::class); - $brandRepository->shouldReceive('existsByCode')->once()->with($this->input->brandCode)->andReturn(false); + $brandRepository->shouldReceive('existsByCode')->once()->with($input->brandCode)->andReturn(false); $campaignRequestRepository = \Mockery::mock(CampaignRequestRepository::class); $campaignRequestRepository->shouldNotReceive('save'); @@ -89,9 +105,9 @@ describe('create campaign request use case validation rules', function (): void $useCase = new CreateCampaignRequest($campaignRequestRepository, $brandRepository); // WHEN - $action = fn (): Output => $useCase->execute($this->input); + $action = fn (): Output => $useCase->execute($input); // THEN - expect($action)->toThrow(BrandNotFoundException::class); + expect($action)->toThrow(\DomainException::class); }); }); diff --git a/tests/Unit/Application/UseCases/CampaignRequest/Find/FindCampaignRequestTest.php b/tests/Unit/Application/UseCases/CampaignRequest/Find/FindCampaignRequestTest.php index 912dc8d..84f7b5b 100644 --- a/tests/Unit/Application/UseCases/CampaignRequest/Find/FindCampaignRequestTest.php +++ b/tests/Unit/Application/UseCases/CampaignRequest/Find/FindCampaignRequestTest.php @@ -10,8 +10,6 @@ use Application\UseCases\CampaignRequest\Find\Output\CampaignRequestFound; use Application\UseCases\CampaignRequest\Find\Output\CampaignRequestNotFound; use Application\UseCases\CampaignRequest\Find\Output\Output; use DateTime; -use Domain\Enums\BarcodeFormat; -use Domain\Exceptions\BrandNotFoundException; use Domain\Models\Entities\Brand; use Domain\Models\Entities\CampaignRequest; use Domain\Models\ValueObjects\Brand\BrandCode; @@ -23,16 +21,20 @@ use Domain\Models\ValueObjects\CampaignRequest\CampaignRequestId; use Domain\Models\ValueObjects\CampaignRequest\CampaignRequestStatus; use Domain\Models\ValueObjects\CampaignRequest\CampaignRequestType; use Domain\Models\ValueObjects\CampaignRequest\RejectionReason; -use Domain\Models\ValueObjects\Coupon\CouponValidity; +use Domain\Models\ValueObjects\Coupon\CouponIncoCode; +use Domain\Models\ValueObjects\Coupon\CouponLegalInformation; +use Domain\Models\ValueObjects\Coupon\CouponLegalInformationArray; +use Domain\Models\ValueObjects\Coupon\CouponName; +use Domain\Models\ValueObjects\Coupon\CouponPicture; +use Domain\Models\ValueObjects\Coupon\CouponProcessingCode; +use Domain\Models\ValueObjects\Coupon\CouponProcessingCodeArray; +use Domain\Models\ValueObjects\Coupon\CouponProductArray; use Domain\Models\ValueObjects\Coupon\CouponValidityInformation; -use Domain\Models\ValueObjects\CouponName; -use Domain\Models\ValueObjects\GS1; -use Domain\Models\ValueObjects\IncoCode; +use Domain\Models\ValueObjects\Coupon\CouponValidityInformationArray; use Domain\Models\ValueObjects\MoneyAmount; use Domain\Models\ValueObjects\Period; use Domain\Models\ValueObjects\PicturePath; use Domain\Models\ValueObjects\PrintRules; -use Domain\Models\ValueObjects\ProcessingCode; use Domain\Models\ValueObjects\RequestedCoupon\RequestedCoupon; use Domain\Models\ValueObjects\RequestedCoupon\RequestedCouponQuantity; use Domain\Ports\Repositories\BrandRepository; @@ -47,44 +49,51 @@ use Shared\ValueObjects\Rcs; covers(FindCampaignRequest::class, Input::class, CampaignRequestFound::class, CampaignRequestNotFound::class); describe('find campaign request use case', function (): void { - beforeEach(function (): void { - $this->requestedCoupon = new RequestedCoupon( - new CouponName('Coupon A'), - new RequestedCouponQuantity(100), - new Period(new DateTime('2026-01-01'), new DateTime('2026-02-01')), - new MoneyAmount(new Money(Currency::EUR, 300)), - new ProcessingCode('PC1'), - new CouponValidity(30, new DateTime('2026-02-01')), - [ - new CouponValidityInformation('Lorem ipsum dolor sit amet consectetur adipiscing elit.', Country::FRANCE), - new CouponValidityInformation('Lorem ipsum dolor sit amet consectetur adipiscing elit.', Country::BENELUX) - ], - [new IncoCode(35784), new IncoCode(15522)], - new PrintRules(maxPerCustomer: 3), - new GS1('01234567890128', BarcodeFormat::EAN13) - ); - }); + $requestedCoupon = new RequestedCoupon( + couponName: new CouponName('Coupon A'), + quantity: new RequestedCouponQuantity(100), + diffusion: new Period(new DateTime('2026-01-01'), new DateTime('2026-02-01')), + moneyAmount: new MoneyAmount(new Money(Currency::EUR, 300)), + couponPicture: new CouponPicture( + new PicturePath('files/product.jpg'), + new PicturePath('files/thumbnail.jpg'), + new PicturePath('files/mobileThumbnail.jpg') + ), + couponProcessingCodes: new CouponProcessingCodeArray([ + new CouponProcessingCode('ABC1234567890', Country::FRANCE), + ]), + couponValidityInformations: new CouponValidityInformationArray([ + new CouponValidityInformation('Lorem ipsum dolor sit amet consectetur adipiscing elit.', Country::FRANCE), + new CouponValidityInformation('Lorem ipsum dolor sit amet consectetur adipiscing elit.', Country::BENELUX) + ]), + couponLegalInformations: new CouponLegalInformationArray([ + new CouponLegalInformation('Lorem ipsum dolor sit amet consectetur adipiscing elit.', Country::FRANCE), + new CouponLegalInformation('Lorem ipsum dolor sit amet consectetur adipiscing elit.', Country::BENELUX) + ]), + couponProducts: new CouponProductArray([new CouponIncoCode(35784), new CouponIncoCode(15522)]), + printRules: new PrintRules(maxPerCustomer: 3), + ); - it('finds a campaign request by its id, along with its brand', function (): void { + it('finds a campaign request by its id, along with its brand', function () use ($requestedCoupon): void { // GIVEN $id = CampaignRequestId::generate(); $brandCode = new BrandCode('AAA01'); $campaignRequest = CampaignRequest::hydrate( - $id, - $brandCode, - new CampaignName('Campaign A'), - new Applicant('John', 'Doe', new Email('john.doe@example.com')), - CampaignRequestType::CLUB, - $this->requestedCoupon, - CampaignRequestStatus::CREATED, + id: $id, + brandCode: $brandCode, + name: new CampaignName('Campaign A'), + applicant: new Applicant('John', 'Doe', new Email('john.doe@example.com')), + campaignType: CampaignRequestType::CLUB, + requestedCoupon: $requestedCoupon, + status: CampaignRequestStatus::CREATED, ); $brand = Brand::hydrate( - BrandId::generate(), - $brandCode, - new BrandName('Brand A'), - new PicturePath('brands/logo.jpg'), - new Rcs('RCS PARIS 111 222 333'), - new Email('contact@example.com'), + id: BrandId::generate(), + code: $brandCode, + name: new BrandName('Brand A'), + logo: new PicturePath('brands/logo.jpg'), + rcs: new Rcs('RCS PARIS 111 222 333'), + contact: new Email('contact@example.com'), ); $repository = \Mockery::mock(CampaignRequestRepository::class); @@ -109,20 +118,20 @@ describe('find campaign request use case', function (): void { ->and($result->rejectionReason)->toBeNull(); }); - it('finds a rejected campaign request with its rejection reason', function (): void { + it('finds a rejected campaign request with its rejection reason', function () use ($requestedCoupon): void { // GIVEN $id = CampaignRequestId::generate(); $code = new BrandCode('AAA01'); $rejectionReason = new RejectionReason('Missing legal documents.'); $campaignRequest = CampaignRequest::hydrate( - $id, - $code, - new CampaignName('Campaign A'), - new Applicant('John', 'Doe', new Email('john.doe@example.com')), - CampaignRequestType::CLUB, - $this->requestedCoupon, - CampaignRequestStatus::REJECTED, - $rejectionReason, + id: $id, + brandCode: $code, + name: new CampaignName('Campaign A'), + applicant: new Applicant('John', 'Doe', new Email('john.doe@example.com')), + campaignType: CampaignRequestType::CLUB, + requestedCoupon: $requestedCoupon, + status: CampaignRequestStatus::REJECTED, + rejectionReason: $rejectionReason, ); $repository = \Mockery::mock(CampaignRequestRepository::class); @@ -131,12 +140,12 @@ describe('find campaign request use case', function (): void { $brandRepository = \Mockery::mock(BrandRepository::class); $brandRepository->shouldReceive('findByCode')->once()->with($code)->andReturn( Brand::hydrate( - BrandId::generate(), - $code, - new BrandName('Brand A'), - new PicturePath('brands/logo.jpg'), - new Rcs('RCS PARIS 111 222 333'), - new Email('contact@example.com'), + id: BrandId::generate(), + code: $code, + name: new BrandName('Brand A'), + logo: new PicturePath('brands/logo.jpg'), + rcs: new Rcs('RCS PARIS 111 222 333'), + contact: new Email('contact@example.com'), ) ); @@ -170,18 +179,18 @@ describe('find campaign request use case', function (): void { expect($result)->toBeInstanceOf(CampaignRequestNotFound::class); }); - it('throws when the campaign request references a brand that no longer exists', function (): void { + it('throws when the campaign request references a brand that no longer exists', function () use ($requestedCoupon): void { // GIVEN $id = CampaignRequestId::generate(); $brandCode = new BrandCode('AAA01'); $campaignRequest = CampaignRequest::hydrate( - $id, - $brandCode, - new CampaignName('Campaign A'), - new Applicant('John', 'Doe', new Email('john.doe@example.com')), - CampaignRequestType::CLUB, - $this->requestedCoupon, - CampaignRequestStatus::CREATED, + id: $id, + brandCode: $brandCode, + name: new CampaignName('Campaign A'), + applicant: new Applicant('John', 'Doe', new Email('john.doe@example.com')), + campaignType: CampaignRequestType::CLUB, + requestedCoupon: $requestedCoupon, + status: CampaignRequestStatus::CREATED, ); $repository = \Mockery::mock(CampaignRequestRepository::class); @@ -196,7 +205,7 @@ describe('find campaign request use case', function (): void { $action = fn (): Output => $useCase->execute(new Input(id: $id)); // THEN - expect($action)->toThrow(BrandNotFoundException::class); + expect($action)->toThrow(\DomainException::class); }); }); diff --git a/tests/Unit/Application/UseCases/CampaignRequest/List/ListCampaignsRequestTest.php b/tests/Unit/Application/UseCases/CampaignRequest/List/ListCampaignsRequestTest.php index cfb1914..3f5f2e4 100644 --- a/tests/Unit/Application/UseCases/CampaignRequest/List/ListCampaignsRequestTest.php +++ b/tests/Unit/Application/UseCases/CampaignRequest/List/ListCampaignsRequestTest.php @@ -5,7 +5,6 @@ declare(strict_types=1); namespace Application\UseCases\CampaignRequest\List; use DateTime; -use Domain\Enums\BarcodeFormat; use Domain\Models\Entities\CampaignRequest; use Domain\Models\ValueObjects\Brand\BrandCode; use Domain\Models\ValueObjects\Campaign\CampaignName; @@ -17,15 +16,20 @@ use Domain\Models\ValueObjects\CampaignRequest\CampaignRequestSortCriteria; use Domain\Models\ValueObjects\CampaignRequest\CampaignRequestSortField; use Domain\Models\ValueObjects\CampaignRequest\CampaignRequestStatus; use Domain\Models\ValueObjects\CampaignRequest\CampaignRequestType; -use Domain\Models\ValueObjects\Coupon\CouponValidity; +use Domain\Models\ValueObjects\Coupon\CouponIncoCode; +use Domain\Models\ValueObjects\Coupon\CouponLegalInformation; +use Domain\Models\ValueObjects\Coupon\CouponLegalInformationArray; +use Domain\Models\ValueObjects\Coupon\CouponName; +use Domain\Models\ValueObjects\Coupon\CouponPicture; +use Domain\Models\ValueObjects\Coupon\CouponProcessingCode; +use Domain\Models\ValueObjects\Coupon\CouponProcessingCodeArray; +use Domain\Models\ValueObjects\Coupon\CouponProductArray; use Domain\Models\ValueObjects\Coupon\CouponValidityInformation; -use Domain\Models\ValueObjects\CouponName; -use Domain\Models\ValueObjects\GS1; -use Domain\Models\ValueObjects\IncoCode; +use Domain\Models\ValueObjects\Coupon\CouponValidityInformationArray; use Domain\Models\ValueObjects\MoneyAmount; use Domain\Models\ValueObjects\Period; +use Domain\Models\ValueObjects\PicturePath; use Domain\Models\ValueObjects\PrintRules; -use Domain\Models\ValueObjects\ProcessingCode; use Domain\Models\ValueObjects\RequestedCoupon\RequestedCoupon; use Domain\Models\ValueObjects\RequestedCoupon\RequestedCouponQuantity; use Domain\Ports\Repositories\CampaignRequestRepository; @@ -39,69 +43,85 @@ use Shared\ValueObjects\Pagination\Pagination; covers(ListCampaignsRequest::class, Input::class, Output::class, CampaignRequestListItem::class); describe('list campaigns request use case', function (): void { - beforeEach(function (): void { - $this->couponA = new RequestedCoupon( - new CouponName('Coupon A'), - new RequestedCouponQuantity(100), - new Period(new DateTime('2026-01-01'), new DateTime('2026-02-01')), - new MoneyAmount(new Money(Currency::EUR, 300)), - new ProcessingCode('PC1'), - new CouponValidity(30, new DateTime('2026-02-01')), - [ - new CouponValidityInformation('Lorem ipsum dolor sit amet consectetur adipiscing elit.', Country::FRANCE), - new CouponValidityInformation('Lorem ipsum dolor sit amet consectetur adipiscing elit.', Country::BENELUX) - ], - [new IncoCode(35784), new IncoCode(15522)], - new PrintRules(maxPerCustomer: 3), - new GS1('01234567890128', BarcodeFormat::EAN13) - ); + $couponA = new RequestedCoupon( + couponName: new CouponName('Coupon A'), + quantity: new RequestedCouponQuantity(100), + diffusion: new Period(new DateTime('2026-01-01'), new DateTime('2026-02-01')), + moneyAmount: new MoneyAmount(new Money(Currency::EUR, 300)), + couponPicture: new CouponPicture( + new PicturePath('files/product.jpg'), + new PicturePath('files/thumbnail.jpg'), + new PicturePath('files/mobileThumbnail.jpg') + ), + couponProcessingCodes: new CouponProcessingCodeArray([ + new CouponProcessingCode('ABC1234567890', Country::FRANCE), + ]), + couponValidityInformations: new CouponValidityInformationArray([ + new CouponValidityInformation('Lorem ipsum dolor sit amet consectetur adipiscing elit.', Country::FRANCE), + new CouponValidityInformation('Lorem ipsum dolor sit amet consectetur adipiscing elit.', Country::BENELUX) + ]), + couponLegalInformations: new CouponLegalInformationArray([ + new CouponLegalInformation('Lorem ipsum dolor sit amet consectetur adipiscing elit.', Country::FRANCE), + new CouponLegalInformation('Lorem ipsum dolor sit amet consectetur adipiscing elit.', Country::BENELUX) + ]), + couponProducts: new CouponProductArray([new CouponIncoCode(35784), new CouponIncoCode(15522)]), + printRules: new PrintRules(maxPerCustomer: 3), + ); - $this->couponB = new RequestedCoupon( - new CouponName('Coupon B'), - new RequestedCouponQuantity(100), - new Period(new DateTime('2026-01-01'), new DateTime('2026-02-01')), - new MoneyAmount(new Money(Currency::EUR, 300)), - new ProcessingCode('PC2'), - new CouponValidity(30, new DateTime('2026-02-01')), - [ - new CouponValidityInformation('Lorem ipsum dolor sit amet consectetur adipiscing elit.', Country::FRANCE), - new CouponValidityInformation('Lorem ipsum dolor sit amet consectetur adipiscing elit.', Country::BENELUX) - ], - [new IncoCode(24514), new IncoCode(46259)], - new PrintRules(maxPerCustomer: 3), - new GS1('01234567890128', BarcodeFormat::EAN13) - ); + $couponB = new RequestedCoupon( + couponName: new CouponName('Coupon B'), + quantity: new RequestedCouponQuantity(100), + diffusion: new Period(new DateTime('2026-01-01'), new DateTime('2026-02-01')), + moneyAmount: new MoneyAmount(new Money(Currency::EUR, 300)), + couponPicture: new CouponPicture( + new PicturePath('files/product.jpg'), + new PicturePath('files/thumbnail.jpg'), + new PicturePath('files/mobileThumbnail.jpg') + ), + couponProcessingCodes: new CouponProcessingCodeArray([ + new CouponProcessingCode('ABC1234567890', Country::FRANCE), + ]), + couponValidityInformations: new CouponValidityInformationArray([ + new CouponValidityInformation('Lorem ipsum dolor sit amet consectetur adipiscing elit.', Country::FRANCE), + new CouponValidityInformation('Lorem ipsum dolor sit amet consectetur adipiscing elit.', Country::BENELUX) + ]), + couponLegalInformations: new CouponLegalInformationArray([ + new CouponLegalInformation('Lorem ipsum dolor sit amet consectetur adipiscing elit.', Country::FRANCE), + new CouponLegalInformation('Lorem ipsum dolor sit amet consectetur adipiscing elit.', Country::BENELUX) + ]), + couponProducts: new CouponProductArray([new CouponIncoCode(35784), new CouponIncoCode(15522)]), + printRules: new PrintRules(maxPerCustomer: 3), + ); - $this->pagination = Pagination::firstPage(); + $pagination = Pagination::firstPage(); - $this->sort = new CampaignRequestSortCriteria( - clauses: [ - new CampaignRequestSortClause(field: CampaignRequestSortField::CreatedAt, direction: \SortDirection::Descending), - ] - ); - }); + $sort = new CampaignRequestSortCriteria( + clauses: [ + new CampaignRequestSortClause(field: CampaignRequestSortField::CreatedAt, direction: \SortDirection::Descending), + ] + ); - it('maps repository campaigns request to list items, preserving pagination and sort', function (): void { + it('maps repository campaigns request to list items, preserving pagination and sort', function () use ($couponA, $couponB, $pagination): void { // GIVEN $campaignRequest1 = CampaignRequest::hydrate( - CampaignRequestId::generate(), - new BrandCode('AAA01'), - new CampaignName('Campaign A'), - new Applicant('John', 'Doe', new Email('john.doe@example.com')), - CampaignRequestType::CLUB, - $this->couponA, - CampaignRequestStatus::CREATED, + id: CampaignRequestId::generate(), + brandCode: new BrandCode('AAA01'), + name: new CampaignName('Campaign A'), + applicant: new Applicant('John', 'Doe', new Email('john.doe@example.com')), + campaignType: CampaignRequestType::CLUB, + requestedCoupon: $couponA, + status: CampaignRequestStatus::CREATED, ); $campaignRequest2 = CampaignRequest::hydrate( - CampaignRequestId::generate(), - new BrandCode('AAA02'), - new CampaignName('Campaign B'), - new Applicant('John', 'Doe', new Email('john.doe@example.com')), - CampaignRequestType::CLUB, - $this->couponB, - CampaignRequestStatus::CREATED, + id: CampaignRequestId::generate(), + brandCode: new BrandCode('AAA02'), + name: new CampaignName('Campaign B'), + applicant: new Applicant('John', 'Doe', new Email('john.doe@example.com')), + campaignType: CampaignRequestType::CLUB, + requestedCoupon: $couponB, + status: CampaignRequestStatus::CREATED, ); - $paginated = new Paginated($this->pagination, [$campaignRequest1, $campaignRequest2], 2); + $paginated = new Paginated($pagination, [$campaignRequest1, $campaignRequest2], 2); $sort = new CampaignRequestSortCriteria( clauses: [ @@ -110,12 +130,12 @@ describe('list campaigns request use case', function (): void { ] ); $filters = new CampaignRequestFilters($campaignRequest2->brandCode, CampaignRequestStatus::CREATED); - $input = new Input(pagination: $this->pagination, sort: $sort, filters: $filters); + $input = new Input(pagination: $pagination, sort: $sort, filters: $filters); $repository = \Mockery::mock(CampaignRequestRepository::class); $repository->shouldReceive('findAll') ->once() - ->with($this->pagination, CampaignRequestSortCriteria::class, CampaignRequestFilters::class) + ->with($pagination, CampaignRequestSortCriteria::class, CampaignRequestFilters::class) ->andReturn($paginated); $useCase = new ListCampaignsRequest($repository); @@ -135,29 +155,29 @@ describe('list campaigns request use case', function (): void { ->and($items[0]->campaignType)->toBe($campaignRequest1->campaignType) ->and($items[0]->status)->toBe($campaignRequest1->status) ->and($result->campaignsRequest->getTotal())->toBe(2) - ->and($result->campaignsRequest->getPagination())->toBe($this->pagination); + ->and($result->campaignsRequest->getPagination())->toBe($pagination); }); - it('lists campaigns request with filters', function (): void { + it('lists campaigns request with filters', function () use ($couponA, $pagination, $sort): 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->couponA, - CampaignRequestStatus::CREATED, + id: CampaignRequestId::generate(), + brandCode: new BrandCode('AAA01'), + name: new CampaignName('Campaign A'), + applicant: new Applicant('John', 'Doe', new Email('john.doe@example.com')), + campaignType: CampaignRequestType::CLUB, + requestedCoupon: $couponA, + status: CampaignRequestStatus::CREATED, ); - $paginated = new Paginated($this->pagination, [$campaignRequest], 1); + $paginated = new Paginated($pagination, [$campaignRequest], 1); $filters = new CampaignRequestFilters($campaignRequest->brandCode, CampaignRequestStatus::CREATED); - $input = new Input(pagination: $this->pagination, sort: $this->sort, filters: $filters); + $input = new Input(pagination: $pagination, sort: $sort, filters: $filters); $repository = \Mockery::mock(CampaignRequestRepository::class); $repository->shouldReceive('findAll') ->once() - ->with($this->pagination, $this->sort, $filters) + ->with($pagination, $sort, $filters) ->andReturn($paginated); $useCase = new ListCampaignsRequest($repository); @@ -173,10 +193,10 @@ describe('list campaigns request use case', function (): void { ->and($items[0]->name)->toBe($campaignRequest->name) ->and($items[0]->status)->toBe($campaignRequest->status) ->and($result->campaignsRequest->getTotal())->toBe(1) - ->and($result->campaignsRequest->getPagination())->toBe($this->pagination); + ->and($result->campaignsRequest->getPagination())->toBe($pagination); }); - it('lists campaigns request with sort but without filters', function (): void { + it('lists campaigns request with sort but without filters', function () use ($couponA, $couponB, $pagination, $sort): void { // GIVEN $campaignRequest1 = CampaignRequest::hydrate( CampaignRequestId::generate(), @@ -184,7 +204,7 @@ describe('list campaigns request use case', function (): void { new CampaignName('Campaign A'), new Applicant('John', 'Doe', new Email('john.doe@example.com')), CampaignRequestType::CLUB, - $this->couponA, + $couponA, CampaignRequestStatus::CREATED, ); $campaignRequest2 = CampaignRequest::hydrate( @@ -193,22 +213,22 @@ describe('list campaigns request use case', function (): void { new CampaignName('Campaign B'), new Applicant('John', 'Doe', new Email('john.doe@example.com')), CampaignRequestType::CLUB, - $this->couponB, + $couponB, CampaignRequestStatus::CREATED, ); - $paginated = new Paginated($this->pagination, [$campaignRequest2, $campaignRequest1], 2); + $paginated = new Paginated($pagination, [$campaignRequest2, $campaignRequest1], 2); $sort = new CampaignRequestSortCriteria( clauses: [ new CampaignRequestSortClause(field: CampaignRequestSortField::BrandCode, direction: \SortDirection::Descending), ] ); - $input = new Input(pagination: $this->pagination, sort: $sort); + $input = new Input(pagination: $pagination, sort: $sort); $repository = \Mockery::mock(CampaignRequestRepository::class); $repository->shouldReceive('findAll') ->once() - ->with($this->pagination, $sort, null) + ->with($pagination, $sort, null) ->andReturn($paginated); $useCase = new ListCampaignsRequest($repository); @@ -222,18 +242,18 @@ describe('list campaigns request use case', function (): void { ->and($items[0]->brandCode)->toBe($campaignRequest2->brandCode) ->and($items[1]->brandCode)->toBe($campaignRequest1->brandCode) ->and($result->campaignsRequest->getTotal())->toBe(2) - ->and($result->campaignsRequest->getPagination())->toBe($this->pagination); + ->and($result->campaignsRequest->getPagination())->toBe($pagination); }); - it('returns an empty list without error when there are no campaigns request', function (): void { + it('returns an empty list without error when there are no campaigns request', function () use ($pagination, $sort): void { // GIVEN - $paginated = new Paginated($this->pagination, [], 0); - $input = new Input(pagination: $this->pagination, sort: $this->sort); + $paginated = new Paginated($pagination, [], 0); + $input = new Input(pagination: $pagination, sort: $sort); $repository = \Mockery::mock(CampaignRequestRepository::class); $repository->shouldReceive('findAll') ->once() - ->with($this->pagination, $this->sort, null) + ->with($pagination, $sort, null) ->andReturn($paginated); $useCase = new ListCampaignsRequest($repository); diff --git a/tests/Unit/Application/UseCases/CampaignRequest/Reject/RejectCampaignRequestTest.php b/tests/Unit/Application/UseCases/CampaignRequest/Reject/RejectCampaignRequestTest.php index 3b8b10c..e0e11a9 100644 --- a/tests/Unit/Application/UseCases/CampaignRequest/Reject/RejectCampaignRequestTest.php +++ b/tests/Unit/Application/UseCases/CampaignRequest/Reject/RejectCampaignRequestTest.php @@ -11,7 +11,6 @@ use Application\UseCases\CampaignRequest\Reject\Output\Output; use Application\UseCases\CampaignRequest\Reject\RejectCampaignRequest; use Carbon\CarbonImmutable; use DateTime; -use Domain\Enums\BarcodeFormat; use Domain\Models\Entities\CampaignRequest; use Domain\Models\ValueObjects\Brand\BrandCode; use Domain\Models\ValueObjects\Campaign\CampaignName; @@ -20,15 +19,20 @@ use Domain\Models\ValueObjects\CampaignRequest\CampaignRequestId; use Domain\Models\ValueObjects\CampaignRequest\CampaignRequestStatus; use Domain\Models\ValueObjects\CampaignRequest\CampaignRequestType; use Domain\Models\ValueObjects\CampaignRequest\RejectionReason; -use Domain\Models\ValueObjects\Coupon\CouponValidity; +use Domain\Models\ValueObjects\Coupon\CouponIncoCode; +use Domain\Models\ValueObjects\Coupon\CouponLegalInformation; +use Domain\Models\ValueObjects\Coupon\CouponLegalInformationArray; +use Domain\Models\ValueObjects\Coupon\CouponName; +use Domain\Models\ValueObjects\Coupon\CouponPicture; +use Domain\Models\ValueObjects\Coupon\CouponProcessingCode; +use Domain\Models\ValueObjects\Coupon\CouponProcessingCodeArray; +use Domain\Models\ValueObjects\Coupon\CouponProductArray; use Domain\Models\ValueObjects\Coupon\CouponValidityInformation; -use Domain\Models\ValueObjects\CouponName; -use Domain\Models\ValueObjects\GS1; -use Domain\Models\ValueObjects\IncoCode; +use Domain\Models\ValueObjects\Coupon\CouponValidityInformationArray; use Domain\Models\ValueObjects\MoneyAmount; use Domain\Models\ValueObjects\Period; +use Domain\Models\ValueObjects\PicturePath; use Domain\Models\ValueObjects\PrintRules; -use Domain\Models\ValueObjects\ProcessingCode; use Domain\Models\ValueObjects\RequestedCoupon\RequestedCoupon; use Domain\Models\ValueObjects\RequestedCoupon\RequestedCouponQuantity; use Domain\Ports\Repositories\CampaignRequestRepository; @@ -40,29 +44,36 @@ use Shared\ValueObjects\Money; covers(RejectCampaignRequest::class, Input::class, CampaignRequestRejected::class, CampaignRequestNotFound::class); describe('reject campaign request use case', function (): void { - beforeEach(function (): void { - $this->requestedCoupon = new RequestedCoupon( - new CouponName('Coupon A'), - new RequestedCouponQuantity(100), - new Period(new DateTime('2026-01-01'), new DateTime('2026-02-01')), - new MoneyAmount(new Money(Currency::EUR, 300)), - new ProcessingCode('PC1'), - new CouponValidity(30, new DateTime('2026-02-01')), - [ - new CouponValidityInformation('Lorem ipsum dolor sit amet consectetur adipiscing elit.', Country::FRANCE), - new CouponValidityInformation('Lorem ipsum dolor sit amet consectetur adipiscing elit.', Country::BENELUX) - ], - [new IncoCode(35784), new IncoCode(15522)], - new PrintRules(maxPerCustomer: 3), - new GS1('01234567890128', BarcodeFormat::EAN13) - ); - }); + $requestedCoupon = new RequestedCoupon( + couponName: new CouponName('Coupon A'), + quantity: new RequestedCouponQuantity(100), + diffusion: new Period(new DateTime('2026-01-01'), new DateTime('2026-02-01')), + moneyAmount: new MoneyAmount(new Money(Currency::EUR, 300)), + couponPicture: new CouponPicture( + new PicturePath('files/product.jpg'), + new PicturePath('files/thumbnail.jpg'), + new PicturePath('files/mobileThumbnail.jpg') + ), + couponProcessingCodes: new CouponProcessingCodeArray([ + new CouponProcessingCode('ABC1234567890', Country::FRANCE), + ]), + couponValidityInformations: new CouponValidityInformationArray([ + new CouponValidityInformation('Lorem ipsum dolor sit amet consectetur adipiscing elit.', Country::FRANCE), + new CouponValidityInformation('Lorem ipsum dolor sit amet consectetur adipiscing elit.', Country::BENELUX) + ]), + couponLegalInformations: new CouponLegalInformationArray([ + new CouponLegalInformation('Lorem ipsum dolor sit amet consectetur adipiscing elit.', Country::FRANCE), + new CouponLegalInformation('Lorem ipsum dolor sit amet consectetur adipiscing elit.', Country::BENELUX) + ]), + couponProducts: new CouponProductArray([new CouponIncoCode(35784), new CouponIncoCode(15522)]), + printRules: new PrintRules(maxPerCustomer: 3), + ); afterEach(function (): void { CarbonImmutable::setTestNow(); }); - it('rejects a campaign request and persists it', function (): void { + it('rejects a campaign request and persists it', function () use ($requestedCoupon): void { // GIVEN CarbonImmutable::setTestNow('2026-06-15 12:00:00 UTC'); $id = CampaignRequestId::generate(); @@ -72,7 +83,7 @@ describe('reject campaign request use case', function (): void { new CampaignName('Campaign A'), new Applicant('John', 'Doe', new Email('john.doe@example.com')), CampaignRequestType::CLUB, - $this->requestedCoupon, + $requestedCoupon, CampaignRequestStatus::CREATED, ); $reason = new RejectionReason('Missing legal documents.'); @@ -82,7 +93,7 @@ describe('reject campaign request use case', function (): void { $repository->shouldReceive('save')->once()->with(\Mockery::on( fn (CampaignRequest $saved): bool => $saved->status === CampaignRequestStatus::REJECTED && $saved->rejectionReason === $reason - && $saved->updatedAt !== null, + && $saved->updatedAt instanceof \DateTimeInterface, )); $useCase = new RejectCampaignRequest($repository); @@ -115,7 +126,7 @@ describe('reject campaign request use case', function (): void { expect($result)->toBeInstanceOf(CampaignRequestNotFound::class); }); - it('propagates the domain exception when the campaign request cannot be rejected', function (): void { + it('propagates the domain exception when the campaign request cannot be rejected', function () use ($requestedCoupon): void { // GIVEN $id = CampaignRequestId::generate(); $campaignRequest = CampaignRequest::hydrate( @@ -124,7 +135,7 @@ describe('reject campaign request use case', function (): void { new CampaignName('Campaign A'), new Applicant('John', 'Doe', new Email('john.doe@example.com')), CampaignRequestType::CLUB, - $this->requestedCoupon, + $requestedCoupon, CampaignRequestStatus::TRANSFORMED, ); @@ -142,19 +153,4 @@ describe('reject campaign request use case', function (): void { // THEN expect($action)->toThrow(\DomainException::class, 'Only a created campaign request can be rejected.'); }); - - it('never touches the repository when a rejection reason is missing', function (): void { - // GIVEN - $repository = \Mockery::mock(CampaignRequestRepository::class); - $repository->shouldNotReceive('findById'); - $repository->shouldNotReceive('save'); - - $useCase = new RejectCampaignRequest($repository); - - // WHEN - $action = fn (): Output => $useCase->execute(new Input(id: CampaignRequestId::generate())); - - // THEN - expect($action)->toThrow(\ArgumentCountError::class); - }); }); diff --git a/tests/Unit/Domain/Enums/BarcodeFormatTest.php b/tests/Unit/Domain/Enums/BarcodeFormatTest.php deleted file mode 100644 index d1af8e0..0000000 --- a/tests/Unit/Domain/Enums/BarcodeFormatTest.php +++ /dev/null @@ -1,23 +0,0 @@ -value)->toBe('ean') - ->and(BarcodeFormat::CODE128->value)->toBe('code128') - ->and(BarcodeFormat::GS1_128->value)->toBe('gs1'); - }); - - it('exposes its case name', function (): void { - // THEN - expect(BarcodeFormat::EAN13->name)->toBe('EAN13'); - }); -}); diff --git a/tests/Unit/Domain/Exceptions/BrandCodeAlreadyExistsExceptionTest.php b/tests/Unit/Domain/Exceptions/BrandCodeAlreadyExistsExceptionTest.php deleted file mode 100644 index 9ead53c..0000000 --- a/tests/Unit/Domain/Exceptions/BrandCodeAlreadyExistsExceptionTest.php +++ /dev/null @@ -1,24 +0,0 @@ -getMessage())->toBe('Brand code "AAA01" already exists.') - ->and($exception)->toBeInstanceOf(\DomainException::class); - }); -}); diff --git a/tests/Unit/Domain/Models/Entities/ActorTest.php b/tests/Unit/Domain/Models/Entities/ActorTest.php index cda40c1..65942dc 100644 --- a/tests/Unit/Domain/Models/Entities/ActorTest.php +++ b/tests/Unit/Domain/Models/Entities/ActorTest.php @@ -12,36 +12,34 @@ use Ramsey\Uuid\Uuid; covers(Actor::class); describe('actor', function (): void { - beforeEach(function (): void { - $this->displayName = new ActorDisplayName('John Doe'); - }); + $displayName = new ActorDisplayName('John Doe'); - it('hydrates an actor', function (): void { + it('hydrates an actor', function () use ($displayName): void { // GIVEN $id = ActorId::generate(); // WHEN $actor = Actor::hydrate( $id, - $this->displayName + $displayName, ); // THEN expect($actor->id())->toBe($id) - ->and($actor->displayName)->toBe($this->displayName) + ->and($actor->displayName)->toBe($displayName) ->and($actor->updatedAt)->toBeNull() ->and($actor->updatedAt())->toBeNull(); }); - it('hydrates an actor with an explicit updatedAt date', function (): void { + it('hydrates an actor with an explicit updatedAt date', function () use ($displayName): void { // GIVEN $updatedAt = new \DateTimeImmutable('2026-06-15 12:00:00', new \DateTimeZone('UTC')); // WHEN $actor = Actor::hydrate( ActorId::generate(), - $this->displayName, - $updatedAt + $displayName, + $updatedAt, ); // THEN @@ -49,13 +47,13 @@ describe('actor', function (): void { ->and($actor->updatedAt())->toBe($updatedAt); }); - it('returns the creation date extracted from the id', function (): void { + it('returns the creation date extracted from the id', function () use ($displayName): 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, - $this->displayName + $displayName, ); // WHEN diff --git a/tests/Unit/Domain/Models/Entities/BrandTest.php b/tests/Unit/Domain/Models/Entities/BrandTest.php index 04777fd..cb80b26 100644 --- a/tests/Unit/Domain/Models/Entities/BrandTest.php +++ b/tests/Unit/Domain/Models/Entities/BrandTest.php @@ -17,44 +17,42 @@ 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'); - }); + $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'); afterEach(function (): void { CarbonImmutable::setTestNow(); }); - it('creates a brand', function (): void { + it('creates a brand', function () use ($code, $name, $logo, $rcs, $contact): void { // GIVEN $id = BrandId::generate(); // WHEN $brand = Brand::hydrate( $id, - $this->code, - $this->name, - $this->logo, - $this->rcs, - $this->contact + $code, + $name, + $logo, + $rcs, + $contact, ); // THEN expect($brand->id())->toBe($id) - ->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->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->updatedAt)->toBeNull() ->and($brand->updatedAt())->toBeNull(); }); - it('creates a brand with an explicit updatedAt date', function (): void { + it('creates a brand with an explicit updatedAt date', function () use ($code, $name, $logo, $rcs, $contact): void { // GIVEN $id = BrandId::generate(); $updatedAt = new \DateTimeImmutable('2026-06-15 12:00:00', new \DateTimeZone('UTC')); @@ -62,12 +60,12 @@ describe('brand', function (): void { // WHEN $brand = Brand::hydrate( $id, - $this->code, - $this->name, - $this->logo, - $this->rcs, - $this->contact, - $updatedAt + $code, + $name, + $logo, + $rcs, + $contact, + $updatedAt, ); // THEN @@ -75,49 +73,49 @@ describe('brand', function (): void { ->and($brand->updatedAt())->toBe($updatedAt); }); - it('creates a new brand with a freshly generated id', function (): void { + it('creates a new brand with a freshly generated id', function () use ($code, $name, $logo, $rcs, $contact): void { // GIVEN / WHEN $brand = Brand::create( - $this->code, - $this->name, - $this->logo, - $this->rcs, - $this->contact + $code, + $name, + $logo, + $rcs, + $contact, ); // THEN expect($brand->id())->toBeInstanceOf(BrandId::class) - ->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->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->updatedAt)->toBeNull() ->and($brand->updatedAt())->toBeNull(); }); - it('generates a different id for each created brand', function (): void { + it('generates a different id for each created brand', function () use ($code, $name, $logo, $rcs, $contact): void { // GIVEN / WHEN $first = Brand::create( - $this->code, - $this->name, - $this->logo, - $this->rcs, - $this->contact + $code, + $name, + $logo, + $rcs, + $contact, ); $second = Brand::create( - $this->code, - $this->name, - $this->logo, - $this->rcs, - $this->contact + $code, + $name, + $logo, + $rcs, + $contact, ); // THEN expect($first->id()->equals($second->id()))->toBeFalse(); }); - it('updates a brand logo and sets updatedAt timestamp', function (): void { + it('updates a brand logo and sets updatedAt timestamp', function () use ($code, $name, $rcs, $contact): void { // GIVEN CarbonImmutable::setTestNow('2026-06-15 12:00:00 UTC'); $id = BrandId::generate(); @@ -125,11 +123,11 @@ describe('brand', function (): void { $logoNew = new PicturePath('files/picture-new.jpg'); $brand = Brand::hydrate( $id, - $this->code, - $this->name, + $code, + $name, $logoOld, - $this->rcs, - $this->contact + $rcs, + $contact, ); // WHEN @@ -137,16 +135,16 @@ describe('brand', function (): void { // THEN expect($brand->id())->toBe($id) - ->and($brand->code)->toBe($this->code) - ->and($brand->name)->toBe($this->name) + ->and($brand->code)->toBe($code) + ->and($brand->name)->toBe($name) ->and($brand->logo)->toBe($logoNew) - ->and($brand->rcs)->toBe($this->rcs) - ->and($brand->contact)->toBe($this->contact) + ->and($brand->rcs)->toBe($rcs) + ->and($brand->contact)->toBe($contact) ->and($brand->updatedAt)->not->toBeNull() ->and($brand->updatedAt()?->format('Y-m-d H:i:s'))->toBe('2026-06-15 12:00:00'); }); - it('updates a brand rcs and sets updatedAt timestamp', function (): void { + it('updates a brand rcs and sets updatedAt timestamp', function () use ($code, $name, $logo, $contact): void { // GIVEN CarbonImmutable::setTestNow('2026-06-15 14:30:00 UTC'); $id = BrandId::generate(); @@ -154,11 +152,11 @@ describe('brand', function (): void { $rcsNew = new Rcs('RCS NANTES 444 555 666'); $brand = Brand::hydrate( $id, - $this->code, - $this->name, - $this->logo, + $code, + $name, + $logo, $rcsOld, - $this->contact + $contact, ); // WHEN @@ -166,16 +164,16 @@ describe('brand', function (): void { // THEN expect($brand->id())->toBe($id) - ->and($brand->code)->toBe($this->code) - ->and($brand->name)->toBe($this->name) - ->and($brand->logo)->toBe($this->logo) + ->and($brand->code)->toBe($code) + ->and($brand->name)->toBe($name) + ->and($brand->logo)->toBe($logo) ->and($brand->rcs)->toBe($rcsNew) - ->and($brand->contact)->toBe($this->contact) + ->and($brand->contact)->toBe($contact) ->and($brand->updatedAt)->not->toBeNull() ->and($brand->updatedAt()?->format('Y-m-d H:i:s'))->toBe('2026-06-15 14:30:00'); }); - it('updates a brand contact and sets updatedAt timestamp', function (): void { + it('updates a brand contact and sets updatedAt timestamp', function () use ($code, $name, $logo, $rcs): void { // GIVEN CarbonImmutable::setTestNow('2026-06-15 16:45:00 UTC'); $id = BrandId::generate(); @@ -183,11 +181,11 @@ describe('brand', function (): void { $contactNew = new Email('contact-new@example.com'); $brand = Brand::hydrate( $id, - $this->code, - $this->name, - $this->logo, - $this->rcs, - $contactOld + $code, + $name, + $logo, + $rcs, + $contactOld, ); // WHEN @@ -195,26 +193,26 @@ describe('brand', function (): void { // THEN expect($brand->id())->toBe($id) - ->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->code)->toBe($code) + ->and($brand->name)->toBe($name) + ->and($brand->logo)->toBe($logo) + ->and($brand->rcs)->toBe($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'); }); - it('returns the creation date extracted from the id', function (): void { + it('returns the creation date extracted from the id', function () use ($code, $name, $logo, $rcs, $contact): void { // GIVEN $expected = new \DateTimeImmutable('2024-06-15 12:34:56.789000', new \DateTimeZone('UTC')); $id = new BrandId(Uuid::uuid7($expected)); $brand = Brand::hydrate( $id, - $this->code, - $this->name, - $this->logo, - $this->rcs, - $this->contact + $code, + $name, + $logo, + $rcs, + $contact, ); // WHEN diff --git a/tests/Unit/Domain/Models/Entities/CampaignRequestTest.php b/tests/Unit/Domain/Models/Entities/CampaignRequestTest.php index 8b79003..0790326 100644 --- a/tests/Unit/Domain/Models/Entities/CampaignRequestTest.php +++ b/tests/Unit/Domain/Models/Entities/CampaignRequestTest.php @@ -6,7 +6,6 @@ namespace Tests\Unit\Domain\Models\Entities; use Carbon\CarbonImmutable; use DateTime; -use Domain\Enums\BarcodeFormat; use Domain\Models\Entities\CampaignRequest; use Domain\Models\ValueObjects\Brand\BrandCode; use Domain\Models\ValueObjects\Campaign\CampaignName; @@ -15,15 +14,20 @@ use Domain\Models\ValueObjects\CampaignRequest\CampaignRequestId; use Domain\Models\ValueObjects\CampaignRequest\CampaignRequestStatus; use Domain\Models\ValueObjects\CampaignRequest\CampaignRequestType; use Domain\Models\ValueObjects\CampaignRequest\RejectionReason; -use Domain\Models\ValueObjects\Coupon\CouponValidity; +use Domain\Models\ValueObjects\Coupon\CouponIncoCode; +use Domain\Models\ValueObjects\Coupon\CouponLegalInformation; +use Domain\Models\ValueObjects\Coupon\CouponLegalInformationArray; +use Domain\Models\ValueObjects\Coupon\CouponName; +use Domain\Models\ValueObjects\Coupon\CouponPicture; +use Domain\Models\ValueObjects\Coupon\CouponProcessingCode; +use Domain\Models\ValueObjects\Coupon\CouponProcessingCodeArray; +use Domain\Models\ValueObjects\Coupon\CouponProductArray; use Domain\Models\ValueObjects\Coupon\CouponValidityInformation; -use Domain\Models\ValueObjects\CouponName; -use Domain\Models\ValueObjects\GS1; -use Domain\Models\ValueObjects\IncoCode; +use Domain\Models\ValueObjects\Coupon\CouponValidityInformationArray; use Domain\Models\ValueObjects\MoneyAmount; use Domain\Models\ValueObjects\Period; +use Domain\Models\ValueObjects\PicturePath; use Domain\Models\ValueObjects\PrintRules; -use Domain\Models\ValueObjects\ProcessingCode; use Domain\Models\ValueObjects\RequestedCoupon\RequestedCoupon; use Domain\Models\ValueObjects\RequestedCoupon\RequestedCouponQuantity; use Ramsey\Uuid\Uuid; @@ -35,77 +39,90 @@ use Shared\ValueObjects\Money; covers(CampaignRequest::class); describe('campaign request', function (): void { - beforeEach(function (): void { - $this->requestedCoupon = new RequestedCoupon( - new CouponName('Coupon A'), - new RequestedCouponQuantity(100), - new Period(new DateTime('2026-01-01'), new DateTime('2026-02-01')), - new MoneyAmount(new Money(Currency::EUR, 300)), - new ProcessingCode('PC1'), - new CouponValidity(30, new DateTime('2026-02-01')), - [ - new CouponValidityInformation('Lorem ipsum dolor sit amet consectetur adipiscing elit.', Country::FRANCE), - new CouponValidityInformation('Lorem ipsum dolor sit amet consectetur adipiscing elit.', Country::BENELUX) - ], - [new IncoCode(35784), new IncoCode(15522)], - new PrintRules(maxPerCustomer: 3), - new GS1('01234567890128', BarcodeFormat::EAN13) - ); - $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.'); - }); + $requestedCoupon = new RequestedCoupon( + couponName: new CouponName('Coupon A'), + quantity: new RequestedCouponQuantity(100), + diffusion: new Period(new DateTime('2026-01-01'), new DateTime('2026-02-01')), + moneyAmount: new MoneyAmount(new Money(Currency::EUR, 300)), + couponPicture: new CouponPicture( + new PicturePath('files/product.jpg'), + new PicturePath('files/thumbnail.jpg'), + new PicturePath('files/mobileThumbnail.jpg') + ), + couponProcessingCodes: new CouponProcessingCodeArray([ + new CouponProcessingCode('ABC1234567890', Country::FRANCE), + ]), + couponValidityInformations: new CouponValidityInformationArray([ + new CouponValidityInformation('Lorem ipsum dolor sit amet consectetur adipiscing elit.', Country::FRANCE), + new CouponValidityInformation('Lorem ipsum dolor sit amet consectetur adipiscing elit.', Country::BENELUX) + ]), + couponLegalInformations: new CouponLegalInformationArray([ + new CouponLegalInformation('Lorem ipsum dolor sit amet consectetur adipiscing elit.', Country::FRANCE), + new CouponLegalInformation('Lorem ipsum dolor sit amet consectetur adipiscing elit.', Country::BENELUX) + ]), + couponProducts: new CouponProductArray([new CouponIncoCode(35784), new CouponIncoCode(15522)]), + printRules: new PrintRules(maxPerCustomer: 3), + ); + $code = new BrandCode('AAA01'); + $name = new CampaignName('Campaign A'); + $applicant = new Applicant('John', 'Doe', new Email('john.doe@example.com')); + $campaignType = CampaignRequestType::CLUB; + $status = CampaignRequestStatus::CREATED; + $transformedStatus = CampaignRequestStatus::TRANSFORMED; + $rejectedStatus = CampaignRequestStatus::REJECTED; + $rejectionReason = new RejectionReason('Missing legal documents.'); afterEach(function (): void { CarbonImmutable::setTestNow(); }); - it('hydrates a campaign request with all its fields', function (): void { + it('hydrates a campaign request with all its fields', function () use ($code, $name, $applicant, $campaignType, $requestedCoupon, $status): void { // GIVEN $id = CampaignRequestId::generate(); // WHEN $campaignRequest = CampaignRequest::hydrate( $id, - $this->code, - $this->name, - $this->applicant, - $this->campaignType, - $this->requestedCoupon, - $this->status, + $code, + $name, + $applicant, + $campaignType, + $requestedCoupon, + $status, ); // THEN expect($campaignRequest->id())->toBe($id) - ->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->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->updatedAt)->toBeNull() ->and($campaignRequest->updatedAt())->toBeNull(); }); - it('hydrates a campaign request with an explicit updatedAt date', function (): void { + it('hydrates a campaign request with an explicit updatedAt date', function () use ( + $code, + $name, + $applicant, + $campaignType, + $requestedCoupon, + $status, + ): void { // GIVEN $updatedAt = new \DateTimeImmutable('2026-06-15 12:00:00', new \DateTimeZone('UTC')); // WHEN $campaignRequest = CampaignRequest::hydrate( CampaignRequestId::generate(), - $this->code, - $this->name, - $this->applicant, - $this->campaignType, - $this->requestedCoupon, - $this->status, + $code, + $name, + $applicant, + $campaignType, + $requestedCoupon, + $status, null, $updatedAt, ); @@ -115,34 +132,49 @@ describe('campaign request', function (): void { ->and($campaignRequest->updatedAt())->toBe($updatedAt); }); - it('hydrates a rejected campaign request with a rejection reason', function (): void { + it('hydrates a rejected campaign request with a rejection reason', function () use ( + $code, + $name, + $applicant, + $campaignType, + $requestedCoupon, + $rejectedStatus, + $rejectionReason, + ): void { // GIVEN / WHEN $campaignRequest = CampaignRequest::hydrate( CampaignRequestId::generate(), - $this->code, - $this->name, - $this->applicant, - $this->campaignType, - $this->requestedCoupon, - $this->rejectedStatus, - $this->rejectionReason, + $code, + $name, + $applicant, + $campaignType, + $requestedCoupon, + $rejectedStatus, + $rejectionReason, ); // THEN - expect($campaignRequest->status)->toBe($this->rejectedStatus) - ->and($campaignRequest->rejectionReason)->toBe($this->rejectionReason); + expect($campaignRequest->status)->toBe($rejectedStatus) + ->and($campaignRequest->rejectionReason)->toBe($rejectionReason); }); - it('rejects a rejected campaign request without a rejection reason', function (): void { + it('rejects a rejected campaign request without a rejection reason', function () use ( + $code, + $name, + $applicant, + $campaignType, + $requestedCoupon, + $rejectedStatus, + ): void { // GIVEN / WHEN $action = fn (): CampaignRequest => CampaignRequest::hydrate( CampaignRequestId::generate(), - $this->code, - $this->name, - $this->applicant, - $this->campaignType, - $this->requestedCoupon, - $this->rejectedStatus, + $code, + $name, + $applicant, + $campaignType, + $requestedCoupon, + $rejectedStatus, ); // THEN @@ -152,17 +184,25 @@ describe('campaign request', function (): void { ); }); - it('rejects a non-rejected campaign request with a rejection reason', function (): void { + it('rejects a non-rejected campaign request with a rejection reason', function () use ( + $code, + $name, + $applicant, + $campaignType, + $requestedCoupon, + $status, + $rejectionReason, + ): void { // WHEN $action = fn (): CampaignRequest => CampaignRequest::hydrate( CampaignRequestId::generate(), - $this->code, - $this->name, - $this->applicant, - $this->campaignType, - $this->requestedCoupon, - $this->status, - $this->rejectionReason, + $code, + $name, + $applicant, + $campaignType, + $requestedCoupon, + $status, + $rejectionReason, ); // THEN @@ -172,25 +212,33 @@ describe('campaign request', function (): void { ); }); - it('rejects a campaign request and sets the rejection reason and updatedAt', function (): void { + it('rejects a campaign request and sets the rejection reason and updatedAt', function () use ( + $code, + $name, + $applicant, + $campaignType, + $requestedCoupon, + $status, + $rejectionReason, + ): void { // GIVEN CarbonImmutable::setTestNow('2026-06-15 12:00:00 UTC'); $campaignRequest = CampaignRequest::hydrate( CampaignRequestId::generate(), - $this->code, - $this->name, - $this->applicant, - $this->campaignType, - $this->requestedCoupon, - $this->status, + $code, + $name, + $applicant, + $campaignType, + $requestedCoupon, + $status, ); // WHEN - $rejected = $campaignRequest->reject($this->rejectionReason); + $rejected = $campaignRequest->reject($rejectionReason); // THEN expect($rejected->status)->toBe(CampaignRequestStatus::REJECTED) - ->and($rejected->rejectionReason)->toBe($this->rejectionReason) + ->and($rejected->rejectionReason)->toBe($rejectionReason) ->and($rejected->id())->toBe($campaignRequest->id()) ->and($rejected->brandCode)->toBe($campaignRequest->brandCode) ->and($rejected->name)->toBe($campaignRequest->name) @@ -201,35 +249,50 @@ describe('campaign request', function (): void { ->and($rejected->updatedAt()?->format('Y-m-d H:i:s'))->toBe('2026-06-15 12:00:00'); }); - it('rejects rejecting a transformed campaign request', function (): void { + it('rejects rejecting a transformed campaign request', function () use ( + $code, + $name, + $applicant, + $campaignType, + $requestedCoupon, + $transformedStatus, + $rejectionReason, + ): void { // GIVEN $campaignRequest = CampaignRequest::hydrate( CampaignRequestId::generate(), - $this->code, - $this->name, - $this->applicant, - $this->campaignType, - $this->requestedCoupon, - $this->transformedStatus, + $code, + $name, + $applicant, + $campaignType, + $requestedCoupon, + $transformedStatus, ); // WHEN - $action = fn (): CampaignRequest => $campaignRequest->reject($this->rejectionReason); + $action = fn (): CampaignRequest => $campaignRequest->reject($rejectionReason); // THEN expect($action)->toThrow(\DomainException::class, 'Only a created campaign request can be rejected.'); }); - it('rejects rejecting an already rejected campaign request', function (): void { + it('rejects rejecting an already rejected campaign request', function () use ( + $code, + $name, + $applicant, + $campaignType, + $requestedCoupon, + $rejectedStatus, + ): void { // GIVEN $campaignRequest = CampaignRequest::hydrate( CampaignRequestId::generate(), - $this->code, - $this->name, - $this->applicant, - $this->campaignType, - $this->requestedCoupon, - $this->rejectedStatus, + $code, + $name, + $applicant, + $campaignType, + $requestedCoupon, + $rejectedStatus, new RejectionReason('First reason.'), ); @@ -240,18 +303,18 @@ describe('campaign request', function (): void { expect($action)->toThrow(\DomainException::class, 'Only a created campaign request can be rejected.'); }); - it('returns the creation date extracted from the id', function (): void { + it('returns the creation date extracted from the id', function () use ($code, $name, $applicant, $campaignType, $requestedCoupon, $status): void { // GIVEN $expected = new \DateTimeImmutable('2024-06-15 12:34:56.789000', new \DateTimeZone('UTC')); $id = new CampaignRequestId(Uuid::uuid7($expected)); $campaignRequest = CampaignRequest::hydrate( $id, - $this->code, - $this->name, - $this->applicant, - $this->campaignType, - $this->requestedCoupon, - $this->status, + $code, + $name, + $applicant, + $campaignType, + $requestedCoupon, + $status, ); // WHEN diff --git a/tests/Unit/Domain/Models/Entities/CampaignTest.php b/tests/Unit/Domain/Models/Entities/CampaignTest.php index 403200c..e48d8a5 100644 --- a/tests/Unit/Domain/Models/Entities/CampaignTest.php +++ b/tests/Unit/Domain/Models/Entities/CampaignTest.php @@ -5,92 +5,83 @@ declare(strict_types=1); namespace Tests\Unit\Domain\Models\Entities; use Carbon\CarbonImmutable; -use Shared\Enums\Country; use Domain\Models\Entities\Campaign; use Domain\Models\ValueObjects\Actor\ActorId; use Domain\Models\ValueObjects\Brand\BrandCode; use Domain\Models\ValueObjects\Campaign\CampaignCode; use Domain\Models\ValueObjects\Campaign\CampaignCoupon; +use Domain\Models\ValueObjects\Campaign\CampaignCouponArray; use Domain\Models\ValueObjects\Campaign\CampaignId; use Domain\Models\ValueObjects\Campaign\CampaignName; -use Domain\Models\ValueObjects\Coupon\CouponQuantity; +use Domain\Models\ValueObjects\Coupon\CouponCode; use Domain\Models\ValueObjects\PrintRules; -use Domain\Models\ValueObjects\ProcessingCode; use Ramsey\Uuid\Uuid; covers(Campaign::class); describe('campaign', function (): void { - 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), - Country::FRANCE, - new PrintRules(maxPerCustomer: 3) - ) - ]; - $this->brandCode = new BrandCode('AAA01'); - $this->actorId = ActorId::generate(); - }); + $code = new CampaignCode('Camp100'); + $name = new CampaignName('Name'); + $publication = \boolval(0); + $printAllAtOnce = \boolval(1); + $coupons = new CampaignCouponArray([ + new CampaignCoupon( + new CouponCode('AAA01'), + new PrintRules(maxPerCustomer: 3) + ) + ]); + $brandCode = new BrandCode('AAA01'); + $actorId = ActorId::generate(); afterEach(function (): void { CarbonImmutable::setTestNow(); }); - it('creates a campaign', function (): void { + it('creates a campaign', function () use ( + $code, + $name, + $publication, + $printAllAtOnce, + $coupons, + $brandCode, + $actorId, + ): void { // GIVEN $id = CampaignId::generate(); // WHEN $campaign = Campaign::hydrate( $id, - $this->code, - $this->name, - $this->publication, - $this->printAllAtOnce, - $this->coupons, - $this->brandCode, - $this->actorId + $code, + $name, + $publication, + $printAllAtOnce, + $coupons, + $brandCode, + $actorId ); // THEN expect($campaign->id())->toBe($id) - ->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->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->updatedAt)->toBeNull() ->and($campaign->updatedAt())->toBeNull(); }); - it('creates a campaign without coupon', function (): void { - // GIVEN - $id = CampaignId::generate(); - - // WHEN - $campaign = Campaign::hydrate( - $id, - $this->code, - $this->name, - $this->publication, - $this->printAllAtOnce, - [], - $this->brandCode, - $this->actorId - ); - - // THEN - expect($campaign->coupons)->toBe([]); - }); - - it('creates a coupon with an explicit updatedAt date', function (): void { + it('creates a coupon with an explicit updatedAt date', function () use ( + $code, + $name, + $publication, + $printAllAtOnce, + $coupons, + $brandCode, + $actorId, + ): void { // GIVEN $id = CampaignId::generate(); $updatedAt = new \DateTimeImmutable('2026-09-22 10:00:00', new \DateTimeZone('UTC')); @@ -98,13 +89,13 @@ describe('campaign', function (): void { // WHEN $campaign = Campaign::hydrate( $id, - $this->code, - $this->name, - $this->publication, - $this->printAllAtOnce, - $this->coupons, - $this->brandCode, - $this->actorId, + $code, + $name, + $publication, + $printAllAtOnce, + $coupons, + $brandCode, + $actorId, $updatedAt ); @@ -113,68 +104,92 @@ describe('campaign', function (): void { ->and($campaign->updatedAt())->toBe($updatedAt); }); - it('creates a new campaign with a freshly generated id', function (): void { + it('creates a new campaign with a freshly generated id', function () use ( + $code, + $name, + $publication, + $printAllAtOnce, + $coupons, + $brandCode, + $actorId, + ): void { // GIVEN / WHEN $campaign = Campaign::create( - $this->code, - $this->name, - $this->publication, - $this->printAllAtOnce, - $this->coupons, - $this->brandCode, - $this->actorId + $code, + $name, + $publication, + $printAllAtOnce, + $coupons, + $brandCode, + $actorId ); // THEN expect($campaign->id())->toBeInstanceOf(CampaignId::class) - ->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->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->updatedAt)->toBeNull() ->and($campaign->updatedAt())->toBeNull(); }); - it('generates a different id for each created campaign', function (): void { + it('generates a different id for each created campaign', function () use ( + $code, + $name, + $publication, + $printAllAtOnce, + $coupons, + $brandCode, + $actorId, + ): void { // GIVEN / WHEN $firstCampaign = Campaign::create( - $this->code, - $this->name, - $this->publication, - $this->printAllAtOnce, - $this->coupons, - $this->brandCode, - $this->actorId + $code, + $name, + $publication, + $printAllAtOnce, + $coupons, + $brandCode, + $actorId ); $secondCampaign = Campaign::create( - $this->code, - $this->name, - $this->publication, - $this->printAllAtOnce, - $this->coupons, - $this->brandCode, - $this->actorId + $code, + $name, + $publication, + $printAllAtOnce, + $coupons, + $brandCode, + $actorId ); // THEN expect($firstCampaign->id()->equals($secondCampaign->id()))->toBeFalse(); }); - it('returns the creation date extracted from the id', function (): void { + it('returns the creation date extracted from the id', function () use ( + $code, + $name, + $publication, + $printAllAtOnce, + $coupons, + $brandCode, + $actorId, + ): void { // GIVEN $expected = new \DateTimeImmutable('2024-06-15 12:34:56.789000', new \DateTimeZone('UTC')); $id = new CampaignId(Uuid::uuid7($expected)); $campaign = Campaign::hydrate( $id, - $this->code, - $this->name, - $this->publication, - $this->printAllAtOnce, - $this->coupons, - $this->brandCode, - $this->actorId + $code, + $name, + $publication, + $printAllAtOnce, + $coupons, + $brandCode, + $actorId ); // WHEN @@ -184,21 +199,20 @@ describe('campaign', function (): void { 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 { + it('rejects a campaign with a non-coupon element', function () use ($code, $name, $publication, $printAllAtOnce, $brandCode, $actorId): void { // GIVEN - $id = CampaignId::generate(); - $coupons = ['PC1']; + $id = CampaignId::generate(); // WHEN / THEN expect(fn (): Campaign => Campaign::hydrate( $id, - $this->code, - $this->name, - $this->publication, - $this->printAllAtOnce, - $coupons, - $this->brandCode, - $this->actorId - ))->toThrow(\DomainException::class, 'Campaign coupons must be CampaignCoupon instances.'); + $code, + $name, + $publication, + $printAllAtOnce, + new CampaignCouponArray(['PC1']), + $brandCode, + $actorId + ))->toThrow(\DomainException::class, 'Coupons must be CampaignCoupon instances.'); }); }); diff --git a/tests/Unit/Domain/Models/Entities/CouponTest.php b/tests/Unit/Domain/Models/Entities/CouponTest.php index 6b54f95..003084b 100644 --- a/tests/Unit/Domain/Models/Entities/CouponTest.php +++ b/tests/Unit/Domain/Models/Entities/CouponTest.php @@ -6,7 +6,6 @@ namespace Tests\Unit\Domain\Models\Entities; use Carbon\CarbonImmutable; use DateTime; -use Domain\Enums\BarcodeFormat; use Shared\Enums\Country; use Domain\Models\Entities\Coupon; use Domain\Models\ValueObjects\Actor\ActorId; @@ -15,17 +14,22 @@ use Domain\Models\ValueObjects\Coupon\CouponAvailabilityCalendar; use Domain\Models\ValueObjects\Coupon\CouponCode; use Domain\Models\ValueObjects\Coupon\CouponDiffusion; use Domain\Models\ValueObjects\Coupon\CouponId; +use Domain\Models\ValueObjects\Coupon\CouponIncoCode; use Domain\Models\ValueObjects\Coupon\CouponLegalInformation; -use Domain\Models\ValueObjects\Coupon\CouponValidity; +use Domain\Models\ValueObjects\Coupon\CouponLegalInformationArray; +use Domain\Models\ValueObjects\Coupon\CouponMovement; +use Domain\Models\ValueObjects\Coupon\CouponMovementArray; +use Domain\Models\ValueObjects\Coupon\CouponName; +use Domain\Models\ValueObjects\Coupon\CouponPicture; +use Domain\Models\ValueObjects\Coupon\CouponProcessingCode; +use Domain\Models\ValueObjects\Coupon\CouponProcessingCodeArray; +use Domain\Models\ValueObjects\Coupon\CouponProductArray; +use Domain\Models\ValueObjects\Coupon\CouponQuantity; use Domain\Models\ValueObjects\Coupon\CouponValidityInformation; -use Domain\Models\ValueObjects\CouponName; -use Domain\Models\ValueObjects\CouponPicture; -use Domain\Models\ValueObjects\GS1; -use Domain\Models\ValueObjects\IncoCode; +use Domain\Models\ValueObjects\Coupon\CouponValidityInformationArray; use Domain\Models\ValueObjects\MoneyAmount; use Domain\Models\ValueObjects\Period; use Domain\Models\ValueObjects\PicturePath; -use Domain\Models\ValueObjects\ProcessingCode; use Ramsey\Uuid\Uuid; use Shared\Enums\Currency; use Shared\ValueObjects\Money; @@ -33,274 +37,277 @@ use Shared\ValueObjects\Money; covers(Coupon::class); describe('coupon', function (): void { - beforeEach(function (): void { - $this->code = new CouponCode('COU01'); - $this->name = new CouponName('Name'); - $this->amount = new MoneyAmount(new Money(Currency::EUR, 1)); - $this->picture = new CouponPicture( - new PicturePath('files/product.jpg'), - new PicturePath('files/thumbnail.jpg'), - new PicturePath('files/inactive_thumbnail.jpg') - ); - $this->diffusion = new CouponDiffusion(new Period(new DateTime('2026-01-01'), new DateTime('2026-02-01'))); - $this->validity = new CouponValidity(30, new DateTime('2026-02-01')); - $this->couponValidityInformation = [ - new CouponValidityInformation( - 'Lorem ipsum dolor sit amet consectetur adipiscing elit.', - Country::FRANCE - ) - ]; - $this->couponLegalInformation = [ - new CouponLegalInformation( - 'Lorem ipsum dolor sit amet consectetur adipiscing elit.', - Country::FRANCE - ) - ]; - $this->processingCode = new ProcessingCode('5425003944130'); - $this->brandCode = new BrandCode('AAA01'); - $this->createdBy = ActorId::generate(); - $this->products = [new IncoCode(35784), new IncoCode(15522)]; - $this->availabilityCalendar = new CouponAvailabilityCalendar([new Period(new DateTime('2026-08-25'), new DateTime('2026-10-31'))]); - $this->gs1 = new GS1('110101010', BarcodeFormat::EAN13); - }); + $code = new CouponCode('AAA01'); + $name = new CouponName('Name'); + $amount = new MoneyAmount(new Money(Currency::EUR, 1)); + $picture = new CouponPicture( + new PicturePath('files/product.jpg'), + new PicturePath('files/thumbnail.jpg'), + new PicturePath('files/mobileThumbnail.jpg'), + ); + $diffusion = new CouponDiffusion(new Period(new DateTime('2026-01-01'), new DateTime('2026-02-01'))); + $validityInformations = new CouponValidityInformationArray([ + new CouponValidityInformation( + 'Lorem ipsum dolor sit amet consectetur adipiscing elit.', + Country::FRANCE, + ) + ]); + $legalInformations = new CouponLegalInformationArray([ + new CouponLegalInformation( + 'Lorem ipsum dolor sit amet consectetur adipiscing elit.', + Country::FRANCE, + ) + ]); + $processingCode = new CouponProcessingCodeArray([ + new CouponProcessingCode('5425003944130', Country::FRANCE), + ]); + $brandCode = new BrandCode('AAA01'); + $createdBy = ActorId::generate(); + $products = new CouponProductArray([new CouponIncoCode(35784), new CouponIncoCode(15522)]); + $movements = new CouponMovementArray([new CouponMovement(100)]); + $quantity = new CouponQuantity(100); + $availabilityCalendar = new CouponAvailabilityCalendar([ + new Period( + new DateTime('2026-08-25'), + new DateTime('2026-10-31'), + )]); afterEach(function (): void { CarbonImmutable::setTestNow(); }); - it('creates a coupon', function (): void { + it('creates a coupon', function () use ( + $code, + $name, + $amount, + $picture, + $diffusion, + $validityInformations, + $legalInformations, + $processingCode, + $brandCode, + $createdBy, + $products, + $movements, + $quantity, + $availabilityCalendar, + ): void { // GIVEN $id = CouponId::generate(); // WHEN $coupon = Coupon::hydrate( - $id, - $this->code, - $this->name, - $this->amount, - $this->picture, - $this->diffusion, - $this->validity, - $this->couponValidityInformation, - $this->couponLegalInformation, - $this->processingCode, - $this->brandCode, - $this->createdBy, - $this->products, - $this->availabilityCalendar, - $this->gs1, + id: $id, + code: $code, + name: $name, + amount: $amount, + picture: $picture, + diffusion: $diffusion, + validityInformations: $validityInformations, + legalInformations: $legalInformations, + processingCode: $processingCode, + brandCode: $brandCode, + createdBy: $createdBy, + products: $products, + movements: $movements, + quantity: $quantity, + availabilityCalendar: $availabilityCalendar, ); // THEN expect($coupon->id())->toBe($id) - ->and($coupon->code)->toBe($this->code) - ->and($coupon->name)->toBe($this->name) - ->and($coupon->amount)->toBe($this->amount) - ->and($coupon->picture)->toBe($this->picture) - ->and($coupon->diffusion)->toBe($this->diffusion) - ->and($coupon->validity)->toBe($this->validity) - ->and($coupon->couponValidityInformation)->toBe($this->couponValidityInformation) - ->and($coupon->couponLegalInformation)->toBe($this->couponLegalInformation) - ->and($coupon->processingCode)->toBe($this->processingCode) - ->and($coupon->brandCode)->toBe($this->brandCode) - ->and($coupon->createdBy)->toBe($this->createdBy) - ->and($coupon->products)->toBe($this->products) - ->and($coupon->availabilityCalendar)->toBe($this->availabilityCalendar) - ->and($coupon->gs1)->toBe($this->gs1) + ->and($coupon->code)->toBe($code) + ->and($coupon->name)->toBe($name) + ->and($coupon->amount)->toBe($amount) + ->and($coupon->picture)->toBe($picture) + ->and($coupon->diffusion)->toBe($diffusion) + ->and($coupon->validityInformations)->toBe($validityInformations) + ->and($coupon->legalInformations)->toBe($legalInformations) + ->and($coupon->processingCode)->toBe($processingCode) + ->and($coupon->brandCode)->toBe($brandCode) + ->and($coupon->createdBy)->toBe($createdBy) + ->and($coupon->products)->toBe($products) + ->and($coupon->availabilityCalendar)->toBe($availabilityCalendar) + ->and($coupon->movements)->toBe($movements) ->and($coupon->updatedAt)->toBeNull() ->and($coupon->updatedAt())->toBeNull(); }); - it('hydrates a coupon without gs1', function (): void { + it('hydrates a coupon without availability calendar', function () use ( + $code, + $name, + $amount, + $picture, + $diffusion, + $validityInformations, + $legalInformations, + $processingCode, + $brandCode, + $createdBy, + $products, + $movements, + $quantity, + ): void { // GIVEN $id = CouponId::generate(); // WHEN $coupon = Coupon::hydrate( id: $id, - code: $this->code, - name: $this->name, - amount: $this->amount, - picture: $this->picture, - diffusion: $this->diffusion, - validity: $this->validity, - couponValidityInformation: $this->couponValidityInformation, - couponLegalInformation: $this->couponLegalInformation, - processingCode: $this->processingCode, - brandCode: $this->brandCode, - createdBy: $this->createdBy, - products: $this->products, - availabilityCalendar: $this->availabilityCalendar - ); - - // THEN - expect($coupon->products)->toBe($this->products) - ->and($coupon->availabilityCalendar)->toBe($this->availabilityCalendar) - ->and($coupon->gs1)->toBeNull(); - }); - - it('hydrates a coupon without availability calendar', function (): void { - // GIVEN - $id = CouponId::generate(); - - // WHEN - $coupon = Coupon::hydrate( - id: $id, - code: $this->code, - name: $this->name, - amount: $this->amount, - picture: $this->picture, - diffusion: $this->diffusion, - validity: $this->validity, - couponValidityInformation: $this->couponValidityInformation, - couponLegalInformation: $this->couponLegalInformation, - processingCode: $this->processingCode, - brandCode: $this->brandCode, - createdBy: $this->createdBy, - products: $this->products, - gs1: $this->gs1 + code: $code, + name: $name, + amount: $amount, + picture: $picture, + diffusion: $diffusion, + validityInformations: $validityInformations, + legalInformations: $legalInformations, + processingCode: $processingCode, + brandCode: $brandCode, + createdBy: $createdBy, + products: $products, + movements: $movements, + quantity: $quantity, ); // THEN expect($coupon->availabilityCalendar)->toBeNull() - ->and($coupon->products)->toBe($this->products) - ->and($coupon->gs1)->toBe($this->gs1); + ->and($coupon->products)->toBe($products); }); - it('hydrates a coupon without products', function (): void { + it('hydrates a coupon without products', function () use ( + $code, + $name, + $amount, + $picture, + $diffusion, + $validityInformations, + $legalInformations, + $processingCode, + $brandCode, + $createdBy, + $movements, + $quantity, + $availabilityCalendar, + ): void { // GIVEN $id = CouponId::generate(); // WHEN $coupon = Coupon::hydrate( id: $id, - code: $this->code, - name: $this->name, - amount: $this->amount, - picture: $this->picture, - diffusion: $this->diffusion, - validity: $this->validity, - couponValidityInformation: $this->couponValidityInformation, - couponLegalInformation: $this->couponLegalInformation, - processingCode: $this->processingCode, - brandCode: $this->brandCode, - createdBy: $this->createdBy, - availabilityCalendar: $this->availabilityCalendar, - gs1: $this->gs1 + code: $code, + name: $name, + amount: $amount, + picture: $picture, + diffusion: $diffusion, + validityInformations: $validityInformations, + legalInformations: $legalInformations, + processingCode: $processingCode, + brandCode: $brandCode, + createdBy: $createdBy, + products: new CouponProductArray(), + movements: $movements, + quantity: $quantity, + availabilityCalendar: $availabilityCalendar, ); // THEN - expect($coupon->products)->toBe([]) - ->and($coupon->availabilityCalendar)->toBe($this->availabilityCalendar) - ->and($coupon->gs1)->toBe($this->gs1); + expect($coupon->products->value)->toBe([]) + ->and($coupon->availabilityCalendar)->toBe($availabilityCalendar); }); - it('hydrates a coupon with only required fields', function (): void { + it('throws when validity information contains an invalid item', function () use ( + $code, + $name, + $amount, + $picture, + $diffusion, + $legalInformations, + $processingCode, + $brandCode, + $createdBy, + $products, + $movements, + $quantity, + $availabilityCalendar, + ): void { // GIVEN $id = CouponId::generate(); - // WHEN - $coupon = Coupon::hydrate( + // WHEN / THEN + expect(fn (): Coupon => Coupon::hydrate( id: $id, - code: $this->code, - name: $this->name, - amount: $this->amount, - picture: $this->picture, - diffusion: $this->diffusion, - validity: $this->validity, - couponValidityInformation: $this->couponValidityInformation, - couponLegalInformation: $this->couponLegalInformation, - processingCode: $this->processingCode, - brandCode: $this->brandCode, - createdBy: $this->createdBy, - ); - - // THEN - expect($coupon->id())->toBe($id) - ->and($coupon->products)->toBe([]) - ->and($coupon->availabilityCalendar)->toBeNull() - ->and($coupon->gs1)->toBeNull() - ->and($coupon->updatedAt)->toBeNull() - ->and($coupon->updatedAt())->toBeNull(); + code: $code, + name: $name, + amount: $amount, + picture: $picture, + diffusion: $diffusion, + validityInformations: new CouponValidityInformationArray(['Not a CouponValidityInformation']), + legalInformations: $legalInformations, + processingCode: $processingCode, + brandCode: $brandCode, + createdBy: $createdBy, + products: $products, + movements: $movements, + quantity: $quantity, + availabilityCalendar: $availabilityCalendar, + ))->toThrow(\DomainException::class, 'Value of CouponValidityInformationArray must be CouponValidityInformation instances.'); }); - it('accepts empty validity and legal information lists', function (): void { - // GIVEN - $id = CouponId::generate(); - - // WHEN - $coupon = Coupon::hydrate( - $id, - $this->code, - $this->name, - $this->amount, - $this->picture, - $this->diffusion, - $this->validity, - [], - [], - $this->processingCode, - $this->brandCode, - $this->createdBy, - $this->products, - $this->availabilityCalendar, - $this->gs1, - ); - - // THEN - expect($coupon->couponValidityInformation)->toBe([]) - ->and($coupon->couponLegalInformation)->toBe([]); - }); - - it('throws when validity information contains an invalid item', function (): void { + it('throws when legal information contains an invalid item', function () use ( + $code, + $name, + $amount, + $picture, + $diffusion, + $validityInformations, + $processingCode, + $brandCode, + $createdBy, + $products, + $movements, + $quantity, + $availabilityCalendar, + ): void { // GIVEN $id = CouponId::generate(); // WHEN / THEN - expect(fn () => Coupon::hydrate( - $id, - $this->code, - $this->name, - $this->amount, - $this->picture, - $this->diffusion, - $this->validity, - ['Not a CouponValidityInformation'], - $this->couponLegalInformation, - $this->processingCode, - $this->brandCode, - $this->createdBy, - $this->products, - $this->availabilityCalendar, - $this->gs1, - ))->toThrow(\DomainException::class, 'Coupon validity information must be CouponValidityInformation instances.'); + expect(fn (): Coupon => Coupon::hydrate( + id: $id, + code: $code, + name: $name, + amount: $amount, + picture: $picture, + diffusion: $diffusion, + validityInformations: $validityInformations, + legalInformations: new CouponLegalInformationArray(['Not a CouponLegalInformation']), + processingCode: $processingCode, + brandCode: $brandCode, + createdBy: $createdBy, + products: $products, + movements: $movements, + quantity: $quantity, + availabilityCalendar: $availabilityCalendar, + ))->toThrow(\DomainException::class, 'Value of CouponLegalInformationArray must be CouponLegalInformation instances.'); }); - it('throws when legal information contains an invalid item', function (): void { - // GIVEN - $id = CouponId::generate(); - - // WHEN / THEN - expect(fn () => Coupon::hydrate( - $id, - $this->code, - $this->name, - $this->amount, - $this->picture, - $this->diffusion, - $this->validity, - $this->couponValidityInformation, - ['Not a CouponLegalInformation'], - $this->processingCode, - $this->brandCode, - $this->createdBy, - $this->products, - $this->availabilityCalendar, - $this->gs1, - ))->toThrow(\DomainException::class, 'Coupon legal information must be CouponLegalInformation instances.'); - }); - - it('hydrates a coupon with an explicit updatedAt date', function (): void { + it('hydrates a coupon with an explicit updatedAt date', function () use ( + $code, + $name, + $amount, + $picture, + $diffusion, + $validityInformations, + $legalInformations, + $processingCode, + $brandCode, + $createdBy, + $products, + $movements, + $quantity, + $availabilityCalendar, + ): void { // GIVEN $id = CouponId::generate(); $updatedAt = new \DateTimeImmutable('2026-09-22 10:00:00', new \DateTimeZone('UTC')); @@ -308,21 +315,21 @@ describe('coupon', function (): void { // WHEN $coupon = Coupon::hydrate( $id, - $this->code, - $this->name, - $this->amount, - $this->picture, - $this->diffusion, - $this->validity, - $this->couponValidityInformation, - $this->couponLegalInformation, - $this->processingCode, - $this->brandCode, - $this->createdBy, - $this->products, - $this->availabilityCalendar, - $this->gs1, - $updatedAt + code: $code, + name: $name, + amount: $amount, + picture: $picture, + diffusion: $diffusion, + validityInformations: $validityInformations, + legalInformations: $legalInformations, + processingCode: $processingCode, + brandCode: $brandCode, + createdBy: $createdBy, + products: $products, + movements: $movements, + quantity: $quantity, + availabilityCalendar: $availabilityCalendar, + updatedAt: $updatedAt, ); // THEN @@ -330,104 +337,149 @@ describe('coupon', function (): void { ->and($coupon->updatedAt())->toBe($updatedAt); }); - it('creates a new coupon with a freshly generated id', function (): void { + it('creates a new coupon with a freshly generated id', function () use ( + $code, + $name, + $amount, + $picture, + $diffusion, + $validityInformations, + $legalInformations, + $processingCode, + $brandCode, + $createdBy, + $products, + $movements, + $quantity, + $availabilityCalendar, + ): void { // GIVEN / WHEN $coupon = Coupon::create( - $this->code, - $this->name, - $this->amount, - $this->picture, - $this->diffusion, - $this->validity, - $this->couponValidityInformation, - $this->couponLegalInformation, - $this->processingCode, - $this->brandCode, - $this->createdBy, - $this->products, - $this->availabilityCalendar, - $this->gs1, + code: $code, + name: $name, + amount: $amount, + picture: $picture, + diffusion: $diffusion, + validityInformations: $validityInformations, + legalInformations: $legalInformations, + processingCode: $processingCode, + brandCode: $brandCode, + createdBy: $createdBy, + products: $products, + movements: $movements, + quantity: $quantity, + availabilityCalendar: $availabilityCalendar, ); // THEN expect($coupon->id())->toBeInstanceOf(CouponId::class) - ->and($coupon->code)->toBe($this->code) - ->and($coupon->name)->toBe($this->name) - ->and($coupon->amount)->toBe($this->amount) - ->and($coupon->picture)->toBe($this->picture) - ->and($coupon->diffusion)->toBe($this->diffusion) - ->and($coupon->validity)->toBe($this->validity) - ->and($coupon->couponValidityInformation)->toBe($this->couponValidityInformation) - ->and($coupon->couponLegalInformation)->toBe($this->couponLegalInformation) - ->and($coupon->processingCode)->toBe($this->processingCode) - ->and($coupon->brandCode)->toBe($this->brandCode) - ->and($coupon->createdBy)->toBe($this->createdBy) - ->and($coupon->products)->toBe($this->products) - ->and($coupon->availabilityCalendar)->toBe($this->availabilityCalendar) - ->and($coupon->gs1)->toBe($this->gs1) + ->and($coupon->code)->toBe($code) + ->and($coupon->name)->toBe($name) + ->and($coupon->amount)->toBe($amount) + ->and($coupon->picture)->toBe($picture) + ->and($coupon->diffusion)->toBe($diffusion) + ->and($coupon->validityInformations)->toBe($validityInformations) + ->and($coupon->legalInformations)->toBe($legalInformations) + ->and($coupon->processingCode)->toBe($processingCode) + ->and($coupon->brandCode)->toBe($brandCode) + ->and($coupon->createdBy)->toBe($createdBy) + ->and($coupon->products)->toBe($products) + ->and($coupon->movements)->toBe($movements) + ->and($coupon->quantity)->toBe($quantity) + ->and($coupon->availabilityCalendar)->toBe($availabilityCalendar) ->and($coupon->updatedAt)->toBeNull() ->and($coupon->updatedAt())->toBeNull(); }); - it('generates a different id for each created coupon', function (): void { + it('generates a different id for each created coupon', function () use ( + $code, + $name, + $amount, + $picture, + $diffusion, + $validityInformations, + $legalInformations, + $processingCode, + $brandCode, + $createdBy, + $products, + $movements, + $quantity, + $availabilityCalendar, + ): void { // GIVEN / WHEN $firstCoupon = Coupon::create( - $this->code, - $this->name, - $this->amount, - $this->picture, - $this->diffusion, - $this->validity, - $this->couponValidityInformation, - $this->couponLegalInformation, - $this->processingCode, - $this->brandCode, - $this->createdBy, - $this->products, - $this->availabilityCalendar, - $this->gs1, + code: $code, + name: $name, + amount: $amount, + picture: $picture, + diffusion: $diffusion, + validityInformations: $validityInformations, + legalInformations: $legalInformations, + processingCode: $processingCode, + brandCode: $brandCode, + createdBy: $createdBy, + products: $products, + movements: $movements, + quantity: $quantity, + availabilityCalendar: $availabilityCalendar, ); $secondCoupon = Coupon::create( - $this->code, - $this->name, - $this->amount, - $this->picture, - $this->diffusion, - $this->validity, - $this->couponValidityInformation, - $this->couponLegalInformation, - $this->processingCode, - $this->brandCode, - $this->createdBy, - $this->products, - $this->availabilityCalendar, - $this->gs1, + code: $code, + name: $name, + amount: $amount, + picture: $picture, + diffusion: $diffusion, + validityInformations: $validityInformations, + legalInformations: $legalInformations, + processingCode: $processingCode, + brandCode: $brandCode, + createdBy: $createdBy, + products: $products, + movements: $movements, + quantity: $quantity, + availabilityCalendar: $availabilityCalendar, ); // THEN expect($firstCoupon->id()->equals($secondCoupon->id()))->toBeFalse(); }); - it('returns the creation date extracted from the id', function (): void { + it('returns the creation date extracted from the id', function () use ( + $code, + $name, + $amount, + $picture, + $diffusion, + $validityInformations, + $legalInformations, + $processingCode, + $brandCode, + $createdBy, + $products, + $movements, + $quantity, + $availabilityCalendar, + ): void { // GIVEN $expected = new \DateTimeImmutable('2024-06-15 12:34:56.789000', new \DateTimeZone('UTC')); $id = new CouponId(Uuid::uuid7($expected)); $coupon = Coupon::hydrate( $id, - $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->gs1, + code: $code, + name: $name, + amount: $amount, + picture: $picture, + diffusion: $diffusion, + validityInformations: $validityInformations, + legalInformations: $legalInformations, + processingCode: $processingCode, + brandCode: $brandCode, + createdBy: $createdBy, + products: $products, + movements: $movements, + quantity: $quantity, + availabilityCalendar: $availabilityCalendar, ); // WHEN diff --git a/tests/Unit/Domain/Models/Entities/StockTest.php b/tests/Unit/Domain/Models/Entities/StockTest.php index a30b6d7..6d4d469 100644 --- a/tests/Unit/Domain/Models/Entities/StockTest.php +++ b/tests/Unit/Domain/Models/Entities/StockTest.php @@ -7,7 +7,7 @@ namespace Tests\Unit\Domain\Models\Entities; use Domain\Models\Entities\Stock; use Domain\Models\ValueObjects\Actor\ActorId; use Domain\Models\ValueObjects\Campaign\CampaignCode; -use Domain\Models\ValueObjects\ProcessingCode; +use Domain\Models\ValueObjects\Coupon\CouponCode; use Domain\Models\ValueObjects\Stock\StockAdjustment; use Domain\Models\ValueObjects\Stock\StockAmount; use Domain\Models\ValueObjects\Stock\StockId; @@ -16,40 +16,38 @@ 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())]; - }); + $stockAmount = new StockAmount(10); + $campaignCode = new CampaignCode('AAA01'); + $couponCode = new CouponCode('AAA01'); + $stockAdjustment = [new StockAdjustment(5, 'Inventory correction', ActorId::generate())]; - it('hydrates a stock', function (): void { + it('hydrates a stock', function () use ($stockAmount, $campaignCode, $couponCode, $stockAdjustment): void { // GIVEN $id = StockId::generate(); // WHEN $stock = Stock::hydrate( $id, - $this->stockAmount, - $this->campaignCode, - $this->processingCode, - $this->stockAdjustment + $stockAmount, + $campaignCode, + $couponCode, + $stockAdjustment, ); // THEN expect($stock->id())->toBe($id) - ->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->stockAmount)->toBe($stockAmount) + ->and($stock->campaignCode)->toBe($campaignCode) + ->and($stock->couponCode)->toBe($couponCode) + ->and($stock->stockAdjustment)->toBe($stockAdjustment) ->and($stock->updatedAt)->toBeNull() ->and($stock->updatedAt())->toBeNull(); }); - it('hydrates a stock with several stock adjustments', function (): void { + it('hydrates a stock with several stock adjustments', function () use ($stockAmount, $campaignCode, $couponCode): void { // GIVEN - $id = StockId::generate(); - $stockAdjustment = [ + $id = StockId::generate(); + $severalAdjustments = [ new StockAdjustment(5, 'Inventory correction', ActorId::generate()), new StockAdjustment(10, 'Inventory correction', ActorId::generate()), ]; @@ -57,70 +55,70 @@ describe('stock', function (): void { // WHEN $stock = Stock::hydrate( $id, - $this->stockAmount, - $this->campaignCode, - $this->processingCode, - $stockAdjustment, + $stockAmount, + $campaignCode, + $couponCode, + $severalAdjustments, ); // THEN expect($stock->id())->toBe($id) - ->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->stockAmount)->toBe($stockAmount) + ->and($stock->campaignCode)->toBe($campaignCode) + ->and($stock->couponCode)->toBe($couponCode) + ->and($stock->stockAdjustment)->toBe($severalAdjustments) ->and($stock->updatedAt)->toBeNull() ->and($stock->updatedAt())->toBeNull(); }); - it('hydrates a stock without stock adjustement', function (): void { + it('hydrates a stock without stock adjustement', function () use ($stockAmount, $campaignCode, $couponCode): void { // GIVEN $id = StockId::generate(); // WHEN $stock = Stock::hydrate( $id, - $this->stockAmount, - $this->campaignCode, - $this->processingCode + $stockAmount, + $campaignCode, + $couponCode, ); // THEN expect($stock->id())->toBe($id) - ->and($stock->stockAmount)->toBe($this->stockAmount) - ->and($stock->campaignCode)->toBe($this->campaignCode) - ->and($stock->processingCode)->toBe($this->processingCode) + ->and($stock->stockAmount)->toBe($stockAmount) + ->and($stock->campaignCode)->toBe($campaignCode) + ->and($stock->couponCode)->toBe($couponCode) ->and($stock->stockAdjustment)->toBe([]) ->and($stock->updatedAt)->toBeNull() ->and($stock->updatedAt())->toBeNull(); }); - it('rejects a stock with a non-stock-adjustment element', function (): void { + it('rejects a stock with a non-stock-adjustment element', function () use ($stockAmount, $campaignCode, $couponCode): void { // GIVEN - $id = StockId::generate(); - $stockAdjustment = [new StockAmount(5)]; + $id = StockId::generate(); + $invalidAdjustment = [new StockAmount(5)]; // WHEN / THEN expect(fn (): Stock => Stock::hydrate( $id, - $this->stockAmount, - $this->campaignCode, - $this->processingCode, - $stockAdjustment, + $stockAmount, + $campaignCode, + $couponCode, + $invalidAdjustment, ))->toThrow(\DomainException::class, 'Stock adjustment must be StockAdjustment instances.'); }); - it('hydrates a stock with an explicit updatedAt date', function (): void { + it('hydrates a stock with an explicit updatedAt date', function () use ($stockAmount, $campaignCode, $couponCode, $stockAdjustment): void { // GIVEN $updatedAt = new \DateTimeImmutable('2026-06-15 12:00:00', new \DateTimeZone('UTC')); // WHEN $stock = Stock::hydrate( StockId::generate(), - $this->stockAmount, - $this->campaignCode, - $this->processingCode, - $this->stockAdjustment, + $stockAmount, + $campaignCode, + $couponCode, + $stockAdjustment, $updatedAt, ); @@ -129,16 +127,16 @@ describe('stock', function (): void { ->and($stock->updatedAt())->toBe($updatedAt); }); - it('returns the creation date extracted from the id', function (): void { + it('returns the creation date extracted from the id', function () use ($stockAmount, $campaignCode, $couponCode, $stockAdjustment): void { // GIVEN $expected = new \DateTimeImmutable('2024-06-15 12:34:56.789000', new \DateTimeZone('UTC')); $id = new StockId(Uuid::uuid7($expected)); $stock = Stock::hydrate( $id, - $this->stockAmount, - $this->campaignCode, - $this->processingCode, - $this->stockAdjustment + $stockAmount, + $campaignCode, + $couponCode, + $stockAdjustment ); // WHEN diff --git a/tests/Unit/Domain/Models/ValueObjects/Actor/ActorDisplayNameTest.php b/tests/Unit/Domain/Models/ValueObjects/Actor/ActorDisplayNameTest.php index 82b09a3..13a8cc6 100644 --- a/tests/Unit/Domain/Models/ValueObjects/Actor/ActorDisplayNameTest.php +++ b/tests/Unit/Domain/Models/ValueObjects/Actor/ActorDisplayNameTest.php @@ -9,25 +9,22 @@ use Domain\Models\ValueObjects\Actor\ActorDisplayName; covers(ActorDisplayName::class); describe('actor displayname', function (): void { - beforeEach(function (): void { - $this->value = 'Johnny'; - $this->emptyValue = ''; - }); + $value = 'Johnny'; + $emptyValue = ''; - it('creates a valid actor displayname', function (): void { + it('creates a valid actor displayname', function () use ($value): void { // GIVEN / WHEN $name = new ActorDisplayName( - $this->value + $value ); // THEN - expect($name->value)->toBe($this->value); + expect($name->value)->toBe($value); }); - it('rejects invalid actor displayname', function (): void { + it('rejects invalid actor displayname', function () use ($emptyValue): void { // GIVEN / WHEN / THEN - expect(fn (): ActorDisplayName => new ActorDisplayName( - $this->emptyValue - ))->toThrow(\DomainException::class); + expect(fn (): ActorDisplayName => new ActorDisplayName($emptyValue)) + ->toThrow(\DomainException::class); }); }); diff --git a/tests/Unit/Domain/Models/ValueObjects/Brand/BrandNameTest.php b/tests/Unit/Domain/Models/ValueObjects/Brand/BrandNameTest.php index 41a3762..c2f96aa 100644 --- a/tests/Unit/Domain/Models/ValueObjects/Brand/BrandNameTest.php +++ b/tests/Unit/Domain/Models/ValueObjects/Brand/BrandNameTest.php @@ -9,26 +9,21 @@ use Domain\Models\ValueObjects\Brand\BrandName; covers(BrandName::class); describe('brand name', function (): void { - beforeEach(function (): void { - $this->value = 'Name'; - $this->emptyValue = ''; - }); + $value = 'Name'; + $emptyValue = ''; - it('creates a valid brand name', function (): void { + it('creates a valid brand name', function () use ($value): void { // GIVEN / WHEN - $name = new BrandName( - $this->value - ); + $name = new BrandName($value); // THEN - expect($name->value)->toBe($this->value); + expect($name->value)->toBe($value); }); - it('rejects invalid brand name', function (): void { + it('rejects invalid brand name', function () use ($emptyValue): void { // GIVEN / WHEN / THEN - expect(fn (): BrandName => new BrandName( - $this->emptyValue - ))->toThrow(\DomainException::class); + expect(fn (): BrandName => new BrandName($emptyValue)) + ->toThrow(\DomainException::class); }); it('rejects a name that exceeds the maximum allowed length', function (): void { diff --git a/tests/Unit/Domain/Models/ValueObjects/Brand/BrandSortCriteriaTest.php b/tests/Unit/Domain/Models/ValueObjects/Brand/BrandSortCriteriaTest.php new file mode 100644 index 0000000..e9e946e --- /dev/null +++ b/tests/Unit/Domain/Models/ValueObjects/Brand/BrandSortCriteriaTest.php @@ -0,0 +1,34 @@ +field)->toBe(BrandSortField::Name) + ->and($criteria->direction)->toBe(\SortDirection::Ascending); + }); + + it('keeps the given field and direction', function (): void { + // GIVEN + $field = BrandSortField::Name; + $direction = \SortDirection::Descending; + + // WHEN + $criteria = new BrandSortCriteria($field, $direction); + + // THEN + expect($criteria->field)->toBe($field) + ->and($criteria->direction)->toBe($direction); + }); +}); diff --git a/tests/Unit/Domain/Models/ValueObjects/Campaign/CampaignCodeTest.php b/tests/Unit/Domain/Models/ValueObjects/Campaign/CampaignCodeTest.php index 939cefd..59bc1b3 100644 --- a/tests/Unit/Domain/Models/ValueObjects/Campaign/CampaignCodeTest.php +++ b/tests/Unit/Domain/Models/ValueObjects/Campaign/CampaignCodeTest.php @@ -9,23 +9,20 @@ use Domain\Models\ValueObjects\Campaign\CampaignCode; covers(CampaignCode::class); describe('campaign code', function (): void { - beforeEach(function (): void { - $this->code = 'Name'; - $this->emptyCode = ''; - }); + $code = 'Name'; + $emptyCode = ''; - it('creates a valid campaign code', function (): void { + it('creates a valid campaign code', function () use ($code): void { // GIVEN / WHEN - $name = new CampaignCode($this->code); + $name = new CampaignCode($code); // THEN - expect($name->code)->toBe($this->code); + expect($name->code)->toBe($code); }); - it('rejects invalid campaign code', function (): void { + it('rejects invalid campaign code', function () use ($emptyCode): void { // GIVEN / WHEN / THEN - expect(fn (): CampaignCode => new CampaignCode( - $this->emptyCode - ))->toThrow(\DomainException::class); + expect(fn (): CampaignCode => new CampaignCode($emptyCode)) + ->toThrow(\DomainException::class); }); }); diff --git a/tests/Unit/Domain/Models/ValueObjects/Campaign/CampaignCouponArrayTest.php b/tests/Unit/Domain/Models/ValueObjects/Campaign/CampaignCouponArrayTest.php new file mode 100644 index 0000000..8daae6e --- /dev/null +++ b/tests/Unit/Domain/Models/ValueObjects/Campaign/CampaignCouponArrayTest.php @@ -0,0 +1,57 @@ +value)->toBe($coupons); + }); + + it('rejects an empty array', function (): void { + // GIVEN / WHEN + $action = fn (): CampaignCouponArray => new CampaignCouponArray([]); + + // THEN + expect($action)->toThrow(\DomainException::class, 'Coupons requires at least one CampaignCoupon.'); + }); + + it('rejects a non-coupon element', function (): void { + // GIVEN / WHEN + $action = fn (): CampaignCouponArray => new CampaignCouponArray(['PC1']); + + // THEN + expect($action)->toThrow(\DomainException::class, 'Coupons must be CampaignCoupon instances.'); + }); + + it('rejects a non-coupon element mixed with valid coupons', function () use ($coupon): void { + // GIVEN + $coupons = [$coupon, 'PC1']; + + // WHEN + $action = fn (): CampaignCouponArray => new CampaignCouponArray($coupons); + + // THEN + expect($action)->toThrow(\DomainException::class, 'Coupons must be CampaignCoupon instances.'); + }); +}); diff --git a/tests/Unit/Domain/Models/ValueObjects/Campaign/CampaignCouponTest.php b/tests/Unit/Domain/Models/ValueObjects/Campaign/CampaignCouponTest.php index e700f2a..9be21d3 100644 --- a/tests/Unit/Domain/Models/ValueObjects/Campaign/CampaignCouponTest.php +++ b/tests/Unit/Domain/Models/ValueObjects/Campaign/CampaignCouponTest.php @@ -5,78 +5,34 @@ declare(strict_types=1); namespace Tests\Unit\Domain\Models\ValueObjects\Campaign; use Domain\Models\ValueObjects\Campaign\CampaignCoupon; -use Domain\Models\ValueObjects\Coupon\CouponQuantity; +use Domain\Models\ValueObjects\Coupon\CouponCode; use Domain\Models\ValueObjects\PrintRules; -use Domain\Models\ValueObjects\ProcessingCode; -use Shared\Enums\Country; covers(CampaignCoupon::class); describe('campaign coupon', function (): void { - beforeEach(function (): void { - $this->processingCode = new ProcessingCode('PC1'); - $this->quantity = new CouponQuantity(10); - $this->availableIn = Country::FRANCE; - $this->printRules = new PrintRules(maxPerCustomer: 3); - }); + $couponCode = new CouponCode('AAA01'); + $printRules = new PrintRules(maxPerCustomer: 3); - it('creates a valid campaign coupon', function (): void { + it('creates a valid campaign coupon', function () use ($couponCode, $printRules): void { // GIVEN / WHEN $campaignCoupon = new CampaignCoupon( - $this->processingCode, - $this->quantity, - $this->availableIn, - $this->printRules + couponCode: $couponCode, + printRules: $printRules, ); // THEN - expect($campaignCoupon->processingCode)->toBe($this->processingCode) - ->and($campaignCoupon->quantity)->toBe($this->quantity) - ->and($campaignCoupon->availableIn)->toBe($this->availableIn) - ->and($campaignCoupon->printRules)->toBe($this->printRules); + expect($campaignCoupon->couponCode)->toBe($couponCode) + ->and($campaignCoupon->printRules)->toBe($printRules); }); - it('creates a valid campaign coupon whitout warning threshold quantity', function (): void { + it('creates a valid campaign coupon whitout print rules', function () use ($couponCode): void { // GIVEN / WHEN $campaignCoupon = new CampaignCoupon( - processingCode: $this->processingCode, - quantity: $this->quantity, - availableIn: $this->availableIn, - printRules: $this->printRules + couponCode: $couponCode, ); // THEN - 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 / WHEN - $campaignCoupon = new CampaignCoupon( - processingCode: $this->processingCode, - quantity: $this->quantity, - availableIn: $this->availableIn, - ); - - // THEN - expect($campaignCoupon->processingCode)->toBe($this->processingCode) - ->and($campaignCoupon->quantity)->toBe($this->quantity) - ->and($campaignCoupon->availableIn)->toBe($this->availableIn); - }); - - it('creates a valid campaign coupon whitout warning threshold quantity and print rules', function (): void { - // GIVEN / WHEN - $campaignCoupon = new CampaignCoupon( - processingCode: $this->processingCode, - quantity: $this->quantity, - availableIn: $this->availableIn - ); - - // THEN - expect($campaignCoupon->processingCode)->toBe($this->processingCode) - ->and($campaignCoupon->quantity)->toBe($this->quantity) - ->and($campaignCoupon->availableIn)->toBe($this->availableIn); + expect($campaignCoupon->couponCode)->toBe($couponCode); }); }); diff --git a/tests/Unit/Domain/Models/ValueObjects/Campaign/CampaignNameTest.php b/tests/Unit/Domain/Models/ValueObjects/Campaign/CampaignNameTest.php index ad4af96..770ee44 100644 --- a/tests/Unit/Domain/Models/ValueObjects/Campaign/CampaignNameTest.php +++ b/tests/Unit/Domain/Models/ValueObjects/Campaign/CampaignNameTest.php @@ -9,23 +9,20 @@ use Domain\Models\ValueObjects\Campaign\CampaignName; covers(CampaignName::class); describe('campaign name', function (): void { - beforeEach(function (): void { - $this->value = 'Name'; - $this->emptyValue = ''; - }); + $value = 'Name'; + $emptyValue = ''; - it('creates a valid campaign name', function (): void { + it('creates a valid campaign name', function () use ($value): void { // GIVEN / WHEN - $name = new CampaignName($this->value); + $name = new CampaignName($value); // THEN - expect($name->value)->toBe($this->value); + expect($name->value)->toBe($value); }); - it('rejects invalid campaign name', function (): void { + it('rejects invalid campaign name', function () use ($emptyValue): void { // GIVEN / WHEN / THEN - expect(fn (): CampaignName => new CampaignName( - $this->emptyValue - ))->toThrow(\DomainException::class); + expect(fn (): CampaignName => new CampaignName($emptyValue)) + ->toThrow(\DomainException::class); }); }); diff --git a/tests/Unit/Domain/Models/ValueObjects/CampaignRequest/ApplicantTest.php b/tests/Unit/Domain/Models/ValueObjects/CampaignRequest/ApplicantTest.php index 730b5c6..ee75550 100644 --- a/tests/Unit/Domain/Models/ValueObjects/CampaignRequest/ApplicantTest.php +++ b/tests/Unit/Domain/Models/ValueObjects/CampaignRequest/ApplicantTest.php @@ -10,33 +10,31 @@ use Shared\ValueObjects\Email; covers(Applicant::class); describe('applicant', function (): void { - beforeEach(function (): void { - $this->firstname = 'John'; - $this->lastname = 'Doe'; - $this->email = new Email('john.doe@example.com'); - }); + $firstname = 'John'; + $lastname = 'Doe'; + $email = new Email('john.doe@example.com'); - it('creates a valid applicant', function (): void { + it('creates a valid applicant', function () use ($firstname, $lastname, $email): void { // GIVEN / WHEN $applicant = new Applicant( - $this->firstname, - $this->lastname, - $this->email + $firstname, + $lastname, + $email, ); // THEN expect($applicant)->toBeInstanceOf(Applicant::class) - ->and($applicant->firstname)->toBe($this->firstname) - ->and($applicant->lastname)->toBe($this->lastname) - ->and($applicant->email)->toBe($this->email); + ->and($applicant->firstname)->toBe($firstname) + ->and($applicant->lastname)->toBe($lastname) + ->and($applicant->email)->toBe($email); }); - it('rejects an empty or whitespace-only firstname', function (string $firstname): void { + it('rejects an empty or whitespace-only firstname', function (string $invalidFirstname) use ($lastname, $email): void { // WHEN $action = fn (): Applicant => new Applicant( - $firstname, - $this->lastname, - $this->email + $invalidFirstname, + $lastname, + $email, ); // THEN @@ -46,42 +44,42 @@ describe('applicant', function (): void { 'spaces' => ' ', ]); - it('rejects a firstname that exceeds the maximum allowed length', function (): void { + it('rejects a firstname that exceeds the maximum allowed length', function () use ($lastname, $email): void { // GIVEN - $firstname = str_repeat('a', 201); + $tooLongFirstname = str_repeat('a', 201); // WHEN $action = fn (): Applicant => new Applicant( - $firstname, - $this->lastname, - $this->email + $tooLongFirstname, + $lastname, + $email, ); // THEN expect($action)->toThrow(\DomainException::class, 'Applicant firstname exceeds the maximum allowed length.'); }); - it('accepts a firstname of exactly the maximum length', function (): void { + it('accepts a firstname of exactly the maximum length', function () use ($lastname, $email): void { // GIVEN - $firstname = str_repeat('a', 200); + $maxLengthFirstname = str_repeat('a', 200); // WHEN $applicant = new Applicant( - $firstname, - $this->lastname, - $this->email + $maxLengthFirstname, + $lastname, + $email, ); // THEN expect(mb_strlen($applicant->firstname))->toBe(200); }); - it('rejects an empty or whitespace-only lastname', function (string $lastname): void { + it('rejects an empty or whitespace-only lastname', function (string $invalidLastname) use ($firstname, $email): void { // WHEN $action = fn (): Applicant => new Applicant( - $this->firstname, - $lastname, - $this->email + $firstname, + $invalidLastname, + $email, ); // THEN @@ -91,30 +89,30 @@ describe('applicant', function (): void { 'spaces' => ' ', ]); - it('rejects a lastname that exceeds the maximum allowed length', function (): void { + it('rejects a lastname that exceeds the maximum allowed length', function () use ($firstname, $email): void { // GIVEN - $lastname = str_repeat('a', 201); + $tooLongLastname = str_repeat('a', 201); // WHEN $action = fn (): Applicant => new Applicant( - $this->firstname, - $lastname, - $this->email + $firstname, + $tooLongLastname, + $email, ); // THEN expect($action)->toThrow(\DomainException::class, 'Applicant lastname exceeds the maximum allowed length.'); }); - it('accepts a lastname of exactly the maximum length', function (): void { + it('accepts a lastname of exactly the maximum length', function () use ($firstname, $email): void { // GIVEN - $lastname = str_repeat('a', 200); + $maxLengthLastname = str_repeat('a', 200); // WHEN $applicant = new Applicant( - $this->firstname, - $lastname, - $this->email + $firstname, + $maxLengthLastname, + $email, ); // THEN diff --git a/tests/Unit/Domain/Models/ValueObjects/CampaignRequest/CampaignRequestFiltersTest.php b/tests/Unit/Domain/Models/ValueObjects/CampaignRequest/CampaignRequestFiltersTest.php index b85bdb3..a8ca643 100644 --- a/tests/Unit/Domain/Models/ValueObjects/CampaignRequest/CampaignRequestFiltersTest.php +++ b/tests/Unit/Domain/Models/ValueObjects/CampaignRequest/CampaignRequestFiltersTest.php @@ -11,43 +11,41 @@ use Domain\Models\ValueObjects\CampaignRequest\CampaignRequestStatus; covers(CampaignRequestFilters::class); describe('campaign request filters', function (): void { - beforeEach(function (): void { - $this->brandCode = new BrandCode('AAA01'); - $this->status = CampaignRequestStatus::CREATED; - }); + $brandCode = new BrandCode('AAA01'); + $status = CampaignRequestStatus::CREATED; - it('creates filters with both fields set', function (): void { + it('creates filters with both fields set', function () use ($brandCode, $status): void { // GIVEN / WHEN $filters = new CampaignRequestFilters( - brandCode: $this->brandCode, - status: $this->status + brandCode: $brandCode, + status: $status, ); // THEN - expect($filters->brandCode)->toBe($this->brandCode) - ->and($filters->status)->toBe($this->status); + expect($filters->brandCode)->toBe($brandCode) + ->and($filters->status)->toBe($status); }); - it('creates filters with only brandCode set', function (): void { + it('creates filters with only brandCode set', function () use ($brandCode): void { // GIVEN / WHEN $filters = new CampaignRequestFilters( - brandCode: $this->brandCode + brandCode: $brandCode, ); // THEN - expect($filters->brandCode)->toBe($this->brandCode) + expect($filters->brandCode)->toBe($brandCode) ->and($filters->status)->toBeNull(); }); - it('creates filters with only status set', function (): void { + it('creates filters with only status set', function () use ($status): void { // GIVEN / WHEN $filters = new CampaignRequestFilters( - status: $this->status + status: $status, ); // THEN expect($filters->brandCode)->toBeNull() - ->and($filters->status)->toBe($this->status); + ->and($filters->status)->toBe($status); }); it('rejects filters without any field set', function (): void { diff --git a/tests/Unit/Domain/Models/ValueObjects/CampaignRequest/CampaignRequestSortClauseTest.php b/tests/Unit/Domain/Models/ValueObjects/CampaignRequest/CampaignRequestSortClauseTest.php new file mode 100644 index 0000000..f78c0ea --- /dev/null +++ b/tests/Unit/Domain/Models/ValueObjects/CampaignRequest/CampaignRequestSortClauseTest.php @@ -0,0 +1,25 @@ +field)->toBe($field) + ->and($clause->direction)->toBe($direction); + }); +}); diff --git a/tests/Unit/Domain/Models/ValueObjects/CampaignRequest/CampaignRequestSortCriteriaTest.php b/tests/Unit/Domain/Models/ValueObjects/CampaignRequest/CampaignRequestSortCriteriaTest.php index af31c54..42260c6 100644 --- a/tests/Unit/Domain/Models/ValueObjects/CampaignRequest/CampaignRequestSortCriteriaTest.php +++ b/tests/Unit/Domain/Models/ValueObjects/CampaignRequest/CampaignRequestSortCriteriaTest.php @@ -11,48 +11,42 @@ use Domain\Models\ValueObjects\CampaignRequest\CampaignRequestSortField; covers(CampaignRequestSortCriteria::class); describe('campaign request sort criteria', function (): void { - beforeEach(function (): void { - $this->singleClauses = [ - new CampaignRequestSortClause(field: CampaignRequestSortField::BrandCode, direction: \SortDirection::Ascending), - ]; - $this->clauses = [ - new CampaignRequestSortClause(field: CampaignRequestSortField::BrandCode, direction: \SortDirection::Ascending), - new CampaignRequestSortClause(field: CampaignRequestSortField::Status, direction: \SortDirection::Descending), - ]; - $this->invalidClauses = [CampaignRequestSortField::BrandCode]; - $this->emptyClauses = []; - }); + $singleClauses = [ + new CampaignRequestSortClause(field: CampaignRequestSortField::BrandCode, direction: \SortDirection::Ascending), + ]; + $clauses = [ + new CampaignRequestSortClause(field: CampaignRequestSortField::BrandCode, direction: \SortDirection::Ascending), + new CampaignRequestSortClause(field: CampaignRequestSortField::Status, direction: \SortDirection::Descending), + ]; + $invalidClauses = [CampaignRequestSortField::BrandCode]; + $emptyClauses = []; - it('creates a valid campaign request sort criteria with one clause', function (): void { + it('creates a valid campaign request sort criteria with one clause', function () use ($singleClauses): void { // GIVEN / WHEN - $campaignRequestSortCriteria = new CampaignRequestSortCriteria( - $this->singleClauses - ); + $campaignRequestSortCriteria = new CampaignRequestSortCriteria($singleClauses); // THEN - expect($campaignRequestSortCriteria->clauses)->toBe($this->singleClauses); + expect($campaignRequestSortCriteria->clauses)->toBe($singleClauses); }); - it('creates a valid campaign request sort criteria with several clauses', function (): void { + it('creates a valid campaign request sort criteria with several clauses', function () use ($clauses): void { // GIVEN / WHEN - $campaignRequestSortCriteria = new CampaignRequestSortCriteria( - $this->clauses - ); + $campaignRequestSortCriteria = new CampaignRequestSortCriteria($clauses); // THEN - expect($campaignRequestSortCriteria->clauses)->toBe($this->clauses); + expect($campaignRequestSortCriteria->clauses)->toBe($clauses); }); - it('rejects a campaign request sort criteria without clause', function (): void { + it('rejects a campaign request sort criteria without clause', function () use ($emptyClauses): void { // GIVEN / WHEN / THEN - expect(fn (): CampaignRequestSortCriteria => new CampaignRequestSortCriteria($this->emptyClauses)) + expect(fn (): CampaignRequestSortCriteria => new CampaignRequestSortCriteria($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 { + it('rejects a campaign request sort criteria with a non-clause element', function () use ($invalidClauses): void { // GIVEN / WHEN / THEN - expect(fn (): CampaignRequestSortCriteria => new CampaignRequestSortCriteria($this->invalidClauses)) + expect(fn (): CampaignRequestSortCriteria => new CampaignRequestSortCriteria($invalidClauses)) ->toThrow(\DomainException::class, 'CampaignRequestSortCriteria clauses must be CampaignRequestSortClause instances.'); }); }); diff --git a/tests/Unit/Domain/Models/ValueObjects/CampaignRequest/RejectionReasonTest.php b/tests/Unit/Domain/Models/ValueObjects/CampaignRequest/RejectionReasonTest.php index f72656f..ee1ddff 100644 --- a/tests/Unit/Domain/Models/ValueObjects/CampaignRequest/RejectionReasonTest.php +++ b/tests/Unit/Domain/Models/ValueObjects/CampaignRequest/RejectionReasonTest.php @@ -9,22 +9,20 @@ use Domain\Models\ValueObjects\CampaignRequest\RejectionReason; covers(RejectionReason::class); describe('rejection reason', function (): void { - beforeEach(function (): void { - $this->value = 'Missing legal documents.'; - }); + $value = 'Missing legal documents.'; - it('creates a valid rejection reason', function (): void { + it('creates a valid rejection reason', function () use ($value): void { // GIVEN / WHEN - $reason = new RejectionReason($this->value); + $reason = new RejectionReason($value); // THEN expect($reason)->toBeInstanceOf(RejectionReason::class) - ->and($reason->value)->toBe($this->value); + ->and($reason->value)->toBe($value); }); - it('rejects an empty or whitespace-only reason', function (string $value): void { + it('rejects an empty or whitespace-only reason', function (string $invalidValue): void { // WHEN - $action = fn (): RejectionReason => new RejectionReason($value); + $action = fn (): RejectionReason => new RejectionReason($invalidValue); // THEN expect($action)->toThrow(\DomainException::class, 'Rejection reason cannot be empty.'); diff --git a/tests/Unit/Domain/Models/ValueObjects/Coupon/CouponAvailabilityCalendarTest.php b/tests/Unit/Domain/Models/ValueObjects/Coupon/CouponAvailabilityCalendarTest.php index 829915c..5f05e45 100644 --- a/tests/Unit/Domain/Models/ValueObjects/Coupon/CouponAvailabilityCalendarTest.php +++ b/tests/Unit/Domain/Models/ValueObjects/Coupon/CouponAvailabilityCalendarTest.php @@ -11,49 +11,41 @@ use DateTime; covers(CouponAvailabilityCalendar::class); describe('coupon availability calendar', function (): void { - beforeEach(function (): void { - $this->singlePeriods = [ - new Period(new DateTime('2026-01-01'), new DateTime('2026-02-01')), - ]; - $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 = []; - }); + $singlePeriods = [ + new Period(new DateTime('2026-01-01'), new DateTime('2026-02-01')), + ]; + $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')), + ]; + $invalidPeriods = ['2026-08-25', '2026-10-31']; + $emptyPeriods = []; - it('creates a valid coupon availability calendar with one period', function (): void { + it('creates a valid coupon availability calendar with one period', function () use ($singlePeriods): void { // GIVEN / WHEN - $couponAvailabilityCalendar = new CouponAvailabilityCalendar( - $this->singlePeriods - ); + $couponAvailabilityCalendar = new CouponAvailabilityCalendar($singlePeriods); // THEN - expect($couponAvailabilityCalendar->periods)->toBe($this->singlePeriods); + expect($couponAvailabilityCalendar->periods)->toBe($singlePeriods); }); - it('creates a valid coupon availability calendar with several periods', function (): void { + it('creates a valid coupon availability calendar with several periods', function () use ($periods): void { // GIVEN / WHEN - $couponAvailabilityCalendar = new CouponAvailabilityCalendar( - $this->periods - ); + $couponAvailabilityCalendar = new CouponAvailabilityCalendar($periods); // THEN - expect($couponAvailabilityCalendar->periods)->toBe($this->periods); + expect($couponAvailabilityCalendar->periods)->toBe($periods); }); - it('rejects a coupon availability calendar without period', function (): void { + it('rejects a coupon availability calendar without period', function () use ($emptyPeriods): void { // GIVEN / WHEN / THEN - expect(fn (): CouponAvailabilityCalendar => new CouponAvailabilityCalendar( - $this->emptyPeriods - ))->toThrow(\DomainException::class); + expect(fn (): CouponAvailabilityCalendar => new CouponAvailabilityCalendar($emptyPeriods)) + ->toThrow(\DomainException::class); }); - it('rejects a coupon availability calendar with a non-period element', function (): void { + it('rejects a coupon availability calendar with a non-period element', function () use ($invalidPeriods): void { // GIVEN / WHEN / THEN - expect(fn (): CouponAvailabilityCalendar => new CouponAvailabilityCalendar( - $this->invalidPeriods - ))->toThrow(\DomainException::class, 'CouponAvailabilityCalendar periods must be Period instances.'); + expect(fn (): CouponAvailabilityCalendar => new CouponAvailabilityCalendar($invalidPeriods)) + ->toThrow(\DomainException::class, 'CouponAvailabilityCalendar periods must be Period instances.'); }); }); diff --git a/tests/Unit/Domain/Models/ValueObjects/Coupon/CouponDiffusionTest.php b/tests/Unit/Domain/Models/ValueObjects/Coupon/CouponDiffusionTest.php index 83929ee..b893d7b 100644 --- a/tests/Unit/Domain/Models/ValueObjects/Coupon/CouponDiffusionTest.php +++ b/tests/Unit/Domain/Models/ValueObjects/Coupon/CouponDiffusionTest.php @@ -11,15 +11,13 @@ use DateTime; covers(CouponDiffusion::class); describe('coupon diffusion', function (): void { - beforeEach(function (): void { - $this->period = new Period(new DateTime('2026-01-01'), new DateTime('2026-02-01')); - }); + $period = new Period(new DateTime('2026-01-01'), new DateTime('2026-02-01')); - it('creates a valid coupon diffusion with one period', function (): void { + it('creates a valid coupon diffusion with one period', function () use ($period): void { // GIVEN / WHEN - $couponDiffusion = new CouponDiffusion($this->period); + $couponDiffusion = new CouponDiffusion($period); // THEN - expect($couponDiffusion->period)->toBe($this->period); + expect($couponDiffusion->period)->toBe($period); }); }); diff --git a/tests/Unit/Domain/Models/ValueObjects/Coupon/CouponIncoCodeTest.php b/tests/Unit/Domain/Models/ValueObjects/Coupon/CouponIncoCodeTest.php new file mode 100644 index 0000000..3302c49 --- /dev/null +++ b/tests/Unit/Domain/Models/ValueObjects/Coupon/CouponIncoCodeTest.php @@ -0,0 +1,28 @@ +value)->toBe($value); + }); + + it('rejects invalid inco code', function () use ($zeroValue): void { + // GIVEN / WHEN / THEN + expect(fn (): CouponIncoCode => new CouponIncoCode($zeroValue)) + ->toThrow(\DomainException::class); + }); +}); diff --git a/tests/Unit/Domain/Models/ValueObjects/Coupon/CouponLegalInformationArrayTest.php b/tests/Unit/Domain/Models/ValueObjects/Coupon/CouponLegalInformationArrayTest.php new file mode 100644 index 0000000..6bcc31f --- /dev/null +++ b/tests/Unit/Domain/Models/ValueObjects/Coupon/CouponLegalInformationArrayTest.php @@ -0,0 +1,88 @@ +value)->toBe($informations); + }); + + it('accepts additional countries next to France', function () use ($franceInformation, $beneluxInformation): void { + // GIVEN + $informations = [$beneluxInformation, $franceInformation]; + + // WHEN + $array = new CouponLegalInformationArray($informations); + + // THEN + expect($array->value)->toBe($informations); + }); + + it('rejects an empty array', function (): void { + // GIVEN / WHEN + $action = fn (): CouponLegalInformationArray => new CouponLegalInformationArray([]); + + // THEN + expect($action)->toThrow( + \DomainException::class, + 'CouponLegalInformations requires at least one CouponLegalInformation.', + ); + }); + + it('rejects a non legal information element', function (): void { + // GIVEN / WHEN + $action = fn (): CouponLegalInformationArray => new CouponLegalInformationArray(['legal']); + + // THEN + expect($action)->toThrow( + \DomainException::class, + 'Value of CouponLegalInformationArray must be CouponLegalInformation instances.', + ); + }); + + it('rejects a non legal information element mixed with valid ones', function () use ($franceInformation): void { + // GIVEN + $informations = [$franceInformation, 'legal']; + + // WHEN + $action = fn (): CouponLegalInformationArray => new CouponLegalInformationArray($informations); + + // THEN + expect($action)->toThrow( + \DomainException::class, + 'Value of CouponLegalInformationArray must be CouponLegalInformation instances.', + ); + }); + + it('rejects an array without France legal information', function () use ($beneluxInformation): void { + // GIVEN + $informations = [$beneluxInformation]; + + // WHEN + $action = fn (): CouponLegalInformationArray => new CouponLegalInformationArray($informations); + + // THEN + expect($action)->toThrow( + \DomainException::class, + 'CouponLegalInformations requires a CouponLegalInformation for France.', + ); + }); +}); diff --git a/tests/Unit/Domain/Models/ValueObjects/Coupon/CouponLegalInformationTest.php b/tests/Unit/Domain/Models/ValueObjects/Coupon/CouponLegalInformationTest.php index 007b7d6..d404a4a 100644 --- a/tests/Unit/Domain/Models/ValueObjects/Coupon/CouponLegalInformationTest.php +++ b/tests/Unit/Domain/Models/ValueObjects/Coupon/CouponLegalInformationTest.php @@ -10,29 +10,39 @@ use Domain\Models\ValueObjects\Coupon\CouponLegalInformation; covers(CouponLegalInformation::class); describe('coupon legal information', function (): void { - beforeEach(function (): void { - $this->legalText = 'Lorem ipsum dolor sit amet consectetur adipiscing elit.'; - $this->emptyLegalText = ''; - $this->forLocale = Country::FRANCE; - }); + $legalText = 'Lorem ipsum dolor sit amet consectetur adipiscing elit.'; + $emptyLegalText = ''; + $forLocale = Country::FRANCE; - it('creates a valid coupon legal information', function (): void { + it('creates a valid coupon legal information FRANCE', function () use ($legalText, $forLocale): void { // GIVEN / WHEN $couponLegalInformation = new CouponLegalInformation( - $this->legalText, - $this->forLocale + $legalText, + $forLocale, ); // THEN - expect($couponLegalInformation->legalText)->toBe($this->legalText) - ->and($couponLegalInformation->forLocale)->toBe($this->forLocale); + expect($couponLegalInformation->legalText)->toBe($legalText) + ->and($couponLegalInformation->forLocale)->toBe($forLocale); }); - it('rejects invalid coupon legal information', function (): void { + it('creates a valid coupon legal information BENELUX', function () use ($legalText): void { + // GIVEN / WHEN + $couponLegalInformation = new CouponLegalInformation( + $legalText, + Country::BENELUX, + ); + + // THEN + expect($couponLegalInformation->legalText)->toBe($legalText) + ->and($couponLegalInformation->forLocale)->toBe(Country::BENELUX); + }); + + it('rejects invalid coupon legal information', function () use ($emptyLegalText, $forLocale): void { // GIVEN / WHEN / THEN expect(fn (): CouponLegalInformation => new CouponLegalInformation( - $this->emptyLegalText, - $this->forLocale + $emptyLegalText, + $forLocale, ))->toThrow(\DomainException::class); }); }); diff --git a/tests/Unit/Domain/Models/ValueObjects/Coupon/CouponMovementArrayTest.php b/tests/Unit/Domain/Models/ValueObjects/Coupon/CouponMovementArrayTest.php new file mode 100644 index 0000000..183b1f5 --- /dev/null +++ b/tests/Unit/Domain/Models/ValueObjects/Coupon/CouponMovementArrayTest.php @@ -0,0 +1,61 @@ +couponMovements)->toBe($couponMovements); + }); + + it('rejects an empty array', function (): void { + // GIVEN / WHEN + $action = fn (): CouponMovementArray => new CouponMovementArray([]); + + // THEN + expect($action)->toThrow( + \DomainException::class, + 'CouponMovementArray requires at least one CouponMovement.', + ); + }); + + it('rejects a non coupon movement element', function (): void { + // GIVEN / WHEN + $action = fn (): CouponMovementArray => new CouponMovementArray(['movement']); + + // THEN + expect($action)->toThrow( + \DomainException::class, + 'Value of CouponMovementArray must be CouponMovement instances.', + ); + }); + + it('rejects a non coupon movement element mixed with valid ones', function () use ($couponMovement): void { + // GIVEN + $couponMovements = [$couponMovement, 'movement']; + + // WHEN + $action = fn (): CouponMovementArray => new CouponMovementArray($couponMovements); + + // THEN + expect($action)->toThrow( + \DomainException::class, + 'Value of CouponMovementArray must be CouponMovement instances.', + ); + }); +}); diff --git a/tests/Unit/Domain/Models/ValueObjects/Coupon/CouponMovementTest.php b/tests/Unit/Domain/Models/ValueObjects/Coupon/CouponMovementTest.php new file mode 100644 index 0000000..7750d8e --- /dev/null +++ b/tests/Unit/Domain/Models/ValueObjects/Coupon/CouponMovementTest.php @@ -0,0 +1,47 @@ +quantity)->toBe($quantity) + ->and($movement->createdAt)->toBe($createdAt); + }); + + it('defaults the creation date to now', function (): void { + // GIVEN + $before = new \DateTimeImmutable(); + + // WHEN + $movement = new CouponMovement(5); + + // THEN + $after = new \DateTimeImmutable(); + + expect($movement->createdAt)->toBeInstanceOf(\DateTimeImmutable::class) + ->and($movement->createdAt >= $before)->toBeTrue() + ->and($movement->createdAt <= $after)->toBeTrue(); + }); + + it('accepts a negative quantity', function (): void { + // GIVEN / WHEN + $movement = new CouponMovement(-3); + + // THEN + expect($movement->quantity)->toBe(-3); + }); +}); diff --git a/tests/Unit/Domain/Models/ValueObjects/Coupon/CouponNameTest.php b/tests/Unit/Domain/Models/ValueObjects/Coupon/CouponNameTest.php new file mode 100644 index 0000000..1a8e560 --- /dev/null +++ b/tests/Unit/Domain/Models/ValueObjects/Coupon/CouponNameTest.php @@ -0,0 +1,28 @@ +value)->toBe($value); + }); + + it('rejects invalid coupon name', function () use ($emptyValue): void { + // GIVEN / WHEN / THEN + expect(fn (): CouponName => new CouponName($emptyValue)) + ->toThrow(\DomainException::class); + }); +}); diff --git a/tests/Unit/Domain/Models/ValueObjects/Coupon/CouponProcessingCode.php b/tests/Unit/Domain/Models/ValueObjects/Coupon/CouponProcessingCode.php new file mode 100644 index 0000000..7b1012a --- /dev/null +++ b/tests/Unit/Domain/Models/ValueObjects/Coupon/CouponProcessingCode.php @@ -0,0 +1,45 @@ +value)->toBe($value); + }); + + it('creates a valid processing code BENELUX', function () use ($value): void { + // GIVEN / WHEN + $processingCode = new CouponProcessingCode( + $value, + Country::BENELUX, + ); + + // THEN + expect($processingCode->value)->toBe($value); + }); + + it('rejects invalid processing code', function () use ($emptyValue): void { + // GIVEN / WHEN / THEN + expect(fn (): CouponProcessingCode => new CouponProcessingCode( + $emptyValue, + Country::FRANCE, + ))->toThrow(\DomainException::class); + }); +}); diff --git a/tests/Unit/Domain/Models/ValueObjects/Coupon/CouponProcessingCodeArrayTest.php b/tests/Unit/Domain/Models/ValueObjects/Coupon/CouponProcessingCodeArrayTest.php new file mode 100644 index 0000000..6896093 --- /dev/null +++ b/tests/Unit/Domain/Models/ValueObjects/Coupon/CouponProcessingCodeArrayTest.php @@ -0,0 +1,88 @@ +value)->toBe($processingCodes); + }); + + it('accepts additional countries next to France', function () use ($franceCode, $beneluxCode): void { + // GIVEN + $processingCodes = [$beneluxCode, $franceCode]; + + // WHEN + $array = new CouponProcessingCodeArray($processingCodes); + + // THEN + expect($array->value)->toBe($processingCodes); + }); + + it('rejects an empty array', function (): void { + // GIVEN / WHEN + $action = fn (): CouponProcessingCodeArray => new CouponProcessingCodeArray([]); + + // THEN + expect($action)->toThrow( + \DomainException::class, + 'ProcessingCodes requires at least one CouponProcessingCode.', + ); + }); + + it('rejects a non processing code element', function (): void { + // GIVEN / WHEN + $action = fn (): CouponProcessingCodeArray => new CouponProcessingCodeArray(['ABC1234567890']); + + // THEN + expect($action)->toThrow( + \DomainException::class, + 'Value of CouponProcessingCodeArray must be CouponProcessingCode instances.', + ); + }); + + it('rejects a non processing code element mixed with valid ones', function () use ($franceCode): void { + // GIVEN + $processingCodes = [$franceCode, 'ABC1234567890']; + + // WHEN + $action = fn (): CouponProcessingCodeArray => new CouponProcessingCodeArray($processingCodes); + + // THEN + expect($action)->toThrow( + \DomainException::class, + 'Value of CouponProcessingCodeArray must be CouponProcessingCode instances.', + ); + }); + + it('rejects an array without France processing code', function () use ($beneluxCode): void { + // GIVEN + $processingCodes = [$beneluxCode]; + + // WHEN + $action = fn (): CouponProcessingCodeArray => new CouponProcessingCodeArray($processingCodes); + + // THEN + expect($action)->toThrow( + \DomainException::class, + 'CouponProcessingCodes requires a CouponProcessingCode for France.', + ); + }); +}); diff --git a/tests/Unit/Domain/Models/ValueObjects/Coupon/CouponProcessingCodeTest.php b/tests/Unit/Domain/Models/ValueObjects/Coupon/CouponProcessingCodeTest.php new file mode 100644 index 0000000..fb61519 --- /dev/null +++ b/tests/Unit/Domain/Models/ValueObjects/Coupon/CouponProcessingCodeTest.php @@ -0,0 +1,93 @@ +value)->toBe($value) + ->and($code->availableIn)->toBe(Country::FRANCE); + }); + + it('accepts lowercase and uppercase letters and digits', function (): void { + // GIVEN / WHEN + $code = new CouponProcessingCode('aBc1234567xYz', Country::FRANCE); + + // THEN + expect($code->value)->toBe('aBc1234567xYz'); + }); + + it('rejects an invalid value', function (string $invalidValue): void { + // GIVEN / WHEN + $action = fn (): CouponProcessingCode => new CouponProcessingCode($invalidValue, Country::FRANCE); + + // THEN + expect($action)->toThrow( + \DomainException::class, + 'Processing Code must be exactly 13 alphanumeric characters, e.g. ABC1234567890.', + ); + })->with([ + 'empty' => '', + 'too short' => 'ABC123456789', + 'too long' => 'ABC12345678901', + 'special char' => 'ABC123456789!', + 'with space' => 'ABC 123456789', + 'trailing break' => "ABC1234567890\n", + ]); + + it('is equal to another processing code with the same value', function () use ($value): void { + // GIVEN + $code = new CouponProcessingCode($value, Country::FRANCE); + $other = new CouponProcessingCode($value, Country::FRANCE); + + // WHEN / THEN + expect($code->equals($other))->toBeTrue(); + }); + + it('ignores the country when comparing', function () use ($value): void { + // GIVEN + $france = new CouponProcessingCode($value, Country::FRANCE); + $benelux = new CouponProcessingCode($value, Country::BENELUX); + + // WHEN / THEN + expect($france->equals($benelux))->toBeTrue(); + }); + + it('is not equal to a processing code with a different value', function () use ($value): void { + // GIVEN + $code = new CouponProcessingCode($value, Country::FRANCE); + $other = new CouponProcessingCode('XYZ0987654321', Country::FRANCE); + + // WHEN / THEN + expect($code->equals($other))->toBeFalse(); + }); + + it('is not equal to a value that is not a processing code', function () use ($value): void { + // GIVEN + $code = new CouponProcessingCode($value, Country::FRANCE); + + // WHEN / THEN + expect($code->equals($value))->toBeFalse() + ->and($code->equals(null))->toBeFalse(); + }); + + it('is converted to its value as a string', function () use ($value): void { + // GIVEN + $code = new CouponProcessingCode($value, Country::FRANCE); + + // WHEN / THEN + expect((string) $code)->toBe($value); + }); +}); diff --git a/tests/Unit/Domain/Models/ValueObjects/Coupon/CouponProductArrayTest.php b/tests/Unit/Domain/Models/ValueObjects/Coupon/CouponProductArrayTest.php new file mode 100644 index 0000000..342c6f5 --- /dev/null +++ b/tests/Unit/Domain/Models/ValueObjects/Coupon/CouponProductArrayTest.php @@ -0,0 +1,67 @@ +value)->toBe([]); + }); + + it('accepts an empty array explicitly', function (): void { + // GIVEN / WHEN + $array = new CouponProductArray([]); + + // THEN + expect($array->value)->toBe([]); + }); + + it('creates an array with valid products', function () use ($firstProduct, $secondProduct): void { + // GIVEN + $products = [$firstProduct, $secondProduct]; + + // WHEN + $array = new CouponProductArray($products); + + // THEN + expect($array->value)->toBe($products); + }); + + it('rejects a non product element', function (): void { + // GIVEN / WHEN + $action = fn (): CouponProductArray => new CouponProductArray([35784]); + + // THEN + expect($action)->toThrow( + \DomainException::class, + 'Value of CouponProductArray must be CouponIncoCode instances.', + ); + }); + + it('rejects a non product element mixed with valid ones', function () use ($firstProduct): void { + // GIVEN + $products = [$firstProduct, 35784]; + + // WHEN + $action = fn (): CouponProductArray => new CouponProductArray($products); + + // THEN + expect($action)->toThrow( + \DomainException::class, + 'Value of CouponProductArray must be CouponIncoCode instances.', + ); + }); +}); diff --git a/tests/Unit/Domain/Models/ValueObjects/Coupon/CouponQuantityTest.php b/tests/Unit/Domain/Models/ValueObjects/Coupon/CouponQuantityTest.php index 35dd4b3..b4c7295 100644 --- a/tests/Unit/Domain/Models/ValueObjects/Coupon/CouponQuantityTest.php +++ b/tests/Unit/Domain/Models/ValueObjects/Coupon/CouponQuantityTest.php @@ -9,38 +9,30 @@ use Domain\Models\ValueObjects\Coupon\CouponQuantity; covers(CouponQuantity::class); describe('coupon quantity', function (): void { - beforeEach(function (): void { - $this->quantity = 10; - $this->zeroQuantity = 0; - $this->negativeQuantity = -1; - }); + $quantity = 10; + $zeroQuantity = 0; + $negativeQuantity = -1; - it('creates a valid coupon quantity', function (): void { + it('creates a valid coupon quantity', function () use ($quantity): void { // GIVEN / WHEN - $couponQuantity = new CouponQuantity( - $this->quantity - ); + $couponQuantity = new CouponQuantity($quantity); // THEN expect($couponQuantity)->toBeInstanceOf(CouponQuantity::class) - ->and($couponQuantity->quantity)->toBe($this->quantity); + ->and($couponQuantity->value)->toBe($quantity); }); - it('rejects a quantity of zero', function (): void { + it('rejects a quantity of zero', function () use ($zeroQuantity): void { // WHEN - $action = fn (): CouponQuantity => new CouponQuantity( - $this->zeroQuantity - ); + $action = fn (): CouponQuantity => new CouponQuantity($zeroQuantity); // THEN expect($action)->toThrow(\DomainException::class, 'CouponQuantity quantity must be positive.'); }); - it('rejects a negative quantity', function (): void { + it('rejects a negative quantity', function () use ($negativeQuantity): void { // WHEN - $action = fn (): CouponQuantity => new CouponQuantity( - $this->negativeQuantity - ); + $action = fn (): CouponQuantity => new CouponQuantity($negativeQuantity); // THEN expect($action)->toThrow(\DomainException::class, 'CouponQuantity quantity must be positive.'); diff --git a/tests/Unit/Domain/Models/ValueObjects/Coupon/CouponValidityInformationArrayTest.php b/tests/Unit/Domain/Models/ValueObjects/Coupon/CouponValidityInformationArrayTest.php new file mode 100644 index 0000000..1a31273 --- /dev/null +++ b/tests/Unit/Domain/Models/ValueObjects/Coupon/CouponValidityInformationArrayTest.php @@ -0,0 +1,88 @@ +value)->toBe($informations); + }); + + it('accepts additional countries next to France', function () use ($franceInformation, $beneluxInformation): void { + // GIVEN + $informations = [$beneluxInformation, $franceInformation]; + + // WHEN + $array = new CouponValidityInformationArray($informations); + + // THEN + expect($array->value)->toBe($informations); + }); + + it('rejects an empty array', function (): void { + // GIVEN / WHEN + $action = fn (): CouponValidityInformationArray => new CouponValidityInformationArray([]); + + // THEN + expect($action)->toThrow( + \DomainException::class, + 'CouponValidityInformations requires at least one CouponValidityInformation.', + ); + }); + + it('rejects a non validity information element', function (): void { + // GIVEN / WHEN + $action = fn (): CouponValidityInformationArray => new CouponValidityInformationArray(['validity']); + + // THEN + expect($action)->toThrow( + \DomainException::class, + 'Value of CouponValidityInformationArray must be CouponValidityInformation instances.', + ); + }); + + it('rejects a non validity information element mixed with valid ones', function () use ($franceInformation): void { + // GIVEN + $informations = [$franceInformation, 'validity']; + + // WHEN + $action = fn (): CouponValidityInformationArray => new CouponValidityInformationArray($informations); + + // THEN + expect($action)->toThrow( + \DomainException::class, + 'Value of CouponValidityInformationArray must be CouponValidityInformation instances.', + ); + }); + + it('rejects an array without France validity information', function () use ($beneluxInformation): void { + // GIVEN + $informations = [$beneluxInformation]; + + // WHEN + $action = fn (): CouponValidityInformationArray => new CouponValidityInformationArray($informations); + + // THEN + expect($action)->toThrow( + \DomainException::class, + 'CouponValidityInformations requires a CouponValidityInformation for France.', + ); + }); +}); diff --git a/tests/Unit/Domain/Models/ValueObjects/Coupon/CouponValidityInformationTest.php b/tests/Unit/Domain/Models/ValueObjects/Coupon/CouponValidityInformationTest.php index 952a9b2..35aa0e8 100644 --- a/tests/Unit/Domain/Models/ValueObjects/Coupon/CouponValidityInformationTest.php +++ b/tests/Unit/Domain/Models/ValueObjects/Coupon/CouponValidityInformationTest.php @@ -10,29 +10,27 @@ use Shared\Enums\Country; covers(CouponValidityInformation::class); describe('coupon validity information', function (): void { - beforeEach(function (): void { - $this->validityText = 'Lorem ipsum dolor sit amet consectetur adipiscing elit.'; - $this->emptyValidityText = ''; - $this->forLocale = Country::FRANCE; - }); + $validityText = 'Lorem ipsum dolor sit amet consectetur adipiscing elit.'; + $emptyValidityText = ''; + $forLocale = Country::FRANCE; - it('creates a valid coupon validity information', function (): void { + it('creates a valid coupon validity information', function () use ($validityText, $forLocale): void { // GIVEN / WHEN $couponValidityInformation = new CouponValidityInformation( - $this->validityText, - $this->forLocale + $validityText, + $forLocale, ); // THEN - expect($couponValidityInformation->validityText)->toBe($this->validityText) - ->and($couponValidityInformation->forLocale)->toBe($this->forLocale); + expect($couponValidityInformation->validityText)->toBe($validityText) + ->and($couponValidityInformation->forLocale)->toBe($forLocale); }); - it('rejects invalid coupon validity information', function (): void { + it('rejects invalid coupon validity information', function () use ($emptyValidityText, $forLocale): void { // GIVEN / WHEN / THEN expect(fn (): CouponValidityInformation => new CouponValidityInformation( - $this->emptyValidityText, - $this->forLocale + $emptyValidityText, + $forLocale, ))->toThrow(\DomainException::class); }); }); diff --git a/tests/Unit/Domain/Models/ValueObjects/Coupon/CouponValidityTest.php b/tests/Unit/Domain/Models/ValueObjects/Coupon/CouponValidityTest.php deleted file mode 100644 index 57c85c9..0000000 --- a/tests/Unit/Domain/Models/ValueObjects/Coupon/CouponValidityTest.php +++ /dev/null @@ -1,38 +0,0 @@ -days = 30; - $this->zeroDays = 0; - $this->until = new DateTime('2026-11-30'); - }); - - it('creates a valid coupon validity', function (): void { - // GIVEN / WHEN - $couponValidity = new CouponValidity( - $this->days, - $this->until - ); - - // THEN - expect($couponValidity->days)->toBe($this->days) - ->and($couponValidity->until)->toBe($this->until); - }); - - it('rejects invalid coupon validity', function (): void { - // GIVEN / WHEN / THEN - expect(fn (): CouponValidity => new CouponValidity( - $this->zeroDays, - $this->until - ))->toThrow(\DomainException::class); - }); -}); diff --git a/tests/Unit/Domain/Models/ValueObjects/CouponNameTest.php b/tests/Unit/Domain/Models/ValueObjects/CouponNameTest.php deleted file mode 100644 index f1b0524..0000000 --- a/tests/Unit/Domain/Models/ValueObjects/CouponNameTest.php +++ /dev/null @@ -1,33 +0,0 @@ -value = 'Name'; - $this->emptyValue = ''; - }); - - it('creates a valid coupon name', function (): void { - // GIVEN / WHEN - $name = new CouponName( - $this->value - ); - - // THEN - expect($name->value)->toBe($this->value); - }); - - it('rejects invalid coupon name', function (): void { - // GIVEN / WHEN / THEN - expect(fn (): CouponName => new CouponName( - $this->emptyValue - ))->toThrow(\DomainException::class); - }); -}); diff --git a/tests/Unit/Domain/Models/ValueObjects/GS1Test.php b/tests/Unit/Domain/Models/ValueObjects/GS1Test.php deleted file mode 100644 index 1e56044..0000000 --- a/tests/Unit/Domain/Models/ValueObjects/GS1Test.php +++ /dev/null @@ -1,38 +0,0 @@ -value = '1010100101'; - $this->emptyValue = ''; - $this->format = BarcodeFormat::EAN13; - }); - - it('creates a valid gs1', function (): void { - // WHEN - $GS1 = new GS1( - $this->value, - $this->format - ); - - // THEN - expect($GS1->value)->toBe($this->value) - ->and($GS1->format)->toBe($this->format); - }); - - it('rejects invalid gs1', function (): void { - // GIVEN / WHEN / THEN - expect(fn (): GS1 => new GS1( - $this->emptyValue, - $this->format - ))->toThrow(\DomainException::class); - }); -}); diff --git a/tests/Unit/Domain/Models/ValueObjects/IncoCodeTest.php b/tests/Unit/Domain/Models/ValueObjects/IncoCodeTest.php deleted file mode 100644 index 54b03ea..0000000 --- a/tests/Unit/Domain/Models/ValueObjects/IncoCodeTest.php +++ /dev/null @@ -1,33 +0,0 @@ -value = 1010100101; - $this->zeroValue = 0; - }); - - it('creates a valid inco code', function (): void { - // GIVEN / WHEN - $incoCode = new IncoCode( - $this->value - ); - - // THEN - expect($incoCode->value)->toBe($this->value); - }); - - it('rejects invalid inco code', function (): void { - // GIVEN / WHEN / THEN - expect(fn (): IncoCode => new IncoCode( - $this->zeroValue - ))->toThrow(\DomainException::class); - }); -}); diff --git a/tests/Unit/Domain/Models/ValueObjects/MoneyAmountTest.php b/tests/Unit/Domain/Models/ValueObjects/MoneyAmountTest.php index 84ea7e2..ee12511 100644 --- a/tests/Unit/Domain/Models/ValueObjects/MoneyAmountTest.php +++ b/tests/Unit/Domain/Models/ValueObjects/MoneyAmountTest.php @@ -11,36 +11,26 @@ use Shared\ValueObjects\Money; covers(MoneyAmount::class); describe('money amount', function (): void { - beforeEach(function (): void { - $this->money = new Money(Currency::EUR, 300); - $this->secondMoney = new Money(Currency::EUR, 200); - $this->minimumMoney = new Money(Currency::EUR, 1); - $this->zeroMoney = new Money(Currency::EUR, 0); - $this->aboveLimitMoney = new Money(Currency::EUR, 301); - }); + $money = new Money(Currency::EUR, 300); + $secondMoney = new Money(Currency::EUR, 200); + $minimumMoney = new Money(Currency::EUR, 1); + $zeroMoney = new Money(Currency::EUR, 0); + $aboveLimitMoney = new Money(Currency::EUR, 301); - it('wraps a Money value', function (): void { + it('wraps a Money value', function () use ($money): void { // GIVEN / WHEN - $amount = new MoneyAmount( - $this->money - ); + $amount = new MoneyAmount($money); // THEN expect($amount)->toBeInstanceOf(MoneyAmount::class) - ->and($amount->value)->toBe($this->money); + ->and($amount->value)->toBe($money); }); - it('implements equals by delegating to the wrapped Money', function (): void { + it('implements equals by delegating to the wrapped Money', function () use ($money, $secondMoney): void { // GIVEN / WHEN - $amount1 = new MoneyAmount( - $this->money - ); - $amount2 = new MoneyAmount( - $this->money - ); - $amount3 = new MoneyAmount( - $this->secondMoney - ); + $amount1 = new MoneyAmount($money); + $amount2 = new MoneyAmount($money); + $amount3 = new MoneyAmount($secondMoney); // THEN expect($amount1->equals($amount2))->toBeTrue() @@ -48,41 +38,39 @@ describe('money amount', function (): void { ->and($amount1->equals(null))->toBeFalse(); }); - it('converts to string by delegating to the wrapped Money', function (): void { + it('converts to string by delegating to the wrapped Money', function () use ($money): void { // GIVEN / WHEN - $amount = new MoneyAmount( - $this->money - ); + $amount = new MoneyAmount($money); // THEN expect((string) $amount)->toBe((string) $amount->value); }); - it('accepts the minimum amount of 0.01€', function (): void { + it('accepts the minimum amount of 0.01€', function () use ($minimumMoney): void { // GIVEN / WHEN - $amount = new MoneyAmount($this->minimumMoney); + $amount = new MoneyAmount($minimumMoney); // THEN - expect($amount->value)->toBe($this->minimumMoney); + expect($amount->value)->toBe($minimumMoney); }); - it('accepts the maximum amount of 3.00€', function (): void { + it('accepts the maximum amount of 3.00€', function () use ($money): void { // GIVEN / WHEN - $amount = new MoneyAmount($this->money); + $amount = new MoneyAmount($money); // THEN - expect($amount->value)->toBe($this->money); + expect($amount->value)->toBe($money); }); - it('throws when the amount is zero', function (): void { + it('throws when the amount is zero', function () use ($zeroMoney): void { // GIVEN / WHEN / THEN - expect(fn () => new MoneyAmount($this->zeroMoney)) + expect(fn (): MoneyAmount => new MoneyAmount($zeroMoney)) ->toThrow(\DomainException::class, 'Money amount must be between 0.01€ and 3.00€'); }); - it('throws when the amount is above 3.00€', function (): void { + it('throws when the amount is above 3.00€', function () use ($aboveLimitMoney): void { // GIVEN / WHEN / THEN - expect(fn () => new MoneyAmount($this->aboveLimitMoney)) + expect(fn (): MoneyAmount => new MoneyAmount($aboveLimitMoney)) ->toThrow(\DomainException::class, 'Money amount must be between 0.01€ and 3.00€'); }); }); diff --git a/tests/Unit/Domain/Models/ValueObjects/PrintRulesTest.php b/tests/Unit/Domain/Models/ValueObjects/PrintRulesTest.php index d45e709..e84fd8e 100644 --- a/tests/Unit/Domain/Models/ValueObjects/PrintRulesTest.php +++ b/tests/Unit/Domain/Models/ValueObjects/PrintRulesTest.php @@ -9,48 +9,46 @@ 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; - }); + $maxPerCustomer = 3; + $minMaxPerCustomer = 1; + $zeroMaxPerCustomer = 0; + $cooldownDays = 30; + $minCooldownDays = 1; + $zeroCooldownDays = 0; - it('creates print rules with both fields set', function (): void { + it('creates print rules with both fields set', function () use ($maxPerCustomer, $cooldownDays): void { // GIVEN / WHEN $printRules = new PrintRules( - maxPerCustomer: $this->maxPerCustomer, - cooldownDays: $this->cooldownDays + maxPerCustomer: $maxPerCustomer, + cooldownDays: $cooldownDays ); // THEN expect($printRules)->toBeInstanceOf(PrintRules::class) - ->and($printRules->maxPerCustomer)->toBe($this->maxPerCustomer) - ->and($printRules->cooldownDays)->toBe($this->cooldownDays); + ->and($printRules->maxPerCustomer)->toBe($maxPerCustomer) + ->and($printRules->cooldownDays)->toBe($cooldownDays); }); - it('creates print rules with only maxPerCustomer set', function (): void { + it('creates print rules with only maxPerCustomer set', function () use ($maxPerCustomer): void { // GIVEN / WHEN $printRules = new PrintRules( - maxPerCustomer: $this->maxPerCustomer + maxPerCustomer: $maxPerCustomer ); // THEN - expect($printRules->maxPerCustomer)->toBe($this->maxPerCustomer) + expect($printRules->maxPerCustomer)->toBe($maxPerCustomer) ->and($printRules->cooldownDays)->toBeNull(); }); - it('creates print rules with only cooldownDays set', function (): void { + it('creates print rules with only cooldownDays set', function () use ($cooldownDays): void { // GIVEN / WHEN $printRules = new PrintRules( - cooldownDays: $this->cooldownDays + cooldownDays: $cooldownDays ); // THEN expect($printRules->maxPerCustomer)->toBeNull() - ->and($printRules->cooldownDays)->toBe($this->cooldownDays); + ->and($printRules->cooldownDays)->toBe($cooldownDays); }); it('rejects print rules without any field set', function (): void { @@ -59,29 +57,29 @@ describe('print rules', function (): void { ->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 { + it('accepts the minimum value of 1 for both fields', function () use ($minMaxPerCustomer, $minCooldownDays): void { // GIVEN / WHEN $printRules = new PrintRules( - maxPerCustomer: $this->minMaxPerCustomer, - cooldownDays: $this->minCooldownDays + maxPerCustomer: $minMaxPerCustomer, + cooldownDays: $minCooldownDays ); // THEN - expect($printRules->maxPerCustomer)->toBe($this->minMaxPerCustomer) - ->and($printRules->cooldownDays)->toBe($this->minCooldownDays); + expect($printRules->maxPerCustomer)->toBe($minMaxPerCustomer) + ->and($printRules->cooldownDays)->toBe($minCooldownDays); }); - it('rejects a maxPerCustomer equal to zero', function (): void { + it('rejects a maxPerCustomer equal to zero', function () use ($zeroMaxPerCustomer): void { // GIVEN / WHEN / THEN expect(fn (): PrintRules => new PrintRules( - maxPerCustomer: $this->zeroMaxPerCustomer + maxPerCustomer: $zeroMaxPerCustomer ))->toThrow(\InvalidArgumentException::class, 'Max per customer must be greater than 0.'); }); - it('rejects a cooldownDays equal to zero', function (): void { + it('rejects a cooldownDays equal to zero', function () use ($zeroCooldownDays): void { // GIVEN / WHEN / THEN expect(fn (): PrintRules => new PrintRules( - cooldownDays: $this->zeroCooldownDays + cooldownDays: $zeroCooldownDays ))->toThrow(\InvalidArgumentException::class, 'Cooldown days must be greater than 0.'); }); }); diff --git a/tests/Unit/Domain/Models/ValueObjects/ProcessingCodeTest.php b/tests/Unit/Domain/Models/ValueObjects/ProcessingCodeTest.php deleted file mode 100644 index 2946cd1..0000000 --- a/tests/Unit/Domain/Models/ValueObjects/ProcessingCodeTest.php +++ /dev/null @@ -1,33 +0,0 @@ -value = '1010100101'; - $this->emptyValue = ''; - }); - - it('creates a valid processing code', function (): void { - // GIVEN / WHEN - $processingCode = new ProcessingCode( - $this->value - ); - - // THEN - expect($processingCode->value)->toBe($this->value); - }); - - it('rejects invalid processing code', function (): void { - // GIVEN / WHEN / THEN - expect(fn (): ProcessingCode => new ProcessingCode( - $this->emptyValue - ))->toThrow(\DomainException::class); - }); -}); diff --git a/tests/Unit/Domain/Models/ValueObjects/RawPictureTest.php b/tests/Unit/Domain/Models/ValueObjects/RawPictureTest.php index 8df5a9c..73e7858 100644 --- a/tests/Unit/Domain/Models/ValueObjects/RawPictureTest.php +++ b/tests/Unit/Domain/Models/ValueObjects/RawPictureTest.php @@ -61,7 +61,7 @@ describe('raw picture', function (): void { it('measures size in raw bytes rather than characters', function (): void { // GIVEN - $halfLimit = (int) (RawPicturePath::MAX_SIZE_IN_BYTES / 2) + 1; + $halfLimit = RawPicturePath::MAX_SIZE_IN_BYTES / 2 + 1; $content = str_repeat('é', $halfLimit); // WHEN diff --git a/tests/Unit/Domain/Models/ValueObjects/RequestedCoupon/PeriodTest.php b/tests/Unit/Domain/Models/ValueObjects/RequestedCoupon/PeriodTest.php index e161db9..22e3449 100644 --- a/tests/Unit/Domain/Models/ValueObjects/RequestedCoupon/PeriodTest.php +++ b/tests/Unit/Domain/Models/ValueObjects/RequestedCoupon/PeriodTest.php @@ -10,22 +10,17 @@ use DateTime; covers(Period::class); describe('period', function (): void { - beforeEach(function (): void { - $this->start = new DateTime('2026-01-01'); - $this->end = new DateTime('2026-01-31'); - }); + $start = new DateTime('2026-01-01'); + $end = new DateTime('2026-01-31'); - it('creates a valid period', function (): void { + it('creates a valid period', function () use ($start, $end): void { // GIVEN / WHEN - $period = new Period( - $this->start, - $this->end - ); + $period = new Period($start, $end); // THEN expect($period)->toBeInstanceOf(Period::class) - ->and($period->start)->toBe($this->start) - ->and($period->end)->toBe($this->end); + ->and($period->start)->toBe($start) + ->and($period->end)->toBe($end); }); it('accepts a period whose end equals its start', function (): void { @@ -43,7 +38,7 @@ describe('period', function (): void { it('throws when the end is before the start', function (): void { // GIVEN / WHEN / THEN - expect(fn () => new Period(new DateTime('2026-02-01'), new DateTime('2026-01-01'))) + expect(fn (): Period => new Period(new DateTime('2026-02-01'), new DateTime('2026-01-01'))) ->toThrow(\DomainException::class, 'Period end date cannot be before its start date.'); }); }); diff --git a/tests/Unit/Domain/Models/ValueObjects/RequestedCoupon/RequestedCouponQuantityTest.php b/tests/Unit/Domain/Models/ValueObjects/RequestedCoupon/RequestedCouponQuantityTest.php index e5f835c..e6bfc09 100644 --- a/tests/Unit/Domain/Models/ValueObjects/RequestedCoupon/RequestedCouponQuantityTest.php +++ b/tests/Unit/Domain/Models/ValueObjects/RequestedCoupon/RequestedCouponQuantityTest.php @@ -9,38 +9,30 @@ use Domain\Models\ValueObjects\RequestedCoupon\RequestedCouponQuantity; covers(RequestedCouponQuantity::class); describe('quantity', function (): void { - beforeEach(function (): void { - $this->value = 10; - $this->zeroValue = 0; - $this->negativeValue = -1; - }); + $value = 10; + $zeroValue = 0; + $negativeValue = -1; - it('creates a valid quantity', function (): void { + it('creates a valid quantity', function () use ($value): void { // GIVEN / WHEN - $quantity = new RequestedCouponQuantity( - $this->value - ); + $quantity = new RequestedCouponQuantity($value); // THEN expect($quantity)->toBeInstanceOf(RequestedCouponQuantity::class) - ->and($quantity->value)->toBe($this->value); + ->and($quantity->value)->toBe($value); }); - it('rejects a quantity of zero', function (): void { + it('rejects a quantity of zero', function () use ($zeroValue): void { // GIVEN / WHEN - $action = fn (): RequestedCouponQuantity => new RequestedCouponQuantity( - $this->zeroValue - ); + $action = fn (): RequestedCouponQuantity => new RequestedCouponQuantity($zeroValue); // THEN expect($action)->toThrow(\DomainException::class, 'Quantity should be more than 0.'); }); - it('rejects a negative quantity', function (): void { + it('rejects a negative quantity', function () use ($negativeValue): void { // GIVEN / WHEN - $action = fn (): RequestedCouponQuantity => new RequestedCouponQuantity( - $this->negativeValue - ); + $action = fn (): RequestedCouponQuantity => new RequestedCouponQuantity($negativeValue); // THEN expect($action)->toThrow(\DomainException::class, 'Quantity should be more than 0.'); diff --git a/tests/Unit/Domain/Models/ValueObjects/RequestedCoupon/RequestedCouponTest.php b/tests/Unit/Domain/Models/ValueObjects/RequestedCoupon/RequestedCouponTest.php index f55da32..fa7ed31 100644 --- a/tests/Unit/Domain/Models/ValueObjects/RequestedCoupon/RequestedCouponTest.php +++ b/tests/Unit/Domain/Models/ValueObjects/RequestedCoupon/RequestedCouponTest.php @@ -4,18 +4,21 @@ declare(strict_types=1); namespace Tests\Unit\Domain\Models\ValueObjects\RequestedCoupon; -use Carbon\CarbonImmutable; use DateTime; -use Domain\Enums\BarcodeFormat; -use Domain\Models\ValueObjects\Coupon\CouponValidity; +use Domain\Models\ValueObjects\Coupon\CouponIncoCode; +use Domain\Models\ValueObjects\Coupon\CouponLegalInformation; +use Domain\Models\ValueObjects\Coupon\CouponLegalInformationArray; +use Domain\Models\ValueObjects\Coupon\CouponName; +use Domain\Models\ValueObjects\Coupon\CouponPicture; +use Domain\Models\ValueObjects\Coupon\CouponProcessingCode; +use Domain\Models\ValueObjects\Coupon\CouponProcessingCodeArray; +use Domain\Models\ValueObjects\Coupon\CouponProductArray; use Domain\Models\ValueObjects\Coupon\CouponValidityInformation; -use Domain\Models\ValueObjects\CouponName; -use Domain\Models\ValueObjects\GS1; -use Domain\Models\ValueObjects\IncoCode; +use Domain\Models\ValueObjects\Coupon\CouponValidityInformationArray; use Domain\Models\ValueObjects\MoneyAmount; use Domain\Models\ValueObjects\Period; +use Domain\Models\ValueObjects\PicturePath; use Domain\Models\ValueObjects\PrintRules; -use Domain\Models\ValueObjects\ProcessingCode; use Domain\Models\ValueObjects\RequestedCoupon\RequestedCoupon; use Domain\Models\ValueObjects\RequestedCoupon\RequestedCouponQuantity; use Shared\Enums\Country; @@ -25,210 +28,265 @@ use Shared\ValueObjects\Money; covers(RequestedCoupon::class); describe('requested coupon', function (): void { - beforeEach(function (): void { - $this->name = new CouponName('Coupon A'); - $this->quantity = new RequestedCouponQuantity(100); - $this->diffusion = new Period(new DateTime('2026-01-01'), new DateTime('2026-02-01')); - $this->moneyAmount = new MoneyAmount(new Money(Currency::EUR, 300)); - $this->processingCode = new ProcessingCode('PC1'); - $this->couponValidity = new CouponValidity(30, new DateTime('2026-02-01')); - $this->franceValidityInformation = new CouponValidityInformation( - 'Lorem ipsum dolor sit amet consectetur adipiscing elit.', - Country::FRANCE - ); - $this->beneluxValidityInformation = new CouponValidityInformation( - 'Lorem ipsum dolor sit amet consectetur adipiscing elit.', - Country::BENELUX - ); - $this->couponValidityInformation = [ - $this->franceValidityInformation, - $this->beneluxValidityInformation, - ]; - $this->products = [new IncoCode(35784), new IncoCode(15522)]; - $this->printRules = new PrintRules(maxPerCustomer: 3); - $this->gs1 = new GS1('01234567890128', BarcodeFormat::EAN13); - }); + $couponName = new CouponName('Coupon A'); + $quantity = new RequestedCouponQuantity(100); + $diffusion = new Period(new DateTime('2026-01-01'), new DateTime('2026-02-01')); + $moneyAmount = new MoneyAmount(new Money(Currency::EUR, 300)); + $couponPicture = new CouponPicture( + new PicturePath('files/product.jpg'), + new PicturePath('files/thumbnail.jpg'), + new PicturePath('files/mobileThumbnail.jpg') + ); - afterEach(function (): void { - CarbonImmutable::setTestNow(); - }); + $franceProcessingCode = new CouponProcessingCode('ABC1234567890', Country::FRANCE); + $beneluxProcessingCode = new CouponProcessingCode('XYZ9876543210', Country::BENELUX); + $couponProcessingCodes = new CouponProcessingCodeArray([ + $franceProcessingCode, + $beneluxProcessingCode, + ]); - it('hydrates a requested coupon with all its fields', function (): void { + $franceValidityInformation = new CouponValidityInformation( + 'Lorem ipsum dolor sit amet consectetur adipiscing elit.', + Country::FRANCE + ); + $beneluxValidityInformation = new CouponValidityInformation( + 'Lorem ipsum dolor sit amet consectetur adipiscing elit.', + Country::BENELUX + ); + $couponValidityInformations = new CouponValidityInformationArray([ + $franceValidityInformation, + $beneluxValidityInformation, + ]); + + $franceLegalInformation = new CouponLegalInformation( + 'Lorem ipsum dolor sit amet consectetur adipiscing elit.', + Country::FRANCE + ); + $beneluxLegalInformation = new CouponLegalInformation( + 'Lorem ipsum dolor sit amet consectetur adipiscing elit.', + Country::BENELUX + ); + $couponLegalInformations = new CouponLegalInformationArray([ + $franceLegalInformation, + $beneluxLegalInformation, + ]); + + $couponProducts = new CouponProductArray([ + new CouponIncoCode(35784), + new CouponIncoCode(15522), + ]); + $printRules = new PrintRules(maxPerCustomer: 3); + + it('hydrates a requested coupon with all its fields', function () use ( + $couponName, + $quantity, + $diffusion, + $moneyAmount, + $couponPicture, + $couponProcessingCodes, + $couponValidityInformations, + $couponLegalInformations, + $couponProducts, + $printRules, + ): void { // GIVEN / WHEN $coupon = new RequestedCoupon( - $this->name, - $this->quantity, - $this->diffusion, - $this->moneyAmount, - $this->processingCode, - $this->couponValidity, - $this->couponValidityInformation, - $this->products, - $this->printRules, - $this->gs1, + couponName: $couponName, + quantity: $quantity, + diffusion: $diffusion, + moneyAmount: $moneyAmount, + couponPicture: $couponPicture, + couponProcessingCodes: $couponProcessingCodes, + couponValidityInformations: $couponValidityInformations, + couponLegalInformations: $couponLegalInformations, + couponProducts: $couponProducts, + printRules: $printRules, ); // THEN - expect($coupon->name)->toBe($this->name) - ->and($coupon->quantity)->toBe($this->quantity) - ->and($coupon->diffusion)->toBe($this->diffusion) - ->and($coupon->moneyAmount)->toBe($this->moneyAmount) - ->and($coupon->processingCode)->toBe($this->processingCode) - ->and($coupon->couponValidity)->toBe($this->couponValidity) - ->and($coupon->couponValidityInformation)->toBe($this->couponValidityInformation) - ->and($coupon->products)->toBe($this->products) - ->and($coupon->printRules)->toBe($this->printRules) - ->and($coupon->gs1)->toBe($this->gs1); + expect($coupon->couponName)->toBe($couponName) + ->and($coupon->quantity)->toBe($quantity) + ->and($coupon->diffusion)->toBe($diffusion) + ->and($coupon->moneyAmount)->toBe($moneyAmount) + ->and($coupon->couponPicture)->toBe($couponPicture) + ->and($coupon->couponProcessingCodes)->toBe($couponProcessingCodes) + ->and($coupon->couponValidityInformations)->toBe($couponValidityInformations) + ->and($coupon->couponLegalInformations)->toBe($couponLegalInformations) + ->and($coupon->couponProducts)->toBe($couponProducts) + ->and($coupon->printRules)->toBe($printRules); }); - it('hydrates a requested coupon without products', function (): void { + it('hydrates a requested coupon without products', function () use ( + $couponName, + $quantity, + $diffusion, + $moneyAmount, + $couponPicture, + $couponProcessingCodes, + $couponValidityInformations, + $couponLegalInformations, + $printRules, + ): void { // GIVEN / WHEN $coupon = new RequestedCoupon( - name: $this->name, - quantity: $this->quantity, - diffusion: $this->diffusion, - moneyAmount: $this->moneyAmount, - processingCode: $this->processingCode, - couponValidity: $this->couponValidity, - couponValidityInformation: $this->couponValidityInformation, - printRules: $this->printRules, - gs1: $this->gs1 + couponName: $couponName, + quantity: $quantity, + diffusion: $diffusion, + moneyAmount: $moneyAmount, + couponPicture: $couponPicture, + couponProcessingCodes: $couponProcessingCodes, + couponValidityInformations: $couponValidityInformations, + couponLegalInformations: $couponLegalInformations, + couponProducts: new CouponProductArray(), + printRules: $printRules, ); // THEN - expect($coupon->products)->toBe([]) - ->and($coupon->printRules)->toBe($this->printRules) - ->and($coupon->gs1)->toBe($this->gs1); + expect($coupon->couponProducts->value)->toBe([]) + ->and($coupon->printRules)->toBe($printRules); }); - it('hydrates a requested coupon without print rules', function (): void { + it('hydrates a requested coupon without print rules', function () use ( + $couponName, + $quantity, + $diffusion, + $moneyAmount, + $couponPicture, + $couponProcessingCodes, + $couponValidityInformations, + $couponLegalInformations, + $couponProducts, + ): void { // GIVEN / WHEN $coupon = new RequestedCoupon( - name: $this->name, - quantity: $this->quantity, - diffusion: $this->diffusion, - moneyAmount: $this->moneyAmount, - processingCode: $this->processingCode, - couponValidity: $this->couponValidity, - couponValidityInformation: $this->couponValidityInformation, - products: $this->products, - gs1: $this->gs1 + couponName: $couponName, + quantity: $quantity, + diffusion: $diffusion, + moneyAmount: $moneyAmount, + couponPicture: $couponPicture, + couponProcessingCodes: $couponProcessingCodes, + couponValidityInformations: $couponValidityInformations, + couponLegalInformations: $couponLegalInformations, + couponProducts: $couponProducts, ); // THEN expect($coupon->printRules)->toBeNull() - ->and($coupon->products)->toBe($this->products) - ->and($coupon->gs1)->toBe($this->gs1); + ->and($coupon->couponProducts)->toBe($couponProducts); }); - it('hydrates a requested coupon without gs1', function (): void { + it('accepts a france only coupon', function () use ( + $couponName, + $quantity, + $diffusion, + $moneyAmount, + $couponPicture, + $franceProcessingCode, + $franceValidityInformation, + $franceLegalInformation, + $couponProducts, + ): void { // GIVEN / WHEN $coupon = new RequestedCoupon( - name: $this->name, - quantity: $this->quantity, - diffusion: $this->diffusion, - moneyAmount: $this->moneyAmount, - processingCode: $this->processingCode, - couponValidity: $this->couponValidity, - couponValidityInformation: $this->couponValidityInformation, - products: $this->products, - printRules: $this->printRules + couponName: $couponName, + quantity: $quantity, + diffusion: $diffusion, + moneyAmount: $moneyAmount, + couponPicture: $couponPicture, + couponProcessingCodes: new CouponProcessingCodeArray([ + $franceProcessingCode, + ]), + couponValidityInformations: new CouponValidityInformationArray([ + $franceValidityInformation, + ]), + couponLegalInformations: new CouponLegalInformationArray([ + $franceLegalInformation, + ]), + couponProducts: $couponProducts, ); // THEN - expect($coupon->gs1)->toBeNull() - ->and($coupon->products)->toBe($this->products) - ->and($coupon->printRules)->toBe($this->printRules); + expect($coupon)->toBeInstanceOf(RequestedCoupon::class); }); - it('hydrates a requested coupon with only required fields', function (): void { - // GIVEN / WHEN - $coupon = new RequestedCoupon( - name: $this->name, - quantity: $this->quantity, - diffusion: $this->diffusion, - moneyAmount: $this->moneyAmount, - processingCode: $this->processingCode, - couponValidity: $this->couponValidity, - couponValidityInformation: $this->couponValidityInformation, - ); - - // THEN - expect($coupon->products)->toBe([]) - ->and($coupon->printRules)->toBeNull() - ->and($coupon->gs1)->toBeNull(); - }); - - it('throws when there is no validity information for france', function (): void { + it('throws when there is no processing code for france', function () use ( + $couponName, + $quantity, + $diffusion, + $moneyAmount, + $couponPicture, + $beneluxProcessingCode, + $couponValidityInformations, + $couponLegalInformations, + $couponProducts, + ): void { // GIVEN / WHEN / THEN - expect(fn () => new RequestedCoupon( - name: $this->name, - quantity: $this->quantity, - diffusion: $this->diffusion, - moneyAmount: $this->moneyAmount, - processingCode: $this->processingCode, - couponValidity: $this->couponValidity, - couponValidityInformation: [], - ))->toThrow(\DomainException::class, 'A coupon requires validity information for France.'); + expect(fn (): RequestedCoupon => new RequestedCoupon( + couponName: $couponName, + quantity: $quantity, + diffusion: $diffusion, + moneyAmount: $moneyAmount, + couponPicture: $couponPicture, + couponProcessingCodes: new CouponProcessingCodeArray([ + $beneluxProcessingCode, + ]), + couponValidityInformations: $couponValidityInformations, + couponLegalInformations: $couponLegalInformations, + couponProducts: $couponProducts, + ))->toThrow(\DomainException::class, 'CouponProcessingCodes requires a CouponProcessingCode for France.'); }); - it('throws when validity information contains an invalid item', function (): void { + it('throws when a benelux processing code has no benelux validity information', function () use ( + $couponName, + $quantity, + $diffusion, + $moneyAmount, + $couponPicture, + $couponProcessingCodes, + $franceValidityInformation, + $couponLegalInformations, + $couponProducts, + ): void { // GIVEN / WHEN / THEN - expect(fn () => new RequestedCoupon( - $this->name, - $this->quantity, - $this->diffusion, - $this->moneyAmount, - $this->processingCode, - $this->couponValidity, - ['Not a CouponValidityInformation'], - $this->products, - $this->printRules, - $this->gs1 - ))->toThrow(\DomainException::class, 'Coupon validity information must be CouponValidityInformation instances.'); + expect(fn (): RequestedCoupon => new RequestedCoupon( + couponName: $couponName, + quantity: $quantity, + diffusion: $diffusion, + moneyAmount: $moneyAmount, + couponPicture: $couponPicture, + couponProcessingCodes: $couponProcessingCodes, + couponValidityInformations: new CouponValidityInformationArray([ + $franceValidityInformation, + ]), + couponLegalInformations: $couponLegalInformations, + couponProducts: $couponProducts, + ))->toThrow(\DomainException::class, 'A coupon requires validity information for BENELUX.'); }); - it('throws when a gs1 coupon has no benelux validity information', function (): void { + it('throws when a benelux processing code has no benelux legal information', function () use ( + $couponName, + $quantity, + $diffusion, + $moneyAmount, + $couponPicture, + $couponProcessingCodes, + $couponValidityInformations, + $franceLegalInformation, + $couponProducts, + ): void { // GIVEN / WHEN / THEN - expect(fn () => new RequestedCoupon( - name: $this->name, - quantity: $this->quantity, - diffusion: $this->diffusion, - moneyAmount: $this->moneyAmount, - processingCode: $this->processingCode, - couponValidity: $this->couponValidity, - couponValidityInformation: [$this->franceValidityInformation], - gs1: $this->gs1, - ))->toThrow(\DomainException::class, 'A GS1 coupon requires validity information for Benelux.'); - }); - - it('throws when a gs1 coupon has only benelux validity information', function (): void { - // GIVEN / WHEN / THEN - expect(fn () => new RequestedCoupon( - name: $this->name, - quantity: $this->quantity, - diffusion: $this->diffusion, - moneyAmount: $this->moneyAmount, - processingCode: $this->processingCode, - couponValidity: $this->couponValidity, - couponValidityInformation: [$this->beneluxValidityInformation], - gs1: $this->gs1, - ))->toThrow(\DomainException::class, 'A coupon requires validity information for France.'); - }); - - it('accepts a coupon without gs1 and without benelux validity information', function (): void { - // GIVEN / WHEN - $coupon = new RequestedCoupon( - name: $this->name, - quantity: $this->quantity, - diffusion: $this->diffusion, - moneyAmount: $this->moneyAmount, - processingCode: $this->processingCode, - couponValidity: $this->couponValidity, - couponValidityInformation: [$this->franceValidityInformation], - ); - - // THEN - expect($coupon->gs1)->toBeNull(); + expect(fn (): RequestedCoupon => new RequestedCoupon( + couponName: $couponName, + quantity: $quantity, + diffusion: $diffusion, + moneyAmount: $moneyAmount, + couponPicture: $couponPicture, + couponProcessingCodes: $couponProcessingCodes, + couponValidityInformations: $couponValidityInformations, + couponLegalInformations: new CouponLegalInformationArray([ + $franceLegalInformation, + ]), + couponProducts: $couponProducts, + ))->toThrow(\DomainException::class, 'A coupon requires legal information for BENELUX.'); }); }); diff --git a/tests/Unit/Domain/Models/ValueObjects/Stock/StockAdjustmentTest.php b/tests/Unit/Domain/Models/ValueObjects/Stock/StockAdjustmentTest.php index 42441f1..c897668 100644 --- a/tests/Unit/Domain/Models/ValueObjects/Stock/StockAdjustmentTest.php +++ b/tests/Unit/Domain/Models/ValueObjects/Stock/StockAdjustmentTest.php @@ -10,73 +10,71 @@ 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'; - }); + $quantity = 10; + $zeroQuantity = 0; + $negativeQuantity = -5; + $reason = 'Inventory correction'; - it('creates a valid stock adjustment', function (): void { + it('creates a valid stock adjustment', function () use ($quantity, $reason): void { // GIVEN $actorId = ActorId::generate(); // WHEN $adjustment = new StockAdjustment( - $this->quantity, - $this->reason, - $actorId + $quantity, + $reason, + $actorId, ); // THEN expect($adjustment)->toBeInstanceOf(StockAdjustment::class) - ->and($adjustment->quantity)->toBe($this->quantity) - ->and($adjustment->reason)->toBe($this->reason) + ->and($adjustment->quantity)->toBe($quantity) + ->and($adjustment->reason)->toBe($reason) ->and($adjustment->actorId)->toBe($actorId) ->and($adjustment->createdAt)->toBeNull(); }); - it('creates a stock adjustment with an explicit createdAt date', function (): void { + it('creates a stock adjustment with an explicit createdAt date', function () use ($quantity, $reason): void { // GIVEN $actorId = ActorId::generate(); $createdAt = new \DateTimeImmutable('2026-06-15 12:00:00', new \DateTimeZone('UTC')); // WHEN $adjustment = new StockAdjustment( - $this->quantity, - $this->reason, + $quantity, + $reason, $actorId, - $createdAt + $createdAt, ); // THEN expect($adjustment->createdAt)->toBe($createdAt); }); - it('rejects a quantity of zero', function (): void { + it('rejects a quantity of zero', function () use ($zeroQuantity, $reason): void { // GIVEN $actorId = ActorId::generate(); // WHEN $action = fn (): StockAdjustment => new StockAdjustment( - $this->zeroQuantity, - $this->reason, - $actorId + $zeroQuantity, + $reason, + $actorId, ); // THEN expect($action)->toThrow(\DomainException::class, 'Stock Amount should be more than 0.'); }); - it('rejects a negative quantity', function (): void { + it('rejects a negative quantity', function () use ($negativeQuantity, $reason): void { // GIVEN $actorId = ActorId::generate(); // WHEN $action = fn (): StockAdjustment => new StockAdjustment( - $this->negativeQuantity, - $this->reason, - $actorId + $negativeQuantity, + $reason, + $actorId, ); // THEN diff --git a/tests/Unit/Domain/Models/ValueObjects/Stock/StockAmountTest.php b/tests/Unit/Domain/Models/ValueObjects/Stock/StockAmountTest.php index 59a7753..539a44d 100644 --- a/tests/Unit/Domain/Models/ValueObjects/Stock/StockAmountTest.php +++ b/tests/Unit/Domain/Models/ValueObjects/Stock/StockAmountTest.php @@ -9,38 +9,30 @@ use Domain\Models\ValueObjects\Stock\StockAmount; covers(StockAmount::class); describe('stock amount', function (): void { - beforeEach(function (): void { - $this->value = 10; - $this->zeroValue = 0; - $this->negativeValue = -1; - }); + $value = 10; + $zeroValue = 0; + $negativeValue = -1; - it('creates a valid stock amount', function (): void { + it('creates a valid stock amount', function () use ($value): void { // GIVEN / WHEN - $amount = new StockAmount( - $this->value - ); + $amount = new StockAmount($value); // THEN expect($amount)->toBeInstanceOf(StockAmount::class) - ->and($amount->value)->toBe($this->value); + ->and($amount->value)->toBe($value); }); - it('accepts a zero value', function (): void { + it('accepts a zero value', function () use ($zeroValue): void { // GIVEN / WHEN - $amount = new StockAmount( - $this->zeroValue - ); + $amount = new StockAmount($zeroValue); // THEN - expect($amount->value)->toBe($this->zeroValue); + expect($amount->value)->toBe($zeroValue); }); - it('rejects a negative value', function (): void { + it('rejects a negative value', function () use ($negativeValue): void { // GIVEN / WHEN - $action = fn (): StockAmount => new StockAmount( - $this->negativeValue - ); + $action = fn (): StockAmount => new StockAmount($negativeValue); // THEN expect($action)->toThrow(\DomainException::class, 'Stock Amount should be positive.'); diff --git a/tests/Unit/Domain/Enums/CountryTest.php b/tests/Unit/Shared/Enums/CountryTest.php similarity index 57% rename from tests/Unit/Domain/Enums/CountryTest.php rename to tests/Unit/Shared/Enums/CountryTest.php index 5a6c7b8..e548d13 100644 --- a/tests/Unit/Domain/Enums/CountryTest.php +++ b/tests/Unit/Shared/Enums/CountryTest.php @@ -4,9 +4,10 @@ declare(strict_types=1); namespace Tests\Unit\Shared\Enums; +use Shared\DDD\Exceptions\UnknownCountryIsoCodeException; use Shared\Enums\Country; -covers(Country::class); +covers(Country::class, UnknownCountryIsoCodeException::class); describe('country', function (): void { it('has the expected cases with their backing values', function (): void { @@ -19,4 +20,12 @@ describe('country', function (): void { // THEN expect(Country::FRANCE->name)->toBe('FRANCE'); }); + + it('is a domain exception', function (): void { + // GIVEN / WHEN + $exception = UnknownCountryIsoCodeException::fromIsoCode('XX'); + + // THEN + expect($exception)->toBeInstanceOf(\DomainException::class); + }); }); diff --git a/tests/Unit/Shared/Helpers/DatesHelperTest.php b/tests/Unit/Shared/Helpers/DatesHelperTest.php index 9a683ab..e7ac93a 100644 --- a/tests/Unit/Shared/Helpers/DatesHelperTest.php +++ b/tests/Unit/Shared/Helpers/DatesHelperTest.php @@ -250,7 +250,7 @@ describe('dates helper', function (): void { $invalidInput = 'not-a-valid-date'; // WHEN / THEN - expect(fn () => DatesHelper::parseDateTime($invalidInput)) + expect(fn (): \DateTimeInterface => DatesHelper::parseDateTime($invalidInput)) ->toThrow(InvalidDateFormatException::class); }); @@ -259,7 +259,7 @@ describe('dates helper', function (): void { $emptyInput = ' '; // WHEN / THEN - expect(fn () => DatesHelper::parseDateTime($emptyInput)) + expect(fn (): \DateTimeInterface => DatesHelper::parseDateTime($emptyInput)) ->toThrow(InvalidDateFormatException::class); }); }); diff --git a/tests/Unit/Shared/Helpers/UuidHelperTest.php b/tests/Unit/Shared/Helpers/UuidHelperTest.php index 4e4d3ad..67985f5 100644 --- a/tests/Unit/Shared/Helpers/UuidHelperTest.php +++ b/tests/Unit/Shared/Helpers/UuidHelperTest.php @@ -11,7 +11,7 @@ use Ramsey\Uuid\Uuid; use Shared\Helpers\Exceptions\InvalidUuidException; use Shared\Helpers\UuidHelper; -covers(UuidHelper::class); +covers(UuidHelper::class, InvalidUuidException::class); describe('Uuid Helper Unit Tests', function (): void { it('prevents direct instantiation but covers constructor', function (): void { @@ -23,7 +23,7 @@ describe('Uuid Helper Unit Tests', function (): void { $constructor->invoke($reflection->newInstanceWithoutConstructor()); // THEN - expect(fn () => $reflection->newInstance()) + expect(fn (): UuidHelper => $reflection->newInstance()) ->toThrow(\ReflectionException::class); }); @@ -83,6 +83,7 @@ describe('Uuid Helper Unit Tests', function (): void { public function unserialize(string $data): void { + // Keep empty. } public function __serialize(): array @@ -92,6 +93,7 @@ describe('Uuid Helper Unit Tests', function (): void { public function __unserialize(array $data): void { + // Keep empty. } }; @@ -127,7 +129,15 @@ describe('Uuid Helper Unit Tests', function (): void { $uuidV4 = Uuid::uuid4(); // WHEN & THEN - expect(fn () => UuidHelper::toDateTime($uuidV4)) + expect(fn (): \DateTimeImmutable => UuidHelper::toDateTime($uuidV4)) ->toThrow(InvalidUuidException::class, 'Cannot extract datetime from non-V7 UUID'); }); + + it('is an invalid argument exception', function (): void { + // GIVEN / WHEN + $exception = InvalidUuidException::forValue('foo'); + + // THEN + expect($exception)->toBeInstanceOf(\InvalidArgumentException::class); + }); }); diff --git a/tests/Unit/Shared/ValueObjects/Pagination/PageNumberTest.php b/tests/Unit/Shared/ValueObjects/Pagination/PageNumberTest.php index f23a423..4dfdff5 100644 --- a/tests/Unit/Shared/ValueObjects/Pagination/PageNumberTest.php +++ b/tests/Unit/Shared/ValueObjects/Pagination/PageNumberTest.php @@ -46,7 +46,7 @@ describe('PageNumber Value Object Unit Tests', function (): void { it('rejects page number less than or equal to zero', function (int $invalidValue): void { // WHEN / THEN - expect(fn () => new PageNumber($invalidValue)) + expect(fn (): PageNumber => new PageNumber($invalidValue)) ->toThrow(\InvalidArgumentException::class, 'Page number must be greater than 0'); })->with([ 'zero' => [0], diff --git a/tests/Unit/Shared/ValueObjects/Pagination/PageSizeTest.php b/tests/Unit/Shared/ValueObjects/Pagination/PageSizeTest.php index fa96ee3..09cbdad 100644 --- a/tests/Unit/Shared/ValueObjects/Pagination/PageSizeTest.php +++ b/tests/Unit/Shared/ValueObjects/Pagination/PageSizeTest.php @@ -54,7 +54,7 @@ describe('PageSize Value Object Unit Tests', function (): void { it('rejects page size less than or equal to zero', function (int $invalidValue): void { // WHEN / THEN - expect(fn () => new PageSize($invalidValue)) + expect(fn (): PageSize => new PageSize($invalidValue)) ->toThrow(\InvalidArgumentException::class, 'Page size must be greater than 0'); })->with([ 'zero' => [0], @@ -64,7 +64,7 @@ describe('PageSize Value Object Unit Tests', function (): void { it('rejects page size exceeding maximum page size', function (): void { // WHEN / THEN - expect(fn () => new PageSize(PageSize::MAX_PAGE_SIZE + 1)) + expect(fn (): PageSize => new PageSize(PageSize::MAX_PAGE_SIZE + 1)) ->toThrow(\InvalidArgumentException::class, 'Page size must be less than or equal to 10000'); });