fix: 🐛 change some file place + add tests

This commit is contained in:
K Muller 2026-09-21 16:46:24 +02:00
parent 2c463eab03
commit f8c2dd2a3f
8 changed files with 366 additions and 1 deletions

View file

@ -2,7 +2,7 @@
declare(strict_types=1);
namespace Domain\Models\ValueObjects\RequestedCoupon;
namespace Domain\Models\ValueObjects;
use Shared\ValueObjects\DateTime;

View file

@ -0,0 +1,81 @@
<?php
declare(strict_types=1);
namespace Tests\Unit\Shared\Enums;
use Domain\Enums\Country;
covers(Country::class);
describe('country', function (): void {
it('has the expected cases with their backing values', function (): void {
// THEN
expect(Country::FR->value())->toBe(1)
->and(Country::BE->value())->toBe(2)
->and(Country::LU->value())->toBe(3);
});
it('exposes its case name', function (): void {
// THEN
expect(Country::FR->name())->toBe('FR');
});
it('returns the defined label', function (): void {
// THEN
expect(Country::FR->label())->toBe('FR')
->and(Country::BE->label())->toBe('BE')
->and(Country::LU->label())->toBe('LU');
});
it('lists all labels keyed by case name', function (): void {
// THEN
expect(Country::toLabels())->toBe([
'FR' => 'FR',
'BE' => 'BE',
'LU' => 'LU',
]);
});
it('lists all values keyed by case name', function (): void {
// THEN
expect(Country::toValues())->toBe([
'FR' => 1,
'BE' => 2,
'LU' => 3,
]);
});
it('makes an instance from its backing value', function (): void {
// THEN
expect(Country::make(1))->toBe(Country::FR);
});
it('makes an instance from a label', function (): void {
// THEN
expect(Country::make('BE', 'label'))->toBe(Country::BE);
});
it('returns the same instance when already an enum case', function (): void {
// THEN
expect(Country::make(Country::LU))->toBe(Country::LU);
});
it('returns null for an unknown value', function (): void {
// THEN
expect(Country::make(99))->toBeNull();
});
it('implements equals correctly', function (): void {
// THEN
expect(Country::FR->equals(Country::FR))->toBeTrue()
->and(Country::FR->equals(Country::BE))->toBeFalse()
->and(Country::FR->equals(null))->toBeFalse();
});
it('serializes to json as its backing value', function (): void {
// THEN
expect(Country::FR->jsonSerialize())->toBe(1)
->and(json_encode(Country::FR))->toBe('1');
});
});

View file

@ -0,0 +1,45 @@
<?php
declare(strict_types=1);
namespace Tests\Unit\Domain\Models\ValueObjects;
use Domain\Models\ValueObjects\MoneyAmount;
use Shared\Enums\Currency;
use Shared\ValueObjects\Money;
covers(MoneyAmount::class);
describe('money amount', function (): void {
it('wraps a Money value', function (): void {
// GIVEN
$money = new Money(Currency::EUR, 300);
// WHEN
$amount = new MoneyAmount($money);
// THEN
expect($amount)->toBeInstanceOf(MoneyAmount::class)
->and($amount->value)->toBe($money);
});
it('implements equals by delegating to the wrapped Money', function (): void {
// GIVEN
$amount1 = new MoneyAmount(new Money(Currency::EUR, 300));
$amount2 = new MoneyAmount(new Money(Currency::EUR, 300));
$amount3 = new MoneyAmount(new Money(Currency::EUR, 500));
// THEN
expect($amount1->equals($amount2))->toBeTrue()
->and($amount1->equals($amount3))->toBeFalse()
->and($amount1->equals(null))->toBeFalse();
});
it('converts to string by delegating to the wrapped Money', function (): void {
// GIVEN
$amount = new MoneyAmount(new Money(Currency::EUR, 300));
// THEN
expect((string) $amount)->toBe((string) $amount->value);
});
});

View file

@ -0,0 +1,26 @@
<?php
declare(strict_types=1);
namespace Tests\Unit\Domain\Models\ValueObjects\RequestedCoupon;
use Domain\Models\ValueObjects\Period;
use Shared\ValueObjects\DateTime;
covers(Period::class);
describe('period', function (): void {
it('creates a valid period', function (): void {
// GIVEN
$start = new DateTime('2026-01-01');
$end = new DateTime('2026-01-31');
// WHEN
$period = new Period($start, $end);
// THEN
expect($period)->toBeInstanceOf(Period::class)
->and($period->start)->toBe($start)
->and($period->end)->toBe($end);
});
});

View file

@ -0,0 +1,66 @@
<?php
declare(strict_types=1);
namespace Tests\Unit\Shared\Enums;
use Shared\Enums\Currency;
covers(Currency::class);
describe('currency', function (): void {
it('has the expected cases with their backing values', function (): void {
// THEN
expect(Currency::EUR->value())->toBe(1);
});
it('exposes its case name', function (): void {
// THEN
expect(Currency::EUR->name())->toBe('EUR');
});
it('returns the defined label', function (): void {
// THEN
expect(Currency::EUR->label())->toBe('Euro');
});
it('lists all labels keyed by case name', function (): void {
// THEN
expect(Currency::toLabels())->toBe([
'EUR' => 'Euro',
]);
});
it('lists all values keyed by case name', function (): void {
// THEN
expect(Currency::toValues())->toBe([
'EUR' => 1,
]);
});
it('makes an instance from its backing value', function (): void {
// THEN
expect(Currency::make(1))->toBe(Currency::EUR);
});
it('makes an instance from a label', function (): void {
// THEN
expect(Currency::make('Euro', 'label'))->toBe(Currency::EUR);
});
it('returns the same instance when already an enum case', function (): void {
// THEN
expect(Currency::make(Currency::EUR))->toBe(Currency::EUR);
});
it('returns null for an unknown value', function (): void {
// THEN
expect(Currency::make(99))->toBeNull();
});
it('implements equals correctly', function (): void {
// THEN
expect(Currency::EUR->equals(Currency::EUR))->toBeTrue()
->and(Currency::EUR->equals(null))->toBeFalse();
});
});

View file

@ -0,0 +1,86 @@
<?php
declare(strict_types=1);
namespace Tests\Unit\Shared\ValueObjects;
use Shared\Helpers\DatesHelper;
use Shared\Helpers\Exceptions\InvalidDateFormatException;
use Shared\ValueObjects\DateTime;
covers(DateTime::class);
describe('date time', function (): void {
it('accepts a DateTimeInterface instance and normalizes it to UTC', function (): void {
// GIVEN
$paris = new \DateTimeImmutable('2026-06-15 14:00:00', new \DateTimeZone('Europe/Paris'));
// WHEN
$dateTime = new DateTime($paris);
// THEN
expect($dateTime->value)->toBeInstanceOf(\DateTimeImmutable::class)
->and($dateTime->value->getTimezone()->getName())->toBe('UTC')
->and($dateTime->value->format('H:i:s'))->toBe('12:00:00');
});
it('parses a valid date string', function (): void {
// WHEN
$dateTime = new DateTime('2026-01-15');
// THEN
expect($dateTime->value->format('Y-m-d'))->toBe('2026-01-15');
});
it('rejects an invalid date string', function (): void {
// WHEN
$action = fn (): DateTime => new DateTime('not-a-date');
// THEN
expect($action)->toThrow(InvalidDateFormatException::class);
});
it('creates the current instant via now', function (): void {
// GIVEN
\Carbon\CarbonImmutable::setTestNow('2026-06-15T12:00:00Z');
// WHEN
$dateTime = DateTime::now();
// THEN
expect($dateTime)->toBeInstanceOf(DateTime::class)
->and($dateTime->value->getTimezone()->getName())->toBe('UTC')
->and($dateTime->value->format(\DateTimeInterface::ATOM))->toBe('2026-06-15T12:00:00+00:00');
// TEARDOWN
\Carbon\CarbonImmutable::setTestNow();
});
it('implements equals correctly', function (): void {
// GIVEN
$a = new DateTime('2026-01-15T10:00:00Z');
$b = new DateTime('2026-01-15T10:00:00Z');
$c = new DateTime('2026-01-16T10:00:00Z');
// THEN
expect($a->equals($b))->toBeTrue()
->and($a->equals($c))->toBeFalse()
->and($a->equals(null))->toBeFalse();
});
it('converts to a RFC3339 string', function (): void {
// GIVEN
$dateTime = new DateTime('2026-01-15T10:00:00Z');
// THEN
expect((string) $dateTime)->toBe(DatesHelper::toRfc3339($dateTime->value));
});
it('serializes to json as a RFC3339 string', function (): void {
// GIVEN
$dateTime = new DateTime('2026-01-15T10:00:00Z');
// THEN
expect($dateTime->jsonSerialize())->toBe((string) $dateTime);
});
});

View file

@ -0,0 +1,61 @@
<?php
declare(strict_types=1);
namespace Tests\Unit\Shared\ValueObjects;
use Shared\Enums\Currency;
use Shared\ValueObjects\Money;
covers(Money::class);
describe('money', function (): void {
it('creates a valid money value', function (): void {
// GIVEN
$value = 300;
// WHEN
$money = new Money(Currency::EUR, $value);
// THEN
expect($money)->toBeInstanceOf(Money::class)
->and($money->currency)->toBe(Currency::EUR)
->and($money->value)->toBe($value);
});
it('accepts a zero value', function (): void {
// WHEN
$money = new Money(Currency::EUR, 0);
// THEN
expect($money->value)->toBe(0);
});
it('rejects a negative value', function (): void {
// WHEN
$action = fn (): Money => new Money(Currency::EUR, -1);
// THEN
expect($action)->toThrow(\DomainException::class, 'Money value cannot be negative.');
});
it('implements equals correctly', function (): void {
// GIVEN
$money1 = new Money(Currency::EUR, 300);
$money2 = new Money(Currency::EUR, 300);
$money3 = new Money(Currency::EUR, 500);
// THEN
expect($money1->equals($money2))->toBeTrue()
->and($money1->equals($money3))->toBeFalse()
->and($money1->equals(null))->toBeFalse();
});
it('converts to string', function (): void {
// GIVEN
$money = new Money(Currency::EUR, 300);
// THEN
expect((string) $money)->toBe('300 EUR');
});
});