ldc-mesbr-app/tests/Unit/Application/UseCases/CampaignRequest/Reject/RejectCampaignRequestTest.php
2026-10-02 12:47:59 +02:00

157 lines
6.5 KiB
PHP

<?php
declare(strict_types=1);
namespace Tests\Unit\Application\UseCases\CampaignRequest\Reject;
use Application\UseCases\CampaignRequest\Reject\Input;
use Application\UseCases\CampaignRequest\Reject\Output\CampaignRequestNotFound;
use Application\UseCases\CampaignRequest\Reject\Output\CampaignRequestRejected;
use Application\UseCases\CampaignRequest\Reject\Output\Output;
use Application\UseCases\CampaignRequest\Reject\RejectCampaignRequest;
use Carbon\CarbonImmutable;
use Domain\Enums\BarcodeFormat;
use Domain\Models\ValueObjects\CampaignRequest\CampaignRequestStatus;
use Domain\Models\ValueObjects\CampaignRequest\CampaignRequestType;
use Domain\Models\Entities\CampaignRequest;
use Domain\Models\ValueObjects\Brand\BrandCode;
use Domain\Models\ValueObjects\CampaignRequest\Applicant;
use Domain\Models\ValueObjects\CampaignRequest\CampaignRequestId;
use Domain\Models\ValueObjects\CampaignRequest\RejectionReason;
use Domain\Models\ValueObjects\Coupon\CouponValidity;
use Domain\Models\ValueObjects\CouponName;
use Domain\Models\ValueObjects\CouponPicture;
use Domain\Models\ValueObjects\CouponProductName;
use Domain\Models\ValueObjects\GS1;
use Domain\Models\ValueObjects\MoneyAmount;
use Domain\Models\ValueObjects\Period;
use Domain\Models\ValueObjects\PicturePath;
use Domain\Models\ValueObjects\PrintRules;
use Domain\Models\ValueObjects\ProcessingCode;
use Domain\Models\ValueObjects\RequestedCoupon\RequestedCouponQuantity;
use Domain\Models\ValueObjects\WarningThresholdQuantity;
use Domain\Ports\Repositories\CampaignRequestRepository;
use Shared\Enums\Currency;
use DateTime;
use Domain\Models\ValueObjects\RequestedCoupon\RequestedCoupon;
use Shared\ValueObjects\Email;
use Shared\ValueObjects\Money;
covers(RejectCampaignRequest::class, Input::class, CampaignRequestRejected::class, CampaignRequestNotFound::class);
describe('reject campaign request use case', function (): void {
afterEach(function (): void {
CarbonImmutable::setTestNow();
});
beforeEach(function (): void {
$this->requestedCoupon = new RequestedCoupon(
new CouponName('Coupon A'),
new CouponProductName('Product A'),
new RequestedCouponQuantity(100),
new WarningThresholdQuantity(10),
new Period(new DateTime('2026-01-01'), new DateTime('2026-02-01')),
new MoneyAmount(new Money(Currency::EUR, 300)),
new ProcessingCode('PC1'),
new CouponPicture(new PicturePath('coupons/product.jpg'), new PicturePath('coupons/thumbnail.jpg')),
new GS1('01234567890128', BarcodeFormat::EAN13),
new CouponValidity(30, new DateTime()),
);
});
it('rejects a campaign request and persists it', function (): void {
// GIVEN
CarbonImmutable::setTestNow('2026-06-15 12:00:00');
$id = CampaignRequestId::generate();
$campaignRequest = CampaignRequest::hydrate(
$id,
new BrandCode('AAA01'),
new Applicant('John', 'Doe', new Email('john.doe@example.com')),
CampaignRequestType::CLUB,
new PrintRules(maxPerCustomer: 3),
$this->requestedCoupon,
CampaignRequestStatus::CREATED,
);
$reason = new RejectionReason('Missing legal documents.');
$repository = \Mockery::mock(CampaignRequestRepository::class);
$repository->shouldReceive('findById')->once()->with($id)->andReturn($campaignRequest);
$repository->shouldReceive('save')->once()->with(\Mockery::on(
fn (CampaignRequest $saved): bool => $saved->status === CampaignRequestStatus::REJECTED
&& $saved->rejectionReason === $reason
&& $saved->updatedAt !== null,
));
$useCase = new RejectCampaignRequest($repository);
// WHEN
$result = $useCase->execute(new Input(id: $id, reason: $reason));
// THEN
expect($result)->toBeInstanceOf(CampaignRequestRejected::class)
->and($result->id)->toBe($id)
->and($result->status)->toBe(CampaignRequestStatus::REJECTED)
->and($result->rejectionReason)->toBe($reason)
->and($result->updatedAt->format('Y-m-d H:i:s'))->toBe('2026-06-15 12:00:00');
});
it('returns CampaignRequestNotFound when the id is not found', function (): void {
// GIVEN
$id = CampaignRequestId::generate();
$repository = \Mockery::mock(CampaignRequestRepository::class);
$repository->shouldReceive('findById')->once()->with($id)->andReturn(null);
$repository->shouldNotReceive('save');
$useCase = new RejectCampaignRequest($repository);
// WHEN
$result = $useCase->execute(new Input(id: $id, reason: new RejectionReason('Missing legal documents.')));
// THEN
expect($result)->toBeInstanceOf(CampaignRequestNotFound::class);
});
it('propagates the domain exception when the campaign request cannot be rejected', function (): void {
// GIVEN
$id = CampaignRequestId::generate();
$campaignRequest = CampaignRequest::hydrate(
$id,
new BrandCode('AAA01'),
new Applicant('John', 'Doe', new Email('john.doe@example.com')),
CampaignRequestType::CLUB,
new PrintRules(maxPerCustomer: 3),
$this->requestedCoupon,
CampaignRequestStatus::TRANSFORMED,
);
$repository = \Mockery::mock(CampaignRequestRepository::class);
$repository->shouldReceive('findById')->once()->with($id)->andReturn($campaignRequest);
$repository->shouldNotReceive('save');
$useCase = new RejectCampaignRequest($repository);
// WHEN
$action = fn (): Output => $useCase->execute(
new Input(id: $id, reason: new RejectionReason('Missing legal documents.'))
);
// 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);
});
});