security = $this->createStub(Security::class); $this->employeeRepository = $this->createStub(EmployeeScopedRepositoryInterface::class); $this->absenceRepository = $this->createStub(AbsenceReadRepositoryInterface::class); $this->formationRepository = $this->createStub(FormationReadRepositoryInterface::class); $this->formationRepository->method('findByDateAndEmployees')->willReturn([]); $this->requestStack = new RequestStack(); } public function testThrowsWhenAnonymous(): void { $this->security->method('getUser')->willReturn(null); $provider = new WorkHourDayContextProvider( $this->security, $this->requestStack, $this->employeeRepository, $this->absenceRepository, $this->formationRepository, $this->buildResolverStub(), new AbsenceSegmentsResolver(), new WorkedHoursCreditPolicy($this->buildResolverStub(), new DailyReferenceMinutesResolver()), $this->buildHolidayResolver(), ); $this->expectException(AccessDeniedHttpException::class); $provider->provide(new Get()); } public function testThrowsWhenDateFormatInvalid(): void { $this->requestStack->push(new Request(query: ['workDate' => '16-02-2026'])); $this->security->method('getUser')->willReturn(new User()); $provider = new WorkHourDayContextProvider( $this->security, $this->requestStack, $this->employeeRepository, $this->absenceRepository, $this->formationRepository, $this->buildResolverStub(), new AbsenceSegmentsResolver(), new WorkedHoursCreditPolicy($this->buildResolverStub(), new DailyReferenceMinutesResolver()), $this->buildHolidayResolver(), ); $this->expectException(UnprocessableEntityHttpException::class); $provider->provide(new Get()); } public function testBuildsRowsWithAbsenceCredits(): void { $user = new User(); $employee = $this->buildEmployee(1, Contract::TRACKING_TIME, 35); $absence = $this->buildAbsence($employee, '2026-02-16', '2026-02-16', true); $this->requestStack->push(new Request(query: ['workDate' => '2026-02-16'])); $this->security->method('getUser')->willReturn($user); $this->employeeRepository->method('findScoped')->with($user)->willReturn([$employee]); $this->absenceRepository->method('findByDateAndEmployees')->willReturn([$absence]); $provider = new WorkHourDayContextProvider( $this->security, $this->requestStack, $this->employeeRepository, $this->absenceRepository, $this->formationRepository, $this->buildResolverStub(), new AbsenceSegmentsResolver(), new WorkedHoursCreditPolicy($this->buildResolverStub(), new DailyReferenceMinutesResolver()), $this->buildHolidayResolver(), ); $result = $provider->provide(new Get()); self::assertSame('2026-02-16', $result->workDate); self::assertCount(1, $result->rows); self::assertSame(1, $result->rows[0]['employeeId']); self::assertSame('Maladie', $result->rows[0]['absenceLabel']); self::assertSame('AM', $result->rows[0]['absenceHalf']); self::assertSame(210, $result->rows[0]['creditedMinutes']); } private function buildEmployee(int $id, string $trackingMode, ?int $weeklyHours): Employee { $contract = new Contract() ->setName('Contrat') ->setTrackingMode($trackingMode) ->setWeeklyHours($weeklyHours) ; $employee = new Employee() ->setFirstName('Jean') ->setLastName('Test') ->setContract($contract) ; $this->setEntityId($employee, $id); return $employee; } private function buildAbsence(Employee $employee, string $startDate, string $endDate, bool $countAsWorked): Absence { $type = new AbsenceType() ->setCode('MAL') ->setLabel('Maladie') ->setColor('#f00') ->setCountAsWorkedHours($countAsWorked) ; return new Absence() ->setEmployee($employee) ->setType($type) ->setStartDate(new DateTime($startDate)) ->setEndDate(new DateTime($endDate)) ->setStartHalf(HalfDay::AM) ->setEndHalf(HalfDay::AM) ; } private function setEntityId(object $entity, int $id): void { $reflection = new ReflectionObject($entity); $property = $reflection->getProperty('id'); $property->setAccessible(true); $property->setValue($entity, $id); } private function buildResolverStub(): EmployeeContractResolver { $resolver = $this->createStub(EmployeeContractResolver::class); $resolver ->method('resolveForEmployeeAndDate') ->willReturnCallback(static fn (Employee $employee): ?Contract => $employee->getContract()) ; return $resolver; } private function buildHolidayResolver(): HolidayVirtualHoursResolver { $service = $this->createStub(PublicHolidayServiceInterface::class); $service->method('getHolidaysDayByYears')->willReturn([]); return new HolidayVirtualHoursResolver( new DailyReferenceMinutesResolver(), $service, $this->createStub(EmployeeContractResolver::class), ); } }