setEnabled(false); $configRepo = $this->createMock(MailConfigurationRepository::class); $configRepo->method('findSingleton')->willReturn($config); $provider = $this->createMock(MailProviderInterface::class); $folderRepo = $this->createMock(MailFolderRepository::class); $messageRepo = $this->createMock(MailMessageRepository::class); $em = $this->createMock(EntityManagerInterface::class); $lockFactory = $this->makeLockFactory(); $service = new MailSyncService( provider: $provider, configRepository: $configRepo, folderRepository: $folderRepo, messageRepository: $messageRepo, entityManager: $em, lockFactory: $lockFactory, logger: new NullLogger(), managerRegistry: $this->createMock(ManagerRegistry::class), ); $report = $service->syncAll(); self::assertSame(0, $report->createdCount); self::assertSame(0, $report->updatedCount); self::assertSame(0, $report->deletedCount); self::assertSame(0, $report->foldersScanned); } public function testSyncAllReturnsEmptyReportWhenLockNotAcquired(): void { $config = new MailConfiguration(); $config->setEnabled(true); $configRepo = $this->createMock(MailConfigurationRepository::class); $configRepo->method('findSingleton')->willReturn($config); $provider = $this->createMock(MailProviderInterface::class); $folderRepo = $this->createMock(MailFolderRepository::class); $messageRepo = $this->createMock(MailMessageRepository::class); $em = $this->createMock(EntityManagerInterface::class); $lockFactory = $this->makeLockFactory(false); $service = new MailSyncService( provider: $provider, configRepository: $configRepo, folderRepository: $folderRepo, messageRepository: $messageRepo, entityManager: $em, lockFactory: $lockFactory, logger: new NullLogger(), managerRegistry: $this->createMock(ManagerRegistry::class), ); $report = $service->syncAll(); self::assertSame(0, $report->createdCount); self::assertContains('lock_not_acquired', $report->errors); } public function testSyncFolderStructureCreatesNewFolders(): void { $config = new MailConfiguration(); $config->setEnabled(true); $configRepo = $this->createMock(MailConfigurationRepository::class); $configRepo->method('findSingleton')->willReturn($config); $folderDto = new MailFolderDto( path: 'INBOX', displayName: 'Inbox', parentPath: null, unreadCount: 5, totalCount: 42, ); $provider = $this->createMock(MailProviderInterface::class); $provider->method('listFolders')->willReturn([$folderDto]); $folderRepo = $this->createMock(MailFolderRepository::class); $folderRepo->method('findByPath')->willReturn(null); $folderRepo->method('findAllOrderedByPath')->willReturn([]); $messageRepo = $this->createMock(MailMessageRepository::class); $em = $this->createMock(EntityManagerInterface::class); $em->expects(self::once())->method('persist'); $em->expects(self::once())->method('flush'); $lockFactory = $this->makeLockFactory(); $service = new MailSyncService( provider: $provider, configRepository: $configRepo, folderRepository: $folderRepo, messageRepository: $messageRepo, entityManager: $em, lockFactory: $lockFactory, logger: new NullLogger(), managerRegistry: $this->createMock(ManagerRegistry::class), ); $service->syncFolderStructure(); } public function testSyncFolderAbortsSuppressionWhenOver50Percent(): void { $config = new MailConfiguration(); $config->setEnabled(true); $configRepo = $this->createMock(MailConfigurationRepository::class); $configRepo->method('findSingleton')->willReturn($config); $folder = new MailFolder(); $folder->setPath('INBOX'); $messageRepo = $this->createMock(MailMessageRepository::class); $messageRepo->method('findMaxUidInFolder')->willReturn(10); $messageRepo->method('findAllUidsByFolder')->willReturn([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); $messageRepo->method('findLastNByFolder')->willReturn([]); $provider = $this->createMock(MailProviderInterface::class); $provider->method('listMessages')->willReturn([]); $folderRepo = $this->createMock(MailFolderRepository::class); $em = $this->createMock(EntityManagerInterface::class); $em->expects(self::never())->method('remove'); $lockFactory = $this->makeLockFactory(); $service = new MailSyncService( provider: $provider, configRepository: $configRepo, folderRepository: $folderRepo, messageRepository: $messageRepo, entityManager: $em, lockFactory: $lockFactory, logger: new NullLogger(), managerRegistry: $this->createMock(ManagerRegistry::class), ); $report = $service->syncFolder($folder); self::assertSame(0, $report->deletedCount); self::assertNotEmpty($report->errors); } private function makeLockFactory(bool $acquired = true): LockFactory { $lock = $this->createMock(SharedLockInterface::class); $lock->method('acquire')->willReturn($acquired); $factory = $this->createMock(LockFactory::class); $factory->method('createLock')->willReturn($lock); return $factory; } }