327 lines
13 KiB
PHP
327 lines
13 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Tests\Unit\Lib\ApiDto;
|
|
|
|
use DateTimeImmutable;
|
|
use DateTimeInterface;
|
|
use InvalidArgumentException;
|
|
use Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto;
|
|
use Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\Brand;
|
|
use Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\Inco;
|
|
use Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\Inco\Additive;
|
|
use Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\Inco\Allegations;
|
|
use Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\Inco\Allergen;
|
|
use Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\Inco\IncoWeight;
|
|
use Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\Inco\Ingredients;
|
|
use Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\Inco\Nutriscore;
|
|
use Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\Inco\PortionAmount;
|
|
use Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\Inco\PortionType;
|
|
use Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\Inco\StorageCondition;
|
|
use Uwil\ArcadiaApiPhpClient\Lib\ApiValueObject\BrandCode;
|
|
use Uwil\ArcadiaApiPhpClient\Lib\ApiValueObject\IncoCompoId;
|
|
use Uwil\ArcadiaApiPhpClient\Lib\ApiValueObject\IncoId;
|
|
|
|
/**
|
|
* Creates a complete Inco object graph with all sub-DTOs instantiated.
|
|
*
|
|
* @param Allergen[]|null $allergens
|
|
* @param Additive[]|null $additives
|
|
*/
|
|
function createCompleteInco(
|
|
string $id = '100',
|
|
string $name = 'Poulet fermier',
|
|
string $libelle = 'Poulet fermier Label Rouge',
|
|
string $description = 'Poulet élevé en plein air 1.2kg',
|
|
?Allegations $allegations = null,
|
|
string $sellArguments = 'Origine France',
|
|
string $preparationAdvices = 'Cuire au four à 180°C pendant 1h15',
|
|
?string $tips = 'Arroser régulièrement',
|
|
?string $sellFamilly = 'Volaille',
|
|
?Ingredients $ingredients = null,
|
|
?PortionAmount $portions = null,
|
|
?array $allergens = null,
|
|
?Brand $brand = null,
|
|
bool $isSeasonal = true,
|
|
?IncoWeight $weight = null,
|
|
?Nutriscore $nutriscore = null,
|
|
?StorageCondition $storageCondition = null,
|
|
?array $additives = null,
|
|
?string $compoId = '200',
|
|
?DateTimeInterface $createdAt = null,
|
|
?DateTimeInterface $updatedAt = null,
|
|
): Inco {
|
|
return new Inco(
|
|
id: new IncoId($id),
|
|
name: $name,
|
|
libelle: $libelle,
|
|
description: $description,
|
|
allegations: $allegations ?? new Allegations(
|
|
allegations: ['Sans OGM'],
|
|
health: ['Riche en protéines'],
|
|
nutrition: ['Faible en matières grasses'],
|
|
other: ['Label Rouge']
|
|
),
|
|
sellArguments: $sellArguments,
|
|
preparationAdvices: $preparationAdvices,
|
|
tips: $tips,
|
|
sellFamilly: $sellFamilly,
|
|
ingredients: $ingredients ?? new Ingredients([
|
|
'groupe1' => ['Poulet fermier 98%', 'Sel 2%'],
|
|
]),
|
|
portions: $portions ?? new PortionAmount(4.0, 6.0, true),
|
|
allergens: $allergens ?? [
|
|
new Allergen(1, 'Gluten'),
|
|
new Allergen(2, 'Soja'),
|
|
],
|
|
brand: $brand ?? new Brand(
|
|
code: new BrandCode('LGL39'),
|
|
label: 'Le Gaulois',
|
|
arcadiaId: 'arc-b1',
|
|
createdAt: new DateTimeImmutable('2024-01-01T00:00:00+00:00'),
|
|
),
|
|
isSeasonal: $isSeasonal,
|
|
weight: $weight ?? new IncoWeight(1200, 1350, 1250),
|
|
nutriscore: $nutriscore ?? new Nutriscore('A'),
|
|
storageCondition: $storageCondition ?? new StorageCondition('Conserver entre 0°C et 4°C'),
|
|
additives: $additives ?? [
|
|
new Additive('E300', 'Acide ascorbique'),
|
|
],
|
|
compo: $compoId !== null ? new IncoCompoId($compoId) : null,
|
|
createdAt: $createdAt ?? new DateTimeImmutable('2024-01-01T10:00:00+00:00'),
|
|
updatedAt: $updatedAt ?? new DateTimeImmutable('2024-06-01T12:00:00+00:00'),
|
|
);
|
|
}
|
|
|
|
describe('Inco DTO test', function (): void {
|
|
it('can instantiate Inco with complete domain object graph', function (): void {
|
|
// GIVEN
|
|
$inco = createCompleteInco();
|
|
|
|
// THEN
|
|
expect($inco)->toBeInstanceOf(Inco::class)
|
|
->and($inco)->toBeInstanceOf(Dto::class)
|
|
->and($inco->id)->toBeInstanceOf(IncoId::class)
|
|
->and($inco->id->value)->toBe('100')
|
|
->and($inco->name)->toBe('Poulet fermier')
|
|
->and($inco->libelle)->toBe('Poulet fermier Label Rouge')
|
|
->and($inco->description)->toBe('Poulet élevé en plein air 1.2kg')
|
|
->and($inco->sellArguments)->toBe('Origine France')
|
|
->and($inco->preparationAdvices)->toBe('Cuire au four à 180°C pendant 1h15')
|
|
->and($inco->tips)->toBe('Arroser régulièrement')
|
|
->and($inco->sellFamilly)->toBe('Volaille')
|
|
->and($inco->isSeasonal)->toBeTrue()
|
|
->and($inco->compo)->toBeInstanceOf(IncoCompoId::class)
|
|
->and($inco->compo?->value)->toBe('200');
|
|
|
|
// Sub-DTOs: Allegations
|
|
expect($inco->allegations)->toBeInstanceOf(Allegations::class)
|
|
->and($inco->allegations->allegations())->toBe(['Sans OGM'])
|
|
->and($inco->allegations->health())->toBe(['Riche en protéines'])
|
|
->and($inco->allegations->nutrition())->toBe(['Faible en matières grasses'])
|
|
->and($inco->allegations->other())->toBe(['Label Rouge']);
|
|
|
|
// Sub-DTOs: Ingredients
|
|
expect($inco->ingredients)->toBeInstanceOf(Ingredients::class)
|
|
->and($inco->ingredients->groups)->toBe(['groupe1' => ['Poulet fermier 98%', 'Sel 2%']])
|
|
->and($inco->ingredients->flatten())->toBe(['Poulet fermier 98%', 'Sel 2%'])
|
|
->and($inco->ingredients->isEmpty())->toBeFalse();
|
|
|
|
// Sub-DTOs: Portions
|
|
expect($inco->portions)->toBeInstanceOf(PortionAmount::class)
|
|
->and($inco->portions?->getLowValue())->toBe(4.0)
|
|
->and($inco->portions?->getHighValue())->toBe(6.0)
|
|
->and($inco->portions?->getType())->toBe(PortionType::approximative);
|
|
|
|
// Sub-DTOs: Allergens
|
|
expect($inco->allergens)->toHaveCount(2)
|
|
->and($inco->allergens[0])->toBeInstanceOf(Allergen::class)
|
|
->and($inco->allergens[0]->code)->toBe(1)
|
|
->and($inco->allergens[0]->label)->toBe('Gluten')
|
|
->and($inco->allergens[1]->code)->toBe(2)
|
|
->and($inco->allergens[1]->label)->toBe('Soja');
|
|
|
|
// Sub-DTOs: Brand
|
|
expect($inco->brand)->toBeInstanceOf(Brand::class)
|
|
->and($inco->brand->code->value)->toBe('LGL39')
|
|
->and($inco->brand->label)->toBe('Le Gaulois');
|
|
|
|
// Sub-DTOs: Weight
|
|
expect($inco->weight)->toBeInstanceOf(IncoWeight::class)
|
|
->and($inco->weight?->netUvc)->toBe(1200)
|
|
->and($inco->weight?->brutUvc)->toBe(1350)
|
|
->and($inco->weight?->moyTheo)->toBe(1250);
|
|
|
|
// Sub-DTOs: Nutriscore
|
|
expect($inco->nutriscore)->toBeInstanceOf(Nutriscore::class)
|
|
->and($inco->nutriscore?->grade)->toBe('A');
|
|
|
|
// Sub-DTOs: StorageCondition
|
|
expect($inco->storageCondition)->toBeInstanceOf(StorageCondition::class)
|
|
->and($inco->storageCondition?->instructions)->toBe('Conserver entre 0°C et 4°C');
|
|
|
|
// Sub-DTOs: Additives
|
|
expect($inco->additives)->toHaveCount(1)
|
|
->and($inco->additives[0])->toBeInstanceOf(Additive::class)
|
|
->and($inco->additives[0]->code)->toBe('E300')
|
|
->and($inco->additives[0]->label)->toBe('Acide ascorbique');
|
|
});
|
|
|
|
it('can jsonSerialize complete Inco graph', function (): void {
|
|
// GIVEN
|
|
$inco = createCompleteInco();
|
|
|
|
// WHEN
|
|
$serialized = $inco->jsonSerialize();
|
|
|
|
// THEN
|
|
expect($serialized)->toBeArray()
|
|
->and($serialized['id'])->toBe('100')
|
|
->and($serialized['name'])->toBe('Poulet fermier')
|
|
->and($serialized['libelle'])->toBe('Poulet fermier Label Rouge')
|
|
->and($serialized['description'])->toBe('Poulet élevé en plein air 1.2kg')
|
|
->and($serialized['sellArguments'])->toBe('Origine France')
|
|
->and($serialized['preparationAdvices'])->toBe('Cuire au four à 180°C pendant 1h15')
|
|
->and($serialized['tips'])->toBe('Arroser régulièrement')
|
|
->and($serialized['sellFamilly'])->toBe('Volaille')
|
|
->and($serialized['isSeasonal'])->toBeTrue()
|
|
->and($serialized['compo'])->toBe('200')
|
|
->and($serialized['brand'])->toBeArray()
|
|
->and($serialized['brand']['label'])->toBe('Le Gaulois')
|
|
->and($serialized['brand']['code'])->toBeInstanceOf(BrandCode::class);
|
|
|
|
// Recursive json_encode check
|
|
$jsonString = json_encode($inco);
|
|
expect($jsonString)->toBeString();
|
|
$decoded = json_decode((string) $jsonString, true);
|
|
expect($decoded)->toBeArray()
|
|
->and($decoded['id'])->toBe('100')
|
|
->and($decoded['brand']['label'])->toBe('Le Gaulois')
|
|
->and($decoded['brand']['code']['value'])->toBe('LGL39')
|
|
->and($decoded['allergens'])->toHaveCount(2)
|
|
->and($decoded['allergens'][0]['label'])->toBe('Gluten')
|
|
->and($decoded['additives'])->toHaveCount(1)
|
|
->and($decoded['additives'][0]['code'])->toBe('E300')
|
|
->and($decoded['nutriscore'])->toBe('A')
|
|
->and($decoded['storageCondition'])->toBe('Conserver entre 0°C et 4°C')
|
|
->and($decoded['weight']['netUvc'])->toBe(1200);
|
|
});
|
|
|
|
it('can instantiate Inco with minimal nullable fields', function (): void {
|
|
// GIVEN / WHEN
|
|
$inco = new Inco(
|
|
id: new IncoId('101'),
|
|
name: 'Minimal',
|
|
libelle: 'Minimal Label',
|
|
description: 'Minimal Description',
|
|
allegations: new Allegations(),
|
|
sellArguments: '',
|
|
preparationAdvices: '',
|
|
tips: null,
|
|
sellFamilly: null,
|
|
ingredients: new Ingredients([]),
|
|
portions: null,
|
|
allergens: [],
|
|
brand: new Brand(new BrandCode('LGL39'), 'Le Gaulois'),
|
|
isSeasonal: false,
|
|
weight: null,
|
|
nutriscore: null,
|
|
storageCondition: null,
|
|
additives: [],
|
|
compo: null,
|
|
createdAt: null,
|
|
updatedAt: null,
|
|
);
|
|
|
|
// THEN
|
|
expect($inco->tips)->toBeNull()
|
|
->and($inco->sellFamilly)->toBeNull()
|
|
->and($inco->portions)->toBeNull()
|
|
->and($inco->allergens)->toBeEmpty()
|
|
->and($inco->weight)->toBeNull()
|
|
->and($inco->nutriscore)->toBeNull()
|
|
->and($inco->storageCondition)->toBeNull()
|
|
->and($inco->additives)->toBeEmpty()
|
|
->and($inco->compo)->toBeNull()
|
|
->and($inco->createdAt)->toBeNull()
|
|
->and($inco->updatedAt)->toBeNull();
|
|
|
|
$serialized = $inco->jsonSerialize();
|
|
expect($serialized['portions'])->toBeNull()
|
|
->and($serialized['weight'])->toBeNull()
|
|
->and($serialized['nutriscore'])->toBeNull()
|
|
->and($serialized['storageCondition'])->toBeNull()
|
|
->and($serialized['compo'])->toBeNull();
|
|
});
|
|
|
|
it('throws InvalidArgumentException if allergens array contains invalid element', function (): void {
|
|
// GIVEN
|
|
$invalidAllergens = ['invalid string'];
|
|
|
|
// THEN
|
|
$this->expectException(InvalidArgumentException::class);
|
|
$this->expectExceptionMessage('Allergens shall be instance of ' . Allergen::class);
|
|
|
|
// WHEN
|
|
new Inco(
|
|
id: new IncoId('100'),
|
|
name: 'Poulet',
|
|
libelle: 'Poulet',
|
|
description: 'Poulet',
|
|
allegations: new Allegations(),
|
|
sellArguments: '',
|
|
preparationAdvices: '',
|
|
tips: null,
|
|
sellFamilly: null,
|
|
ingredients: new Ingredients([]),
|
|
portions: null,
|
|
// @phpstan-ignore argument.type
|
|
allergens: $invalidAllergens,
|
|
brand: new Brand(new BrandCode('LGL39'), 'Le Gaulois'),
|
|
isSeasonal: false,
|
|
weight: null,
|
|
nutriscore: null,
|
|
storageCondition: null,
|
|
additives: [],
|
|
compo: null,
|
|
createdAt: null,
|
|
updatedAt: null,
|
|
);
|
|
});
|
|
|
|
it('throws InvalidArgumentException if additives array contains invalid element', function (): void {
|
|
// GIVEN
|
|
$invalidAdditives = [new \stdClass()];
|
|
|
|
// THEN
|
|
$this->expectException(InvalidArgumentException::class);
|
|
$this->expectExceptionMessage('Additives shall be instance of ' . Additive::class);
|
|
|
|
// WHEN
|
|
new Inco(
|
|
id: new IncoId('100'),
|
|
name: 'Poulet',
|
|
libelle: 'Poulet',
|
|
description: 'Poulet',
|
|
allegations: new Allegations(),
|
|
sellArguments: '',
|
|
preparationAdvices: '',
|
|
tips: null,
|
|
sellFamilly: null,
|
|
ingredients: new Ingredients([]),
|
|
portions: null,
|
|
allergens: [],
|
|
brand: new Brand(new BrandCode('LGL39'), 'Le Gaulois'),
|
|
isSeasonal: false,
|
|
weight: null,
|
|
nutriscore: null,
|
|
storageCondition: null,
|
|
// @phpstan-ignore argument.type
|
|
additives: $invalidAdditives,
|
|
compo: null,
|
|
createdAt: null,
|
|
updatedAt: null,
|
|
);
|
|
});
|
|
});
|