ldc-mesbr-app/tests/Unit/Application/UseCases/PrintableCoupon/Create/CreatePrintableCouponTest.php
2026-10-15 15:39:34 +02:00

429 lines
19 KiB
PHP

<?php
declare(strict_types=1);
namespace Tests\Unit\Application\UseCases\PrintableCoupon\Create;
use Application\UseCases\PrintableCoupon\Create\Input;
use Application\UseCases\PrintableCoupon\Create\Output;
use Application\UseCases\PrintableCoupon\Create\CreatePrintableCoupon;
use DateTime;
use Domain\Models\Entities\Campaign;
use Domain\Models\Entities\Coupon;
use Domain\Models\Entities\PrintableCoupon;
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\CampaignName;
use Domain\Models\ValueObjects\Coupon\CouponAvailabilityCalendar;
use Domain\Models\ValueObjects\Coupon\CouponCode;
use Domain\Models\ValueObjects\Coupon\CouponDiffusion;
use Domain\Models\ValueObjects\Coupon\CouponLegalInformation;
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\CouponProductArray;
use Domain\Models\ValueObjects\Coupon\CouponValidityInformation;
use Domain\Models\ValueObjects\Coupon\CouponValidityInformationArray;
use Domain\Models\ValueObjects\CrmHashcode;
use Domain\Models\ValueObjects\IncoCode;
use Domain\Models\ValueObjects\MoneyAmount;
use Domain\Models\ValueObjects\Period;
use Domain\Models\ValueObjects\PicturePath;
use Domain\Models\ValueObjects\PrintableCoupon\MarketingDirectCode;
use Domain\Models\ValueObjects\PrintableCoupon\PrintableCouponCode;
use Domain\Models\ValueObjects\PrintableCoupon\PrintedCoupon;
use Domain\Models\ValueObjects\PrintableCoupon\PrintedCouponId;
use Domain\Models\ValueObjects\PrintRules;
use Domain\Models\ValueObjects\ProcessingCode;
use Domain\Models\ValueObjects\ProcessingCodeArray;
use Domain\Ports\Repositories\CampaignRepository;
use Domain\Ports\Repositories\CouponRepository;
use Domain\Ports\Repositories\PrintableCouponRepository;
use Ramsey\Uuid\Uuid;
use Shared\Enums\Country;
use Shared\Enums\Currency;
use Shared\Helpers\DatesHelper;
use Shared\ValueObjects\Money;
covers(CreatePrintableCoupon::class, Input::class, Output::class);
describe('create coupon use case', function (): void {
$crmCustomerHashcode = new CrmHashcode(Uuid::uuid4());
$brandCode = new BrandCode('AAA01');
$campaignCode = new CampaignCode('CMP01');
$couponCode = new CouponCode('COU01');
$couponName = new CouponName('Coupon name');
$couponDiffusionStart = new DateTime()->modify('-1 month');
$couponDiffusionEnd = new DateTime()->modify('+1 month');
$makeCoupon = fn (array $overrides = []): Coupon => Coupon::create(
code: $couponCode,
name: $couponName,
amount: new MoneyAmount(new Money(Currency::EUR, 100)),
picture: new CouponPicture(
new PicturePath('files/product.jpg'),
new PicturePath('files/thumbnail.jpg'),
new PicturePath('files/mobile_thumbnail.jpg'),
),
diffusion: new CouponDiffusion(
new Period(
$overrides['diffusionStart'] ?? $couponDiffusionStart,
$overrides['diffusionEnd'] ?? $couponDiffusionEnd,
),
),
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 ProcessingCodeArray([
new ProcessingCode('5425003944130', Country::FRANCE),
]),
brandCode: $brandCode,
createdBy: ActorId::generate(),
products: new CouponProductArray([
new IncoCode(35784) ,
new IncoCode(15522),
]),
movements: new CouponMovementArray([
new CouponMovement(1000)
]),
availabilityCalendar: $overrides['availabilityCalendar'] ?? null,
);
$printRules = fn (?int $maxPerCustomer = null, ?int $cooldownDays = null): ?PrintRules => ($maxPerCustomer || $cooldownDays) ? new PrintRules(
maxPerCustomer: $maxPerCustomer,
cooldownDays: $cooldownDays,
) : null;
$campaignCoupon = fn (?int $maxPerCustomer = null, ?int $cooldownDays = null): CampaignCoupon => new CampaignCoupon(
$couponCode,
$printRules($maxPerCustomer, $cooldownDays),
);
$makeCampaign = fn (?int $maxPerCustomer = null, ?int $cooldownDays = null, bool $published = true): Campaign => Campaign::create(
code: $campaignCode,
name: new CampaignName('Campaign name'),
publication: $published,
printAllAtOnce: true,
coupons: new CampaignCouponArray([
$campaignCoupon($maxPerCustomer, $cooldownDays),
]),
brandCode: $brandCode,
actorId: ActorId::generate(),
);
$printedCoupon = new PrintedCoupon(
id: PrintedCouponId::generate(),
marketingCode: new MarketingDirectCode('0000000000001'),
printableCode: PrintableCouponCode::generate(),
couponCode: $couponCode,
campaignCode: $campaignCode,
customer: $crmCustomerHashcode,
printedAt: DatesHelper::now(),
);
$makeInput = fn (): Input => new Input(
campaignCode: $campaignCode,
couponCode: $couponCode,
crmCustomerHashcode: $crmCustomerHashcode,
);
it('creates a printable coupon', function () use ($makeInput, $crmCustomerHashcode, $makeCampaign, $makeCoupon, $campaignCoupon): void {
// GIVEN
$input = $makeInput();
$campaign = $makeCampaign();
$coupon = $makeCoupon();
$campaignRepository = \Mockery::mock(CampaignRepository::class);
$campaignRepository->shouldReceive('findByCode')->once()->with($campaign->code)->andReturn($campaign);
$campaignRepository->shouldReceive('findCampaignCoupon')->once()->with($campaign->code, $coupon->code)
->andReturn($campaignCoupon());
$couponRepository = \Mockery::mock(CouponRepository::class);
$couponRepository->shouldReceive('findByCode')->once()->with($coupon->code)->andReturn($coupon);
$printableCouponRepository = \Mockery::mock(PrintableCouponRepository::class);
$printableCouponRepository->shouldReceive('save')->once()->with(\Mockery::type(PrintableCoupon::class));
$useCase = new CreatePrintableCoupon($printableCouponRepository, $couponRepository, $campaignRepository);
// WHEN
$result = $useCase->execute($input);
// THEN
expect($result)->toBeInstanceOf(Output::class)
->and($result->campaignCode)->toEqual($campaign->code)
->and($result->couponCode)->toEqual($coupon->code)
->and($result->crmCustomerHashcode)->toEqual($crmCustomerHashcode)
->and($result->distributableUntil)->toEqual($coupon->diffusion->period->end);
});
it('rejects a printable coupon when campaign not found', function () use ($makeInput, $makeCampaign): void {
// GIVEN
$input = $makeInput();
$campaign = $makeCampaign();
$campaignRepository = \Mockery::mock(CampaignRepository::class);
$campaignRepository->shouldReceive('findByCode')->once()->with($campaign->code)->andReturn(null);
$couponRepository = \Mockery::mock(CouponRepository::class);
$printableCouponRepository = \Mockery::mock(PrintableCouponRepository::class);
$useCase = new CreatePrintableCoupon($printableCouponRepository, $couponRepository, $campaignRepository);
// WHEN
$action = fn (): Output => $useCase->execute($input);
// THEN
expect($action)->toThrow(\DomainException::class, sprintf('Campaign from code "%s" not found.', $input->campaignCode->value));
});
it('rejects a printable coupon when campaign is not published', function () use ($makeInput, $makeCampaign): void {
// GIVEN
$input = $makeInput();
$campaign = $makeCampaign(published: false);
$campaignRepository = \Mockery::mock(CampaignRepository::class);
$campaignRepository->shouldReceive('findByCode')->once()->with($campaign->code)->andReturn($campaign);
$couponRepository = \Mockery::mock(CouponRepository::class);
$printableCouponRepository = \Mockery::mock(PrintableCouponRepository::class);
$useCase = new CreatePrintableCoupon($printableCouponRepository, $couponRepository, $campaignRepository);
// WHEN
$action = fn (): Output => $useCase->execute($input);
// THEN
expect($action)->toThrow(\DomainException::class, 'Campaign must be published.');
});
it('rejects a printable coupon when coupon is not found', function () use ($makeInput, $makeCampaign, $makeCoupon): void {
// GIVEN
$input = $makeInput();
$campaign = $makeCampaign();
$coupon = $makeCoupon();
$campaignRepository = \Mockery::mock(CampaignRepository::class);
$campaignRepository->shouldReceive('findByCode')->once()->with($campaign->code)->andReturn($campaign);
$couponRepository = \Mockery::mock(CouponRepository::class);
$couponRepository->shouldReceive('findByCode')->once()->with($coupon->code)->andReturn(null);
$printableCouponRepository = \Mockery::mock(PrintableCouponRepository::class);
$useCase = new CreatePrintableCoupon($printableCouponRepository, $couponRepository, $campaignRepository);
// WHEN
$action = fn (): Output => $useCase->execute($input);
// THEN
expect($action)->toThrow(\DomainException::class, sprintf('Coupon code "%s" not found.', $input->couponCode->value));
});
it('rejects a printable coupon when campaign coupon is not found', function () use ($makeInput, $makeCampaign, $makeCoupon): void {
// GIVEN
$input = $makeInput();
$campaign = $makeCampaign();
$coupon = $makeCoupon();
$campaignRepository = \Mockery::mock(CampaignRepository::class);
$campaignRepository->shouldReceive('findByCode')->once()->with($campaign->code)->andReturn($campaign);
$campaignRepository->shouldReceive('findCampaignCoupon')->once()->with($campaign->code, $coupon->code)
->andReturn(null);
$couponRepository = \Mockery::mock(CouponRepository::class);
$couponRepository->shouldReceive('findByCode')->once()->with($coupon->code)->andReturn($coupon);
$printableCouponRepository = \Mockery::mock(PrintableCouponRepository::class);
$useCase = new CreatePrintableCoupon($printableCouponRepository, $couponRepository, $campaignRepository);
// WHEN
$action = fn (): Output => $useCase->execute($input);
// THEN
expect($action)->toThrow(\DomainException::class, 'Coupon must be associated to the associated campaign coupon.');
});
it('rejects a printable coupon when outside of diffusion start', function () use ($makeInput, $makeCampaign, $makeCoupon, $campaignCoupon): void {
// GIVEN
$input = $makeInput();
$campaign = $makeCampaign();
$coupon = $makeCoupon(['diffusionStart' => new DateTime()->modify('+1 day')]);
$campaignRepository = \Mockery::mock(CampaignRepository::class);
$campaignRepository->shouldReceive('findByCode')->once()->with($campaign->code)->andReturn($campaign);
$campaignRepository->shouldReceive('findCampaignCoupon')->once()->with($campaign->code, $coupon->code)
->andReturn($campaignCoupon());
$couponRepository = \Mockery::mock(CouponRepository::class);
$couponRepository->shouldReceive('findByCode')->once()->with($coupon->code)->andReturn($coupon);
$printableCouponRepository = \Mockery::mock(PrintableCouponRepository::class);
$useCase = new CreatePrintableCoupon($printableCouponRepository, $couponRepository, $campaignRepository);
// WHEN
$action = fn (): Output => $useCase->execute($input);
// THEN
expect($action)->toThrow(\DomainException::class, 'The printing period has not yet begun.');
});
it('rejects a printable coupon when outside of diffusion end', function () use ($makeInput, $makeCampaign, $makeCoupon, $campaignCoupon): void {
// GIVEN
$input = $makeInput();
$campaign = $makeCampaign();
$coupon = $makeCoupon(['diffusionEnd' => new DateTime()->modify('-1 day')]);
$campaignRepository = \Mockery::mock(CampaignRepository::class);
$campaignRepository->shouldReceive('findByCode')->once()->with($campaign->code)->andReturn($campaign);
$campaignRepository->shouldReceive('findCampaignCoupon')->once()->with($campaign->code, $coupon->code)
->andReturn($campaignCoupon());
$couponRepository = \Mockery::mock(CouponRepository::class);
$couponRepository->shouldReceive('findByCode')->once()->with($coupon->code)->andReturn($coupon);
$printableCouponRepository = \Mockery::mock(PrintableCouponRepository::class);
$useCase = new CreatePrintableCoupon($printableCouponRepository, $couponRepository, $campaignRepository);
// WHEN
$action = fn (): Output => $useCase->execute($input);
// THEN
expect($action)->toThrow(\DomainException::class, 'The printing period has expired.');
});
it('creates a printable coupon when calendar period is inside diffusion period', function () use (
$makeInput,
$crmCustomerHashcode,
$makeCampaign,
$makeCoupon,
$campaignCoupon,
): void {
// GIVEN
$calendarStart = new DateTime()->modify('-1 day');
$calendarEnd = new DateTime()->modify('+1 day');
$input = $makeInput();
$campaign = $makeCampaign();
$coupon = $makeCoupon([
'availabilityCalendar' => new CouponAvailabilityCalendar([
new Period(
start: $calendarStart,
end: $calendarEnd,
),
]),
]);
$campaignRepository = \Mockery::mock(CampaignRepository::class);
$campaignRepository->shouldReceive('findByCode')->once()->with($campaign->code)->andReturn($campaign);
$campaignRepository->shouldReceive('findCampaignCoupon')->once()->with($campaign->code, $coupon->code)
->andReturn($campaignCoupon());
$couponRepository = \Mockery::mock(CouponRepository::class);
$couponRepository->shouldReceive('findByCode')->once()->with($coupon->code)->andReturn($coupon);
$printableCouponRepository = \Mockery::mock(PrintableCouponRepository::class);
$printableCouponRepository->shouldReceive('save')->once()->with(\Mockery::type(PrintableCoupon::class));
$useCase = new CreatePrintableCoupon($printableCouponRepository, $couponRepository, $campaignRepository);
// WHEN
$result = $useCase->execute($input);
// THEN
expect($result)->toBeInstanceOf(Output::class)
->and($result->campaignCode)->toEqual($campaign->code)
->and($result->crmCustomerHashcode)->toEqual($crmCustomerHashcode)
->and($result->distributableUntil)->toEqual($calendarEnd);
});
it('rejects a printable coupon when maximum number of prints reached', function () use (
$makeInput,
$crmCustomerHashcode,
$makeCampaign,
$makeCoupon,
$campaignCoupon,
): void {
// GIVEN
$input = $makeInput();
$campaign = $makeCampaign(maxPerCustomer: 1);
$coupon = $makeCoupon();
$campaignRepository = \Mockery::mock(CampaignRepository::class);
$campaignRepository->shouldReceive('findByCode')->once()->with($campaign->code)->andReturn($campaign);
$campaignRepository->shouldReceive('findCampaignCoupon')->once()->with($campaign->code, $coupon->code)
->andReturn($campaignCoupon(maxPerCustomer: 1));
$couponRepository = \Mockery::mock(CouponRepository::class);
$couponRepository->shouldReceive('findByCode')->once()->with($coupon->code)->andReturn($coupon);
$printableCouponRepository = \Mockery::mock(PrintableCouponRepository::class);
$printableCouponRepository->shouldReceive('countPrintedCouponForCustomer')->once()->with(
$campaign->code,
$coupon->code,
$crmCustomerHashcode,
)->andReturn(5);
$useCase = new CreatePrintableCoupon($printableCouponRepository, $couponRepository, $campaignRepository);
// WHEN
$action = fn (): Output => $useCase->execute($input);
// THEN
expect($action)->toThrow(\DomainException::class, 'Maximum number of prints reached.');
});
it('rejects a printable coupon when reprint period has expired', function () use (
$makeInput,
$crmCustomerHashcode,
$makeCampaign,
$makeCoupon,
$campaignCoupon,
$printedCoupon,
): void {
// GIVEN
$input = $makeInput();
$campaign = $makeCampaign(cooldownDays: 30);
$coupon = $makeCoupon();
$campaignRepository = \Mockery::mock(CampaignRepository::class);
$campaignRepository->shouldReceive('findByCode')->once()->with($campaign->code)->andReturn($campaign);
$campaignRepository->shouldReceive('findCampaignCoupon')->once()->with($campaign->code, $coupon->code)
->andReturn($campaignCoupon(cooldownDays: 30));
$couponRepository = \Mockery::mock(CouponRepository::class);
$couponRepository->shouldReceive('findByCode')->once()->with($coupon->code)->andReturn($coupon);
$printableCouponRepository = \Mockery::mock(PrintableCouponRepository::class);
$printableCouponRepository->shouldReceive('countPrintedCouponForCustomer')->once()->with(
$campaign->code,
$coupon->code,
$crmCustomerHashcode,
)->andReturn(5);
$printableCouponRepository->shouldReceive('findLastPrintedCouponForCustomer')->once()->with(
$campaign->code,
$coupon->code,
$crmCustomerHashcode,
)->andReturn($printedCoupon);
$useCase = new CreatePrintableCoupon($printableCouponRepository, $couponRepository, $campaignRepository);
// WHEN
$action = fn (): Output => $useCase->execute($input);
// THEN
expect($action)->toThrow(\DomainException::class, 'Reprint period has not expired.');
});
});