awsService = $this->createMock(AwsService::class); $this->entityManager = $this->createMock(EntityManagerInterface::class); $this->uoRepository = $this->createMock(UsersOrganizationsRepository::class); $this->notificationService = $this->createMock(NotificationService::class); $this->emailNotificationLogger = $this->createMock(LoggerInterface::class); $this->loggerService = $this->createMock(LoggerService::class); // Set the ENV variable used in the service $_ENV['S3_PORTAL_BUCKET'] = 'test-bucket'; $this->service = new OrganizationsService( $this->logoDirectory, $this->awsService, $this->entityManager, $this->uoRepository, $this->notificationService, $this->emailNotificationLogger, $this->loggerService ); } /** * Helper to set private ID property via Reflection */ private function setEntityId(object $entity, int $id): void { $reflection = new \ReflectionClass($entity); if ($reflection->hasProperty('id')) { $property = $reflection->getProperty('id'); // $property->setAccessible(true); // PHP < 8.1 $property->setValue($entity, $id); } } // ========================================== // TEST: handleLogo // ========================================== public function testHandleLogoSuccess(): void { $org = new Organizations(); $this->setEntityId($org, 1); $org->setName('MyOrg'); $file = $this->createMock(UploadedFile::class); $file->method('guessExtension')->willReturn('png'); // Expect AWS Upload $this->awsService->expects($this->once()) ->method('PutDocObj') ->with( 'test-bucket', $file, $this->stringContains('MyOrg_'), // Filename check 'png', 'logo/' ); // Expect Log $this->loggerService->expects($this->once())->method('logAWSAction'); $this->service->handleLogo($org, $file); // Assert URL is set on entity $this->assertStringContainsString('logo/MyOrg_', $org->getLogoUrl()); } public function testHandleLogoThrowsException(): void { $org = new Organizations(); $this->setEntityId($org, 1); $org->setName('MyOrg'); $file = $this->createMock(UploadedFile::class); $file->method('guessExtension')->willReturn('png'); // Simulate AWS Failure $this->awsService->method('PutDocObj') ->willThrowException(new FileException('S3 Down')); // Expect Error Log $this->loggerService->expects($this->once()) ->method('logError') ->with('Failed to upload organization logo to S3', $this->anything()); $this->expectException(FileException::class); $this->expectExceptionMessage('Failed to upload logo to S3: S3 Down'); $this->service->handleLogo($org, $file); } // ========================================== // TEST: appsAccess // ========================================== public function testAppsAccess(): void { $app1 = new Apps(); $this->setEntityId($app1, 10); $app2 = new Apps(); $this->setEntityId($app2, 20); $app3 = new Apps(); $this->setEntityId($app3, 30); $allApps = [$app1, $app2, $app3]; $orgApps = [$app2]; // Org only has access to App 2 $result = $this->service->appsAccess($allApps, $orgApps); $this->assertCount(3, $result); // App 1 -> False $this->assertSame($app1, $result[0]['entity']); $this->assertFalse($result[0]['hasAccess']); // App 2 -> True $this->assertSame($app2, $result[1]['entity']); $this->assertTrue($result[1]['hasAccess']); // App 3 -> False $this->assertSame($app3, $result[2]['entity']); $this->assertFalse($result[2]['hasAccess']); } // ========================================== // TEST: notifyOrganizationAdmins // ========================================== public function testNotifyOrganizationAdminsUserAccepted(): void { // 1. Setup Data $targetUser = new User(); $this->setEntityId($targetUser, 100); $adminUser = new User(); $this->setEntityId($adminUser, 999); $org = new Organizations(); $this->setEntityId($org, 50); $data = ['user' => $targetUser, 'organization' => $org]; // 2. Setup Admin Link (The user who IS admin) $adminUO = new UsersOrganizations(); $this->setEntityId($adminUO, 555); $adminUO->setUsers($adminUser); $adminUO->setOrganization($org); // 3. Setup Role Logic $adminRole = new Roles(); $this->setEntityId($adminRole, 1); $adminRole->setName('ADMIN'); // 4. Setup UOA Logic (Proof that user is Admin of an App) $uoa = new UserOrganizatonApp(); $this->setEntityId($uoa, 777); $uoa->setUserOrganization($adminUO); $uoa->setRole($adminRole); $uoa->setIsActive(true); // 5. Mocks // Mock Roles Repo $rolesRepo = $this->createMock(EntityRepository::class); $rolesRepo->method('findOneBy')->with(['name' => 'ADMIN'])->willReturn($adminRole); // Mock UO Repo (Find potential admins in org) $this->uoRepository->expects($this->once()) ->method('findBy') ->with(['organization' => $org, 'isActive' => true]) ->willReturn([$adminUO]); // Mock UOA Repo (Check if they have ADMIN role) $uoaRepo = $this->createMock(EntityRepository::class); $uoaRepo->method('findOneBy')->willReturn($uoa); $this->entityManager->method('getRepository')->willReturnMap([ [Roles::class, $rolesRepo], [UserOrganizatonApp::class, $uoaRepo], ]); // 6. Expectations $this->notificationService->expects($this->once()) ->method('notifyUserAcceptedInvite') ->with($adminUser, $targetUser, $org); $this->loggerService->expects($this->once()) ->method('logAdminNotified') ->with([ 'admin_user_id' => 999, 'target_user_id' => 100, 'organization_id' => 50, 'case' => 'USER_ACCEPTED' ]); // 7. Run $result = $this->service->notifyOrganizationAdmins($data, 'USER_ACCEPTED'); // The service returns the last admin UO processed (based on loop) $this->assertSame($adminUO, $result); } /** * This test ensures that if the admin is the SAME person as the target user, * they do not get notified (Skip Self Check). */ public function testNotifyOrganizationAdminsSkipsSelf(): void { $user = new User(); $this->setEntityId($user, 100); $org = new Organizations(); $this->setEntityId($org, 50); // Admin IS the user $adminUO = new UsersOrganizations(); $adminUO->setUsers($user); $roleAdmin = new Roles(); $uoa = new UserOrganizatonApp(); // active admin link // Mocks setup $rolesRepo = $this->createMock(EntityRepository::class); $rolesRepo->method('findOneBy')->willReturn($roleAdmin); $this->uoRepository->method('findBy')->willReturn([$adminUO]); $uoaRepo = $this->createMock(EntityRepository::class); $uoaRepo->method('findOneBy')->willReturn($uoa); $this->entityManager->method('getRepository')->willReturnMap([ [Roles::class, $rolesRepo], [UserOrganizatonApp::class, $uoaRepo], ]); // Expectations: Notification service should NEVER be called $this->notificationService->expects($this->never())->method('notifyUserAcceptedInvite'); $this->loggerService->expects($this->never())->method('logAdminNotified'); $this->service->notifyOrganizationAdmins(['user' => $user, 'organization' => $org], 'USER_ACCEPTED'); } }