diff --git a/src/Application/UseCases/Coupon/Find/FindCoupon.php b/src/Application/UseCases/Coupon/Find/FindCoupon.php new file mode 100644 index 0000000..44683f8 --- /dev/null +++ b/src/Application/UseCases/Coupon/Find/FindCoupon.php @@ -0,0 +1,68 @@ + */ +final readonly class FindCoupon implements UseCase +{ + public function __construct( + private CouponRepository $couponRepository, + private BrandRepository $brandRepository, + private CampaignRepository $campaignRepository, + ) { + } + + #[\Override] + public function execute(BaseInput $input): Output + { + Assert::isInstanceOf($input, Input::class); + + $coupon = $this->couponRepository->findByCode($input->code); + + if (! $coupon instanceof Coupon) { + return new CouponNotFound(); + } + + $brand = $this->brandRepository->findByCode($coupon->brandCode); + + if (! $brand instanceof Brand) { + throw new \DomainException(sprintf('Brand code "%s" was not found.', $coupon->brandCode->value)); + } + + $campaigns = $this->campaignRepository->findByCouponCode($coupon->code); + + return new CouponFound( + code: $coupon->code, + name: $coupon->name, + amount: $coupon->amount, + picture: $coupon->picture, + diffusion: $coupon->diffusion, + availabilityCalendar: $coupon->availabilityCalendar, + validityInformations: $coupon->validityInformations, + legalInformations: $coupon->legalInformations, + processingCode: $coupon->processingCode, + brand: $brand, + createdBy: $coupon->createdBy, + products: $coupon->products, + movements: $coupon->movements, + quantity: $coupon->quantity, + campaigns: $campaigns, + createdAt: $coupon->createdAt(), + updatedAt: $coupon->updatedAt(), + ); + } +} diff --git a/src/Application/UseCases/Coupon/Find/Input.php b/src/Application/UseCases/Coupon/Find/Input.php new file mode 100644 index 0000000..37782f2 --- /dev/null +++ b/src/Application/UseCases/Coupon/Find/Input.php @@ -0,0 +1,16 @@ + $campaigns + */ + + public function __construct( + public CouponCode $code, + public CouponName $name, + public MoneyAmount $amount, + public CouponPicture $picture, + public CouponDiffusion $diffusion, + public ?CouponAvailabilityCalendar $availabilityCalendar, + public CouponValidityInformationArray $validityInformations, + public CouponLegalInformationArray $legalInformations, + public CouponProcessingCodeArray $processingCode, + public Brand $brand, + public ActorId $createdBy, + public CouponProductArray $products, + public CouponMovementArray $movements, + public CouponQuantity $quantity, + public array $campaigns, + public \DateTimeInterface $createdAt, + public ?\DateTimeInterface $updatedAt, + ) { + } +} diff --git a/src/Application/UseCases/Coupon/Find/Output/CouponNotFound.php b/src/Application/UseCases/Coupon/Find/Output/CouponNotFound.php new file mode 100644 index 0000000..2e9d3bf --- /dev/null +++ b/src/Application/UseCases/Coupon/Find/Output/CouponNotFound.php @@ -0,0 +1,9 @@ + + */ + public function findByCouponCode(CouponCode $couponCode): array; } diff --git a/tests/Unit/Application/UseCases/Coupon/Find/FindCouponTest.php b/tests/Unit/Application/UseCases/Coupon/Find/FindCouponTest.php new file mode 100644 index 0000000..712bd45 --- /dev/null +++ b/tests/Unit/Application/UseCases/Coupon/Find/FindCouponTest.php @@ -0,0 +1,209 @@ +code, + createdBy: ActorId::generate(), + products: new CouponProductArray([ + new CouponIncoCode(35784), + new CouponIncoCode(15522), + ]), + movements: new CouponMovementArray([ + new CouponMovement(1000) + ]), + quantity: new CouponQuantity(1000), + ); + $campaign = Campaign::create( + code: new CampaignCode('CMP01'), + name: new CampaignName('Campaign name'), + publication: true, + printAllAtOnce: true, + coupons: new CampaignCouponArray([ + new CampaignCoupon($coupon->code), + ]), + brandCode: $brand->code, + actorId: ActorId::generate(), + ); + + it('finds a coupon by its code', function () use ($brand, $coupon, $campaign): void { + // GIVEN + $brandRepository = \Mockery::mock(BrandRepository::class); + $brandRepository->shouldReceive('findByCode')->once()->with($coupon->brandCode)->andReturn($brand); + + $couponRepository = \Mockery::mock(CouponRepository::class); + $couponRepository->shouldReceive('findByCode')->once()->with($coupon->code)->andReturn($coupon); + + $campaignRepository = \Mockery::mock(CampaignRepository::class); + $campaignRepository->shouldReceive('findByCouponCode')->once()->with($coupon->code)->andReturn([$campaign]); + + $useCase = new FindCoupon($couponRepository, $brandRepository, $campaignRepository); + + // WHEN + $result = $useCase->execute(new Input(code: $coupon->code)); + + // THEN + expect($result)->toBeInstanceOf(CouponFound::class) + ->and($result->code)->toBe($coupon->code) + ->and($result->name)->toBe($coupon->name) + ->and($result->amount)->toBe($coupon->amount) + ->and($result->picture)->toBe($coupon->picture) + ->and($result->diffusion)->toBe($coupon->diffusion) + ->and($result->availabilityCalendar)->toBe($coupon->availabilityCalendar) + ->and($result->validityInformations)->toBe($coupon->validityInformations) + ->and($result->legalInformations)->toBe($coupon->legalInformations) + ->and($result->processingCode)->toBe($coupon->processingCode) + ->and($result->brand)->toBe($brand) + ->and($result->createdBy)->toBe($coupon->createdBy) + ->and($result->products)->toBe($coupon->products) + ->and($result->movements)->toBe($coupon->movements) + ->and($result->quantity)->toBe($coupon->quantity) + ->and($result->campaigns)->toBe([$campaign]) + ->and($result->createdAt)->toBeInstanceOf(\DateTimeInterface::class) + ->and($result->updatedAt)->toBeNull(); + }); + + it('returns CouponNotFound when the code is not found', function (): void { + // GIVEN + $code = new CouponCode('COU02'); + + $couponRepository = \Mockery::mock(CouponRepository::class); + $couponRepository->shouldReceive('findByCode')->once()->with($code)->andReturn(null); + + $brandRepository = \Mockery::mock(BrandRepository::class); + $brandRepository->shouldNotReceive('findByCode'); + + $campaignRepository = \Mockery::mock(CampaignRepository::class); + $campaignRepository->shouldNotReceive('findByCouponCode'); + + $useCase = new FindCoupon($couponRepository, $brandRepository, $campaignRepository); + + // WHEN + $result = $useCase->execute(new Input(code: $code)); + + // THEN + expect($result)->toBeInstanceOf(CouponNotFound::class); + }); + + it('throws when the brand of the coupon is not found', function () use ($coupon): void { + // GIVEN + $couponRepository = \Mockery::mock(CouponRepository::class); + $couponRepository->shouldReceive('findByCode')->once()->with($coupon->code)->andReturn($coupon); + + $brandRepository = \Mockery::mock(BrandRepository::class); + $brandRepository->shouldReceive('findByCode')->once()->with($coupon->brandCode)->andReturn(null); + + $campaignRepository = \Mockery::mock(CampaignRepository::class); + $campaignRepository->shouldNotReceive('findByCouponCode'); + + $useCase = new FindCoupon($couponRepository, $brandRepository, $campaignRepository); + + // WHEN / THEN + expect(fn () => $useCase->execute(new Input(code: $coupon->code))) + ->toThrow(\DomainException::class); + }); + + it('finds a coupon not attached to any campaign', function () use ($brand, $coupon): void { + // GIVEN + $couponRepository = \Mockery::mock(CouponRepository::class); + $couponRepository->shouldReceive('findByCode')->once()->with($coupon->code)->andReturn($coupon); + + $brandRepository = \Mockery::mock(BrandRepository::class); + $brandRepository->shouldReceive('findByCode')->once()->with($coupon->brandCode)->andReturn($brand); + + $campaignRepository = \Mockery::mock(CampaignRepository::class); + $campaignRepository->shouldReceive('findByCouponCode')->once()->with($coupon->code)->andReturn([]); + + $useCase = new FindCoupon($couponRepository, $brandRepository, $campaignRepository); + + // WHEN + $result = $useCase->execute(new Input(code: $coupon->code)); + + // THEN + expect($result)->toBeInstanceOf(CouponFound::class) + ->and($result->campaigns)->toBe([]); + }); +});