test: ✅ cover country rules in coupon use case and value objects

This commit is contained in:
Alexis Gousseau 2026-10-13 11:34:05 +02:00
parent 34273a2fd3
commit 5a3815579d
No known key found for this signature in database
GPG key ID: 7F3C786EE12DBB72
13 changed files with 494 additions and 74 deletions

View file

@ -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

View file

@ -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,
);
}
}

View file

@ -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;
}

View file

@ -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.');
}

View file

@ -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;
}

View file

@ -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;
}
}

View file

@ -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,
);
}
}

View file

@ -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);
});
});

View file

@ -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();
});
});

View file

@ -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']);

View file

@ -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 {

View file

@ -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']);

View file

@ -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']);