createMock(TenantRepository::class); $tenantRepository->method('listIds')->willReturn([1, 2, 3]); $tenantRepository->method('listActiveIdsByIds') ->willReturnCallback(static fn (array $ids): array => array_values(array_intersect([1, 2, 3], $ids))); $departmentRepository = $this->createMock(DepartmentRepository::class); $userTenantRepository = $this->createMock(UserTenantRepository::class); $userTenantRepository->method('listTenantIdsByUserId') ->willReturnCallback(static function (int $userId): array { if ($userId === 10) { return [1]; } if ($userId === 99) { return [2]; } return []; }); $permissionService = $this->createMock(PermissionService::class); $permissionService->method('userHas') ->willReturnCallback(static function (int $userId, string $permission) use (&$checkedPermissions): bool { $checkedPermissions[] = $permission; if ($userId !== 10) { return false; } return in_array($permission, [PermissionService::SETTINGS_UPDATE, PermissionService::TENANTS_UPDATE], true); }); $service = new TenantScopeService( $tenantRepository, $departmentRepository, $userTenantRepository, $permissionService ); $this->assertFalse($service->hasGlobalAccess(10)); $this->assertFalse($service->canAccess('users', 99, 10)); $this->assertNotEmpty($checkedPermissions); $this->assertSame( [PermissionService::TENANT_SCOPE_GLOBAL], array_values(array_unique($checkedPermissions)) ); } public function testGlobalPermissionGrantsBypass(): void { $tenantRepository = $this->createMock(TenantRepository::class); $departmentRepository = $this->createMock(DepartmentRepository::class); $userTenantRepository = $this->createMock(UserTenantRepository::class); $permissionService = $this->createMock(PermissionService::class); $permissionService->method('userHas') ->willReturnCallback(static fn (int $userId, string $permission): bool => $userId === 77 && $permission === PermissionService::TENANT_SCOPE_GLOBAL); $service = new TenantScopeService( $tenantRepository, $departmentRepository, $userTenantRepository, $permissionService ); $this->assertTrue($service->hasGlobalAccess(77)); $this->assertTrue($service->canAccess('users', 12345, 77)); } public function testGetUserTenantIdsReturnsAllActiveTenantsWhenGlobalPermissionIsPresent(): void { $tenantRepository = $this->createMock(TenantRepository::class); $tenantRepository->expects($this->once()) ->method('listIds') ->willReturn([1, 2, 3]); $tenantRepository->expects($this->once()) ->method('listActiveIdsByIds') ->with([1, 2, 3]) ->willReturn([1, 3]); $departmentRepository = $this->createMock(DepartmentRepository::class); $userTenantRepository = $this->createMock(UserTenantRepository::class); $userTenantRepository->expects($this->never())->method('listTenantIdsByUserId'); $permissionService = $this->createMock(PermissionService::class); $permissionService->method('userHas') ->willReturnCallback(static fn (int $userId, string $permission): bool => $userId === 55 && $permission === PermissionService::TENANT_SCOPE_GLOBAL); $service = new TenantScopeService( $tenantRepository, $departmentRepository, $userTenantRepository, $permissionService ); $this->assertSame([1, 3], $service->getUserTenantIds(55)); } public function testGetUserTenantIdsWithoutGlobalPermissionUsesAssignedTenantsOnly(): void { $tenantRepository = $this->createMock(TenantRepository::class); $tenantRepository->expects($this->once()) ->method('listActiveIdsByIds') ->with([2, 4]) ->willReturn([2]); $tenantRepository->expects($this->never())->method('listIds'); $departmentRepository = $this->createMock(DepartmentRepository::class); $userTenantRepository = $this->createMock(UserTenantRepository::class); $userTenantRepository->expects($this->once()) ->method('listTenantIdsByUserId') ->with(88) ->willReturn([2, 4, 4]); $permissionService = $this->createMock(PermissionService::class); $permissionService->method('userHas')->willReturn(false); $service = new TenantScopeService( $tenantRepository, $departmentRepository, $userTenantRepository, $permissionService ); $this->assertSame([2], $service->getUserTenantIds(88)); } }