ldc-boutique-cnc-sable/tests/Unit/App/Services/ArcadiaIncoServiceTest.php

226 lines
10 KiB
PHP

<?php
declare(strict_types=1);
namespace Tests\Unit\App\Services;
use App\Dto\ArcadiaIncoDataDto;
use App\Dto\ArcadiaIncoPreviewDto;
use App\Services\ArcadiaIncoService;
use Domain\Enums\Nutriscore;
use Psr\Log\LoggerInterface;
use Uwil\ArcadiaApiPhpClient\Error\ApiError;
use Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\Brand;
use Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\Inco\Allegations;
use Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\Inco\Allergen;
use Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\Inco as LibInco;
use Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\Inco\Ingredients;
use Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\Inco\Nutriscore as IncoNutriscore;
use Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\Inco\PortionAmount;
use Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\IncoCompo\AcideGras;
use Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\IncoCompo\AcidesAmines;
use Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\IncoCompo\ApportsReference;
use Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\IncoCompo as LibIncoCompo;
use Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\IncoCompo\ComposesSpeciaux;
use Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\IncoCompo\Energie;
use Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\IncoCompo\FruitLegumeNoix;
use Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\IncoCompo\GlucidesDetail;
use Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\IncoCompo\Identification;
use Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\IncoCompo\MacronutrimentsPortion;
use Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\IncoCompo\MacronutrimentsRef;
use Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\IncoCompo\Mineraux;
use Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\IncoCompo\NutriScore as CompoNutriScore;
use Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\IncoCompo\PointsNutriScore;
use Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\IncoCompo\Vitamines;
use Uwil\ArcadiaApiPhpClient\Lib\ApiValueObject\BrandCode;
use Uwil\ArcadiaApiPhpClient\Lib\ApiValueObject\IncoCompoId;
use Uwil\ArcadiaApiPhpClient\Lib\ApiValueObject\IncoId;
use Uwil\ArcadiaApiPhpClient\Service\IncoService as LibIncoService;
describe('ArcadiaIncoService', function (): void {
it('throws RuntimeException when API returns an ApiError', function (): void {
// GIVEN
$incoService = \Mockery::mock(LibIncoService::class);
$logger = \Mockery::mock(LoggerInterface::class);
$logger->shouldReceive('debug');
$logger->shouldReceive('error');
$apiError = \Mockery::mock(ApiError::class);
$apiError->shouldReceive('message')->andReturn('Unauthorized');
$incoService->shouldReceive('findBy')
->once()
->with(\Mockery::type(IncoId::class))
->andReturn($apiError);
$service = new ArcadiaIncoService($incoService, $logger);
// WHEN & THEN
expect(fn () => $service->fetchInco(27996))->toThrow(\RuntimeException::class, 'Unauthorized');
});
it('returns null when INCO sheet is not found (404)', function (): void {
// GIVEN
$incoService = \Mockery::mock(LibIncoService::class);
$logger = \Mockery::mock(LoggerInterface::class);
$logger->shouldReceive('debug');
$logger->shouldReceive('info');
$incoService->shouldReceive('findBy')
->once()
->with(\Mockery::type(IncoId::class))
->andReturnNull();
$service = new ArcadiaIncoService($incoService, $logger);
// WHEN
$result = $service->fetchInco(99999);
// THEN
expect($result)->toBeNull();
});
it('fetches and maps complete INCO and composition data', function (): void {
// GIVEN
$incoService = \Mockery::mock(LibIncoService::class);
$logger = \Mockery::mock(LoggerInterface::class);
$logger->shouldReceive('debug');
$logger->shouldReceive('warning');
$incoId = new IncoId('27996');
$libInco = new LibInco(
id: $incoId,
name: 'POULET JAUNE FERMIER',
libelle: 'Poulet Jaune PAC Le Gaulois',
description: 'Volaille fermière de qualité.',
allegations: new Allegations([]),
sellArguments: 'Un poulet savoureux.',
preparationAdvices: 'Cuire à 200°C pendant 1h30.',
tips: 'Arroser régulièrement.',
sellFamilly: 'VOLAILLES',
ingredients: new Ingredients([
'Ingrédients' => ['Poulet Jaune (100%)'],
]),
portions: new PortionAmount(4, 5, true),
allergens: [
new Allergen(1, 'Aucun allergène'),
],
brand: new Brand(new BrandCode('LGL01'), 'Le Gaulois'),
isSeasonal: false,
weight: null,
nutriscore: new IncoNutriscore('A', null),
storageCondition: null,
additives: [],
compo: new IncoCompoId('88888'),
createdAt: new \DateTimeImmutable('2025-01-01T00:00:00Z'),
updatedAt: new \DateTimeImmutable('2025-01-01T00:00:00Z'),
);
$libCompo = new LibIncoCompo(
id: new IncoCompoId('88888'),
idInco: $incoId,
identification: new Identification('Poulet Jaune', '27996', 'SNV'),
energie: new Energie('100', '623', '149', '150', '935', '224'),
macronutrimentsRef: new MacronutrimentsRef(
'7.2', '=', '2.0', '=', '0.0', '=', '0.0', '=', '0.0', '=', '21.0', '=', '0.16', '=', '64'
),
macronutrimentsPortion: new MacronutrimentsPortion(
'10.8', '=', '3.0', '=', '0.0', '=', '0.0', '=', '0.0', '=', '31.5', '=', '0.24', '=', '96'
),
fnl: new FruitLegumeNoix('0', '0', '0', '0', '0', '0', '0', '0'),
apportsReference: new ApportsReference(
'11', '11', '15', '15', '0', '0', '63', '4', '150g', '2.9'
),
nutriScore: new CompoNutriScore(
'2', '2', '5', '5', '-3', '-3', 'A', 'A', '1', '0'
),
pointsNutriScore: new PointsNutriScore('0', '0', '0', '0', '0', '0', '0'),
mineraux: new Mineraux('0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0'),
vitamines: new Vitamines('0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0'),
glucidesDetail: new GlucidesDetail('0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0'),
acideGras: new AcideGras(
'0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0',
'0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0',
'0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0',
'0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0',
'0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0',
'0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0',
'0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0'
),
acidesAmines: new AcidesAmines('0', '0', '0'),
composesSpeciaux: new ComposesSpeciaux('0', '0', '0', '0'),
);
$incoService->shouldReceive('findBy')->once()->andReturn($libInco);
$incoService->shouldReceive('findCompo')->once()->andReturn($libCompo);
$service = new ArcadiaIncoService($incoService, $logger);
// WHEN
$result = $service->fetchInco(27996);
// THEN
expect($result)->toBeInstanceOf(ArcadiaIncoDataDto::class)
->and($result->noArt)->toBe(27996)
->and($result->name)->toBe('POULET JAUNE FERMIER')
->and($result->brand)->toBe('Le Gaulois')
->and($result->nutriscore)->toBe(Nutriscore::A)
->and($result->allergens)->toBe('Aucun allergène')
->and($result->servings)->toBe('entre 4 et 5')
->and($result->preparation)->toContain('Cuire à 200°C')
->and($result->ingredients)->toContain('Poulet Jaune (100%)')
->and($result->nutrition->kj)->toBe(623.0)
->and($result->nutrition->kcal)->toBe(149.0)
->and($result->nutrition->fatG)->toBe(7.2)
->and($result->nutrition->proteinG)->toBe(21.0);
});
it('returns preview DTO correctly', function (): void {
// GIVEN
$incoService = \Mockery::mock(LibIncoService::class);
$logger = \Mockery::mock(LoggerInterface::class);
$logger->shouldReceive('debug');
$incoId = new IncoId('27996');
$libInco = new LibInco(
id: $incoId,
name: 'POULET JAUNE FERMIER',
libelle: 'Poulet Jaune PAC Le Gaulois',
description: 'Volaille fermière.',
allegations: new Allegations([]),
sellArguments: 'Savoureux.',
preparationAdvices: 'Four.',
tips: null,
sellFamilly: null,
ingredients: new Ingredients([
['Poulet'],
]),
portions: null,
allergens: [],
brand: new Brand(new BrandCode('LGL01'), 'Le Gaulois'),
isSeasonal: false,
weight: null,
nutriscore: new IncoNutriscore('A', null),
storageCondition: null,
additives: [],
compo: null,
createdAt: null,
updatedAt: null,
);
$incoService->shouldReceive('findBy')->once()->andReturn($libInco);
$incoService->shouldReceive('findCompo')->once()->andReturnNull();
$service = new ArcadiaIncoService($incoService, $logger);
// WHEN
$preview = $service->getPreview(27996);
// THEN
expect($preview)->toBeInstanceOf(ArcadiaIncoPreviewDto::class)
->and($preview->noArt)->toBe(27996)
->and($preview->name)->toBe('POULET JAUNE FERMIER')
->and($preview->brand)->toBe('Le Gaulois')
->and($preview->nutriscore)->toBe('A');
});
});