From fdf52465fe7e091e810f47ae3be350bd8b6a3660 Mon Sep 17 00:00:00 2001 From: Charles Date: Tue, 9 Dec 2025 16:31:20 +0100 Subject: [PATCH] Organization service test --- tests/Service/OrganizationsServiceTest.php | 271 +++++++++++++++++++++ 1 file changed, 271 insertions(+) create mode 100644 tests/Service/OrganizationsServiceTest.php diff --git a/tests/Service/OrganizationsServiceTest.php b/tests/Service/OrganizationsServiceTest.php new file mode 100644 index 0000000..d410144 --- /dev/null +++ b/tests/Service/OrganizationsServiceTest.php @@ -0,0 +1,271 @@ +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'); + } +} \ No newline at end of file