458 lines
16 KiB
PHP
458 lines
16 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Tests\Unit\Domain\Models\Entities;
|
|
|
|
use Carbon\CarbonImmutable;
|
|
use DateTimeInterface;
|
|
use Domain\Models\Entities\Campaign;
|
|
use Domain\Models\Entities\Coupon;
|
|
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\CampaignId;
|
|
use Domain\Models\ValueObjects\Campaign\CampaignName;
|
|
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\IncoCode;
|
|
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\ProcessingCodeArray;
|
|
use Ramsey\Uuid\Uuid;
|
|
use Shared\Enums\Country;
|
|
use Shared\Enums\Currency;
|
|
use Shared\ValueObjects\Money;
|
|
use Tests\Fixtures\Campaign\CampaignFactory;
|
|
|
|
covers(Campaign::class);
|
|
|
|
describe('campaign', function (): void {
|
|
$code = CampaignCode::generate();
|
|
$name = new CampaignName('Name');
|
|
$publication = \boolval(1);
|
|
$printAllAtOnce = \boolval(1);
|
|
$coupons = new CampaignCouponArray([
|
|
new CampaignCoupon(
|
|
CouponCode::generate(),
|
|
new PrintRules(maxPerCustomer: 3)
|
|
)
|
|
]);
|
|
$brandCode = new BrandCode('AAA01');
|
|
$actorId = ActorId::generate();
|
|
|
|
$couponName = new CouponName('Coupon name');
|
|
$couponDiffusionStart = new \DateTime()->modify('-1 month');
|
|
$couponDiffusionEnd = new \DateTime()->modify('+1 month');
|
|
$makeCoupon = fn (array $overrides = []): Coupon => Coupon::create(
|
|
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,
|
|
);
|
|
|
|
afterEach(function (): void {
|
|
CarbonImmutable::setTestNow();
|
|
});
|
|
|
|
it('creates a campaign', function (): void {
|
|
// GIVEN
|
|
$name = new CampaignName('Name');
|
|
$coupons = new CampaignCouponArray([
|
|
new CampaignCoupon(
|
|
CouponCode::generate(),
|
|
new PrintRules(maxPerCustomer: 3)
|
|
)
|
|
]);
|
|
$brandCode = new BrandCode('AAA01');
|
|
$actorId = ActorId::generate();
|
|
|
|
// WHEN
|
|
$campaign = Campaign::create(
|
|
$name,
|
|
true,
|
|
$coupons,
|
|
$brandCode,
|
|
$actorId
|
|
);
|
|
|
|
// THEN
|
|
expect($campaign->id())->toBeInstanceOf(CampaignId::class)
|
|
->and($campaign->code)->toBeInstanceOf(CampaignCode::class)
|
|
->and($campaign->name)->toBe($name)
|
|
->and($campaign->publication)->toBeFalse()
|
|
->and($campaign->printAllAtOnce)->toBeTrue()
|
|
->and($campaign->coupons)->toBe($coupons)
|
|
->and($campaign->brandCode)->toBe($brandCode)
|
|
->and($campaign->actorId)->toBe($actorId)
|
|
->and($campaign->createdAt())->toBeInstanceOf(DateTimeInterface::class)
|
|
->and($campaign->updatedAt())->toBeNull();
|
|
});
|
|
|
|
it('generates a different id for each created campaign', function () use (
|
|
$name,
|
|
$printAllAtOnce,
|
|
$coupons,
|
|
$brandCode,
|
|
$actorId,
|
|
): void {
|
|
// GIVEN / WHEN
|
|
$firstCampaign = Campaign::create(
|
|
$name,
|
|
$printAllAtOnce,
|
|
$coupons,
|
|
$brandCode,
|
|
$actorId
|
|
);
|
|
$secondCampaign = Campaign::create(
|
|
$name,
|
|
$printAllAtOnce,
|
|
$coupons,
|
|
$brandCode,
|
|
$actorId
|
|
);
|
|
|
|
// THEN
|
|
expect($firstCampaign->id()->equals($secondCampaign->id()))->toBeFalse();
|
|
});
|
|
|
|
it('returns the creation date extracted from the id', function () use (
|
|
$name,
|
|
$printAllAtOnce,
|
|
$coupons,
|
|
$brandCode,
|
|
$actorId,
|
|
): void {
|
|
// GIVEN
|
|
$expected = new \DateTimeImmutable('2024-06-15 12:34:56.789000', new \DateTimeZone('UTC'));
|
|
$id = new CampaignId(Uuid::uuid7($expected));
|
|
$campaign = Campaign::hydrate(
|
|
$id,
|
|
CampaignCode::generate(),
|
|
$name,
|
|
false,
|
|
$printAllAtOnce,
|
|
$coupons,
|
|
$brandCode,
|
|
$actorId
|
|
);
|
|
|
|
// WHEN
|
|
$result = $campaign->createdAt();
|
|
|
|
// THEN
|
|
expect($result->format('Y-m-d H:i:s.v'))->toBe($expected->format('Y-m-d H:i:s.v'));
|
|
});
|
|
|
|
it('rejects a campaign with a non-coupon element', function () use ($code, $name, $publication, $printAllAtOnce, $brandCode, $actorId): void {
|
|
// GIVEN
|
|
$id = CampaignId::generate();
|
|
|
|
// WHEN / THEN
|
|
expect(fn (): Campaign => Campaign::hydrate(
|
|
$id,
|
|
$code,
|
|
$name,
|
|
$publication,
|
|
$printAllAtOnce,
|
|
new CampaignCouponArray(['PC1']),
|
|
$brandCode,
|
|
$actorId
|
|
))->toThrow(
|
|
\DomainException::class,
|
|
'CampaignCouponArray only accepts CampaignCoupon.'
|
|
);
|
|
});
|
|
|
|
it('unpublish a campaign and sets updatedAt timestamp', function () use ($code, $name, $printAllAtOnce, $coupons, $brandCode, $actorId): void {
|
|
// GIVEN
|
|
CarbonImmutable::setTestNow('2026-06-15 16:45:00 UTC');
|
|
$id = CampaignId::generate();
|
|
$oldPublicationStatus = true;
|
|
$newPublicationStatus = false;
|
|
$campaign = Campaign::hydrate(
|
|
$id,
|
|
$code,
|
|
$name,
|
|
$oldPublicationStatus,
|
|
$printAllAtOnce,
|
|
$coupons,
|
|
$brandCode,
|
|
$actorId
|
|
);
|
|
|
|
// WHEN
|
|
$campaign = $campaign->unpublish();
|
|
|
|
// THEN
|
|
expect($campaign->id())->toBe($id)
|
|
->and($campaign->code)->toBe($code)
|
|
->and($campaign->name)->toBe($name)
|
|
->and($campaign->publication)->toBe($newPublicationStatus)
|
|
->and($campaign->printAllAtOnce)->toBe($printAllAtOnce)
|
|
->and($campaign->coupons)->toBe($coupons)
|
|
->and($campaign->createdAt())->toBeInstanceOf(DateTimeInterface::class)
|
|
->and($campaign->updatedAt())->toBeInstanceOf(DateTimeInterface::class)
|
|
->and($campaign->updatedAt()->format('Y-m-d H:i:s'))->toBe('2026-06-15 16:45:00');
|
|
});
|
|
|
|
it('publishes a campaign and sets updatedAt timestamp', function () use (
|
|
$code,
|
|
$name,
|
|
$printAllAtOnce,
|
|
$coupons,
|
|
$brandCode,
|
|
$actorId,
|
|
): void {
|
|
// GIVEN
|
|
CarbonImmutable::setTestNow('2026-06-15 16:45:00 UTC');
|
|
$id = CampaignId::generate();
|
|
$oldPublicationStatus = false;
|
|
$newPublicationStatus = true;
|
|
$campaign = Campaign::hydrate(
|
|
$id,
|
|
$code,
|
|
$name,
|
|
$oldPublicationStatus,
|
|
$printAllAtOnce,
|
|
$coupons,
|
|
$brandCode,
|
|
$actorId
|
|
);
|
|
|
|
// WHEN
|
|
$campaign = $campaign->publish();
|
|
|
|
// THEN
|
|
expect($campaign->id())->toBe($id)
|
|
->and($campaign->code)->toBe($code)
|
|
->and($campaign->name)->toBe($name)
|
|
->and($campaign->publication)->toBe($newPublicationStatus)
|
|
->and($campaign->printAllAtOnce)->toBe($printAllAtOnce)
|
|
->and($campaign->coupons)->toBe($coupons)
|
|
->and($campaign->createdAt())->toBeInstanceOf(DateTimeInterface::class)
|
|
->and($campaign->updatedAt())->toBeInstanceOf(DateTimeInterface::class)
|
|
->and($campaign->updatedAt()->format('Y-m-d H:i:s'))->toBe('2026-06-15 16:45:00');
|
|
});
|
|
|
|
it('unpublishes a campaign when there are no available coupons in it', function () use (
|
|
$code,
|
|
$name,
|
|
$printAllAtOnce,
|
|
$coupons,
|
|
$brandCode,
|
|
$actorId,
|
|
$makeCoupon
|
|
): void {
|
|
// GIVEN
|
|
CarbonImmutable::setTestNow('2026-06-15 16:45:00 UTC');
|
|
$id = CampaignId::generate();
|
|
$oldPublicationStatus = true;
|
|
$newPublicationStatus = false;
|
|
$campaign = Campaign::hydrate(
|
|
$id,
|
|
$code,
|
|
$name,
|
|
$oldPublicationStatus,
|
|
$printAllAtOnce,
|
|
$coupons,
|
|
$brandCode,
|
|
$actorId
|
|
);
|
|
$expired = $makeCoupon([
|
|
'diffusionStart' => new \DateTime('2026-05-01'),
|
|
'diffusionEnd' => new \DateTime('2026-06-30'),
|
|
]);
|
|
|
|
// WHEN
|
|
CarbonImmutable::setTestNow('2026-08-16 00:00:00 UTC');
|
|
$campaign = $campaign->unpublishWhenNoAvailableCoupons([$expired]);
|
|
|
|
// THEN
|
|
expect($campaign->id())->toBe($id)
|
|
->and($campaign->code)->toBe($code)
|
|
->and($campaign->name)->toBe($name)
|
|
->and($campaign->publication)->toBe($newPublicationStatus)
|
|
->and($campaign->printAllAtOnce)->toBe($printAllAtOnce)
|
|
->and($campaign->coupons)->toBe($coupons)
|
|
->and($campaign->createdAt())->toBeInstanceOf(DateTimeInterface::class)
|
|
->and($campaign->updatedAt())->toBeInstanceOf(DateTimeInterface::class);
|
|
});
|
|
|
|
it('keeps a campaign published when there are available coupons in it', function () use (
|
|
$code,
|
|
$name,
|
|
$printAllAtOnce,
|
|
$coupons,
|
|
$brandCode,
|
|
$actorId,
|
|
$makeCoupon
|
|
): void {
|
|
// GIVEN
|
|
CarbonImmutable::setTestNow('2026-06-15 16:45:00 UTC');
|
|
$id = CampaignId::generate();
|
|
$oldPublicationStatus = true;
|
|
$newPublicationStatus = true;
|
|
$campaign = Campaign::hydrate(
|
|
$id,
|
|
$code,
|
|
$name,
|
|
$oldPublicationStatus,
|
|
$printAllAtOnce,
|
|
$coupons,
|
|
$brandCode,
|
|
$actorId
|
|
);
|
|
$notExpired = $makeCoupon([
|
|
'diffusionStart' => new \DateTime('2026-06-01'),
|
|
'diffusionEnd' => new \DateTime('2026-08-30'),
|
|
]);
|
|
|
|
// WHEN
|
|
CarbonImmutable::setTestNow('2026-08-16 00:00:00 UTC');
|
|
$campaign = $campaign->unpublishWhenNoAvailableCoupons([$notExpired]);
|
|
|
|
// THEN
|
|
expect($campaign->id())->toBe($id)
|
|
->and($campaign->code)->toBe($code)
|
|
->and($campaign->name)->toBe($name)
|
|
->and($campaign->publication)->toBe($newPublicationStatus)
|
|
->and($campaign->printAllAtOnce)->toBe($printAllAtOnce)
|
|
->and($campaign->coupons)->toBe($coupons)
|
|
->and($campaign->createdAt())->toBeInstanceOf(DateTimeInterface::class)
|
|
->and($campaign->updatedAt())->toBeInstanceOf(DateTimeInterface::class);
|
|
});
|
|
|
|
it('gets the codes of the associated coupons', function (): void {
|
|
// GIVEN
|
|
$couponCode1 = CouponCode::generate();
|
|
$couponCode2 = CouponCode::generate();
|
|
$expectedCoupons = [$couponCode1->value, $couponCode2->value];
|
|
$campaign = CampaignFactory::hydrated(couponCodes: [$couponCode1, $couponCode2]);
|
|
|
|
// WHEN
|
|
$coupons = array_map(static fn (CouponCode $c): string => $c->value, $campaign->couponCodes());
|
|
|
|
// THEN
|
|
expect($coupons)->toBe($expectedCoupons);
|
|
});
|
|
|
|
it('removes a coupon from a campaign', function () use (
|
|
$code,
|
|
$name,
|
|
$printAllAtOnce,
|
|
$coupons,
|
|
$brandCode,
|
|
$actorId,
|
|
$makeCoupon
|
|
): void {
|
|
// GIVEN
|
|
CarbonImmutable::setTestNow('2026-06-15 16:45:00 UTC');
|
|
$id = CampaignId::generate();
|
|
$coupon = $makeCoupon();
|
|
$expectedCoupons = $coupons;
|
|
$coupons = $coupons->addCoupon(new CampaignCoupon($coupon->code));
|
|
$campaign = Campaign::hydrate(
|
|
$id,
|
|
$code,
|
|
$name,
|
|
true,
|
|
$printAllAtOnce,
|
|
$coupons,
|
|
$brandCode,
|
|
$actorId
|
|
);
|
|
|
|
// WHEN
|
|
CarbonImmutable::setTestNow('2026-08-16 00:00:00 UTC');
|
|
$campaign = $campaign->removeCoupon($coupon->code);
|
|
|
|
// THEN
|
|
expect($campaign->id())->toBe($id)
|
|
->and($campaign->coupons)->toEqual($expectedCoupons)
|
|
->and($campaign->createdAt())->toBeInstanceOf(DateTimeInterface::class)
|
|
->and($campaign->updatedAt())->toBeInstanceOf(DateTimeInterface::class);
|
|
});
|
|
|
|
it('adds a coupon to a campaign', function () use (
|
|
$code,
|
|
$name,
|
|
$printAllAtOnce,
|
|
$coupons,
|
|
$brandCode,
|
|
$actorId,
|
|
): void {
|
|
// GIVEN
|
|
CarbonImmutable::setTestNow('2026-06-15 16:45:00 UTC');
|
|
$id = CampaignId::generate();
|
|
$couponCode = CouponCode::generate();
|
|
$campaign = Campaign::hydrate(
|
|
$id,
|
|
$code,
|
|
$name,
|
|
true,
|
|
$printAllAtOnce,
|
|
$coupons,
|
|
$brandCode,
|
|
$actorId
|
|
);
|
|
$expectedCoupons = $coupons->addCoupon(new CampaignCoupon($couponCode));
|
|
|
|
// WHEN
|
|
$updatedCampaign = $campaign->addCoupon($couponCode);
|
|
|
|
// THEN
|
|
expect($updatedCampaign->id())->toBe($id)
|
|
->and($updatedCampaign->coupons)->toEqual($expectedCoupons)
|
|
->and($updatedCampaign->createdAt())->toBeInstanceOf(DateTimeInterface::class)
|
|
->and($updatedCampaign->updatedAt())->toBeInstanceOf(DateTimeInterface::class);
|
|
});
|
|
});
|