createMock(EntityManagerInterface::class); $em->expects(self::never())->method('remove'); $em->expects(self::once())->method('flush'); $result = $this->buildProcessor($em)->process( $this->payload(['employeeId' => 7]), new Post(), ); self::assertSame(0, $result->deleted); self::assertSame(1, $result->processed); } public function testEmptyEntryWithDeleteFlagRemovesExistingRow(): void { $em = $this->createMock(EntityManagerInterface::class); $em->expects(self::once())->method('remove'); $em->expects(self::once())->method('flush'); $result = $this->buildProcessor($em)->process( $this->payload(['employeeId' => 7, 'delete' => true]), new Post(), ); self::assertSame(1, $result->deleted); } public function testNonEmptyEntryStillUpdatesRegardlessOfFlag(): void { $em = $this->createMock(EntityManagerInterface::class); $em->expects(self::never())->method('remove'); $em->expects(self::once())->method('flush'); $result = $this->buildProcessor($em)->process( $this->payload([ 'employeeId' => 7, 'morningFrom' => '08:00', 'morningTo' => '12:30', 'afternoonFrom' => '14:00', 'afternoonTo' => '18:00', ]), new Post(), ); self::assertSame(1, $result->updated); } /** * @param array $entry */ private function payload(array $entry): WorkHourBulkUpsert { $payload = new WorkHourBulkUpsert(); $payload->workDate = '2026-06-24'; $payload->entries = [$entry]; return $payload; } private function buildProcessor(EntityManagerInterface $em): WorkHourBulkUpsertProcessor { $user = new User()->setUsername('Elodie')->setRoles(['ROLE_ADMIN']); $employee = new Employee()->setFirstName('Delphine')->setLastName('BACHELIER'); // Ligne existante NON vide (journée complète saisie entre-temps par un autre utilisateur). $existing = new WorkHour() ->setEmployee($employee) ->setWorkDate(new DateTimeImmutable('2026-06-24')) ->setMorningFrom('08:00') ->setMorningTo('12:00') ->setAfternoonFrom('14:00') ->setAfternoonTo('18:00') ; $contract = new Contract()->setTrackingMode(TrackingMode::TIME)->setWeeklyHours(35); $security = $this->createStub(Security::class); $security->method('getUser')->willReturn($user); $employeeRepository = $this->createStub(EmployeeScopedRepositoryInterface::class); $employeeRepository->method('findAccessibleByIds')->willReturn([7 => $employee]); $workHourRepository = $this->createStub(WorkHourReadRepositoryInterface::class); $workHourRepository->method('findByDateAndEmployeesIndexedByEmployeeId')->willReturn([7 => $existing]); $absenceRepository = $this->createStub(AbsenceReadRepositoryInterface::class); $absenceRepository->method('findByDateAndEmployees')->willReturn([]); $contractResolver = $this->createStub(EmployeeContractResolver::class); $contractResolver->method('resolveForEmployeeAndDate')->willReturn($contract); $contractResolver->method('resolveIsDriverForEmployeeAndDate')->willReturn(false); return new WorkHourBulkUpsertProcessor( $em, $security, $employeeRepository, $workHourRepository, $absenceRepository, $contractResolver, $this->createStub(AuditLogger::class), ); } }