ldc-arcadia-genesis-php-client/tests/Unit/Lib/ApiDto/IncoTest.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,
);
});
});