diff --git a/src/Infra/Adapters/Directories/EloquentEmployeeDirectory.php b/src/Infra/Adapters/Directories/EloquentEmployeeDirectory.php index 1c24d62..7721b72 100644 --- a/src/Infra/Adapters/Directories/EloquentEmployeeDirectory.php +++ b/src/Infra/Adapters/Directories/EloquentEmployeeDirectory.php @@ -91,17 +91,13 @@ final class EloquentEmployeeDirectory implements EmployeeDirectory return new \DateTimeImmutable($value); } - private function toNullableDateTime(mixed $value): ?\DateTimeInterface + private function toNullableDateTime(\DateTimeInterface | string | null $value): ?\DateTimeInterface { if ($value === null) { return null; } - if ($value instanceof \DateTimeInterface || is_string($value)) { - return $this->toDateTime($value); - } - - throw new \UnexpectedValueException('Invalid datetime value type.'); + return $this->toDateTime($value); } private function toSqlColumn(EmployeeSortColumn $sortBy): string diff --git a/tests/Integration/Infra/Adapters/Directories/EloquentEmployeeDirectoryTest.php b/tests/Integration/Infra/Adapters/Directories/EloquentEmployeeDirectoryTest.php new file mode 100644 index 0000000..b7fb105 --- /dev/null +++ b/tests/Integration/Infra/Adapters/Directories/EloquentEmployeeDirectoryTest.php @@ -0,0 +1,154 @@ +delete(); +}); + +describe('EloquentEmployeeDirectory Integration Tests', function (): void { + it('finds an employee by public employee number and maps to domain entity', function (): void { + // GIVEN + $directory = new EloquentEmployeeDirectory(); + + EmployeeModel::factory()->create([ + 'public_employee_number' => '1234567', + 'last_name' => 'DUPONT', + 'first_name' => 'Jean', + 'start_date' => '2024-01-01', + 'end_date' => '2025-01-01', + 'planned_end_date' => '2025-06-01', + 'establishment' => '01', + 'site' => 'LDCSER', + 'service' => '097', + 'created_at' => '2024-01-01 10:00:00', + 'updated_at' => '2024-01-02 12:00:00', + ]); + + $employeeId = new ArcadiaEmployeeId('1234567'); + + // WHEN + $result = $directory->find($employeeId); + + // THEN + expect($result)->toBeInstanceOf(Employee::class) + ->and($result?->id()->value)->toBe('1234567') + ->and($result?->lastName)->toBe('DUPONT') + ->and($result?->firstName)->toBe('Jean') + ->and($result?->startDate->format('Y-m-d'))->toBe('2024-01-01') + ->and($result?->endDate?->format('Y-m-d'))->toBe('2025-01-01') + ->and($result?->plannedEndDate?->format('Y-m-d'))->toBe('2025-06-01') + ->and($result?->establishment->value)->toBe('01') + ->and($result?->site->value)->toBe('LDCSER') + ->and($result?->service->value)->toBe('097') + ->and($result?->createdAt->format('Y-m-d H:i:s'))->toBe('2024-01-01 10:00:00') + ->and($result?->updatedAt?->format('Y-m-d H:i:s'))->toBe('2024-01-02 12:00:00'); + }); + + it('converts string dates to DateTimeInterface using toDateTime helper', function (): void { + // GIVEN + $directory = new EloquentEmployeeDirectory(); + $reflection = new \ReflectionMethod(EloquentEmployeeDirectory::class, 'toDateTime'); + + // WHEN + /** @var \DateTimeInterface $result */ + $result = $reflection->invoke($directory, '2024-01-15 14:30:00'); + + // THEN + expect($result)->toBeInstanceOf(\DateTimeInterface::class) + ->and($result->format('Y-m-d H:i:s'))->toBe('2024-01-15 14:30:00'); + }); + + it('returns null when employee is not found', function (): void { + // GIVEN + $directory = new EloquentEmployeeDirectory(); + $employeeId = new ArcadiaEmployeeId('9999999'); + + // WHEN + $result = $directory->find($employeeId); + + // THEN + expect($result)->toBeNull(); + }); + + it('throws UnexpectedValueException when created_at is null', function (): void { + // GIVEN + $directory = new EloquentEmployeeDirectory(); + + EmployeeModel::query()->insert([ + 'public_employee_number' => '7654321', + 'last_name' => 'MARTIN', + 'first_name' => 'Alice', + 'start_date' => '2024-01-01', + 'establishment' => '01', + 'site' => 'LDCSER', + 'service' => '097', + 'created_at' => null, + 'updated_at' => null, + ]); + + $employeeId = new ArcadiaEmployeeId('7654321'); + + // WHEN & THEN + expect(fn () => $directory->find($employeeId)) + ->toThrow(\UnexpectedValueException::class, 'Employee created_at cannot be null.'); + }); + + it('returns paginated employees sorted by specified columns', function (EmployeeSortColumn $sortColumn, \SortDirection $direction): void { + // GIVEN + $directory = new EloquentEmployeeDirectory(); + + EmployeeModel::factory()->create([ + 'public_employee_number' => '1000001', + 'start_date' => '2024-01-01', + 'end_date' => '2024-06-01', + 'planned_end_date' => '2024-12-01', + 'created_at' => '2024-01-01 08:00:00', + 'updated_at' => '2024-01-01 09:00:00', + ]); + + EmployeeModel::factory()->create([ + 'public_employee_number' => '1000002', + 'start_date' => '2024-02-01', + 'end_date' => '2024-07-01', + 'planned_end_date' => '2024-12-15', + 'created_at' => '2024-02-01 08:00:00', + 'updated_at' => '2024-02-01 09:00:00', + ]); + + $pagination = Pagination::ofPage(1, 10); + $sorter = new EmployeeSorter($sortColumn, $direction); + + // WHEN + $result = $directory->findAll($pagination, $sorter); + + // THEN + expect($result)->toBeInstanceOf(Paginated::class) + ->and($result->getTotal())->toBe(2) + ->and(count($result->getItems()))->toBe(2); + })->with([ + 'arcadiaId asc' => [EmployeeSortColumn::arcadiaId, \SortDirection::Ascending], + 'arcadiaId desc' => [EmployeeSortColumn::arcadiaId, \SortDirection::Descending], + 'startDate asc' => [EmployeeSortColumn::startDate, \SortDirection::Ascending], + 'endDate desc' => [EmployeeSortColumn::endDate, \SortDirection::Descending], + 'plannedEndDate asc' => [EmployeeSortColumn::plannedEndDate, \SortDirection::Ascending], + 'createdAt desc' => [EmployeeSortColumn::createdAt, \SortDirection::Descending], + 'updatedAt asc' => [EmployeeSortColumn::updatedAt, \SortDirection::Ascending], + ]); +});