test: ✅ cover country rules in coupon use case and value objects
This commit is contained in:
parent
34273a2fd3
commit
5a3815579d
13 changed files with 494 additions and 74 deletions
|
|
@ -8,6 +8,7 @@ use Domain\Models\ValueObjects\Actor\ActorId;
|
|||
use Domain\Models\ValueObjects\Brand\BrandCode;
|
||||
use Domain\Models\ValueObjects\Coupon\CouponAvailabilityCalendar;
|
||||
use Domain\Models\ValueObjects\Coupon\CouponCode;
|
||||
use Domain\Models\ValueObjects\Coupon\CouponCountryCoverage;
|
||||
use Domain\Models\ValueObjects\Coupon\CouponDiffusion;
|
||||
use Domain\Models\ValueObjects\Coupon\CouponId;
|
||||
use Domain\Models\ValueObjects\Coupon\CouponLegalInformationArray;
|
||||
|
|
@ -45,6 +46,11 @@ final readonly class Coupon implements AggregateRoot, HasTimestampIdentifier
|
|||
public ?CouponAvailabilityCalendar $availabilityCalendar = null,
|
||||
public ?\DateTimeInterface $updatedAt = null,
|
||||
) {
|
||||
CouponCountryCoverage::assert(
|
||||
$this->processingCode,
|
||||
$this->validityInformations,
|
||||
$this->legalInformations,
|
||||
);
|
||||
}
|
||||
|
||||
public function id(): CouponId
|
||||
|
|
|
|||
|
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Domain\Models\ValueObjects\Coupon;
|
||||
|
||||
use Shared\Enums\Country;
|
||||
|
||||
final readonly class CouponCountryCoverage
|
||||
{
|
||||
private function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
public static function assert(
|
||||
CouponProcessingCodeArray $processingCodes,
|
||||
CouponValidityInformationArray $validityInformations,
|
||||
CouponLegalInformationArray $legalInformations,
|
||||
): void {
|
||||
foreach ($processingCodes->value as $processingCode) {
|
||||
$country = $processingCode->availableIn;
|
||||
|
||||
if (! self::hasInformationFor($country, $validityInformations)) {
|
||||
throw new \DomainException(sprintf('A coupon requires validity information for %s.', $country->name));
|
||||
}
|
||||
|
||||
if (! self::hasInformationFor($country, $legalInformations)) {
|
||||
throw new \DomainException(sprintf('A coupon requires legal information for %s.', $country->name));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static function hasInformationFor(
|
||||
Country $country,
|
||||
CouponValidityInformationArray | CouponLegalInformationArray $informations,
|
||||
): bool {
|
||||
return array_any(
|
||||
$informations->value,
|
||||
fn (CouponValidityInformation | CouponLegalInformation $information): bool => $information->forLocale === $country,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -17,12 +17,22 @@ final readonly class CouponLegalInformationArray
|
|||
}
|
||||
|
||||
$hasFrance = false;
|
||||
$countries = [];
|
||||
|
||||
foreach ($this->value as $couponLegalInformation) {
|
||||
if (! $couponLegalInformation instanceof CouponLegalInformation) {
|
||||
throw new \DomainException('Value of CouponLegalInformationArray must be CouponLegalInformation instances.');
|
||||
}
|
||||
|
||||
if (isset($countries[$couponLegalInformation->forLocale->name])) {
|
||||
throw new \DomainException(sprintf(
|
||||
'Only one CouponLegalInformation is allowed per country, duplicate for %s.',
|
||||
$couponLegalInformation->forLocale->name,
|
||||
));
|
||||
}
|
||||
|
||||
$countries[$couponLegalInformation->forLocale->name] = true;
|
||||
|
||||
if ($couponLegalInformation->forLocale === Country::FRANCE) {
|
||||
$hasFrance = true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,13 +8,13 @@ final readonly class CouponMovementArray
|
|||
{
|
||||
public function __construct(
|
||||
/** @var CouponMovement[] */
|
||||
public array $couponMovements,
|
||||
public array $value,
|
||||
) {
|
||||
if ($this->couponMovements === []) {
|
||||
if ($this->value === []) {
|
||||
throw new \DomainException('CouponMovementArray requires at least one CouponMovement.');
|
||||
}
|
||||
|
||||
foreach ($this->couponMovements as $couponMovement) {
|
||||
foreach ($this->value as $couponMovement) {
|
||||
if (! $couponMovement instanceof CouponMovement) {
|
||||
throw new \DomainException('Value of CouponMovementArray must be CouponMovement instances.');
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,12 +17,22 @@ final readonly class CouponProcessingCodeArray
|
|||
}
|
||||
|
||||
$hasFrance = false;
|
||||
$countries = [];
|
||||
|
||||
foreach ($this->value as $processingCode) {
|
||||
if (! $processingCode instanceof CouponProcessingCode) {
|
||||
throw new \DomainException('Value of CouponProcessingCodeArray must be CouponProcessingCode instances.');
|
||||
}
|
||||
|
||||
if (isset($countries[$processingCode->availableIn->name])) {
|
||||
throw new \DomainException(sprintf(
|
||||
'Only one CouponProcessingCode is allowed per country, duplicate for %s.',
|
||||
$processingCode->availableIn->name,
|
||||
));
|
||||
}
|
||||
|
||||
$countries[$processingCode->availableIn->name] = true;
|
||||
|
||||
if ($processingCode->availableIn === Country::FRANCE) {
|
||||
$hasFrance = true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,13 +17,23 @@ final readonly class CouponValidityInformationArray
|
|||
}
|
||||
|
||||
$hasFrance = false;
|
||||
$countries = [];
|
||||
|
||||
foreach ($this->value as $couponLegalInformation) {
|
||||
if (! $couponLegalInformation instanceof CouponValidityInformation) {
|
||||
foreach ($this->value as $couponValidityInformation) {
|
||||
if (! $couponValidityInformation instanceof CouponValidityInformation) {
|
||||
throw new \DomainException('Value of CouponValidityInformationArray must be CouponValidityInformation instances.');
|
||||
}
|
||||
|
||||
if ($couponLegalInformation->forLocale === Country::FRANCE) {
|
||||
if (isset($countries[$couponValidityInformation->forLocale->name])) {
|
||||
throw new \DomainException(sprintf(
|
||||
'Only one CouponValidityInformation is allowed per country, duplicate for %s.',
|
||||
$couponValidityInformation->forLocale->name,
|
||||
));
|
||||
}
|
||||
|
||||
$countries[$couponValidityInformation->forLocale->name] = true;
|
||||
|
||||
if ($couponValidityInformation->forLocale === Country::FRANCE) {
|
||||
$hasFrance = true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,19 +4,16 @@ declare(strict_types=1);
|
|||
|
||||
namespace Domain\Models\ValueObjects\RequestedCoupon;
|
||||
|
||||
use Domain\Models\ValueObjects\Coupon\CouponLegalInformation;
|
||||
use Domain\Models\ValueObjects\Coupon\CouponCountryCoverage;
|
||||
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\Coupon\CouponValidityInformationArray;
|
||||
use Domain\Models\ValueObjects\MoneyAmount;
|
||||
use Domain\Models\ValueObjects\Period;
|
||||
use Domain\Models\ValueObjects\PrintRules;
|
||||
use Shared\Enums\Country;
|
||||
|
||||
final readonly class RequestedCoupon
|
||||
{
|
||||
|
|
@ -32,39 +29,10 @@ final readonly class RequestedCoupon
|
|||
public CouponProductArray $couponProducts,
|
||||
public ?PrintRules $printRules = null,
|
||||
) {
|
||||
if ($this->hasProcessingCodeFor(Country::BENELUX)) {
|
||||
$this->assertCountryIsFullyCovered(Country::BENELUX);
|
||||
}
|
||||
}
|
||||
|
||||
private function assertCountryIsFullyCovered(Country $country): void
|
||||
{
|
||||
if (! $this->hasProcessingCodeFor($country)) {
|
||||
throw new \DomainException(sprintf('A coupon requires a processing code for %s.', $country->name));
|
||||
}
|
||||
|
||||
if (! $this->hasInformationFor($country, $this->couponValidityInformations)) {
|
||||
throw new \DomainException(sprintf('A coupon requires validity information for %s.', $country->name));
|
||||
}
|
||||
|
||||
if (! $this->hasInformationFor($country, $this->couponLegalInformations)) {
|
||||
throw new \DomainException(sprintf('A coupon requires legal information for %s.', $country->name));
|
||||
}
|
||||
}
|
||||
|
||||
private function hasProcessingCodeFor(Country $country): bool
|
||||
{
|
||||
return array_any(
|
||||
$this->couponProcessingCodes->value,
|
||||
fn (CouponProcessingCode $processingCode): bool => $processingCode->availableIn === $country,
|
||||
);
|
||||
}
|
||||
|
||||
private function hasInformationFor(Country $country, CouponValidityInformationArray | CouponLegalInformationArray $informations): bool
|
||||
{
|
||||
return array_any(
|
||||
$informations->value,
|
||||
fn (CouponValidityInformation | CouponLegalInformation $information): bool => $information->forLocale === $country,
|
||||
CouponCountryCoverage::assert(
|
||||
$this->couponProcessingCodes,
|
||||
$this->couponValidityInformations,
|
||||
$this->couponLegalInformations,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,11 +11,13 @@ use DateTime;
|
|||
use Domain\Models\Entities\Coupon;
|
||||
use Domain\Models\ValueObjects\Actor\ActorId;
|
||||
use Domain\Models\ValueObjects\Brand\BrandCode;
|
||||
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\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;
|
||||
|
|
@ -30,6 +32,7 @@ use Domain\Models\ValueObjects\Period;
|
|||
use Domain\Models\ValueObjects\PicturePath;
|
||||
use Domain\Ports\Repositories\BrandRepository;
|
||||
use Domain\Ports\Repositories\CouponRepository;
|
||||
use Shared\DDD\UseCases\Input as BaseInput;
|
||||
use Shared\Enums\Country;
|
||||
use Shared\Enums\Currency;
|
||||
use Shared\ValueObjects\Money;
|
||||
|
|
@ -37,41 +40,33 @@ use Shared\ValueObjects\Money;
|
|||
covers(CreateCoupon::class, Input::class, Output::class);
|
||||
|
||||
describe('create coupon use case', function (): void {
|
||||
$makeInput = fn (): Input => new Input(
|
||||
code: new CouponCode('COU01'),
|
||||
name: new CouponName('Coupon name'),
|
||||
amount: new MoneyAmount(new Money(Currency::EUR, 100)),
|
||||
picture: new CouponPicture(
|
||||
$makeInput = fn (array $overrides = []): Input => new Input(...array_merge([
|
||||
'code' => new CouponCode('COU01'),
|
||||
'name' => new CouponName('Coupon name'),
|
||||
'amount' => new MoneyAmount(new Money(Currency::EUR, 100)),
|
||||
'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'),
|
||||
),
|
||||
'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,
|
||||
),
|
||||
'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,
|
||||
),
|
||||
'legalInformations' => new CouponLegalInformationArray([
|
||||
new CouponLegalInformation('Lorem ipsum dolor sit amet consectetur adipiscing elit.', Country::FRANCE),
|
||||
]),
|
||||
processingCode: new CouponProcessingCodeArray([
|
||||
'processingCode' => new CouponProcessingCodeArray([
|
||||
new CouponProcessingCode('5425003944130', Country::FRANCE),
|
||||
]),
|
||||
brandCode: new BrandCode('TST89'),
|
||||
createdBy: ActorId::generate(),
|
||||
products: new CouponProductArray(),
|
||||
quantity: new CouponQuantity(1000),
|
||||
);
|
||||
'brandCode' => new BrandCode('TST89'),
|
||||
'createdBy' => ActorId::generate(),
|
||||
'products' => new CouponProductArray(),
|
||||
'quantity' => new CouponQuantity(1000),
|
||||
'availabilityCalendar' => null,
|
||||
], $overrides));
|
||||
|
||||
it('creates a coupon when the brand exists and the code is unique', function () use ($makeInput): void {
|
||||
// GIVEN
|
||||
|
|
@ -148,4 +143,227 @@ describe('create coupon use case', function (): void {
|
|||
// THEN
|
||||
expect($action)->toThrow(\DomainException::class, 'Coupon code "COU01" already exists.');
|
||||
});
|
||||
|
||||
it('initialises the stock with a first movement equal to the initial quantity', function () use ($makeInput): void {
|
||||
// GIVEN
|
||||
$input = $makeInput();
|
||||
|
||||
$brandRepository = \Mockery::mock(BrandRepository::class);
|
||||
$brandRepository->shouldReceive('existsByCode')->once()->andReturn(true);
|
||||
|
||||
$couponRepository = \Mockery::mock(CouponRepository::class);
|
||||
$couponRepository->shouldReceive('existsByCode')->once()->andReturn(false);
|
||||
$couponRepository->shouldReceive('save')->once()->with(\Mockery::on(
|
||||
fn (Coupon $saved): bool => count($saved->movements->value) === 1
|
||||
&& $saved->movements->value[0] instanceof CouponMovement
|
||||
&& $saved->movements->value[0]->quantity === $input->quantity->value,
|
||||
));
|
||||
|
||||
$useCase = new CreateCoupon($couponRepository, $brandRepository);
|
||||
|
||||
// WHEN
|
||||
$result = $useCase->execute($input);
|
||||
|
||||
// THEN
|
||||
expect($result->movements)->toBeInstanceOf(CouponMovementArray::class)
|
||||
->and($result->movements->value)->toHaveCount(1);
|
||||
});
|
||||
|
||||
it('creates a coupon with an availability calendar', function () use ($makeInput): void {
|
||||
// GIVEN
|
||||
$calendar = new CouponAvailabilityCalendar([
|
||||
new Period(new DateTime('2026-01-05'), new DateTime('2026-01-15')),
|
||||
new Period(new DateTime('2026-01-20'), new DateTime('2026-01-25')),
|
||||
]);
|
||||
$input = $makeInput(['availabilityCalendar' => $calendar]);
|
||||
|
||||
$brandRepository = \Mockery::mock(BrandRepository::class);
|
||||
$brandRepository->shouldReceive('existsByCode')->once()->andReturn(true);
|
||||
|
||||
$couponRepository = \Mockery::mock(CouponRepository::class);
|
||||
$couponRepository->shouldReceive('existsByCode')->once()->andReturn(false);
|
||||
$couponRepository->shouldReceive('save')->once()->with(\Mockery::on(
|
||||
fn (Coupon $saved): bool => $saved->availabilityCalendar === $calendar,
|
||||
));
|
||||
|
||||
$useCase = new CreateCoupon($couponRepository, $brandRepository);
|
||||
|
||||
// WHEN
|
||||
$result = $useCase->execute($input);
|
||||
|
||||
// THEN
|
||||
expect($result->availabilityCalendar)->toBe($calendar);
|
||||
});
|
||||
|
||||
it('creates a GS1 coupon with France and Benelux', function () use ($makeInput): void {
|
||||
// GIVEN
|
||||
$processingCode = new CouponProcessingCodeArray([
|
||||
new CouponProcessingCode('5425003944130', Country::FRANCE),
|
||||
new CouponProcessingCode('5425003944147', Country::BENELUX),
|
||||
]);
|
||||
$validityInformations = 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),
|
||||
]);
|
||||
$legalInformations = 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),
|
||||
]);
|
||||
$input = $makeInput([
|
||||
'processingCode' => $processingCode,
|
||||
'validityInformations' => $validityInformations,
|
||||
'legalInformations' => $legalInformations,
|
||||
]);
|
||||
|
||||
$brandRepository = \Mockery::mock(BrandRepository::class);
|
||||
$brandRepository->shouldReceive('existsByCode')->once()->andReturn(true);
|
||||
|
||||
$couponRepository = \Mockery::mock(CouponRepository::class);
|
||||
$couponRepository->shouldReceive('existsByCode')->once()->andReturn(false);
|
||||
$couponRepository->shouldReceive('save')->once()->with(\Mockery::type(Coupon::class));
|
||||
|
||||
$useCase = new CreateCoupon($couponRepository, $brandRepository);
|
||||
|
||||
// WHEN
|
||||
$result = $useCase->execute($input);
|
||||
|
||||
// THEN
|
||||
expect($result->processingCode)->toBe($processingCode)
|
||||
->and($result->validityInformations)->toBe($validityInformations)
|
||||
->and($result->legalInformations)->toBe($legalInformations);
|
||||
});
|
||||
|
||||
it('rejects a coupon that targets only benelux', function () use ($makeInput): void {
|
||||
// GIVEN / WHEN
|
||||
$action = fn (): Input => $makeInput([
|
||||
'processingCode' => new CouponProcessingCodeArray([
|
||||
new CouponProcessingCode('5425003944147', Country::BENELUX),
|
||||
]),
|
||||
'validityInformations' => new CouponValidityInformationArray([
|
||||
new CouponValidityInformation('Lorem ipsum dolor sit amet consectetur adipiscing elit.', Country::BENELUX),
|
||||
]),
|
||||
'legalInformations' => new CouponLegalInformationArray([
|
||||
new CouponLegalInformation('Lorem ipsum dolor sit amet consectetur adipiscing elit.', Country::BENELUX),
|
||||
]),
|
||||
]);
|
||||
|
||||
// THEN
|
||||
expect($action)->toThrow(\DomainException::class);
|
||||
});
|
||||
|
||||
it('rejects a benelux processing code without benelux legal information', function () use ($makeInput): void {
|
||||
// GIVEN
|
||||
$input = $makeInput([
|
||||
'processingCode' => new CouponProcessingCodeArray([
|
||||
new CouponProcessingCode('5425003944130', Country::FRANCE),
|
||||
new CouponProcessingCode('5425003944147', Country::BENELUX),
|
||||
]),
|
||||
'validityInformations' => 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),
|
||||
]),
|
||||
]);
|
||||
|
||||
$brandRepository = \Mockery::mock(BrandRepository::class);
|
||||
$brandRepository->shouldReceive('existsByCode')->andReturn(true);
|
||||
|
||||
$couponRepository = \Mockery::mock(CouponRepository::class);
|
||||
$couponRepository->shouldReceive('existsByCode')->andReturn(false);
|
||||
$couponRepository->shouldNotReceive('save');
|
||||
|
||||
$useCase = new CreateCoupon($couponRepository, $brandRepository);
|
||||
|
||||
// WHEN
|
||||
$action = fn (): Output => $useCase->execute($input);
|
||||
|
||||
// THEN
|
||||
expect($action)->toThrow(\DomainException::class, 'A coupon requires legal information for BENELUX.');
|
||||
});
|
||||
|
||||
it('rejects a benelux processing code without benelux validity information', function () use ($makeInput): void {
|
||||
// GIVEN
|
||||
$input = $makeInput([
|
||||
'processingCode' => new CouponProcessingCodeArray([
|
||||
new CouponProcessingCode('5425003944130', Country::FRANCE),
|
||||
new CouponProcessingCode('5425003944147', Country::BENELUX),
|
||||
]),
|
||||
'legalInformations' => 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),
|
||||
]),
|
||||
]);
|
||||
|
||||
$brandRepository = \Mockery::mock(BrandRepository::class);
|
||||
$brandRepository->shouldReceive('existsByCode')->andReturn(true);
|
||||
|
||||
$couponRepository = \Mockery::mock(CouponRepository::class);
|
||||
$couponRepository->shouldReceive('existsByCode')->andReturn(false);
|
||||
$couponRepository->shouldNotReceive('save');
|
||||
|
||||
$useCase = new CreateCoupon($couponRepository, $brandRepository);
|
||||
|
||||
// WHEN
|
||||
$action = fn (): Output => $useCase->execute($input);
|
||||
|
||||
// THEN
|
||||
expect($action)->toThrow(\DomainException::class, 'A coupon requires validity information for BENELUX.');
|
||||
});
|
||||
|
||||
it('rejects two processing codes for the same country', function () use ($makeInput): void {
|
||||
// GIVEN / WHEN
|
||||
$action = fn (): Input => $makeInput([
|
||||
'processingCode' => new CouponProcessingCodeArray([
|
||||
new CouponProcessingCode('5425003944130', Country::FRANCE),
|
||||
new CouponProcessingCode('5425003944147', Country::FRANCE),
|
||||
]),
|
||||
]);
|
||||
|
||||
// THEN
|
||||
expect($action)->toThrow(\DomainException::class);
|
||||
});
|
||||
|
||||
it('rejects two legal informations for the same country', function () use ($makeInput): void {
|
||||
// GIVEN / WHEN
|
||||
$action = fn (): Input => $makeInput([
|
||||
'legalInformations' => 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::FRANCE),
|
||||
]),
|
||||
]);
|
||||
|
||||
// THEN
|
||||
expect($action)->toThrow(\DomainException::class);
|
||||
});
|
||||
|
||||
it('rejects two validity informations for the same country', function () use ($makeInput): void {
|
||||
// GIVEN / WHEN
|
||||
$action = fn (): Input => $makeInput([
|
||||
'validityInformations' => 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::FRANCE),
|
||||
]),
|
||||
]);
|
||||
|
||||
// THEN
|
||||
expect($action)->toThrow(\DomainException::class);
|
||||
});
|
||||
});
|
||||
|
||||
describe('create coupon use case assertions', function (): void {
|
||||
it('rejects an input of the wrong type', function (): void {
|
||||
// GIVEN
|
||||
$useCase = new CreateCoupon(
|
||||
\Mockery::mock(CouponRepository::class),
|
||||
\Mockery::mock(BrandRepository::class),
|
||||
);
|
||||
$invalidInput = new class () implements BaseInput
|
||||
{
|
||||
};
|
||||
|
||||
// WHEN
|
||||
$action = fn (): Output => $useCase->execute($invalidInput);
|
||||
|
||||
// THEN
|
||||
expect($action)->toThrow(\InvalidArgumentException::class);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -0,0 +1,104 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Unit\Domain\Models\ValueObjects\Coupon;
|
||||
|
||||
use Domain\Models\ValueObjects\Coupon\CouponCountryCoverage;
|
||||
use Domain\Models\ValueObjects\Coupon\CouponLegalInformation;
|
||||
use Domain\Models\ValueObjects\Coupon\CouponLegalInformationArray;
|
||||
use Domain\Models\ValueObjects\Coupon\CouponProcessingCode;
|
||||
use Domain\Models\ValueObjects\Coupon\CouponProcessingCodeArray;
|
||||
use Domain\Models\ValueObjects\Coupon\CouponValidityInformation;
|
||||
use Domain\Models\ValueObjects\Coupon\CouponValidityInformationArray;
|
||||
use Shared\Enums\Country;
|
||||
|
||||
covers(CouponCountryCoverage::class);
|
||||
|
||||
describe('coupon country coverage', function (): void {
|
||||
$franceCode = new CouponProcessingCode('ABC1234567890', Country::FRANCE);
|
||||
$beneluxCode = new CouponProcessingCode('XYZ0987654321', Country::BENELUX);
|
||||
$franceValidity = new CouponValidityInformation('Lorem ipsum dolor sit amet consectetur adipiscing elit.', Country::FRANCE);
|
||||
$beneluxValidity = new CouponValidityInformation('Lorem ipsum dolor sit amet consectetur adipiscing elit.', Country::BENELUX);
|
||||
$franceLegal = new CouponLegalInformation('Lorem ipsum dolor sit amet consectetur adipiscing elit.', Country::FRANCE);
|
||||
$beneluxLegal = new CouponLegalInformation('Lorem ipsum dolor sit amet consectetur adipiscing elit.', Country::BENELUX);
|
||||
|
||||
it('accepts a france only coupon', function () use ($franceCode, $franceValidity, $franceLegal): void {
|
||||
// GIVEN / WHEN
|
||||
$action = fn () => CouponCountryCoverage::assert(
|
||||
new CouponProcessingCodeArray([$franceCode]),
|
||||
new CouponValidityInformationArray([$franceValidity]),
|
||||
new CouponLegalInformationArray([$franceLegal]),
|
||||
);
|
||||
|
||||
// THEN
|
||||
expect($action)->not->toThrow(\DomainException::class);
|
||||
});
|
||||
|
||||
it('accepts a coupon fully covered for france and benelux', function () use (
|
||||
$franceCode,
|
||||
$beneluxCode,
|
||||
$franceValidity,
|
||||
$beneluxValidity,
|
||||
$franceLegal,
|
||||
$beneluxLegal,
|
||||
): void {
|
||||
// GIVEN / WHEN
|
||||
$action = fn () => CouponCountryCoverage::assert(
|
||||
new CouponProcessingCodeArray([$franceCode, $beneluxCode]),
|
||||
new CouponValidityInformationArray([$franceValidity, $beneluxValidity]),
|
||||
new CouponLegalInformationArray([$franceLegal, $beneluxLegal]),
|
||||
);
|
||||
|
||||
// THEN
|
||||
expect($action)->not->toThrow(\DomainException::class);
|
||||
});
|
||||
|
||||
it('rejects a benelux processing code without benelux validity information', function () use (
|
||||
$franceCode,
|
||||
$beneluxCode,
|
||||
$franceValidity,
|
||||
$franceLegal,
|
||||
$beneluxLegal,
|
||||
): void {
|
||||
// GIVEN / WHEN
|
||||
$action = fn () => CouponCountryCoverage::assert(
|
||||
new CouponProcessingCodeArray([$franceCode, $beneluxCode]),
|
||||
new CouponValidityInformationArray([$franceValidity]),
|
||||
new CouponLegalInformationArray([$franceLegal, $beneluxLegal]),
|
||||
);
|
||||
|
||||
// THEN
|
||||
expect($action)->toThrow(\DomainException::class, 'A coupon requires validity information for BENELUX.');
|
||||
});
|
||||
|
||||
it('rejects a benelux processing code without benelux legal information', function () use (
|
||||
$franceCode,
|
||||
$beneluxCode,
|
||||
$franceValidity,
|
||||
$beneluxValidity,
|
||||
$franceLegal,
|
||||
): void {
|
||||
// GIVEN / WHEN
|
||||
$action = fn () => CouponCountryCoverage::assert(
|
||||
new CouponProcessingCodeArray([$franceCode, $beneluxCode]),
|
||||
new CouponValidityInformationArray([$franceValidity, $beneluxValidity]),
|
||||
new CouponLegalInformationArray([$franceLegal]),
|
||||
);
|
||||
|
||||
// THEN
|
||||
expect($action)->toThrow(\DomainException::class, 'A coupon requires legal information for BENELUX.');
|
||||
});
|
||||
|
||||
it('cannot be instantiated from the outside', function (): void {
|
||||
// GIVEN
|
||||
$reflection = new \ReflectionClass(CouponCountryCoverage::class);
|
||||
$constructor = $reflection->getConstructor();
|
||||
|
||||
// WHEN
|
||||
$constructor?->invoke($reflection->newInstanceWithoutConstructor());
|
||||
|
||||
// THEN
|
||||
expect($constructor?->isPrivate())->toBeTrue();
|
||||
});
|
||||
});
|
||||
|
|
@ -11,8 +11,8 @@ use Shared\Enums\Country;
|
|||
covers(CouponLegalInformationArray::class);
|
||||
|
||||
describe('coupon legal information array', function (): void {
|
||||
$franceInformation = new CouponLegalInformation('Mentions légales France.', Country::FRANCE);
|
||||
$beneluxInformation = new CouponLegalInformation('Legal information Benelux.', Country::BENELUX);
|
||||
$franceInformation = new CouponLegalInformation('Lorem ipsum dolor sit amet consectetur adipiscing elit.', Country::FRANCE);
|
||||
$beneluxInformation = new CouponLegalInformation('Lorem ipsum dolor sit amet consectetur adipiscing elit.', Country::BENELUX);
|
||||
|
||||
it('creates an array containing a France legal information', function () use ($franceInformation): void {
|
||||
// GIVEN
|
||||
|
|
@ -47,6 +47,23 @@ describe('coupon legal information array', function (): void {
|
|||
);
|
||||
});
|
||||
|
||||
it('rejects two legal informations for the same country', function () use ($franceInformation): void {
|
||||
// GIVEN
|
||||
$informations = [
|
||||
$franceInformation,
|
||||
new CouponLegalInformation('Lorem ipsum dolor sit amet consectetur adipiscing elit.', Country::FRANCE),
|
||||
];
|
||||
|
||||
// WHEN
|
||||
$action = fn (): CouponLegalInformationArray => new CouponLegalInformationArray($informations);
|
||||
|
||||
// THEN
|
||||
expect($action)->toThrow(
|
||||
\DomainException::class,
|
||||
'Only one CouponLegalInformation is allowed per country, duplicate for FRANCE.',
|
||||
);
|
||||
});
|
||||
|
||||
it('rejects a non legal information element', function (): void {
|
||||
// GIVEN / WHEN
|
||||
$action = fn (): CouponLegalInformationArray => new CouponLegalInformationArray(['legal']);
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ describe('coupon movement array', function (): void {
|
|||
$array = new CouponMovementArray($couponMovements);
|
||||
|
||||
// THEN
|
||||
expect($array->couponMovements)->toBe($couponMovements);
|
||||
expect($array->value)->toBe($couponMovements);
|
||||
});
|
||||
|
||||
it('rejects an empty array', function (): void {
|
||||
|
|
|
|||
|
|
@ -47,6 +47,24 @@ describe('coupon processing code array', function (): void {
|
|||
);
|
||||
});
|
||||
|
||||
|
||||
it('rejects two legal informations for the same country', function () use ($franceCode): void {
|
||||
// GIVEN
|
||||
$informations = [
|
||||
$franceCode,
|
||||
new CouponProcessingCode('XYZ0987654321', Country::FRANCE),
|
||||
];
|
||||
|
||||
// WHEN
|
||||
$action = fn (): CouponProcessingCodeArray => new CouponProcessingCodeArray($informations);
|
||||
|
||||
// THEN
|
||||
expect($action)->toThrow(
|
||||
\DomainException::class,
|
||||
'Only one CouponProcessingCode is allowed per country, duplicate for FRANCE.',
|
||||
);
|
||||
});
|
||||
|
||||
it('rejects a non processing code element', function (): void {
|
||||
// GIVEN / WHEN
|
||||
$action = fn (): CouponProcessingCodeArray => new CouponProcessingCodeArray(['ABC1234567890']);
|
||||
|
|
|
|||
|
|
@ -11,8 +11,8 @@ use Shared\Enums\Country;
|
|||
covers(CouponValidityInformationArray::class);
|
||||
|
||||
describe('coupon validity information array', function (): void {
|
||||
$franceInformation = new CouponValidityInformation('Valable en France.', Country::FRANCE);
|
||||
$beneluxInformation = new CouponValidityInformation('Valid in Benelux.', Country::BENELUX);
|
||||
$franceInformation = new CouponValidityInformation('Lorem ipsum dolor sit amet consectetur adipiscing elit.', Country::FRANCE);
|
||||
$beneluxInformation = new CouponValidityInformation('Lorem ipsum dolor sit amet consectetur adipiscing elit.', Country::BENELUX);
|
||||
|
||||
it('creates an array containing a France validity information', function () use ($franceInformation): void {
|
||||
// GIVEN
|
||||
|
|
@ -47,6 +47,23 @@ describe('coupon validity information array', function (): void {
|
|||
);
|
||||
});
|
||||
|
||||
it('rejects two legal informations for the same country', function () use ($franceInformation): void {
|
||||
// GIVEN
|
||||
$informations = [
|
||||
$franceInformation,
|
||||
new CouponValidityInformation('Lorem ipsum dolor sit amet consectetur adipiscing elit.', Country::FRANCE),
|
||||
];
|
||||
|
||||
// WHEN
|
||||
$action = fn (): CouponValidityInformationArray => new CouponValidityInformationArray($informations);
|
||||
|
||||
// THEN
|
||||
expect($action)->toThrow(
|
||||
\DomainException::class,
|
||||
'Only one CouponValidityInformation is allowed per country, duplicate for FRANCE.',
|
||||
);
|
||||
});
|
||||
|
||||
it('rejects a non validity information element', function (): void {
|
||||
// GIVEN / WHEN
|
||||
$action = fn (): CouponValidityInformationArray => new CouponValidityInformationArray(['validity']);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue