refactor(brand): make ArcadiaId strictly string value extending EntityId

This commit is contained in:
Jérémy Munsch 2026-08-24 21:45:04 +02:00
parent 55576a6971
commit e31767cfd0
Signed by: forge-joniko
GPG key ID: D7EFE2F5CB6CC0E4
47 changed files with 150 additions and 146 deletions

View file

@ -731,9 +731,10 @@ components:
description: Nom / libellé de la marque.
example: "Le Gaulois"
arcadiaId:
type: integer
type: string
pattern: '^[1-9]\d{0,2}$'
description: Identifiant numérique amont Arcadia.
example: 1
example: "1"
createdAt:
type: string
format: date-time

View file

@ -4,14 +4,20 @@ declare(strict_types=1);
namespace Domain\Models\ValueObjects\Brand;
final readonly class ArcadiaId
use Shared\DDD\Model\EntityId;
final readonly class ArcadiaId extends EntityId
{
private const string PATTERN = '/^[1-9]\d{0,2}$/';
public function __construct(
public readonly int $value
string $value
) {
if ($value < 1 || $value > 999) {
parent::__construct($value);
if (! preg_match(self::PATTERN, $value)) {
throw new \DomainException(
"Invalid arcadia id '{$value}' does not match pattern /^\d{1,3}$/"
"Invalid arcadia id '{$value}' does not match pattern " . self::PATTERN
);
}
}

View file

@ -38,7 +38,7 @@ final class EloquentArcadiaBrandDirectory implements ArcadiaBrandDirectory
private function toDomain(ArcadiaBrandModel $model): ArcadiaBrand
{
return new ArcadiaBrand(
id: new ArcadiaId((int) $model->code),
id: new ArcadiaId((string) $model->code),
name: new BrandName((string) $model->libelle),
);
}

View file

@ -236,7 +236,7 @@ final class EloquentIncoSheetDirectory implements IncoSheetDirectory
? (string) $raw['code']
: '1';
$arcadiaId = new ArcadiaId(is_numeric($rawCode) ? (int) $rawCode : 1);
$arcadiaId = new ArcadiaId($rawCode);
$brand = $this->brandResolver->resolve($arcadiaId);
return new IncoBrand(
@ -323,8 +323,8 @@ final class EloquentIncoSheetDirectory implements IncoSheetDirectory
foreach ($brandCodes as $brandCode) {
$brand = $this->brandRepository->findByCode($brandCode);
if ($brand !== null) {
$resolved[] = (int) $brand->arcadiaId->value;
$resolved[] = $brand->arcadiaId->value;
$resolved[] = (string) $brand->arcadiaId->value;
}
}

View file

@ -126,8 +126,8 @@ final class EloquentIncoShortSheetDirectory implements IncoShortSheetDirectory
foreach ($brandCodes as $brandCode) {
$brand = $this->brandRepository->findByCode($brandCode);
if ($brand !== null) {
$resolved[] = (int) $brand->arcadiaId->value;
$resolved[] = $brand->arcadiaId->value;
$resolved[] = (string) $brand->arcadiaId->value;
}
}

View file

@ -10,7 +10,7 @@ use Domain\Ports\Directories\ArcadiaBrandDirectory;
final class InMemoryArcadiaBrandDirectory implements ArcadiaBrandDirectory
{
/** @var array<int, ArcadiaBrand> */
/** @var array<string, ArcadiaBrand> */
private array $brands = [];
public function add(ArcadiaBrand ...$brands): self

View file

@ -121,12 +121,12 @@ final readonly class BrandCache
}
/**
* @return array{id: string, name: string, code: string, arcadiaId: int}
* @return array{id: string, name: string, code: string, arcadiaId: string}
*/
private function toCacheData(Brand $brand): array
{
return [
'id' => (string) $brand->id()->value,
'id' => $brand->id()->value,
'name' => $brand->name->value,
'code' => $brand->code->value,
'arcadiaId' => $brand->arcadiaId->value,
@ -141,7 +141,7 @@ final readonly class BrandCache
|| ! is_string($data['id'])
|| ! is_string($data['name'])
|| ! is_string($data['code'])
|| ! is_int($data['arcadiaId'])
|| ! is_string($data['arcadiaId'])
) {
return null;
}

View file

@ -21,7 +21,7 @@ final class ArcadiaIdCast implements CastsAttributes
throw new \InvalidArgumentException('Value must be an integer or numeric string');
}
return new ArcadiaId((int) $value);
return new ArcadiaId((string) $value);
}
public function set(Model $model, string $key, mixed $value, array $attributes): int
@ -31,15 +31,11 @@ final class ArcadiaIdCast implements CastsAttributes
}
if ($value instanceof ArcadiaId) {
return $value->value;
return (int) $value->value;
}
if (is_int($value)) {
return (new ArcadiaId($value))->value;
}
if (is_string($value) && ctype_digit($value)) {
return (new ArcadiaId((int) $value))->value;
if (is_int($value) || (is_string($value) && ctype_digit($value))) {
return (int) (new ArcadiaId((string) $value))->value;
}
throw new \InvalidArgumentException('Value must be an integer, numeric string, or instance of ArcadiaId');

View file

@ -18,7 +18,7 @@ final readonly class LogBrandCreated
{
$this->logger->info(
sprintf(
'Created new brand code "%s" for brand "%s" (Arcadia ID %d)',
'Created new brand code "%s" for brand "%s" (Arcadia ID %s)',
$event->brandCode->value,
$event->brandName->value,
$event->arcadiaId->value

View file

@ -18,7 +18,7 @@ final readonly class LogBrandRenamed
{
$this->logger->info(
sprintf(
'Brand "%s" (code %s, Arcadia ID %d) renamed to "%s"',
'Brand "%s" (code %s, Arcadia ID %s) renamed to "%s"',
$event->oldName->value,
$event->brandCode->value,
$event->arcadiaId->value,

View file

@ -15,7 +15,7 @@ final readonly class BrandDto implements ApiResponseDto
public string $id,
public string $code,
public string $name,
public int $arcadiaId,
public string $arcadiaId,
public string $createdAt,
) {
}

View file

@ -10,7 +10,7 @@ final readonly class BrandRow
public string $id,
public string $code,
public string $name,
public int $arcadiaId,
public string $arcadiaId,
public string $createdAt,
) {
}

View file

@ -84,7 +84,7 @@ describe('IncoSheets Index Feature API', function (): void {
$test = $this;
ArcadiaBrand::query()->firstOrCreate(['code' => 504], ['libelle' => 'LE GAULOIS PROFESSIONNEL']);
$brand = app(BrandResolver::class)->resolve(new ArcadiaId(504));
$brand = app(BrandResolver::class)->resolve(new ArcadiaId('504'));
IncoSheet::factory()->create([
'id' => '9999001',

View file

@ -61,7 +61,7 @@ describe('IncoSheets Search Feature API', function (): void {
$query = urlencode('%10001%');
ArcadiaBrand::query()->firstOrCreate(['code' => 1], ['libelle' => 'Standard']);
$brand = app(BrandResolver::class)->resolve(new ArcadiaId(1));
$brand = app(BrandResolver::class)->resolve(new ArcadiaId('1'));
// WHEN
$response = $test->getJson("/api/v1/inco-sheets/search?incoIdLike={$query}&brands[]={$brand->code->value}");

View file

@ -28,7 +28,7 @@ describe('BrandSeeder Integration Test', function (): void {
$brand = Brand::query()->where('uwil_public_id', 'LGL01')->first();
expect($brand)->not->toBeNull()
->and($brand?->arcadia_id->value)->toBe(1)
->and($brand?->arcadia_id->value)->toBe('1')
->and($brand?->name)->toBe('Le Gaulois');
});
});

View file

@ -53,7 +53,7 @@ describe('EloquentIncoSheetDirectory Integration Tests', function (): void {
->and((string) $result?->id->value)->toBe('1000101')
->and($result?->name)->toBe('Poulet Rôti')
->and($result?->brand->name->value)->toBe('Le Gaulois')
->and($result?->brand->arcadiaBrandCode->value)->toBe(1)
->and($result?->brand->arcadiaBrandCode->value)->toBe('1')
->and($result?->brand->code->value)->toMatch('/^[A-Z]{3}\d{2}$/');
});
@ -76,8 +76,8 @@ describe('EloquentIncoSheetDirectory Integration Tests', function (): void {
$repository = app(BrandRepository::class);
$directory = new EloquentIncoSheetDirectory($resolver, $repository);
$brand1 = $resolver->resolve(new ArcadiaId(1));
$brand2 = $resolver->resolve(new ArcadiaId(2));
$brand1 = $resolver->resolve(new ArcadiaId('1'));
$brand2 = $resolver->resolve(new ArcadiaId('2'));
IncoSheetModel::factory()->create([
'id' => '1000101',
@ -107,7 +107,7 @@ describe('EloquentIncoSheetDirectory Integration Tests', function (): void {
$repository = app(BrandRepository::class);
$directory = new EloquentIncoSheetDirectory($resolver, $repository);
$brand1 = $resolver->resolve(new ArcadiaId(1));
$brand1 = $resolver->resolve(new ArcadiaId('1'));
IncoSheetModel::factory()->create([
'id' => '1234501',

View file

@ -70,7 +70,7 @@ describe('EloquentIncoShortSheetDirectory Integration Tests', function (): void
$repository = app(BrandRepository::class);
$directory = new EloquentIncoShortSheetDirectory($repository);
$brand1 = $resolver->resolve(new ArcadiaId(1));
$brand1 = $resolver->resolve(new ArcadiaId('1'));
IncoSheetModel::factory()->create([
'id' => '1000101',
@ -100,7 +100,7 @@ describe('EloquentIncoShortSheetDirectory Integration Tests', function (): void
$repository = app(BrandRepository::class);
$directory = new EloquentIncoShortSheetDirectory($repository);
$brand1 = $resolver->resolve(new ArcadiaId(1));
$brand1 = $resolver->resolve(new ArcadiaId('1'));
IncoSheetModel::factory()->create([
'id' => '1234501',

View file

@ -35,7 +35,7 @@ describe('EloquentBrandRepository Integration Tests', function (): void {
$repository = new EloquentBrandRepository($brandCache);
$brandId = new BrandId(Uuid::uuid7());
$brandCode = new BrandCode('LGL01');
$arcadiaId = new ArcadiaId(1);
$arcadiaId = new ArcadiaId('1');
$name = new BrandName('Le Gaulois');
$brand = Brand::hydrate(
@ -53,7 +53,7 @@ describe('EloquentBrandRepository Integration Tests', function (): void {
expect($found)->not->toBeNull()
->and($found->id()->value)->toBe($brandId->value)
->and($found->code->value)->toBe('LGL01')
->and($found->arcadiaId->value)->toBe(1)
->and($found->arcadiaId->value)->toBe('1')
->and($found->name->value)->toBe('Le Gaulois');
});
@ -134,7 +134,7 @@ describe('EloquentBrandRepository Integration Tests', function (): void {
'uwil_public_id' => 'PRD01',
]);
$arcadiaId = new ArcadiaId(30);
$arcadiaId = new ArcadiaId('30');
// WHEN
$firstResult = $repository->findByArcadiaId($arcadiaId);
@ -146,7 +146,7 @@ describe('EloquentBrandRepository Integration Tests', function (): void {
// THEN
expect($firstResult)->not->toBeNull()
->and($secondResult)->not->toBeNull()
->and($secondResult->arcadiaId->value)->toBe(30)
->and($secondResult->arcadiaId->value)->toBe('30')
->and($secondResult->name->value)->toBe('Père Dodu');
});
@ -191,7 +191,7 @@ describe('EloquentBrandRepository Integration Tests', function (): void {
$brandId = new BrandId(Uuid::fromString($rawId));
$brandCode = new BrandCode('FDA01');
$arcadiaId = new ArcadiaId(40);
$arcadiaId = new ArcadiaId('40');
// Pollute cache with invalid data (simulating stale objects or corrupted cache)
Cache::put("brand:id:{$rawId}", 'corrupted_string');
@ -229,7 +229,7 @@ describe('EloquentBrandRepository Integration Tests', function (): void {
id: new BrandId(Uuid::fromString($rawId)),
name: new BrandName('To Delete'),
code: new BrandCode('DEL01'),
arcadiaId: new ArcadiaId(50),
arcadiaId: new ArcadiaId('50'),
);
// Prime cache
@ -254,7 +254,7 @@ describe('EloquentBrandRepository Integration Tests', function (): void {
id: new BrandId(Uuid::uuid7()),
name: new BrandName('Ghost'),
code: new BrandCode('GHO01'),
arcadiaId: new ArcadiaId(999),
arcadiaId: new ArcadiaId('999'),
);
// WHEN & THEN

View file

@ -77,7 +77,15 @@ final class IncoSheetTestBuilder
$this->sellArguments = 'Délicieux et tendre';
$this->preparationAdvices = 'Cuire 45min à 180°C';
$this->ingredients = new Ingredients(['Ingrédients' => ['Viande de dinde (66%)']]);
$this->brand = new IncoBrand(new BrandName('Le Gaulois'), new BrandCode('LGL01'), new ArcadiaId(504));
$this->brand = new IncoBrand(new BrandName('Le Gaulois'), new BrandCode('LGL01'), new ArcadiaId('504'));
$this->isSeasonal = false;
$this->weight = null;
$this->nutriscore = null;
$this->storageCondition = null;
$this->additives = [];
$this->compo = null;
$this->createdAt = new \DateTimeImmutable('2024-01-01 00:00:00');
$this->updatedAt = null;
}
public static function create(): self
@ -113,13 +121,13 @@ final class IncoSheetTestBuilder
return $this;
}
public function withBrand(IncoBrand | string $brand, ?string $code = null, ?int $arcadiaId = null): self
public function withBrand(IncoBrand | string $brand, ?string $code = null, ?string $arcadiaId = null): self
{
if (is_string($brand)) {
$this->brand = new IncoBrand(
name: new BrandName($brand),
code: new BrandCode($code ?? 'LGL01'),
arcadiaBrandCode: new ArcadiaId($arcadiaId ?? 1),
arcadiaBrandCode: new ArcadiaId($arcadiaId ?? '1'),
);
} else {
$this->brand = $brand;

View file

@ -16,13 +16,13 @@ final class BrandMother
public static function create(
?string $name = null,
?string $code = null,
?int $arcadiaId = null,
?string $arcadiaId = null,
): Brand {
return Brand::hydrate(
id: new BrandId(Uuid::uuid7()),
name: new BrandName($name ?? 'Le Gaulois'),
code: new BrandCode($code ?? 'LGL01'),
arcadiaId: new ArcadiaId($arcadiaId ?? 1),
arcadiaId: new ArcadiaId($arcadiaId ?? '1'),
);
}
}

View file

@ -34,7 +34,7 @@ describe('FindBrand Usecase Unit Tests', function (): void {
$brandId = new BrandId(Uuid::uuid7());
$brandName = new BrandName('Le Gaulois');
$brandCode = new BrandCode('LGL01');
$arcadiaId = new ArcadiaId(1);
$arcadiaId = new ArcadiaId('1');
$brand = Brand::hydrate(
id: $brandId,
@ -63,7 +63,7 @@ describe('FindBrand Usecase Unit Tests', function (): void {
$brandId = new BrandId(Uuid::uuid7());
$brandName = new BrandName('Maître CoQ');
$brandCode = new BrandCode('MCQ02');
$arcadiaId = new ArcadiaId(2);
$arcadiaId = new ArcadiaId('2');
$brand = Brand::hydrate(
id: $brandId,

View file

@ -48,8 +48,8 @@ describe('FindAllBrands Usecase Unit Tests', function (): void {
it('returns existing brands and generates missing ones automatically', function () use ($arcadiaDirectory, $brandRepository, $codeGenerator, $dispatcher): void {
// GIVEN
$arcadia1 = new ArcadiaBrand(new ArcadiaId(1), new BrandName('Le Gaulois'));
$arcadia2 = new ArcadiaBrand(new ArcadiaId(2), new BrandName('Maître CoQ'));
$arcadia1 = new ArcadiaBrand(new ArcadiaId('1'), new BrandName('Le Gaulois'));
$arcadia2 = new ArcadiaBrand(new ArcadiaId('2'), new BrandName('Maître CoQ'));
$arcadiaDirectory->add($arcadia1, $arcadia2);
// Already existing brand in our repository for Arcadia 1
@ -57,7 +57,7 @@ describe('FindAllBrands Usecase Unit Tests', function (): void {
id: new BrandId(Uuid::uuid7()),
name: new BrandName('Le Gaulois'),
code: new BrandCode('LGL01'),
arcadiaId: new ArcadiaId(1),
arcadiaId: new ArcadiaId('1'),
);
$brandRepository->add($existingBrand);
@ -81,19 +81,19 @@ describe('FindAllBrands Usecase Unit Tests', function (): void {
->and($result->brands[0]->name->value)->toBe('Le Gaulois');
// Second item was generated and persisted
expect($result->brands[1]->arcadiaId->value)->toBe(2)
expect($result->brands[1]->arcadiaId->value)->toBe('2')
->and($result->brands[1]->name->value)->toBe('Maître CoQ')
->and(str_starts_with($result->brands[1]->code->value, 'MCQ'))->toBeTrue();
// Check that the second brand was persisted in repository
expect($brandRepository->findByArcadiaId(new ArcadiaId(2)))->not->toBeNull();
expect($brandRepository->findByArcadiaId(new ArcadiaId('2')))->not->toBeNull();
});
it('slices brands according to pagination', function () use ($arcadiaDirectory, $brandRepository, $codeGenerator, $dispatcher): void {
// GIVEN
$arcadia1 = new ArcadiaBrand(new ArcadiaId(1), new BrandName('Le Gaulois'));
$arcadia2 = new ArcadiaBrand(new ArcadiaId(2), new BrandName('Maître CoQ'));
$arcadia3 = new ArcadiaBrand(new ArcadiaId(3), new BrandName('Loué'));
$arcadia1 = new ArcadiaBrand(new ArcadiaId('1'), new BrandName('Le Gaulois'));
$arcadia2 = new ArcadiaBrand(new ArcadiaId('2'), new BrandName('Maître CoQ'));
$arcadia3 = new ArcadiaBrand(new ArcadiaId('3'), new BrandName('Loué'));
$arcadiaDirectory->add($arcadia1, $arcadia2, $arcadia3);
$resolver = new BrandResolver($arcadiaDirectory, $brandRepository, $codeGenerator, $dispatcher);

View file

@ -58,7 +58,7 @@ describe('FindAllIncoSheets DTOs Instantiation & Logic', function (): void {
ingredients: new Ingredients(['main' => ['Viande']]),
portions: null,
allergens: [],
brand: new IncoBrand(new BrandName('Le Gaulois'), new BrandCode('LGL01'), new ArcadiaId(1)),
brand: new IncoBrand(new BrandName('Le Gaulois'), new BrandCode('LGL01'), new ArcadiaId('1')),
isSeasonal: true,
weight: null,
nutriscore: null,

View file

@ -56,7 +56,7 @@ describe('FindAllIncoSheets Usecase Unit Tests', function (): void {
ingredients: new Ingredients(['Ingrédients' => ['Viande de dinde (66%)']]),
portions: null,
allergens: [],
brand: new IncoBrand(new BrandName('Le Gaulois'), new BrandCode('LGL01'), new ArcadiaId(1)),
brand: new IncoBrand(new BrandName('Le Gaulois'), new BrandCode('LGL01'), new ArcadiaId('1')),
isSeasonal: true,
weight: null,
nutriscore: null,

View file

@ -46,7 +46,7 @@ describe('FindIncoSheet DTO Instantiation', function (): void {
ingredients: new Ingredients(['main' => ['Viande']]),
portions: null,
allergens: [],
brand: new IncoBrand(new BrandName('Le Gaulois'), new BrandCode('LGL01'), new ArcadiaId(1)),
brand: new IncoBrand(new BrandName('Le Gaulois'), new BrandCode('LGL01'), new ArcadiaId('1')),
isSeasonal: true,
weight: null,
nutriscore: null,

View file

@ -55,7 +55,7 @@ describe('SearchIncoSheets Usecase Unit Tests', function (): void {
ingredients: new Ingredients(['Ingrédients' => ['Viande de dinde (66%)']]),
portions: null,
allergens: [],
brand: new IncoBrand(new BrandName('Le Gaulois'), new BrandCode('LGL01'), new ArcadiaId(1)),
brand: new IncoBrand(new BrandName('Le Gaulois'), new BrandCode('LGL01'), new ArcadiaId('1')),
isSeasonal: true,
weight: null,
nutriscore: null,

View file

@ -22,7 +22,7 @@ describe('IncoSheetBuilder Unit Tests', function (): void {
$id = new IncoId('12345');
$allegations = new Allegations(['Volaille française']);
$ingredients = new Ingredients(['Ingrédients' => ['Viande de dinde (66%)']]);
$brand = new IncoBrand(new BrandName('Le Gaulois'), new BrandCode('LGL01'), new ArcadiaId(504));
$brand = new IncoBrand(new BrandName('Le Gaulois'), new BrandCode('LGL01'), new ArcadiaId('504'));
// WHEN
$sheet = IncoSheet::builder()
@ -48,7 +48,7 @@ describe('IncoSheetBuilder Unit Tests', function (): void {
->and($sheet->sellFamilly)->toBe('Boucherie')
->and($sheet->brand->name->value)->toBe('Le Gaulois')
->and($sheet->brand->code->value)->toBe('LGL01')
->and($sheet->brand->arcadiaBrandCode->value)->toBe(504)
->and($sheet->brand->arcadiaBrandCode->value)->toBe('504')
->and($sheet->isSeasonal)->toBeTrue();
});

View file

@ -22,7 +22,7 @@ describe('IncoSheet Domain Entity Unit Tests', function (): void {
$id = new IncoId('12345');
$allegations = new Allegations(['Volaille française']);
$ingredients = new Ingredients(['Ingrédients' => ['Viande de dinde (66%)']]);
$brand = new IncoBrand(new BrandName('Le Gaulois'), new BrandCode('LGL01'), new ArcadiaId(504));
$brand = new IncoBrand(new BrandName('Le Gaulois'), new BrandCode('LGL01'), new ArcadiaId('504'));
// WHEN
$sheet = IncoSheet::hydrate(
@ -55,7 +55,7 @@ describe('IncoSheet Domain Entity Unit Tests', function (): void {
->and($sheet->name)->toBe('Poulet Rôti 1kg')
->and($sheet->brand->name->value)->toBe('Le Gaulois')
->and($sheet->brand->code->value)->toBe('LGL01')
->and($sheet->brand->arcadiaBrandCode->value)->toBe(504)
->and($sheet->brand->arcadiaBrandCode->value)->toBe('504')
->and($sheet->isSeasonal)->toBeTrue();
});
});

View file

@ -19,7 +19,7 @@ describe('BrandCreated Domain Event Unit Tests', function (): void {
$brandId = new BrandId(Uuid::uuid7());
$brandName = new BrandName('Duc');
$brandCode = new BrandCode('DUC01');
$arcadiaId = new ArcadiaId(42);
$arcadiaId = new ArcadiaId('42');
$now = new \DateTimeImmutable();
// WHEN

View file

@ -20,7 +20,7 @@ describe('BrandRenamed Domain Event Unit Tests', function (): void {
$oldName = new BrandName('Le Gaulois');
$newName = new BrandName('Le Gaulois France');
$brandCode = new BrandCode('LGL01');
$arcadiaId = new ArcadiaId(1);
$arcadiaId = new ArcadiaId('1');
$now = new \DateTimeImmutable();
// WHEN

View file

@ -18,7 +18,7 @@ describe('Brand Entity', function (): void {
it('creates a brand and implements HasTimestampIdentifier', function (): void {
// GIVEN
$brandCode = new BrandCode('LGL89');
$arcadiaId = new ArcadiaId(123);
$arcadiaId = new ArcadiaId('123');
$brandName = new BrandName('Arcadia Brand Name');
// WHEN
@ -42,7 +42,7 @@ describe('Brand Entity', function (): void {
$uuid = Uuid::uuid7();
$brandId = new BrandId($uuid);
$brandCode = new BrandCode('LGL89');
$arcadiaId = new ArcadiaId(123);
$arcadiaId = new ArcadiaId('123');
$brandName = new BrandName('Arcadia Brand Name');
// WHEN
@ -67,7 +67,7 @@ describe('Brand Entity', function (): void {
// GIVEN
$brandId = new BrandId(Uuid::uuid7());
$brandCode = new BrandCode('LGL01');
$arcadiaId = new ArcadiaId(1);
$arcadiaId = new ArcadiaId('1');
$oldName = new BrandName('Le Gaulois');
$newName = new BrandName('Le Gaulois France');

View file

@ -13,7 +13,7 @@ covers(ArcadiaBrand::class);
describe('ArcadiaBrand Value Object Unit Tests', function (): void {
it('instantiates an ArcadiaBrand VO correctly', function (): void {
// GIVEN
$id = new ArcadiaId(1);
$id = new ArcadiaId('1');
$name = new BrandName('Le Gaulois');
// WHEN

View file

@ -9,26 +9,19 @@ use Domain\Models\ValueObjects\Brand\ArcadiaId;
covers(ArcadiaId::class);
describe('ArcadiaId Value Object Instantiation', function (): void {
it('accepts valid positive arcadia ids', function (int $data): void {
// GIVEN
$data = (int) $data;
// WHEN
it('accepts valid positive arcadia ids', function (string $data): void {
// GIVEN & WHEN
$arcadiaId = new ArcadiaId($data);
// THEN
expect($arcadiaId->value)->toBe($data);
})->with([1, 999, 123]);
})->with(['1', '999', '123']);
});
describe('ArcadiaId Value Object Domain Rules', function (): void {
it('rejects less than 1 or more than 999', function (int $invalid): void {
// WHEN
it('rejects invalid arcadia ids', function (string $invalid): void {
// WHEN & THEN
expect(fn () => new ArcadiaId($invalid))
// THEN
->toThrow(\DomainException::class);
// GIVEN
})->with([0, 1000, -1, -999]);
})->with(['0', '1000', '-1', 'abc', '', '01']);
});

View file

@ -16,7 +16,7 @@ describe('IncoBrand Value Object Unit Tests', function (): void {
// GIVEN
$name = new BrandName('Le Gaulois');
$code = new BrandCode('LGL01');
$arcadiaCode = new ArcadiaId(504);
$arcadiaCode = new ArcadiaId('504');
// WHEN
$brand = new IncoBrand($name, $code, $arcadiaCode);
@ -24,6 +24,6 @@ describe('IncoBrand Value Object Unit Tests', function (): void {
// THEN
expect($brand->name->value)->toBe('Le Gaulois')
->and($brand->code->value)->toBe('LGL01')
->and($brand->arcadiaBrandCode->value)->toBe(504);
->and($brand->arcadiaBrandCode->value)->toBe('504');
});
});

View file

@ -39,7 +39,7 @@ describe('BrandResolver Unit Tests', function (): void {
id: new BrandId(Uuid::uuid7()),
name: new BrandName('Le Gaulois'),
code: new BrandCode('LGL01'),
arcadiaId: new ArcadiaId(1),
arcadiaId: new ArcadiaId('1'),
);
$brandRepository->add($existing);
@ -57,7 +57,7 @@ describe('BrandResolver Unit Tests', function (): void {
$resolver = new BrandResolver($arcadiaDirectory, $brandRepository, $codeGenerator, $dispatcher);
// WHEN
$resolved = $resolver->resolve(new ArcadiaId(1));
$resolved = $resolver->resolve(new ArcadiaId('1'));
// THEN
expect($resolved)->toBe($existing)
@ -66,7 +66,7 @@ describe('BrandResolver Unit Tests', function (): void {
it('generates, persists and dispatches BrandCreated event when brand is not in repository but in Arcadia directory', function () use ($arcadiaDirectory, $brandRepository, $codeGenerator): void {
// GIVEN
$arcadiaDirectory->add(new ArcadiaBrand(new ArcadiaId(2), new BrandName('Maître CoQ')));
$arcadiaDirectory->add(new ArcadiaBrand(new ArcadiaId('2'), new BrandName('Maître CoQ')));
$dispatcher = new class () implements DomainEventDispatcher
{
@ -82,21 +82,21 @@ describe('BrandResolver Unit Tests', function (): void {
$resolver = new BrandResolver($arcadiaDirectory, $brandRepository, $codeGenerator, $dispatcher);
// WHEN
$resolved = $resolver->resolve(new ArcadiaId(2));
$resolved = $resolver->resolve(new ArcadiaId('2'));
// THEN
expect($resolved->arcadiaId->value)->toBe(2)
expect($resolved->arcadiaId->value)->toBe('2')
->and($resolved->name->value)->toBe('Maître CoQ')
->and(str_starts_with($resolved->code->value, 'MCQ'))->toBeTrue();
expect($brandRepository->findByArcadiaId(new ArcadiaId(2)))->not->toBeNull();
expect($brandRepository->findByArcadiaId(new ArcadiaId('2')))->not->toBeNull();
expect($dispatcher->dispatched)->toHaveCount(1)
->and($dispatcher->dispatched[0])->toBeInstanceOf(BrandCreated::class);
/** @var BrandCreated $event */
$event = $dispatcher->dispatched[0];
expect($event->arcadiaId->value)->toBe(2)
expect($event->arcadiaId->value)->toBe('2')
->and($event->brandName->value)->toBe('Maître CoQ')
->and($event->brandCode->value)->toBe($resolved->code->value)
->and($event->brandId->value)->toBe($resolved->id->value);
@ -114,7 +114,7 @@ describe('BrandResolver Unit Tests', function (): void {
$resolver = new BrandResolver($arcadiaDirectory, $brandRepository, $codeGenerator, $dispatcher);
// WHEN & THEN
expect(fn () => $resolver->resolve(new ArcadiaId(999)))
expect(fn () => $resolver->resolve(new ArcadiaId('999')))
->toThrow(BrandNotFoundException::class, 'Arcadia brand with ID 999 not found.');
});
@ -125,12 +125,12 @@ describe('BrandResolver Unit Tests', function (): void {
id: $brandId,
name: new BrandName('Le Gaulois'),
code: new BrandCode('LGL01'),
arcadiaId: new ArcadiaId(1),
arcadiaId: new ArcadiaId('1'),
);
$brandRepository->add($oldBrand);
// Arcadia has renamed the brand
$arcadiaDirectory->add(new ArcadiaBrand(new ArcadiaId(1), new BrandName('Le Gaulois France')));
$arcadiaDirectory->add(new ArcadiaBrand(new ArcadiaId('1'), new BrandName('Le Gaulois France')));
$dispatcher = new class () implements DomainEventDispatcher
{
@ -146,14 +146,14 @@ describe('BrandResolver Unit Tests', function (): void {
$resolver = new BrandResolver($arcadiaDirectory, $brandRepository, $codeGenerator, $dispatcher);
// WHEN
$resolved = $resolver->resolve(new ArcadiaId(1));
$resolved = $resolver->resolve(new ArcadiaId('1'));
// THEN
expect($resolved->name->value)->toBe('Le Gaulois France')
->and($resolved->id()->value)->toBe($brandId->value)
->and($resolved->code->value)->toBe('LGL01');
$persisted = $brandRepository->findByArcadiaId(new ArcadiaId(1));
$persisted = $brandRepository->findByArcadiaId(new ArcadiaId('1'));
expect($persisted)->not->toBeNull()
->and($persisted->name->value)->toBe('Le Gaulois France');
@ -174,7 +174,7 @@ describe('BrandResolver Unit Tests', function (): void {
id: $brandId,
name: new BrandName('Historical Brand'),
code: new BrandCode('HST01'),
arcadiaId: new ArcadiaId(77),
arcadiaId: new ArcadiaId('77'),
);
$brandRepository->add($existing);
@ -195,7 +195,7 @@ describe('BrandResolver Unit Tests', function (): void {
$resolver = new BrandResolver($arcadiaDirectory, $brandRepository, $codeGenerator, $dispatcher);
// WHEN
$resolved = $resolver->resolve(new ArcadiaId(77));
$resolved = $resolver->resolve(new ArcadiaId('77'));
// THEN
expect($resolved)->toBe($existing)

View file

@ -16,14 +16,14 @@ describe('InMemoryArcadiaBrandDirectory Unit Tests', function (): void {
// GIVEN
$directory = new InMemoryArcadiaBrandDirectory();
$brand = new ArcadiaBrand(
id: new ArcadiaId(1),
id: new ArcadiaId('1'),
name: new BrandName('Le Gaulois'),
);
$directory->add($brand);
// WHEN
$found = $directory->find(new ArcadiaId(1));
$missing = $directory->find(new ArcadiaId(999));
$found = $directory->find(new ArcadiaId('1'));
$missing = $directory->find(new ArcadiaId('999'));
// THEN
expect($found)->toBe($brand)
@ -33,8 +33,8 @@ describe('InMemoryArcadiaBrandDirectory Unit Tests', function (): void {
it('returns all arcadia brands and clears them', function (): void {
// GIVEN
$directory = new InMemoryArcadiaBrandDirectory();
$b1 = new ArcadiaBrand(new ArcadiaId(1), new BrandName('Le Gaulois'));
$b2 = new ArcadiaBrand(new ArcadiaId(2), new BrandName('Maître CoQ'));
$b1 = new ArcadiaBrand(new ArcadiaId('1'), new BrandName('Le Gaulois'));
$b2 = new ArcadiaBrand(new ArcadiaId('2'), new BrandName('Maître CoQ'));
$directory->add($b1, $b2);
// WHEN

View file

@ -22,7 +22,7 @@ use Shared\Pagination;
covers(InMemoryIncoSheetDirectory::class);
describe('InMemoryIncoSheetDirectory Unit Tests', function (): void {
function createSheet(string $idStr, string $brandCode, string $brandLabel, int $arcadiaId = 1): IncoSheet
function createSheet(string $idStr, string $brandCode, string $brandLabel, string $arcadiaId = '1'): IncoSheet
{
return IncoSheet::hydrate(
id: new IncoId($idStr),
@ -52,7 +52,7 @@ describe('InMemoryIncoSheetDirectory Unit Tests', function (): void {
it('finds an inco sheet by id', function (): void {
// GIVEN
$directory = new InMemoryIncoSheetDirectory();
$sheet = createSheet('12345', 'LGL01', 'Le Gaulois', 1);
$sheet = createSheet('12345', 'LGL01', 'Le Gaulois', '1');
$directory->add($sheet);
// WHEN
@ -67,8 +67,8 @@ describe('InMemoryIncoSheetDirectory Unit Tests', function (): void {
it('filters, sorts and paginates inco sheets', function (): void {
// GIVEN
$directory = new InMemoryIncoSheetDirectory();
$sheet1 = createSheet('10001', 'LGL01', 'Le Gaulois', 1);
$sheet2 = createSheet('10002', 'MCQ01', 'Maître CoQ', 2);
$sheet1 = createSheet('10001', 'LGL01', 'Le Gaulois', '1');
$sheet2 = createSheet('10002', 'MCQ01', 'Maître CoQ', '2');
$directory->add($sheet1, $sheet2);
$pagination = Pagination::ofPage(1, 10);
@ -87,9 +87,9 @@ describe('InMemoryIncoSheetDirectory Unit Tests', function (): void {
it('searches inco sheets by id query and brands', function (): void {
// GIVEN
$directory = new InMemoryIncoSheetDirectory();
$sheet1 = createSheet('12345', 'LGL01', 'Le Gaulois', 1);
$sheet2 = createSheet('12999', 'LGL01', 'Le Gaulois', 1);
$sheet3 = createSheet('54321', 'MCQ01', 'Maître CoQ', 2);
$sheet1 = createSheet('12345', 'LGL01', 'Le Gaulois', '1');
$sheet2 = createSheet('12999', 'LGL01', 'Le Gaulois', '1');
$sheet3 = createSheet('54321', 'MCQ01', 'Maître CoQ', '2');
$directory->add($sheet1, $sheet2, $sheet3);
$query = new IncoIdQuery('12%');
@ -106,7 +106,7 @@ describe('InMemoryIncoSheetDirectory Unit Tests', function (): void {
it('clears all sheets', function (): void {
// GIVEN
$directory = new InMemoryIncoSheetDirectory();
$sheet = createSheet('12345', 'LGL01', 'Le Gaulois', 1);
$sheet = createSheet('12345', 'LGL01', 'Le Gaulois', '1');
$directory->add($sheet);
// WHEN

View file

@ -16,7 +16,7 @@ use Ramsey\Uuid\Uuid;
covers(InMemoryBrandRepository::class);
describe('InMemoryBrandRepository Unit Tests', function (): void {
function createBrand(string $code, string $name, int $arcadiaId): Brand
function createBrand(string $code, string $name, string $arcadiaId): Brand
{
return Brand::hydrate(
id: new BrandId(Uuid::uuid7()),
@ -29,7 +29,7 @@ describe('InMemoryBrandRepository Unit Tests', function (): void {
it('finds a brand by id and by code', function (): void {
// GIVEN
$repository = new InMemoryBrandRepository();
$brand = createBrand('LGL01', 'Le Gaulois', 1);
$brand = createBrand('LGL01', 'Le Gaulois', '1');
$repository->add($brand);
// WHEN
@ -48,7 +48,7 @@ describe('InMemoryBrandRepository Unit Tests', function (): void {
it('persists and deletes a brand', function (): void {
// GIVEN
$repository = new InMemoryBrandRepository();
$brand = createBrand('MCQ02', 'Maître CoQ', 2);
$brand = createBrand('MCQ02', 'Maître CoQ', '2');
// WHEN
$repository->persist($brand);
@ -66,7 +66,7 @@ describe('InMemoryBrandRepository Unit Tests', function (): void {
it('throws BrandNotFoundException when deleting non-existent brand', function (): void {
// GIVEN
$repository = new InMemoryBrandRepository();
$brand = createBrand('MCQ02', 'Maître CoQ', 2);
$brand = createBrand('MCQ02', 'Maître CoQ', '2');
// WHEN & THEN
expect(fn () => $repository->delete($brand))
@ -76,7 +76,7 @@ describe('InMemoryBrandRepository Unit Tests', function (): void {
it('clears all brands', function (): void {
// GIVEN
$repository = new InMemoryBrandRepository();
$brand = createBrand('MCQ02', 'Maître CoQ', 2);
$brand = createBrand('MCQ02', 'Maître CoQ', '2');
$repository->add($brand);
// WHEN

View file

@ -26,7 +26,7 @@ describe('BrandCache Unit Tests', function (): void {
'id' => $uuid,
'name' => 'Le Gaulois',
'code' => 'LGL01',
'arcadiaId' => 1,
'arcadiaId' => '1',
];
/** @var CacheInterface&Mockery\MockInterface $cacheMock */
@ -46,7 +46,7 @@ describe('BrandCache Unit Tests', function (): void {
->and($result->id()->value)->toBe($brandId->value)
->and($result->name->value)->toBe('Le Gaulois')
->and($result->code->value)->toBe('LGL01')
->and($result->arcadiaId->value)->toBe(1);
->and($result->arcadiaId->value)->toBe('1');
});
it('returns brand from cache when found by code', function (): void {
@ -58,7 +58,7 @@ describe('BrandCache Unit Tests', function (): void {
'id' => $uuid,
'name' => 'Le Gaulois',
'code' => 'LGL01',
'arcadiaId' => 1,
'arcadiaId' => '1',
];
/** @var CacheInterface&Mockery\MockInterface $cacheMock */
@ -82,13 +82,13 @@ describe('BrandCache Unit Tests', function (): void {
it('returns brand from cache when found by arcadia id', function (): void {
// GIVEN
$uuid = Uuid::uuid7()->toString();
$arcadiaId = new ArcadiaId(10);
$arcadiaId = new ArcadiaId('10');
$cacheData = [
'id' => $uuid,
'name' => 'Maître CoQ',
'code' => 'MCQ01',
'arcadiaId' => 10,
'arcadiaId' => '10',
];
/** @var CacheInterface&Mockery\MockInterface $cacheMock */
@ -105,7 +105,7 @@ describe('BrandCache Unit Tests', function (): void {
// THEN
expect($result)->not->toBeNull()
->and($result->arcadiaId->value)->toBe(10)
->and($result->arcadiaId->value)->toBe('10')
->and($result->name->value)->toBe('Maître CoQ');
});
@ -136,14 +136,14 @@ describe('BrandCache Unit Tests', function (): void {
id: new BrandId(Uuid::fromString($uuid)),
name: new BrandName('Doux'),
code: new BrandCode('DOX01'),
arcadiaId: new ArcadiaId(20),
arcadiaId: new ArcadiaId('20'),
);
$expectedData = [
'id' => $uuid,
'name' => 'Doux',
'code' => 'DOX01',
'arcadiaId' => 20,
'arcadiaId' => '20',
];
/** @var CacheInterface&Mockery\MockInterface $cacheMock */
@ -174,7 +174,7 @@ describe('BrandCache Unit Tests', function (): void {
id: new BrandId(Uuid::fromString($uuid)),
name: new BrandName('Père Dodu'),
code: new BrandCode('PRD01'),
arcadiaId: new ArcadiaId(30),
arcadiaId: new ArcadiaId('30'),
);
/** @var CacheInterface&Mockery\MockInterface $cacheMock */
@ -209,7 +209,7 @@ describe('BrandCache Unit Tests', function (): void {
'id' => $uuid,
'name' => 'Brand 1',
'code' => 'BRD01',
'arcadiaId' => 1,
'arcadiaId' => '1',
],
];

View file

@ -23,7 +23,7 @@ describe('ArcadiaIdCast Unit Tests', function (): void {
// THEN
expect($result)->toBeInstanceOf(ArcadiaId::class)
->and($result->value)->toBe(123);
->and($result->value)->toBe('123');
});
it('transforms numeric string from database to ArcadiaId instance on get', function () use ($cast, $model): void {
@ -35,7 +35,7 @@ describe('ArcadiaIdCast Unit Tests', function (): void {
// THEN
expect($result)->toBeInstanceOf(ArcadiaId::class)
->and($result->value)->toBe(456);
->and($result->value)->toBe('456');
});
it('throws InvalidArgumentException on get when value is null', function () use ($cast, $model): void {
@ -56,7 +56,7 @@ describe('ArcadiaIdCast Unit Tests', function (): void {
it('transforms ArcadiaId instance to integer on set', function () use ($cast, $model): void {
// GIVEN
$arcadiaId = new ArcadiaId(789);
$arcadiaId = new ArcadiaId('789');
// WHEN
$result = $cast->set($model, 'arcadia_id', $arcadiaId, []);

View file

@ -24,7 +24,7 @@ describe('InvalidateBrandCache Listener Unit Tests', function (): void {
brandId: new BrandId(Uuid::uuid7()),
brandName: new BrandName('Le Gaulois'),
brandCode: new BrandCode('LGL01'),
arcadiaId: new ArcadiaId(1),
arcadiaId: new ArcadiaId('1'),
occurredAt: new \DateTimeImmutable(),
);
@ -49,7 +49,7 @@ describe('InvalidateBrandCache Listener Unit Tests', function (): void {
oldName: new BrandName('Le Gaulois'),
newName: new BrandName('Le Gaulois France'),
brandCode: new BrandCode('LGL01'),
arcadiaId: new ArcadiaId(1),
arcadiaId: new ArcadiaId('1'),
occurredAt: new \DateTimeImmutable(),
);

View file

@ -22,7 +22,7 @@ describe('LogBrandCreated Listener Unit Tests', function (): void {
$brandId = new BrandId(Uuid::uuid7());
$brandName = new BrandName('Le Gaulois');
$brandCode = new BrandCode('LGL01');
$arcadiaId = new ArcadiaId(1);
$arcadiaId = new ArcadiaId('1');
$event = new BrandCreated(
brandId: $brandId,
brandName: $brandName,
@ -41,7 +41,7 @@ describe('LogBrandCreated Listener Unit Tests', function (): void {
'brand_id' => $brandId->value,
'code' => 'LGL01',
'name' => 'Le Gaulois',
'arcadia_id' => 1,
'arcadia_id' => '1',
]
);

View file

@ -23,7 +23,7 @@ describe('LogBrandRenamed Listener Unit Tests', function (): void {
$oldName = new BrandName('Le Gaulois');
$newName = new BrandName('Le Gaulois France');
$brandCode = new BrandCode('LGL01');
$arcadiaId = new ArcadiaId(1);
$arcadiaId = new ArcadiaId('1');
$event = new BrandRenamed(
brandId: $brandId,
oldName: $oldName,
@ -44,7 +44,7 @@ describe('LogBrandRenamed Listener Unit Tests', function (): void {
'code' => 'LGL01',
'old_name' => 'Le Gaulois',
'new_name' => 'Le Gaulois France',
'arcadia_id' => 1,
'arcadia_id' => '1',
]
);

View file

@ -22,7 +22,7 @@ describe('BrandDto Unit Tests', function (): void {
$id = new BrandId(Uuid::uuid7());
$name = new BrandName('Le Gaulois');
$code = new BrandCode('LGL01');
$arcadiaId = new ArcadiaId(1);
$arcadiaId = new ArcadiaId('1');
$createdAt = new \DateTimeImmutable('2024-01-15T08:00:00Z');
$view = new BrandShowView(
@ -40,13 +40,13 @@ describe('BrandDto Unit Tests', function (): void {
expect($dto->id)->toBe((string) $id)
->and($dto->name)->toBe('Le Gaulois')
->and($dto->code)->toBe('LGL01')
->and($dto->arcadiaId)->toBe(1)
->and($dto->arcadiaId)->toBe('1')
->and($dto->createdAt)->toBe(DatesHelper::toRfc3339($createdAt))
->and($dto->toArray())->toBe([
'id' => (string) $id,
'code' => 'LGL01',
'name' => 'Le Gaulois',
'arcadiaId' => 1,
'arcadiaId' => '1',
'createdAt' => DatesHelper::toRfc3339($createdAt),
]);
});
@ -56,7 +56,7 @@ describe('BrandDto Unit Tests', function (): void {
$id = new BrandId(Uuid::uuid7());
$name = new BrandName('Maître CoQ');
$code = new BrandCode('MCQ02');
$arcadiaId = new ArcadiaId(2);
$arcadiaId = new ArcadiaId('2');
$createdAt = new \DateTimeImmutable('2024-01-15T08:00:00Z');
$item = new BrandItem(
@ -74,6 +74,6 @@ describe('BrandDto Unit Tests', function (): void {
expect($dto->id)->toBe((string) $id)
->and($dto->name)->toBe('Maître CoQ')
->and($dto->code)->toBe('MCQ02')
->and($dto->arcadiaId)->toBe(2);
->and($dto->arcadiaId)->toBe('2');
});
});

View file

@ -24,7 +24,7 @@ describe('BrandShowPresenter Unit Tests', function (): void {
$id = new BrandId(Uuid::uuid7());
$name = new BrandName('Le Gaulois');
$code = new BrandCode('LGL01');
$arcadiaId = new ArcadiaId(1);
$arcadiaId = new ArcadiaId('1');
$createdAt = new \DateTimeImmutable('2024-01-15T08:00:00Z');
$output = new Output(

View file

@ -26,7 +26,7 @@ describe('BrandsIndexPresenter Unit Tests', function (): void {
id: new BrandId(Uuid::uuid7()),
name: new BrandName('Le Gaulois'),
code: new BrandCode('LGL01'),
arcadiaId: new ArcadiaId(1),
arcadiaId: new ArcadiaId('1'),
createdAt: new \DateTimeImmutable('2024-01-15T08:00:00Z'),
);

View file

@ -27,7 +27,7 @@ describe('ListBrandsPresenter Unit Tests', function (): void {
$id = new BrandId(Uuid::uuid7());
$name = new BrandName('Le Gaulois');
$code = new BrandCode('LGL01');
$arcadiaId = new ArcadiaId(1);
$arcadiaId = new ArcadiaId('1');
$createdAt = new \DateTimeImmutable('2024-01-15 08:00:00');
$item = new BrandItem($id, $name, $code, $arcadiaId, $createdAt);
@ -49,7 +49,7 @@ describe('ListBrandsPresenter Unit Tests', function (): void {
->and($view->rows[0]->id)->toBe((string) $id)
->and($view->rows[0]->code)->toBe('LGL01')
->and($view->rows[0]->name)->toBe('Le Gaulois')
->and($view->rows[0]->arcadiaId)->toBe(1)
->and($view->rows[0]->arcadiaId)->toBe('1')
->and($view->rows[0]->createdAt)->toBe('2024-01-15T08:00:00+00:00');
});