diff --git a/src/Infra/Adapters/Directories/InMemoryEmployeeDirectory.php b/src/Infra/Adapters/Directories/InMemoryEmployeeDirectory.php new file mode 100644 index 0000000..52ec7a8 --- /dev/null +++ b/src/Infra/Adapters/Directories/InMemoryEmployeeDirectory.php @@ -0,0 +1,95 @@ + */ + private array $employees = []; + + public function add(Employee ...$employees): self + { + foreach ($employees as $employee) { + $this->employees[$employee->id()->value] = $employee; + } + + return $this; + } + + public function clear(): void + { + $this->employees = []; + } + + #[\Override] + public function find(ArcadiaEmployeeId $id): ?Employee + { + return $this->employees[$id->value] ?? null; + } + + #[\Override] + public function findAll( + Pagination $pagination, + EmployeeSorter $sorter, + ?EmployeeFilter $filter = null, + ): Paginated { + $filtered = array_values($this->employees); + + if ($filter !== null) { + $filtered = array_filter($filtered, static function (Employee $employee) use ($filter): bool { + if ($filter->establishment !== null && $employee->establishment->value !== $filter->establishment->value) { + return false; + } + + if (! empty($filter->sites)) { + $siteValues = array_map(static fn ($site): string => $site->value, $filter->sites); + if (! in_array($employee->site->value, $siteValues, true)) { + return false; + } + } + + if ($filter->service !== null && $employee->service->value !== $filter->service->value) { + return false; + } + + if ($filter->since !== null && $employee->startDate < $filter->since) { + return false; + } + + return true; + }); + $filtered = array_values($filtered); + } + + usort($filtered, static function (Employee $a, Employee $b) use ($sorter): int { + $cmp = match ($sorter->sortBy) { + EmployeeSortColumn::arcadiaId => strcmp($a->publicEmployeeNumber->value, $b->publicEmployeeNumber->value), + EmployeeSortColumn::startDate => $a->startDate <=> $b->startDate, + EmployeeSortColumn::endDate => $a->endDate <=> $b->endDate, + EmployeeSortColumn::plannedEndDate => $a->plannedEndDate <=> $b->plannedEndDate, + EmployeeSortColumn::createdAt => $a->createdAt <=> $b->createdAt, + EmployeeSortColumn::updatedAt => $a->updatedAt <=> $b->updatedAt, + }; + + return $sorter->direction === \SortDirection::Ascending ? $cmp : -$cmp; + }); + + $total = count($filtered); + $offset = $pagination->from(); + $limit = $pagination->pageSize(); + $paged = array_slice($filtered, $offset, $limit); + + return new Paginated($pagination, $paged, $total); + } +} diff --git a/src/Infra/Adapters/Directories/InMemoryIncoCompositionDirectory.php b/src/Infra/Adapters/Directories/InMemoryIncoCompositionDirectory.php new file mode 100644 index 0000000..fe67d92 --- /dev/null +++ b/src/Infra/Adapters/Directories/InMemoryIncoCompositionDirectory.php @@ -0,0 +1,48 @@ + */ + private array $compositions = []; + + public function add(IncoComposition ...$compositions): self + { + foreach ($compositions as $composition) { + $this->compositions[$composition->id->value] = $composition; + } + + return $this; + } + + public function clear(): void + { + $this->compositions = []; + } + + #[\Override] + public function find(IncoCompositionId $id): ?IncoComposition + { + return $this->compositions[$id->value] ?? null; + } + + #[\Override] + public function findByIncoId(IncoId $incoId): ?IncoComposition + { + foreach ($this->compositions as $composition) { + if ($composition->idInco->equals($incoId)) { + return $composition; + } + } + + return null; + } +} diff --git a/src/Infra/Adapters/Directories/InMemoryIncoSheetDirectory.php b/src/Infra/Adapters/Directories/InMemoryIncoSheetDirectory.php new file mode 100644 index 0000000..f11349d --- /dev/null +++ b/src/Infra/Adapters/Directories/InMemoryIncoSheetDirectory.php @@ -0,0 +1,115 @@ + */ + private array $sheets = []; + + public function add(IncoSheet ...$sheets): self + { + foreach ($sheets as $sheet) { + $this->sheets[(string) $sheet->id->value] = $sheet; + } + + return $this; + } + + public function clear(): void + { + $this->sheets = []; + } + + #[\Override] + public function find(IncoId $id): ?IncoSheet + { + return $this->sheets[(string) $id->value] ?? null; + } + + #[\Override] + public function findAll( + Pagination $pagination, + IncoSheetSorter $sorter, + ?IncoSheetFilter $filter = null, + ): Paginated { + $filtered = array_values($this->sheets); + + if ($filter !== null) { + $filtered = array_filter($filtered, static function (IncoSheet $sheet) use ($filter): bool { + if ($filter->since !== null) { + $createdAtMatches = $sheet->createdAt !== null && $sheet->createdAt >= $filter->since; + $updatedAtMatches = $sheet->updatedAt !== null && $sheet->updatedAt >= $filter->since; + if (! $createdAtMatches && ! $updatedAtMatches) { + return false; + } + } + + if (! empty($filter->brands) && ! in_array($sheet->brand->code, $filter->brands, true)) { + return false; + } + + return true; + }); + $filtered = array_values($filtered); + } + + usort($filtered, static function (IncoSheet $a, IncoSheet $b) use ($sorter): int { + $cmp = match ($sorter->sortBy) { + IncoSheetSortColumn::incoSheetId => (int) $a->id->value <=> (int) $b->id->value, + IncoSheetSortColumn::createdAt => $a->createdAt <=> $b->createdAt, + IncoSheetSortColumn::updatedAt => $a->updatedAt <=> $b->updatedAt, + }; + + return $sorter->direction === \SortDirection::Ascending ? $cmp : -$cmp; + }); + + $total = count($filtered); + $offset = $pagination->from(); + $limit = $pagination->pageSize(); + $paged = array_slice($filtered, $offset, $limit); + + return new Paginated($pagination, $paged, $total); + } + + #[\Override] + public function search( + IncoIdQuery $query, + Pagination $pagination, + string ...$brands, + ): Paginated { + $regex = '/^' . str_replace('%', '.*', preg_quote($query->value, '/')) . '$/'; + + $filtered = array_filter($this->sheets, static function (IncoSheet $sheet) use ($regex, $brands): bool { + if (! preg_match($regex, (string) $sheet->id->value)) { + return false; + } + + if (! empty($brands) && ! in_array($sheet->brand->code, $brands, true)) { + return false; + } + + return true; + }); + + $filtered = array_values($filtered); + $total = count($filtered); + $offset = $pagination->from(); + $limit = $pagination->pageSize(); + $paged = array_slice($filtered, $offset, $limit); + + return new Paginated($pagination, $paged, $total); + } +} diff --git a/src/Infra/Adapters/Directories/InMemoryIncoShortSheetDirectory.php b/src/Infra/Adapters/Directories/InMemoryIncoShortSheetDirectory.php new file mode 100644 index 0000000..85ab1a5 --- /dev/null +++ b/src/Infra/Adapters/Directories/InMemoryIncoShortSheetDirectory.php @@ -0,0 +1,103 @@ + */ + private array $sheets = []; + + public function add(IncoShortSheet ...$sheets): self + { + foreach ($sheets as $sheet) { + $this->sheets[(string) $sheet->id->value] = $sheet; + } + + return $this; + } + + public function clear(): void + { + $this->sheets = []; + } + + #[\Override] + public function find(IncoId $id): ?IncoShortSheet + { + return $this->sheets[(string) $id->value] ?? null; + } + + #[\Override] + public function findAll( + Pagination $pagination, + IncoSheetSorter $sorter, + ?IncoSheetFilter $filter = null, + ): Paginated { + $filtered = array_values($this->sheets); + + if ($filter !== null) { + $filtered = array_filter($filtered, static function (IncoShortSheet $sheet) use ($filter): bool { + if ($filter->since !== null) { + $createdAtMatches = $sheet->createdAt !== null && $sheet->createdAt >= $filter->since; + $updatedAtMatches = $sheet->updatedAt !== null && $sheet->updatedAt >= $filter->since; + if (! $createdAtMatches && ! $updatedAtMatches) { + return false; + } + } + + return true; + }); + $filtered = array_values($filtered); + } + + usort($filtered, static function (IncoShortSheet $a, IncoShortSheet $b) use ($sorter): int { + $cmp = match ($sorter->sortBy) { + IncoSheetSortColumn::incoSheetId => (int) $a->id->value <=> (int) $b->id->value, + IncoSheetSortColumn::createdAt => $a->createdAt <=> $b->createdAt, + IncoSheetSortColumn::updatedAt => $a->updatedAt <=> $b->updatedAt, + }; + + return $sorter->direction === \SortDirection::Ascending ? $cmp : -$cmp; + }); + + $total = count($filtered); + $offset = $pagination->from(); + $limit = $pagination->pageSize(); + $paged = array_slice($filtered, $offset, $limit); + + return new Paginated($pagination, $paged, $total); + } + + #[\Override] + public function search( + IncoIdQuery $query, + Pagination $pagination, + string ...$brands, + ): Paginated { + $regex = '/^' . str_replace('%', '.*', preg_quote($query->value, '/')) . '$/'; + + $filtered = array_filter($this->sheets, static function (IncoShortSheet $sheet) use ($regex): bool { + return (bool) preg_match($regex, (string) $sheet->id->value); + }); + + $filtered = array_values($filtered); + $total = count($filtered); + $offset = $pagination->from(); + $limit = $pagination->pageSize(); + $paged = array_slice($filtered, $offset, $limit); + + return new Paginated($pagination, $paged, $total); + } +} diff --git a/src/Infra/Adapters/Repositories/InMemoryClientRepository.php b/src/Infra/Adapters/Repositories/InMemoryClientRepository.php new file mode 100644 index 0000000..7546945 --- /dev/null +++ b/src/Infra/Adapters/Repositories/InMemoryClientRepository.php @@ -0,0 +1,74 @@ + */ + private array $clients = []; + + public function add(Client ...$clients): self + { + foreach ($clients as $client) { + $this->clients[$client->id->value] = $client; + } + + return $this; + } + + public function clear(): void + { + $this->clients = []; + } + + #[\Override] + public function find(ClientId $id): ?Client + { + return $this->clients[$id->value] ?? null; + } + + #[\Override] + public function persist(Client $client): void + { + $this->clients[$client->id->value] = $client; + } + + #[\Override] + public function delete(Client $client): void + { + unset($this->clients[$client->id->value]); + } + + #[\Override] + public function findByIdentifier(ClientIdentifier $identifier): ?Client + { + foreach ($this->clients as $client) { + if ($client->identifier->equals($identifier)) { + return $client; + } + } + + return null; + } + + #[\Override] + public function findAll(Pagination $pagination): Paginated + { + $items = array_values($this->clients); + $total = count($items); + $offset = $pagination->from(); + $limit = $pagination->pageSize(); + $paged = array_slice($items, $offset, $limit); + + return new Paginated($pagination, $paged, $total); + } +} diff --git a/tests/Unit/Application/Arcadia/Usecase/Employees/FindAllEmployeesTest.php b/tests/Unit/Application/Arcadia/Usecase/Employees/FindAllEmployeesTest.php index ca306be..6bfa95d 100644 --- a/tests/Unit/Application/Arcadia/Usecase/Employees/FindAllEmployeesTest.php +++ b/tests/Unit/Application/Arcadia/Usecase/Employees/FindAllEmployeesTest.php @@ -7,6 +7,7 @@ namespace Tests\Unit\Application\Arcadia\Usecase\Employees; use Application\Arcadia\Usecase\Employees\FindAll\FindAllEmployees; use Application\Arcadia\Usecase\Employees\FindAll\Input; use Application\Arcadia\Usecase\Employees\FindAll\Output; +use Application\Arcadia\Usecase\Employees\FindAll\PaginatedEmployee; use Domain\Models\Entities\Employee; use Domain\Models\ValueObjects\Employees\ArcadiaEmployeeId; use Domain\Models\ValueObjects\Employees\EmployeeSortColumn; @@ -14,24 +15,25 @@ use Domain\Models\ValueObjects\Employees\EmployeeSorter; use Domain\Models\ValueObjects\EstablishmentCode; use Domain\Models\ValueObjects\ServiceId; use Domain\Models\ValueObjects\SiteId; -use Domain\Ports\Directories\EmployeeDirectory; -use Shared\Paginated; +use Infra\Adapters\Directories\InMemoryEmployeeDirectory; use Shared\Pagination; -covers(FindAllEmployees::class); +covers( + FindAllEmployees::class, + Input::class, + Output::class, + PaginatedEmployee::class, +); describe('FindAllEmployees Usecase Unit Tests', function (): void { - it('executes successfully and returns paginated output DTO', function (): void { - // GIVEN - $pagination = Pagination::ofPage(1, 10); - $sorter = new EmployeeSorter( - EmployeeSortColumn::arcadiaId, - \SortDirection::Ascending - ); - $input = new Input($pagination, $sorter); - $employeeDirectory = \Mockery::mock(EmployeeDirectory::class); - $usecase = new FindAllEmployees($employeeDirectory); + $directory = new InMemoryEmployeeDirectory(); + beforeEach(function () use ($directory): void { + $directory->clear(); + }); + + it('executes successfully and returns paginated output DTO', function () use ($directory): void { + // GIVEN $employeeId = new ArcadiaEmployeeId('01155902'); $employee = new Employee( publicEmployeeNumber: $employeeId, @@ -47,16 +49,14 @@ describe('FindAllEmployees Usecase Unit Tests', function (): void { updatedAt: null, ); - $paginatedResult = new Paginated($pagination, [$employee], 1); - $employeeDirectory->shouldReceive('findAll') - ->once() - ->withArgs(function (Pagination $actualPagination, EmployeeSorter $actualSorter, mixed $actualFilter) use ($pagination): bool { - return $actualPagination === $pagination - && $actualSorter->sortBy === EmployeeSortColumn::arcadiaId - && $actualSorter->direction === \SortDirection::Ascending - && $actualFilter === null; - }) - ->andReturn($paginatedResult); + $directory->add($employee); + $usecase = new FindAllEmployees($directory); + $pagination = Pagination::ofPage(1, 10); + $sorter = new EmployeeSorter( + EmployeeSortColumn::arcadiaId, + \SortDirection::Ascending + ); + $input = new Input($pagination, $sorter); // WHEN $result = $usecase->execute($input); diff --git a/tests/Unit/Application/Arcadia/Usecase/Employees/FindEmployeeTest.php b/tests/Unit/Application/Arcadia/Usecase/Employees/FindEmployeeTest.php index eb92261..61bb007 100644 --- a/tests/Unit/Application/Arcadia/Usecase/Employees/FindEmployeeTest.php +++ b/tests/Unit/Application/Arcadia/Usecase/Employees/FindEmployeeTest.php @@ -13,19 +13,25 @@ use Domain\Models\ValueObjects\Employees\ArcadiaEmployeeId; use Domain\Models\ValueObjects\EstablishmentCode; use Domain\Models\ValueObjects\ServiceId; use Domain\Models\ValueObjects\SiteId; -use Domain\Ports\Directories\EmployeeDirectory; +use Infra\Adapters\Directories\InMemoryEmployeeDirectory; -covers(FindEmployee::class); +covers( + FindEmployee::class, + Input::class, + Output::class, +); describe('FindEmployee Usecase Unit Tests', function (): void { - it('executes successfully and returns Output when employee exists', function (): void { - // GIVEN - $employeeId = new ArcadiaEmployeeId('01155902'); - $input = new Input($employeeId); - $employeeDirectory = \Mockery::mock(EmployeeDirectory::class); - $usecase = new FindEmployee($employeeDirectory); + $directory = new InMemoryEmployeeDirectory(); - $employee = new Employee( + beforeEach(function () use ($directory): void { + $directory->clear(); + }); + + it('executes successfully and returns Output when employee exists', function () use ($directory): void { + // GIVEN + $employeeId = new ArcadiaEmployeeId('01155902'); + $employee = new Employee( publicEmployeeNumber: $employeeId, lastName: 'DOE', firstName: 'John', @@ -39,10 +45,9 @@ describe('FindEmployee Usecase Unit Tests', function (): void { updatedAt: null, ); - $employeeDirectory->shouldReceive('find') - ->once() - ->with($employeeId) - ->andReturn($employee); + $directory->add($employee); + $usecase = new FindEmployee($directory); + $input = new Input($employeeId); // WHEN $result = $usecase->execute($input); @@ -57,20 +62,16 @@ describe('FindEmployee Usecase Unit Tests', function (): void { ->and($result->service)->toBe('097'); }); - it('throws NotFoundException when employee does not exist', function (): void { + it('throws NotFoundException when employee does not exist', function () use ($directory): void { // GIVEN - $employeeId = new ArcadiaEmployeeId('01155902'); - $input = new Input($employeeId); - $employeeDirectory = \Mockery::mock(EmployeeDirectory::class); - $usecase = new FindEmployee($employeeDirectory); + $employeeId = new ArcadiaEmployeeId('01155902'); + $usecase = new FindEmployee($directory); + $input = new Input($employeeId); - $employeeDirectory->shouldReceive('find') - ->once() - ->with($employeeId) - ->andReturnNull(); + // WHEN + $execution = fn () => $usecase->execute($input); - // WHEN & THEN - expect(fn () => $usecase->execute($input)) - ->toThrow(NotFoundException::class, "Employee '01155902' not found."); + // THEN + expect($execution)->toThrow(NotFoundException::class, "Employee '01155902' not found."); }); }); diff --git a/tests/Unit/Application/Arcadia/Usecase/IncoCompositions/FindIncoCompositionTest.php b/tests/Unit/Application/Arcadia/Usecase/IncoCompositions/FindIncoCompositionTest.php index a9a44f2..8d65b3b 100644 --- a/tests/Unit/Application/Arcadia/Usecase/IncoCompositions/FindIncoCompositionTest.php +++ b/tests/Unit/Application/Arcadia/Usecase/IncoCompositions/FindIncoCompositionTest.php @@ -29,17 +29,26 @@ 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 Domain\Ports\Directories\IncoCompositionDirectory; -use Mockery; +use Infra\Adapters\Directories\InMemoryIncoCompositionDirectory; -covers(FindIncoComposition::class); +covers( + FindIncoComposition::class, + Input::class, + Output::class, +); describe('FindIncoComposition Usecase Unit Tests', function (): void { - it('executes successfully and returns Output when composition exists', function (): void { - // GIVEN - $incoId = new IncoId('12345'); - $compoId = new IncoCompositionId('67890'); - $identification = new Identification('Poulet Rôti 1kg Test', '12345', 'LDC'); + $directory = new InMemoryIncoCompositionDirectory(); + + beforeEach(function () use ($directory): void { + $directory->clear(); + }); + + function createComposition(string $compoIdStr, string $incoIdStr): IncoComposition + { + $compoId = new IncoCompositionId($compoIdStr); + $incoId = new IncoId($incoIdStr); + $identification = new Identification('Poulet Rôti 1kg Test', $incoIdStr, 'LDC'); $energyValues = new EnergyValues('100g', '0', '0'); $energy = new Energy($energyValues, $energyValues); @@ -63,7 +72,7 @@ describe('FindIncoComposition Usecase Unit Tests', function (): void { $amino = new AminoAcids('0', '0', '0'); $special = new SpecialCompounds('0', '0', '0', '0'); - $composition = new IncoComposition( + return new IncoComposition( id: $compoId, idInco: $incoId, identification: $identification, @@ -81,9 +90,13 @@ describe('FindIncoComposition Usecase Unit Tests', function (): void { aminoAcids: $amino, specialCompounds: $special, ); + } - $directory = Mockery::mock(IncoCompositionDirectory::class); - $directory->shouldReceive('findByIncoId')->with($incoId)->andReturn($composition); + it('executes successfully and returns Output when composition exists', function () use ($directory): void { + // GIVEN + $incoId = new IncoId('12345'); + $composition = createComposition('67890', '12345'); + $directory->add($composition); $usecase = new FindIncoComposition($directory); $input = new Input($incoId); @@ -93,17 +106,13 @@ describe('FindIncoComposition Usecase Unit Tests', function (): void { // THEN expect($output)->toBeInstanceOf(Output::class) - ->and($output->id)->toBe($compoId) - ->and($output->idInco)->toBe($incoId); + ->and($output->id->value)->toBe('67890') + ->and($output->idInco->value)->toBe('12345'); }); - it('throws NotFoundException when composition does not exist', function (): void { + it('throws NotFoundException when composition does not exist', function () use ($directory): void { // GIVEN - $incoId = new IncoId('99999'); - - $directory = Mockery::mock(IncoCompositionDirectory::class); - $directory->shouldReceive('findByIncoId')->with($incoId)->andReturn(null); - + $incoId = new IncoId('99999'); $usecase = new FindIncoComposition($directory); $input = new Input($incoId); diff --git a/tests/Unit/Application/Arcadia/Usecase/Incosheets/FindAllIncoSheetsTest.php b/tests/Unit/Application/Arcadia/Usecase/Incosheets/FindAllIncoSheetsTest.php index 6447fef..abbab84 100644 --- a/tests/Unit/Application/Arcadia/Usecase/Incosheets/FindAllIncoSheetsTest.php +++ b/tests/Unit/Application/Arcadia/Usecase/Incosheets/FindAllIncoSheetsTest.php @@ -7,6 +7,8 @@ namespace Tests\Unit\Application\Arcadia\Usecase\IncoSheets; use Application\Arcadia\Usecase\IncoSheets\FindAll\FindAllIncoSheets; use Application\Arcadia\Usecase\IncoSheets\FindAll\Input; use Application\Arcadia\Usecase\IncoSheets\FindAll\Output; +use Application\Arcadia\Usecase\IncoSheets\FindAll\PaginatedIncoSheet; +use Application\Arcadia\Usecase\IncoSheets\FindAll\PaginatedShortIncoSheet; use Domain\Models\Entities\Inco\IncoSheet; use Domain\Models\ValueObjects\Inco\Allegations; use Domain\Models\ValueObjects\Inco\Brand; @@ -14,23 +16,29 @@ use Domain\Models\ValueObjects\Inco\IncoId; use Domain\Models\ValueObjects\Inco\IncoSheetSortColumn; use Domain\Models\ValueObjects\Inco\IncoSheetSorter; use Domain\Models\ValueObjects\Inco\Ingredients; -use Domain\Ports\Directories\IncoSheetDirectory; -use Domain\Ports\Directories\IncoShortSheetDirectory; -use Shared\Paginated; +use Infra\Adapters\Directories\InMemoryIncoSheetDirectory; +use Infra\Adapters\Directories\InMemoryIncoShortSheetDirectory; use Shared\Pagination; -covers(FindAllIncoSheets::class); +covers( + FindAllIncoSheets::class, + Input::class, + Output::class, + PaginatedIncoSheet::class, + PaginatedShortIncoSheet::class, +); describe('FindAllIncoSheets Usecase Unit Tests', function (): void { - it('executes successfully and returns paginated output DTO', function (): void { - // GIVEN - $pagination = Pagination::ofPage(1, 10); - $sorter = new IncoSheetSorter(IncoSheetSortColumn::incoSheetId, \SortDirection::Ascending); - $input = new Input($pagination, $sorter); - $incoDirectory = \Mockery::mock(IncoSheetDirectory::class); - $shortIncoDirectory = \Mockery::mock(IncoShortSheetDirectory::class); - $usecase = new FindAllIncoSheets($incoDirectory, $shortIncoDirectory); + $directory = new InMemoryIncoSheetDirectory(); + $shortDirectory = new InMemoryIncoShortSheetDirectory(); + beforeEach(function () use ($directory, $shortDirectory): void { + $directory->clear(); + $shortDirectory->clear(); + }); + + it('executes successfully and returns paginated output DTO', function () use ($directory, $shortDirectory): void { + // GIVEN $incoId = new IncoId('12345'); $incoSheet = new IncoSheet( id: $incoId, @@ -56,16 +64,11 @@ describe('FindAllIncoSheets Usecase Unit Tests', function (): void { updatedAt: null, ); - $paginatedResult = new Paginated($pagination, [$incoSheet], 1); - $incoDirectory->shouldReceive('findAll') - ->once() - ->withArgs(function (Pagination $actualPagination, IncoSheetSorter $actualSorter, mixed $actualFilter) use ($pagination): bool { - return $actualPagination === $pagination - && $actualSorter->sortBy === IncoSheetSortColumn::incoSheetId - && $actualSorter->direction === \SortDirection::Ascending - && $actualFilter === null; - }) - ->andReturn($paginatedResult); + $directory->add($incoSheet); + $usecase = new FindAllIncoSheets($directory, $shortDirectory); + $pagination = Pagination::ofPage(1, 10); + $sorter = new IncoSheetSorter(IncoSheetSortColumn::incoSheetId, \SortDirection::Ascending); + $input = new Input($pagination, $sorter); // WHEN $result = $usecase->execute($input); diff --git a/tests/Unit/Application/Arcadia/Usecase/Incosheets/FindIncoSheetTest.php b/tests/Unit/Application/Arcadia/Usecase/Incosheets/FindIncoSheetTest.php index 7de1234..386a538 100644 --- a/tests/Unit/Application/Arcadia/Usecase/Incosheets/FindIncoSheetTest.php +++ b/tests/Unit/Application/Arcadia/Usecase/Incosheets/FindIncoSheetTest.php @@ -13,18 +13,24 @@ use Domain\Models\ValueObjects\Inco\Allegations; use Domain\Models\ValueObjects\Inco\Brand; use Domain\Models\ValueObjects\Inco\IncoId; use Domain\Models\ValueObjects\Inco\Ingredients; -use Domain\Ports\Directories\IncoSheetDirectory; +use Infra\Adapters\Directories\InMemoryIncoSheetDirectory; -covers(FindIncoSheet::class); +covers( + FindIncoSheet::class, + Input::class, + Output::class, +); describe('FindIncoSheet Usecase Unit Tests', function (): void { - it('executes successfully and returns Output when inco sheet exists', function (): void { - // GIVEN - $incoId = new IncoId('12345'); - $input = new Input($incoId); - $incoDirectory = \Mockery::mock(IncoSheetDirectory::class); - $usecase = new FindIncoSheet($incoDirectory); + $directory = new InMemoryIncoSheetDirectory(); + beforeEach(function () use ($directory): void { + $directory->clear(); + }); + + it('executes successfully and returns Output when inco sheet exists', function () use ($directory): void { + // GIVEN + $incoId = new IncoId('12345'); $incoSheet = new IncoSheet( id: $incoId, name: 'Poulet Rôti 1kg', @@ -49,10 +55,9 @@ describe('FindIncoSheet Usecase Unit Tests', function (): void { updatedAt: null, ); - $incoDirectory->shouldReceive('find') - ->once() - ->with($incoId) - ->andReturn($incoSheet); + $directory->add($incoSheet); + $usecase = new FindIncoSheet($directory); + $input = new Input($incoId); // WHEN $result = $usecase->execute($input); @@ -66,17 +71,11 @@ describe('FindIncoSheet Usecase Unit Tests', function (): void { ->and($result->isSeasonal)->toBeTrue(); }); - it('throws NotFoundException when inco sheet does not exist', function (): void { + it('throws NotFoundException when inco sheet does not exist', function () use ($directory): void { // GIVEN - $incoId = new IncoId('99999'); - $input = new Input($incoId); - $incoDirectory = \Mockery::mock(IncoSheetDirectory::class); - $usecase = new FindIncoSheet($incoDirectory); - - $incoDirectory->shouldReceive('find') - ->once() - ->with($incoId) - ->andReturnNull(); + $incoId = new IncoId('99999'); + $usecase = new FindIncoSheet($directory); + $input = new Input($incoId); // WHEN & THEN expect(fn () => $usecase->execute($input)) diff --git a/tests/Unit/Application/Arcadia/Usecase/Incosheets/SearchIncoSheetsTest.php b/tests/Unit/Application/Arcadia/Usecase/Incosheets/SearchIncoSheetsTest.php new file mode 100644 index 0000000..86b0f27 --- /dev/null +++ b/tests/Unit/Application/Arcadia/Usecase/Incosheets/SearchIncoSheetsTest.php @@ -0,0 +1,110 @@ +clear(); + $shortDirectory->clear(); + }); + + it('searches full inco sheets matching query and brands', function () use ($directory, $shortDirectory): void { + // GIVEN + $incoSheet = new IncoSheet( + id: new IncoId('12345'), + name: 'Poulet Rôti 1kg', + label: 'Poulet Rôti Classe A', + description: 'Poulet prêt à cuire', + allegations: new Allegations(['Volaille française']), + sellArguments: 'Délicieux', + preparationAdvices: 'Cuire 45min à 180°C', + tips: null, + sellFamilly: 'Boucherie', + ingredients: new Ingredients(['Ingrédients' => ['Viande de dinde (66%)']]), + portions: null, + allergens: [], + brand: new Brand('M01', 'Le Gaulois'), + isSeasonal: true, + weight: null, + nutriscore: null, + storageCondition: null, + additives: [], + compo: null, + createdAt: null, + updatedAt: null, + ); + + $directory->add($incoSheet); + $usecase = new SearchIncoSheets($directory, $shortDirectory); + $input = new Input( + query: new IncoIdQuery('12%'), + short: false, + pagination: Pagination::ofPage(1, 10), + brands: 'M01', + ); + + // WHEN + $result = $usecase->execute($input); + + // THEN + expect($result)->toBeInstanceOf(Output::class) + ->and($result->total)->toBe(1) + ->and($result->incoSheets)->toHaveCount(1); + }); + + it('searches short inco sheets when short option is true', function () use ($directory, $shortDirectory): void { + // GIVEN + $shortSheet = new IncoShortSheet( + id: new IncoId('12345'), + name: 'Poulet Rôti 1kg', + label: 'Poulet Rôti Classe A', + createdAt: null, + updatedAt: null, + ); + + $shortDirectory->add($shortSheet); + $usecase = new SearchIncoSheets($directory, $shortDirectory); + $input = new Input( + query: new IncoIdQuery('12%'), + short: true, + pagination: Pagination::ofPage(1, 10), + ); + + // WHEN + $result = $usecase->execute($input); + + // THEN + expect($result)->toBeInstanceOf(Output::class) + ->and($result->total)->toBe(1) + ->and($result->incoSheets)->toHaveCount(1); + }); +}); diff --git a/tests/Unit/Infra/Adapters/Directories/InMemoryEmployeeDirectoryTest.php b/tests/Unit/Infra/Adapters/Directories/InMemoryEmployeeDirectoryTest.php new file mode 100644 index 0000000..9a2e0db --- /dev/null +++ b/tests/Unit/Infra/Adapters/Directories/InMemoryEmployeeDirectoryTest.php @@ -0,0 +1,122 @@ +add($employee); + + // WHEN + $found = $directory->find($employeeId); + $missing = $directory->find(new ArcadiaEmployeeId('99999999')); + + // THEN + expect($found)->toBe($employee) + ->and($missing)->toBeNull(); + }); + + it('filters, sorts and paginates employees correctly', function (): void { + // GIVEN + $directory = new InMemoryEmployeeDirectory(); + + $emp1 = new Employee( + publicEmployeeNumber: new ArcadiaEmployeeId('00000001'), + lastName: 'ALICE', + firstName: 'A', + startDate: new \DateTimeImmutable('2024-01-01'), + endDate: null, + plannedEndDate: null, + establishment: new EstablishmentCode('EST01'), + site: new SiteId('SITEO'), + service: new ServiceId('097'), + createdAt: new \DateTimeImmutable('2024-01-01'), + updatedAt: null, + ); + + $emp2 = new Employee( + publicEmployeeNumber: new ArcadiaEmployeeId('00000002'), + lastName: 'BOB', + firstName: 'B', + startDate: new \DateTimeImmutable('2024-02-01'), + endDate: null, + plannedEndDate: null, + establishment: new EstablishmentCode('EST02'), + site: new SiteId('SITETWO'), + service: new ServiceId('098'), + createdAt: new \DateTimeImmutable('2024-02-01'), + updatedAt: null, + ); + + $directory->add($emp1, $emp2); + + $pagination = Pagination::ofPage(1, 10); + $sorter = new EmployeeSorter(EmployeeSortColumn::arcadiaId, \SortDirection::Descending); + $filter = new EmployeeFilter(establishment: new EstablishmentCode('EST01')); + + // WHEN + $result = $directory->findAll($pagination, $sorter, $filter); + + // THEN + expect($result->getTotal())->toBe(1) + ->and($result->getItems())->toHaveCount(1) + ->and($result->getItems()[0]->publicEmployeeNumber->value)->toBe('00000001'); + }); + + it('clears all stored employees', function (): void { + // GIVEN + $directory = new InMemoryEmployeeDirectory(); + $employeeId = new ArcadiaEmployeeId('01155902'); + $employee = new Employee( + publicEmployeeNumber: $employeeId, + lastName: 'DOE', + firstName: 'John', + startDate: new \DateTimeImmutable('2024-01-01'), + endDate: null, + plannedEndDate: null, + establishment: new EstablishmentCode('EST01'), + site: new SiteId('LDCSER'), + service: new ServiceId('097'), + createdAt: new \DateTimeImmutable('2024-01-01'), + updatedAt: null, + ); + $directory->add($employee); + + // WHEN + $directory->clear(); + + // THEN + expect($directory->find($employeeId))->toBeNull(); + }); +}); diff --git a/tests/Unit/Infra/Adapters/Directories/InMemoryIncoCompositionDirectoryTest.php b/tests/Unit/Infra/Adapters/Directories/InMemoryIncoCompositionDirectoryTest.php new file mode 100644 index 0000000..f2af456 --- /dev/null +++ b/tests/Unit/Infra/Adapters/Directories/InMemoryIncoCompositionDirectoryTest.php @@ -0,0 +1,105 @@ +add($composition); + + // WHEN + $foundById = $directory->find(new IncoCompositionId('67890')); + $foundByIncoId = $directory->findByIncoId(new IncoId('12345')); + $notFound = $directory->findByIncoId(new IncoId('99999')); + + // THEN + expect($foundById)->toBe($composition) + ->and($foundByIncoId)->toBe($composition) + ->and($notFound)->toBeNull(); + }); + + it('clears all compositions', function (): void { + // GIVEN + $directory = new InMemoryIncoCompositionDirectory(); + $composition = createComposition('67890', '12345'); + $directory->add($composition); + + // WHEN + $directory->clear(); + + // THEN + expect($directory->find(new IncoCompositionId('67890')))->toBeNull(); + }); +}); diff --git a/tests/Unit/Infra/Adapters/Directories/InMemoryIncoSheetDirectoryTest.php b/tests/Unit/Infra/Adapters/Directories/InMemoryIncoSheetDirectoryTest.php new file mode 100644 index 0000000..b9f9598 --- /dev/null +++ b/tests/Unit/Infra/Adapters/Directories/InMemoryIncoSheetDirectoryTest.php @@ -0,0 +1,115 @@ + ['Test']]), + portions: null, + allergens: [], + brand: new Brand($brandCode, $brandLabel), + isSeasonal: true, + weight: null, + nutriscore: null, + storageCondition: null, + additives: [], + compo: null, + createdAt: new \DateTimeImmutable('2024-01-01'), + updatedAt: new \DateTimeImmutable('2024-01-02'), + ); + } + + it('finds an inco sheet by id', function (): void { + // GIVEN + $directory = new InMemoryIncoSheetDirectory(); + $sheet = createSheet('12345', 'M01', 'Le Gaulois'); + $directory->add($sheet); + + // WHEN + $found = $directory->find(new IncoId('12345')); + $missing = $directory->find(new IncoId('99999')); + + // THEN + expect($found)->toBe($sheet) + ->and($missing)->toBeNull(); + }); + + it('filters, sorts and paginates inco sheets', function (): void { + // GIVEN + $directory = new InMemoryIncoSheetDirectory(); + $sheet1 = createSheet('10001', 'M01', 'Le Gaulois'); + $sheet2 = createSheet('10002', 'M02', 'Maître CoQ'); + $directory->add($sheet1, $sheet2); + + $pagination = Pagination::ofPage(1, 10); + $sorter = new IncoSheetSorter(IncoSheetSortColumn::incoSheetId, \SortDirection::Descending); + $filter = new IncoSheetFilter(null, 'M01'); + + // WHEN + $result = $directory->findAll($pagination, $sorter, $filter); + + // THEN + expect($result->getTotal())->toBe(1) + ->and($result->getItems())->toHaveCount(1) + ->and((string) $result->getItems()[0]->id->value)->toBe('10001'); + }); + + it('searches inco sheets by id query and brands', function (): void { + // GIVEN + $directory = new InMemoryIncoSheetDirectory(); + $sheet1 = createSheet('12345', 'M01', 'Le Gaulois'); + $sheet2 = createSheet('12999', 'M01', 'Le Gaulois'); + $sheet3 = createSheet('54321', 'M02', 'Maître CoQ'); + $directory->add($sheet1, $sheet2, $sheet3); + + $query = new IncoIdQuery('12%'); + $pagination = Pagination::ofPage(1, 10); + + // WHEN + $result = $directory->search($query, $pagination, 'M01'); + + // THEN + expect($result->getTotal())->toBe(2) + ->and($result->getItems())->toHaveCount(2); + }); + + it('clears all sheets', function (): void { + // GIVEN + $directory = new InMemoryIncoSheetDirectory(); + $sheet = createSheet('12345', 'M01', 'Le Gaulois'); + $directory->add($sheet); + + // WHEN + $directory->clear(); + + // THEN + expect($directory->find(new IncoId('12345')))->toBeNull(); + }); +}); diff --git a/tests/Unit/Infra/Adapters/Directories/InMemoryIncoShortSheetDirectoryTest.php b/tests/Unit/Infra/Adapters/Directories/InMemoryIncoShortSheetDirectoryTest.php new file mode 100644 index 0000000..6e731d5 --- /dev/null +++ b/tests/Unit/Infra/Adapters/Directories/InMemoryIncoShortSheetDirectoryTest.php @@ -0,0 +1,96 @@ +add($sheet); + + // WHEN + $found = $directory->find(new IncoId('12345')); + $missing = $directory->find(new IncoId('99999')); + + // THEN + expect($found)->toBe($sheet) + ->and($missing)->toBeNull(); + }); + + it('filters, sorts and paginates short sheets', function (): void { + // GIVEN + $directory = new InMemoryIncoShortSheetDirectory(); + $sheet1 = createShortSheet('10001'); + $sheet2 = createShortSheet('10002'); + $directory->add($sheet1, $sheet2); + + $pagination = Pagination::ofPage(1, 10); + $sorter = new IncoSheetSorter(IncoSheetSortColumn::incoSheetId, \SortDirection::Descending); + $filter = new IncoSheetFilter(new \DateTimeImmutable('2023-01-01')); + + // WHEN + $result = $directory->findAll($pagination, $sorter, $filter); + + // THEN + expect($result->getTotal())->toBe(2) + ->and($result->getItems())->toHaveCount(2) + ->and((string) $result->getItems()[0]->id->value)->toBe('10002'); + }); + + it('searches short sheets by query', function (): void { + // GIVEN + $directory = new InMemoryIncoShortSheetDirectory(); + $sheet1 = createShortSheet('12345'); + $sheet2 = createShortSheet('12999'); + $sheet3 = createShortSheet('54321'); + $directory->add($sheet1, $sheet2, $sheet3); + + $query = new IncoIdQuery('12%'); + $pagination = Pagination::ofPage(1, 10); + + // WHEN + $result = $directory->search($query, $pagination); + + // THEN + expect($result->getTotal())->toBe(2) + ->and($result->getItems())->toHaveCount(2); + }); + + it('clears all short sheets', function (): void { + // GIVEN + $directory = new InMemoryIncoShortSheetDirectory(); + $sheet = createShortSheet('12345'); + $directory->add($sheet); + + // WHEN + $directory->clear(); + + // THEN + expect($directory->find(new IncoId('12345')))->toBeNull(); + }); +}); diff --git a/tests/Unit/Infra/Adapters/Repositories/InMemoryClientRepositoryTest.php b/tests/Unit/Infra/Adapters/Repositories/InMemoryClientRepositoryTest.php new file mode 100644 index 0000000..fdbd53c --- /dev/null +++ b/tests/Unit/Infra/Adapters/Repositories/InMemoryClientRepositoryTest.php @@ -0,0 +1,95 @@ +add($client); + + // WHEN + $foundById = $repository->find($client->id); + $foundByIdentifier = $repository->findByIdentifier($client->identifier); + $missing = $repository->findByIdentifier(new ClientIdentifier('arcadia_id_' . str_repeat('9', 32))); + + // THEN + expect($foundById)->toBe($client) + ->and($foundByIdentifier)->toBe($client) + ->and($missing)->toBeNull(); + }); + + it('persists and deletes a client', function (): void { + // GIVEN + $repository = new InMemoryClientRepository(); + $client = createClient('arcadia_id_' . str_repeat('2', 32)); + + // WHEN + $repository->persist($client); + $foundAfterPersist = $repository->find($client->id); + + $repository->delete($client); + $foundAfterDelete = $repository->find($client->id); + + // THEN + expect($foundAfterPersist)->toBe($client) + ->and($foundAfterDelete)->toBeNull(); + }); + + it('finds all clients with pagination', function (): void { + // GIVEN + $repository = new InMemoryClientRepository(); + $client1 = createClient('arcadia_id_' . str_repeat('1', 32)); + $client2 = createClient('arcadia_id_' . str_repeat('2', 32)); + $repository->add($client1, $client2); + + $pagination = Pagination::ofPage(1, 10); + + // WHEN + $result = $repository->findAll($pagination); + + // THEN + expect($result->getTotal())->toBe(2) + ->and($result->getItems())->toHaveCount(2); + }); + + it('clears all clients', function (): void { + // GIVEN + $repository = new InMemoryClientRepository(); + $client = createClient('arcadia_id_' . str_repeat('3', 32)); + $repository->add($client); + + // WHEN + $repository->clear(); + + // THEN + expect($repository->find($client->id))->toBeNull(); + }); +});