test(presentation): add missing unit tests for API and Console presenters

This commit is contained in:
Jérémy Munsch 2026-08-20 18:22:20 +02:00
parent b26baaaf86
commit aca627ff78
11 changed files with 675 additions and 0 deletions

View file

@ -0,0 +1,54 @@
<?php
declare(strict_types=1);
namespace Tests\Unit\Presentation\Api\V1\Presenters\Employees;
use Application\Arcadia\Usecase\Employees\Find\Output;
use Domain\Models\ValueObjects\Employees\ArcadiaEmployeeId;
use Presentation\Api\V1\Presenters\Employees\EmployeeShowPresenter;
use Presentation\Api\V1\Presenters\Employees\EmployeeShowView;
covers(
EmployeeShowPresenter::class,
EmployeeShowView::class,
);
describe('EmployeeShowPresenter Unit Tests', function (): void {
it('transforms Output into EmployeeShowView', function (): void {
// GIVEN
$id = new ArcadiaEmployeeId('1234567');
$startDate = new \DateTimeImmutable('2024-01-01');
$endDate = new \DateTimeImmutable('2024-12-31');
$plannedEndDate = new \DateTimeImmutable('2025-06-30');
$output = new Output(
id: $id,
firstName: 'Jean',
lastName: 'DUPONT',
establishment: '01',
site: 'LDCSER',
service: '097',
startDate: $startDate,
endDate: $endDate,
plannedEndDate: $plannedEndDate,
);
$presenter = new EmployeeShowPresenter();
// WHEN
$view = $presenter->present($output);
// THEN
expect($view)->toBeInstanceOf(EmployeeShowView::class)
->and($view->id)->toBe($id)
->and($view->firstName)->toBe('Jean')
->and($view->lastName)->toBe('DUPONT')
->and($view->establishment)->toBe('01')
->and($view->site)->toBe('LDCSER')
->and($view->service)->toBe('097')
->and($view->startDate)->toBe($startDate)
->and($view->endDate)->toBe($endDate)
->and($view->plannedEndDate)->toBe($plannedEndDate);
});
});

View file

@ -0,0 +1,53 @@
<?php
declare(strict_types=1);
namespace Tests\Unit\Presentation\Api\V1\Presenters\Employees;
use Application\Arcadia\Usecase\Employees\FindAll\Output;
use Application\Arcadia\Usecase\Employees\FindAll\PaginatedEmployee;
use Domain\Models\ValueObjects\Employees\ArcadiaEmployeeId;
use Presentation\Api\V1\Presenters\Employees\EmployeesIndexPresenter;
use Presentation\Api\V1\Presenters\Employees\EmployeesIndexView;
covers(
EmployeesIndexPresenter::class,
EmployeesIndexView::class,
);
describe('EmployeesIndexPresenter Unit Tests', function (): void {
it('transforms Output into EmployeesIndexView', function (): void {
// GIVEN
$employee = new PaginatedEmployee(
id: new ArcadiaEmployeeId('1234567'),
firstName: 'Jean',
lastName: 'DUPONT',
establishment: '01',
site: 'LDCSER',
service: '097',
startDate: new \DateTimeImmutable('2024-01-01'),
endDate: null,
plannedEndDate: null,
);
$output = new Output(
employees: [$employee],
total: 1,
page: 1,
perPage: 25,
);
$presenter = new EmployeesIndexPresenter();
// WHEN
$view = $presenter->present($output);
// THEN
expect($view)->toBeInstanceOf(EmployeesIndexView::class)
->and($view->employees)->toHaveCount(1)
->and($view->employees[0])->toBe($employee)
->and($view->total)->toBe(1)
->and($view->page)->toBe(1)
->and($view->perPage)->toBe(25);
});
});

View file

@ -0,0 +1,89 @@
<?php
declare(strict_types=1);
namespace Tests\Unit\Presentation\Api\V1\Presenters\IncoCompositions;
use Application\Arcadia\Usecase\IncoCompositions\Find\Output;
use Domain\Models\ValueObjects\Inco\Composition\AminoAcids;
use Domain\Models\ValueObjects\Inco\Composition\CarbohydratesDetail;
use Domain\Models\ValueObjects\Inco\Composition\Energy;
use Domain\Models\ValueObjects\Inco\Composition\EnergyValues;
use Domain\Models\ValueObjects\Inco\Composition\FattyAcids;
use Domain\Models\ValueObjects\Inco\Composition\FreshProduceAndNuts;
use Domain\Models\ValueObjects\Inco\Composition\FruitAndNutAmount;
use Domain\Models\ValueObjects\Inco\Composition\Identification;
use Domain\Models\ValueObjects\Inco\Composition\IncoCompositionId;
use Domain\Models\ValueObjects\Inco\Composition\Minerals;
use Domain\Models\ValueObjects\Inco\Composition\NutrientValue;
use Domain\Models\ValueObjects\Inco\Composition\NutriScore;
use Domain\Models\ValueObjects\Inco\Composition\NutriScorePoints;
use Domain\Models\ValueObjects\Inco\Composition\PercentageValue;
use Domain\Models\ValueObjects\Inco\Composition\PortionMacronutrients;
use Domain\Models\ValueObjects\Inco\Composition\ReferenceIntakes;
use Domain\Models\ValueObjects\Inco\Composition\ReferenceMacronutrients;
use Domain\Models\ValueObjects\Inco\Composition\SpecialCompounds;
use Domain\Models\ValueObjects\Inco\Composition\Vitamins;
use Domain\Models\ValueObjects\Inco\IncoId;
use Presentation\Api\V1\Presenters\IncoCompositions\IncoCompositionShowPresenter;
use Presentation\Api\V1\Presenters\IncoCompositions\IncoCompositionShowView;
covers(
IncoCompositionShowPresenter::class,
IncoCompositionShowView::class,
);
describe('IncoCompositionShowPresenter Unit Tests', function (): void {
it('transforms Output into IncoCompositionShowView', function (): void {
// GIVEN
$energyValues = new EnergyValues('100g', '0', '0');
$energy = new Energy($energyValues, $energyValues);
$nutrient = new NutrientValue('0');
$refMacro = new ReferenceMacronutrients($nutrient, $nutrient, $nutrient, $nutrient, $nutrient, $nutrient, $nutrient);
$porMacro = new PortionMacronutrients($nutrient, $nutrient, $nutrient, $nutrient, $nutrient, $nutrient, $nutrient);
$fnAmount = new FruitAndNutAmount();
$pct = new PercentageValue('0');
$fresh = new FreshProduceAndNuts($fnAmount, $fnAmount, $pct, $pct);
$refIntakes = new ReferenceIntakes($pct, $pct, $pct, $pct, $pct, $pct, $pct, $pct, '100g');
$nutriScore = new NutriScore('0', '0', 'A');
$nutriScorePoints = new NutriScorePoints('0', '0', '0', '0', '0', '0', '0');
$minerals = new Minerals('0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0');
$vitamins = new Vitamins('0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0');
$carbs = new CarbohydratesDetail('0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0');
$fatty = new FattyAcids('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');
$amino = new AminoAcids('0', '0', '0');
$special = new SpecialCompounds('0', '0', '0', '0');
$output = new Output(
id: new IncoCompositionId('1000101'),
idInco: new IncoId('1000101'),
identification: new Identification(name: 'Composition Test', number: '10001', source: 'Arcadia'),
energy: $energy,
referenceMacronutrients: $refMacro,
portionMacronutrients: $porMacro,
freshProduceAndNuts: $fresh,
referenceIntakes: $refIntakes,
nutriScore: $nutriScore,
nutriScorePoints: $nutriScorePoints,
minerals: $minerals,
vitamins: $vitamins,
carbohydratesDetail: $carbs,
fattyAcids: $fatty,
aminoAcids: $amino,
specialCompounds: $special,
);
$presenter = new IncoCompositionShowPresenter();
// WHEN
$view = $presenter->present($output);
// THEN
expect($view)->toBeInstanceOf(IncoCompositionShowView::class)
->and($view->output)->toBe($output);
});
});

View file

@ -0,0 +1,49 @@
<?php
declare(strict_types=1);
namespace Tests\Unit\Presentation\Api\V1\Presenters\IncoSheets;
use Application\Arcadia\Usecase\IncoSheets\FindAll\Output;
use Application\Arcadia\Usecase\IncoSheets\FindAll\PaginatedShortIncoSheet;
use Domain\Models\ValueObjects\Inco\IncoId;
use Presentation\Api\V1\Presenters\IncoSheets\IncoSheetsIndexPresenter;
use Presentation\Api\V1\Presenters\IncoSheets\IncoSheetsIndexView;
covers(
IncoSheetsIndexPresenter::class,
IncoSheetsIndexView::class,
);
describe('IncoSheetsIndexPresenter Unit Tests', function (): void {
it('transforms Output into IncoSheetsIndexView', function (): void {
// GIVEN
$sheet = new PaginatedShortIncoSheet(
id: new IncoId('1000101'),
name: 'Poulet Rôti',
label: 'Le Gaulois',
createdAt: new \DateTimeImmutable('2024-01-01'),
updatedAt: new \DateTimeImmutable('2024-01-02'),
);
$output = new Output(
incoSheets: [$sheet],
total: 1,
page: 1,
perPage: 25,
);
$presenter = new IncoSheetsIndexPresenter();
// WHEN
$view = $presenter->present($output);
// THEN
expect($view)->toBeInstanceOf(IncoSheetsIndexView::class)
->and($view->incoSheets)->toHaveCount(1)
->and($view->incoSheets[0])->toBe($sheet)
->and($view->total)->toBe(1)
->and($view->page)->toBe(1)
->and($view->perPage)->toBe(25);
});
});

View file

@ -0,0 +1,49 @@
<?php
declare(strict_types=1);
namespace Tests\Unit\Presentation\Api\V1\Presenters\IncoSheets;
use Application\Arcadia\Usecase\IncoSheets\Search\Output;
use Application\Arcadia\Usecase\IncoSheets\Search\PaginatedShortIncoSheet;
use Domain\Models\ValueObjects\Inco\IncoId;
use Presentation\Api\V1\Presenters\IncoSheets\IncoSheetsSearchPresenter;
use Presentation\Api\V1\Presenters\IncoSheets\IncoSheetsSearchView;
covers(
IncoSheetsSearchPresenter::class,
IncoSheetsSearchView::class,
);
describe('IncoSheetsSearchPresenter Unit Tests', function (): void {
it('transforms Output into IncoSheetsSearchView', function (): void {
// GIVEN
$sheet = new PaginatedShortIncoSheet(
id: new IncoId('1000101'),
name: 'Poulet Rôti',
label: 'Le Gaulois',
createdAt: new \DateTimeImmutable('2024-01-01'),
updatedAt: new \DateTimeImmutable('2024-01-02'),
);
$output = new Output(
incoSheets: [$sheet],
total: 1,
page: 1,
perPage: 25,
);
$presenter = new IncoSheetsSearchPresenter();
// WHEN
$view = $presenter->present($output);
// THEN
expect($view)->toBeInstanceOf(IncoSheetsSearchView::class)
->and($view->incoSheets)->toHaveCount(1)
->and($view->incoSheets[0])->toBe($sheet)
->and($view->total)->toBe(1)
->and($view->page)->toBe(1)
->and($view->perPage)->toBe(25);
});
});

View file

@ -0,0 +1,59 @@
<?php
declare(strict_types=1);
namespace Tests\Unit\Presentation\Api\V1\Presenters\IncoSheets;
use Application\Arcadia\Usecase\IncoSheets\Find\Output;
use Domain\Models\ValueObjects\Brand\ArcadiaId;
use Domain\Models\ValueObjects\Brand\BrandCode;
use Domain\Models\ValueObjects\Brand\BrandName;
use Domain\Models\ValueObjects\Inco\Allegations;
use Domain\Models\ValueObjects\Inco\IncoBrand;
use Domain\Models\ValueObjects\Inco\IncoId;
use Domain\Models\ValueObjects\Inco\Ingredients;
use Presentation\Api\V1\Presenters\IncoSheets\IncoSheetsShowPresenter;
use Presentation\Api\V1\Presenters\IncoSheets\IncoSheetsShowView;
covers(
IncoSheetsShowPresenter::class,
IncoSheetsShowView::class,
);
describe('IncoSheetsShowPresenter Unit Tests', function (): void {
it('transforms Output into IncoSheetsShowView', function (): void {
// GIVEN
$output = new Output(
id: new IncoId('1000101'),
name: 'Poulet Rôti',
label: 'Le Gaulois',
description: 'Poulet rôti de qualité',
allegations: new Allegations([]),
sellArguments: 'Délicieux',
preparationAdvices: 'Réchauffer 10min',
tips: null,
sellFamilly: null,
ingredients: new Ingredients([]),
portions: null,
allergens: [],
brand: new IncoBrand(new BrandName('Le Gaulois'), new BrandCode('LGL01'), new ArcadiaId(1)),
isSeasonal: false,
weight: null,
nutriscore: null,
storageCondition: null,
additives: [],
compo: null,
createdAt: new \DateTimeImmutable('2024-01-01'),
updatedAt: new \DateTimeImmutable('2024-01-02'),
);
$presenter = new IncoSheetsShowPresenter();
// WHEN
$view = $presenter->present($output);
// THEN
expect($view)->toBeInstanceOf(IncoSheetsShowView::class)
->and($view->output)->toBe($output);
});
});

View file

@ -0,0 +1,49 @@
<?php
declare(strict_types=1);
namespace Tests\Unit\Presentation\Console\Presenters\Clients;
use Application\Security\Models\ValueObjects\ClientIdentifier;
use Application\Security\Models\ValueObjects\ClientName;
use Application\Security\Models\ValueObjects\ClientSecret;
use Application\Security\Models\ValueObjects\Entities\ClientId;
use Application\Security\Usecases\CreateClient\Output;
use Presentation\Console\Presenters\Clients\CreateClientPresenter;
use Presentation\Console\Presenters\Clients\CreateClientView;
use Ramsey\Uuid\Uuid;
covers(
CreateClientPresenter::class,
CreateClientView::class,
);
describe('CreateClientPresenter Unit Tests', function (): void {
it('transforms Output into CreateClientView', function (): void {
// GIVEN
$clientId = new ClientId(Uuid::uuid7());
$name = new ClientName('Test Client');
$identifier = new ClientIdentifier('arcadia_id_' . str_repeat('a', 32));
$secret = new ClientSecret('arcadia_sec_' . str_repeat('b', 86));
$output = new Output(
clientId: $clientId,
clientIdentifier: $identifier,
clientSecret: $secret,
clientName: $name,
);
$presenter = new CreateClientPresenter();
// WHEN
$view = $presenter->present($output);
// THEN
expect($view)->toBeInstanceOf(CreateClientView::class)
->and($view->id)->toBe($clientId->value)
->and($view->name)->toBe('Test Client')
->and($view->identifier)->toBe($identifier->value)
->and($view->secret)->toBe($secret->value)
->and($view->message)->toBe("API client 'Test Client' created successfully.");
});
});

View file

@ -0,0 +1,61 @@
<?php
declare(strict_types=1);
namespace Tests\Unit\Presentation\Console\Presenters\Clients;
use Application\Security\Models\Entities\Client;
use Application\Security\Models\ValueObjects\ClientIdentifier;
use Application\Security\Models\ValueObjects\ClientName;
use Application\Security\Models\ValueObjects\ClientSecret;
use Application\Security\Models\ValueObjects\Entities\ClientId;
use Application\Security\Usecases\FindClient\Output;
use Presentation\Console\Presenters\Clients\FindClientPresenter;
use Presentation\Console\Presenters\Clients\FindClientView;
use Presentation\Console\Presenters\Clients\FoundClientItem;
use Ramsey\Uuid\Uuid;
use Shared\Paginated;
use Shared\Pagination;
covers(
FindClientPresenter::class,
FindClientView::class,
FoundClientItem::class,
);
describe('FindClientPresenter Unit Tests', function (): void {
it('transforms Output into FindClientView with formatted client items', function (): void {
// GIVEN
$clientId = new ClientId(Uuid::uuid7());
$name = new ClientName('Test Client');
$identifier = new ClientIdentifier('arcadia_id_' . str_repeat('a', 32));
$secret = new ClientSecret('arcadia_sec_' . str_repeat('b', 86));
$client = Client::hydrate(
id: $clientId,
name: $name,
identifier: $identifier,
secret: $secret,
isActive: true,
updatedAt: new \DateTimeImmutable('2024-01-01'),
);
$paginated = new Paginated(Pagination::ofPage(1, 10), [$client], 1);
$output = new Output($paginated);
$presenter = new FindClientPresenter();
// WHEN
$view = $presenter->present($output);
// THEN
expect($view)->toBeInstanceOf(FindClientView::class)
->and($view->total)->toBe(1)
->and($view->clients)->toHaveCount(1)
->and($view->clients[0]->id)->toBe($clientId->value)
->and($view->clients[0]->name)->toBe('Test Client')
->and($view->clients[0]->identifier)->toBe($identifier->value)
->and($view->clients[0]->isActive)->toBeTrue()
->and($view->clients[0]->display)->toBe("Test Client (Active) - {$identifier->value}");
});
});

View file

@ -0,0 +1,99 @@
<?php
declare(strict_types=1);
namespace Tests\Unit\Presentation\Console\Presenters\Clients;
use Application\Security\Models\ValueObjects\ClientIdentifier;
use Application\Security\Models\ValueObjects\ClientName;
use Application\Security\Models\ValueObjects\Entities\ClientId;
use Application\Security\Usecases\FindAllClients\Output;
use Application\Security\Usecases\FindAllClients\PaginatedClient;
use Presentation\Console\Presenters\Clients\ClientRow;
use Presentation\Console\Presenters\Clients\ListClientsPresenter;
use Presentation\Console\Presenters\Clients\ListClientsView;
use Ramsey\Uuid\Uuid;
covers(
ListClientsPresenter::class,
ListClientsView::class,
ClientRow::class,
);
describe('ListClientsPresenter Unit Tests', function (): void {
it('transforms Output into ListClientsView with formatted rows', function (): void {
// GIVEN
$clientId = new ClientId(Uuid::uuid7());
$identifier = new ClientIdentifier('arcadia_id_' . str_repeat('a', 32));
$name = new ClientName('Client 1');
$updatedAt = new \DateTimeImmutable('2024-01-15 10:30:00');
$client = new PaginatedClient(
id: $clientId,
identifier: $identifier,
name: $name,
isActive: true,
createdAt: new \DateTimeImmutable('2024-01-01 08:00:00'),
updatedAt: $updatedAt,
);
$output = new Output(
clients: [$client],
total: 1,
page: 1,
perPage: 25,
);
$presenter = new ListClientsPresenter();
// WHEN
$view = $presenter->present($output);
// THEN
expect($view)->toBeInstanceOf(ListClientsView::class)
->and($view->total)->toBe(1)
->and($view->page)->toBe(1)
->and($view->perPage)->toBe(25)
->and($view->totalPages)->toBe(1)
->and($view->rows)->toHaveCount(1)
->and($view->rows[0]->id)->toBe($clientId->value)
->and($view->rows[0]->name)->toBe('Client 1')
->and($view->rows[0]->identifier)->toBe($identifier->value)
->and($view->rows[0]->status)->toBe('Active')
->and($view->rows[0]->lastUpdatedAt)->toBe('2024-01-15 10:30:00');
});
it('handles inactive client status and calculates total pages correctly', function (): void {
// GIVEN
$clientId = new ClientId(Uuid::uuid7());
$identifier = new ClientIdentifier('arcadia_id_' . str_repeat('b', 32));
$name = new ClientName('Inactive Client');
$updatedAt = new \DateTimeImmutable('2024-01-15 10:30:00');
$client = new PaginatedClient(
id: $clientId,
identifier: $identifier,
name: $name,
isActive: false,
createdAt: new \DateTimeImmutable('2024-01-01 08:00:00'),
updatedAt: $updatedAt,
);
$output = new Output(
clients: [$client],
total: 55,
page: 1,
perPage: 25,
);
$presenter = new ListClientsPresenter();
// WHEN
$view = $presenter->present($output);
// THEN
expect($view)->toBeInstanceOf(ListClientsView::class)
->and($view->totalPages)->toBe(3)
->and($view->rows[0]->status)->toBe('Inactive');
});
});

View file

@ -0,0 +1,40 @@
<?php
declare(strict_types=1);
namespace Tests\Unit\Presentation\Console\Presenters\Clients;
use Application\Security\Models\ValueObjects\ClientIdentifier;
use Application\Security\Models\ValueObjects\ClientSecret;
use Application\Security\Usecases\RegenerateClientSecret\Output;
use Presentation\Console\Presenters\Clients\RegenerateClientSecretPresenter;
use Presentation\Console\Presenters\Clients\RegenerateClientSecretView;
covers(
RegenerateClientSecretPresenter::class,
RegenerateClientSecretView::class,
);
describe('RegenerateClientSecretPresenter Unit Tests', function (): void {
it('transforms Output into RegenerateClientSecretView', function (): void {
// GIVEN
$identifier = new ClientIdentifier('arcadia_id_' . str_repeat('a', 32));
$newSecret = new ClientSecret('arcadia_sec_' . str_repeat('c', 86));
$output = new Output(
newSecret: $newSecret,
clientIdentifier: $identifier,
);
$presenter = new RegenerateClientSecretPresenter();
// WHEN
$view = $presenter->present($output);
// THEN
expect($view)->toBeInstanceOf(RegenerateClientSecretView::class)
->and($view->identifier)->toBe($identifier->value)
->and($view->secret)->toBe($newSecret->value)
->and($view->message)->toBe("Secret for client '{$identifier->value}' regenerated successfully.");
});
});

View file

@ -0,0 +1,73 @@
<?php
declare(strict_types=1);
namespace Tests\Unit\Presentation\Console\Presenters\Clients;
use Application\Security\Models\ValueObjects\ClientIdentifier;
use Application\Security\Models\ValueObjects\ClientName;
use Application\Security\Models\ValueObjects\Entities\ClientId;
use Application\Security\Usecases\ToggleClientStatus\Output;
use Presentation\Console\Presenters\Clients\SetClientStatusPresenter;
use Presentation\Console\Presenters\Clients\ToggleClientStatusView;
use Ramsey\Uuid\Uuid;
covers(
SetClientStatusPresenter::class,
ToggleClientStatusView::class,
);
describe('SetClientStatusPresenter Unit Tests', function (): void {
it('transforms Output into ToggleClientStatusView for active status', function (): void {
// GIVEN
$clientId = new ClientId(Uuid::uuid7());
$identifier = new ClientIdentifier('arcadia_id_' . str_repeat('a', 32));
$name = new ClientName('Client 1');
$output = new Output(
clientId: $clientId,
clientIdentifier: $identifier,
clientName: $name,
isActive: true,
);
$presenter = new SetClientStatusPresenter();
// WHEN
$view = $presenter->present($output);
// THEN
expect($view)->toBeInstanceOf(ToggleClientStatusView::class)
->and($view->id)->toBe($clientId->value)
->and($view->name)->toBe('Client 1')
->and($view->identifier)->toBe($identifier->value)
->and($view->isActive)->toBeTrue()
->and($view->statusLabel)->toBe('Active')
->and($view->message)->toBe("Client 'Client 1' (ID: {$clientId->value}) is now Active.");
});
it('transforms Output into ToggleClientStatusView for inactive status', function (): void {
// GIVEN
$clientId = new ClientId(Uuid::uuid7());
$identifier = new ClientIdentifier('arcadia_id_' . str_repeat('b', 32));
$name = new ClientName('Client 2');
$output = new Output(
clientId: $clientId,
clientIdentifier: $identifier,
clientName: $name,
isActive: false,
);
$presenter = new SetClientStatusPresenter();
// WHEN
$view = $presenter->present($output);
// THEN
expect($view)->toBeInstanceOf(ToggleClientStatusView::class)
->and($view->isActive)->toBeFalse()
->and($view->statusLabel)->toBe('Inactive')
->and($view->message)->toBe("Client 'Client 2' (ID: {$clientId->value}) is now Inactive.");
});
});