This is an automated email from the ASF dual-hosted git repository. yasith pushed a commit to branch feat/grpc-armeria-migration in repository https://gitbox.apache.org/repos/asf/airavata.git
commit 8ff089aa6e1aaedea51fe5e36215201870a76f0d Author: yasithdev <[email protected]> AuthorDate: Wed Apr 1 14:35:43 2026 -0400 test: add unit tests for SharingHelper, AirvataFileService, CredentialStoreServerHandler --- .../compute/service/ResourceServiceTest.java | 460 +++++++++++++++++++++ .../handler/CredentialStoreServerHandlerTest.java | 299 ++++++++++++++ .../service/ResourceSharingServiceTest.java | 197 +++++++++ .../sharing/service/SharingHelperTest.java | 193 +++++++++ .../storage/service/AirvataFileServiceTest.java | 199 +++++++++ 5 files changed, 1348 insertions(+) diff --git a/airavata-api/compute-service/src/test/java/org/apache/airavata/compute/service/ResourceServiceTest.java b/airavata-api/compute-service/src/test/java/org/apache/airavata/compute/service/ResourceServiceTest.java index 21802fa799..6819f7763d 100644 --- a/airavata-api/compute-service/src/test/java/org/apache/airavata/compute/service/ResourceServiceTest.java +++ b/airavata-api/compute-service/src/test/java/org/apache/airavata/compute/service/ResourceServiceTest.java @@ -26,9 +26,14 @@ import java.util.Map; import org.apache.airavata.execution.handler.RegistryServerHandler; import org.apache.airavata.execution.service.ServiceException; import org.apache.airavata.model.appcatalog.computeresource.proto.*; +import org.apache.airavata.model.appcatalog.gatewayprofile.proto.GatewayResourceProfile; +import org.apache.airavata.model.appcatalog.groupresourceprofile.proto.GroupComputeResourcePreference; import org.apache.airavata.model.appcatalog.storageresource.proto.StorageResourceDescription; import org.apache.airavata.model.data.movement.proto.DMType; +import org.apache.airavata.model.data.movement.proto.GridFTPDataMovement; +import org.apache.airavata.model.data.movement.proto.LOCALDataMovement; import org.apache.airavata.model.data.movement.proto.SCPDataMovement; +import org.apache.airavata.model.data.movement.proto.UnicoreDataMovement; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -224,4 +229,459 @@ class ResourceServiceTest { assertTrue(result); verify(registryHandler).deleteBatchQueue("cr-001", "normal"); } + + // --- Compute Resource (additional) --- + + @Test + void getAllComputeResourceNames_returnsMap() throws Exception { + Map<String, String> names = Map.of("cr-001", "cluster.example.com"); + when(registryHandler.getAllComputeResourceNames()).thenReturn(names); + + Map<String, String> result = resourceService.getAllComputeResourceNames(); + + assertEquals(1, result.size()); + assertEquals("cluster.example.com", result.get("cr-001")); + } + + @Test + void getAllComputeResourceNames_wrapsException() throws Exception { + when(registryHandler.getAllComputeResourceNames()).thenThrow(new RuntimeException("DB error")); + + assertThrows(ServiceException.class, () -> resourceService.getAllComputeResourceNames()); + } + + @Test + void updateComputeResource_returnsTrue() throws Exception { + ComputeResourceDescription desc = ComputeResourceDescription.getDefaultInstance(); + when(registryHandler.updateComputeResource("cr-001", desc)).thenReturn(true); + + boolean result = resourceService.updateComputeResource("cr-001", desc); + + assertTrue(result); + } + + @Test + void updateComputeResource_wrapsException() throws Exception { + ComputeResourceDescription desc = ComputeResourceDescription.getDefaultInstance(); + when(registryHandler.updateComputeResource("cr-001", desc)).thenThrow(new RuntimeException("fail")); + + assertThrows(ServiceException.class, () -> resourceService.updateComputeResource("cr-001", desc)); + } + + @Test + void getComputeResource_wrapsException() throws Exception { + when(registryHandler.getComputeResource("cr-bad")).thenThrow(new RuntimeException("not found")); + + assertThrows(ServiceException.class, () -> resourceService.getComputeResource("cr-bad")); + } + + @Test + void deleteComputeResource_wrapsException() throws Exception { + when(registryHandler.deleteComputeResource("cr-bad")).thenThrow(new RuntimeException("fail")); + + assertThrows(ServiceException.class, () -> resourceService.deleteComputeResource("cr-bad")); + } + + // --- Storage Resource (additional) --- + + @Test + void registerStorageResource_returnsId() throws Exception { + StorageResourceDescription desc = StorageResourceDescription.newBuilder().setHostName("storage.example.com").build(); + when(registryHandler.registerStorageResource(desc)).thenReturn("sr-001"); + + String result = resourceService.registerStorageResource(desc); + + assertEquals("sr-001", result); + } + + @Test + void registerStorageResource_wrapsException() throws Exception { + StorageResourceDescription desc = StorageResourceDescription.getDefaultInstance(); + when(registryHandler.registerStorageResource(desc)).thenThrow(new RuntimeException("fail")); + + assertThrows(ServiceException.class, () -> resourceService.registerStorageResource(desc)); + } + + @Test + void updateStorageResource_returnsTrue() throws Exception { + StorageResourceDescription desc = StorageResourceDescription.getDefaultInstance(); + when(registryHandler.updateStorageResource("sr-001", desc)).thenReturn(true); + + boolean result = resourceService.updateStorageResource("sr-001", desc); + + assertTrue(result); + } + + @Test + void updateStorageResource_wrapsException() throws Exception { + StorageResourceDescription desc = StorageResourceDescription.getDefaultInstance(); + when(registryHandler.updateStorageResource("sr-001", desc)).thenThrow(new RuntimeException("fail")); + + assertThrows(ServiceException.class, () -> resourceService.updateStorageResource("sr-001", desc)); + } + + @Test + void deleteStorageResource_returnsTrue() throws Exception { + when(registryHandler.deleteStorageResource("sr-001")).thenReturn(true); + + boolean result = resourceService.deleteStorageResource("sr-001"); + + assertTrue(result); + } + + // --- Job Submission (additional) --- + + @Test + void addLocalSubmissionDetails_returnsId() throws Exception { + LOCALSubmission submission = LOCALSubmission.getDefaultInstance(); + when(registryHandler.addLocalSubmissionDetails("cr-001", 1, submission)).thenReturn("js-local-001"); + + String result = resourceService.addLocalSubmissionDetails("cr-001", 1, submission); + + assertEquals("js-local-001", result); + } + + @Test + void addLocalSubmissionDetails_wrapsException() throws Exception { + LOCALSubmission submission = LOCALSubmission.getDefaultInstance(); + when(registryHandler.addLocalSubmissionDetails("cr-001", 1, submission)).thenThrow(new RuntimeException("fail")); + + assertThrows(ServiceException.class, () -> resourceService.addLocalSubmissionDetails("cr-001", 1, submission)); + } + + @Test + void updateLocalSubmissionDetails_returnsTrue() throws Exception { + LOCALSubmission submission = LOCALSubmission.getDefaultInstance(); + when(registryHandler.updateLocalSubmissionDetails("js-local-001", submission)).thenReturn(true); + + boolean result = resourceService.updateLocalSubmissionDetails("js-local-001", submission); + + assertTrue(result); + } + + @Test + void addSSHForkJobSubmissionDetails_returnsId() throws Exception { + SSHJobSubmission submission = SSHJobSubmission.getDefaultInstance(); + when(registryHandler.addSSHForkJobSubmissionDetails("cr-001", 1, submission)).thenReturn("js-fork-001"); + + String result = resourceService.addSSHForkJobSubmissionDetails("cr-001", 1, submission); + + assertEquals("js-fork-001", result); + } + + @Test + void addSSHForkJobSubmissionDetails_wrapsException() throws Exception { + SSHJobSubmission submission = SSHJobSubmission.getDefaultInstance(); + when(registryHandler.addSSHForkJobSubmissionDetails("cr-001", 1, submission)).thenThrow(new RuntimeException("fail")); + + assertThrows(ServiceException.class, () -> resourceService.addSSHForkJobSubmissionDetails("cr-001", 1, submission)); + } + + @Test + void getSSHJobSubmission_returnsSubmission() throws Exception { + SSHJobSubmission submission = SSHJobSubmission.newBuilder().setJobSubmissionInterfaceId("js-ssh-001").build(); + when(registryHandler.getSSHJobSubmission("js-ssh-001")).thenReturn(submission); + + SSHJobSubmission result = resourceService.getSSHJobSubmission("js-ssh-001"); + + assertNotNull(result); + assertEquals("js-ssh-001", result.getJobSubmissionInterfaceId()); + } + + @Test + void addCloudJobSubmissionDetails_returnsId() throws Exception { + CloudJobSubmission submission = CloudJobSubmission.getDefaultInstance(); + when(registryHandler.addCloudJobSubmissionDetails("cr-001", 1, submission)).thenReturn("js-cloud-001"); + + String result = resourceService.addCloudJobSubmissionDetails("cr-001", 1, submission); + + assertEquals("js-cloud-001", result); + } + + @Test + void getCloudJobSubmission_returnsSubmission() throws Exception { + CloudJobSubmission submission = CloudJobSubmission.newBuilder().setJobSubmissionInterfaceId("js-cloud-001").build(); + when(registryHandler.getCloudJobSubmission("js-cloud-001")).thenReturn(submission); + + CloudJobSubmission result = resourceService.getCloudJobSubmission("js-cloud-001"); + + assertNotNull(result); + assertEquals("js-cloud-001", result.getJobSubmissionInterfaceId()); + } + + @Test + void addUNICOREJobSubmissionDetails_returnsId() throws Exception { + UnicoreJobSubmission submission = UnicoreJobSubmission.getDefaultInstance(); + when(registryHandler.addUNICOREJobSubmissionDetails("cr-001", 1, submission)).thenReturn("js-unicore-001"); + + String result = resourceService.addUNICOREJobSubmissionDetails("cr-001", 1, submission); + + assertEquals("js-unicore-001", result); + } + + @Test + void getUnicoreJobSubmission_returnsSubmission() throws Exception { + UnicoreJobSubmission submission = UnicoreJobSubmission.newBuilder().setJobSubmissionInterfaceId("js-unicore-001").build(); + when(registryHandler.getUnicoreJobSubmission("js-unicore-001")).thenReturn(submission); + + UnicoreJobSubmission result = resourceService.getUnicoreJobSubmission("js-unicore-001"); + + assertNotNull(result); + assertEquals("js-unicore-001", result.getJobSubmissionInterfaceId()); + } + + @Test + void updateSSHJobSubmissionDetails_returnsTrue() throws Exception { + SSHJobSubmission submission = SSHJobSubmission.getDefaultInstance(); + when(registryHandler.updateSSHJobSubmissionDetails("js-001", submission)).thenReturn(true); + + boolean result = resourceService.updateSSHJobSubmissionDetails("js-001", submission); + + assertTrue(result); + } + + @Test + void updateCloudJobSubmissionDetails_returnsTrue() throws Exception { + CloudJobSubmission submission = CloudJobSubmission.getDefaultInstance(); + when(registryHandler.updateCloudJobSubmissionDetails("js-001", submission)).thenReturn(true); + + boolean result = resourceService.updateCloudJobSubmissionDetails("js-001", submission); + + assertTrue(result); + } + + @Test + void updateUnicoreJobSubmissionDetails_returnsTrue() throws Exception { + UnicoreJobSubmission submission = UnicoreJobSubmission.getDefaultInstance(); + when(registryHandler.updateUnicoreJobSubmissionDetails("js-001", submission)).thenReturn(true); + + boolean result = resourceService.updateUnicoreJobSubmissionDetails("js-001", submission); + + assertTrue(result); + } + + @Test + void deleteJobSubmissionInterface_wrapsException() throws Exception { + when(registryHandler.deleteJobSubmissionInterface("cr-001", "js-bad")).thenThrow(new RuntimeException("fail")); + + assertThrows(ServiceException.class, () -> resourceService.deleteJobSubmissionInterface("cr-001", "js-bad")); + } + + // --- Data Movement (additional) --- + + @Test + void addLocalDataMovementDetails_returnsId() throws Exception { + LOCALDataMovement movement = LOCALDataMovement.getDefaultInstance(); + when(registryHandler.addLocalDataMovementDetails("cr-001", DMType.COMPUTE_RESOURCE, 0, movement)).thenReturn("dm-local-001"); + + String result = resourceService.addLocalDataMovementDetails("cr-001", DMType.COMPUTE_RESOURCE, 0, movement); + + assertEquals("dm-local-001", result); + } + + @Test + void updateLocalDataMovementDetails_returnsTrue() throws Exception { + LOCALDataMovement movement = LOCALDataMovement.getDefaultInstance(); + when(registryHandler.updateLocalDataMovementDetails("dm-001", movement)).thenReturn(true); + + boolean result = resourceService.updateLocalDataMovementDetails("dm-001", movement); + + assertTrue(result); + } + + @Test + void getLocalDataMovement_returnsMovement() throws Exception { + LOCALDataMovement movement = LOCALDataMovement.newBuilder().setDataMovementInterfaceId("dm-local-001").build(); + when(registryHandler.getLocalDataMovement("dm-local-001")).thenReturn(movement); + + LOCALDataMovement result = resourceService.getLocalDataMovement("dm-local-001"); + + assertNotNull(result); + assertEquals("dm-local-001", result.getDataMovementInterfaceId()); + } + + @Test + void updateSCPDataMovementDetails_returnsTrue() throws Exception { + SCPDataMovement movement = SCPDataMovement.getDefaultInstance(); + when(registryHandler.updateSCPDataMovementDetails("dm-001", movement)).thenReturn(true); + + boolean result = resourceService.updateSCPDataMovementDetails("dm-001", movement); + + assertTrue(result); + } + + @Test + void getSCPDataMovement_returnsMovement() throws Exception { + SCPDataMovement movement = SCPDataMovement.newBuilder().setDataMovementInterfaceId("dm-scp-001").build(); + when(registryHandler.getSCPDataMovement("dm-scp-001")).thenReturn(movement); + + SCPDataMovement result = resourceService.getSCPDataMovement("dm-scp-001"); + + assertNotNull(result); + assertEquals("dm-scp-001", result.getDataMovementInterfaceId()); + } + + @Test + void addUnicoreDataMovementDetails_returnsId() throws Exception { + UnicoreDataMovement movement = UnicoreDataMovement.getDefaultInstance(); + when(registryHandler.addUnicoreDataMovementDetails("cr-001", DMType.COMPUTE_RESOURCE, 0, movement)).thenReturn("dm-unicore-001"); + + String result = resourceService.addUnicoreDataMovementDetails("cr-001", DMType.COMPUTE_RESOURCE, 0, movement); + + assertEquals("dm-unicore-001", result); + } + + @Test + void updateUnicoreDataMovementDetails_returnsTrue() throws Exception { + UnicoreDataMovement movement = UnicoreDataMovement.getDefaultInstance(); + when(registryHandler.updateUnicoreDataMovementDetails("dm-001", movement)).thenReturn(true); + + boolean result = resourceService.updateUnicoreDataMovementDetails("dm-001", movement); + + assertTrue(result); + } + + @Test + void getUnicoreDataMovement_returnsMovement() throws Exception { + UnicoreDataMovement movement = UnicoreDataMovement.newBuilder().setDataMovementInterfaceId("dm-unicore-001").build(); + when(registryHandler.getUnicoreDataMovement("dm-unicore-001")).thenReturn(movement); + + UnicoreDataMovement result = resourceService.getUnicoreDataMovement("dm-unicore-001"); + + assertNotNull(result); + assertEquals("dm-unicore-001", result.getDataMovementInterfaceId()); + } + + @Test + void addGridFTPDataMovementDetails_returnsId() throws Exception { + GridFTPDataMovement movement = GridFTPDataMovement.getDefaultInstance(); + when(registryHandler.addGridFTPDataMovementDetails("cr-001", DMType.COMPUTE_RESOURCE, 0, movement)).thenReturn("dm-gridftp-001"); + + String result = resourceService.addGridFTPDataMovementDetails("cr-001", DMType.COMPUTE_RESOURCE, 0, movement); + + assertEquals("dm-gridftp-001", result); + } + + @Test + void updateGridFTPDataMovementDetails_returnsTrue() throws Exception { + GridFTPDataMovement movement = GridFTPDataMovement.getDefaultInstance(); + when(registryHandler.updateGridFTPDataMovementDetails("dm-001", movement)).thenReturn(true); + + boolean result = resourceService.updateGridFTPDataMovementDetails("dm-001", movement); + + assertTrue(result); + } + + @Test + void getGridFTPDataMovement_returnsMovement() throws Exception { + GridFTPDataMovement movement = GridFTPDataMovement.newBuilder().setDataMovementInterfaceId("dm-gridftp-001").build(); + when(registryHandler.getGridFTPDataMovement("dm-gridftp-001")).thenReturn(movement); + + GridFTPDataMovement result = resourceService.getGridFTPDataMovement("dm-gridftp-001"); + + assertNotNull(result); + assertEquals("dm-gridftp-001", result.getDataMovementInterfaceId()); + } + + @Test + void deleteDataMovementInterface_wrapsException() throws Exception { + when(registryHandler.deleteDataMovementInterface("cr-001", "dm-bad", DMType.COMPUTE_RESOURCE)) + .thenThrow(new RuntimeException("fail")); + + assertThrows(ServiceException.class, + () -> resourceService.deleteDataMovementInterface("cr-001", "dm-bad", DMType.COMPUTE_RESOURCE)); + } + + // --- Resource Job Manager (additional) --- + + @Test + void updateResourceJobManager_returnsTrue() throws Exception { + ResourceJobManager manager = ResourceJobManager.getDefaultInstance(); + when(registryHandler.updateResourceJobManager("rjm-001", manager)).thenReturn(true); + + boolean result = resourceService.updateResourceJobManager("rjm-001", manager); + + assertTrue(result); + } + + @Test + void getResourceJobManager_returnsManager() throws Exception { + ResourceJobManager manager = ResourceJobManager.newBuilder() + .setResourceJobManagerId("rjm-001") + .setResourceJobManagerType(ResourceJobManagerType.SLURM) + .build(); + when(registryHandler.getResourceJobManager("rjm-001")).thenReturn(manager); + + ResourceJobManager result = resourceService.getResourceJobManager("rjm-001"); + + assertNotNull(result); + assertEquals("rjm-001", result.getResourceJobManagerId()); + assertEquals(ResourceJobManagerType.SLURM, result.getResourceJobManagerType()); + } + + @Test + void registerResourceJobManager_wrapsException() throws Exception { + ResourceJobManager manager = ResourceJobManager.getDefaultInstance(); + when(registryHandler.registerResourceJobManager(manager)).thenThrow(new RuntimeException("fail")); + + assertThrows(ServiceException.class, () -> resourceService.registerResourceJobManager(manager)); + } + + @Test + void deleteResourceJobManager_wrapsException() throws Exception { + when(registryHandler.deleteResourceJobManager("rjm-bad")).thenThrow(new RuntimeException("fail")); + + assertThrows(ServiceException.class, () -> resourceService.deleteResourceJobManager("rjm-bad")); + } + + // --- Batch Queue (additional) --- + + @Test + void deleteBatchQueue_wrapsException() throws Exception { + when(registryHandler.deleteBatchQueue("cr-001", "bad-queue")).thenThrow(new RuntimeException("fail")); + + assertThrows(ServiceException.class, () -> resourceService.deleteBatchQueue("cr-001", "bad-queue")); + } + + // --- ComputeResourceProvider SPI delegates --- + + @Test + void getGatewayResourceProfile_returnsProfile() throws Exception { + GatewayResourceProfile profile = GatewayResourceProfile.newBuilder().setGatewayId("gw-001").build(); + when(registryHandler.getGatewayResourceProfile("gw-001")).thenReturn(profile); + + GatewayResourceProfile result = resourceService.getGatewayResourceProfile("gw-001"); + + assertNotNull(result); + assertEquals("gw-001", result.getGatewayId()); + } + + @Test + void getGatewayResourceProfile_wrapsException() throws Exception { + when(registryHandler.getGatewayResourceProfile("gw-bad")).thenThrow(new RuntimeException("fail")); + + assertThrows(ServiceException.class, () -> resourceService.getGatewayResourceProfile("gw-bad")); + } + + @Test + void getGroupComputeResourcePreference_returnsPreference() throws Exception { + GroupComputeResourcePreference pref = GroupComputeResourcePreference.newBuilder() + .setComputeResourceId("cr-001") + .build(); + when(registryHandler.getGroupComputeResourcePreference("cr-001", "grp-001")).thenReturn(pref); + + GroupComputeResourcePreference result = resourceService.getGroupComputeResourcePreference("cr-001", "grp-001"); + + assertNotNull(result); + assertEquals("cr-001", result.getComputeResourceId()); + } + + @Test + void getGroupComputeResourcePreference_wrapsException() throws Exception { + when(registryHandler.getGroupComputeResourcePreference("cr-bad", "grp-bad")) + .thenThrow(new RuntimeException("fail")); + + assertThrows(ServiceException.class, + () -> resourceService.getGroupComputeResourcePreference("cr-bad", "grp-bad")); + } } diff --git a/airavata-api/credential-service/src/test/java/org/apache/airavata/credential/handler/CredentialStoreServerHandlerTest.java b/airavata-api/credential-service/src/test/java/org/apache/airavata/credential/handler/CredentialStoreServerHandlerTest.java new file mode 100644 index 0000000000..7f63624a43 --- /dev/null +++ b/airavata-api/credential-service/src/test/java/org/apache/airavata/credential/handler/CredentialStoreServerHandlerTest.java @@ -0,0 +1,299 @@ +/** +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ +package org.apache.airavata.credential.handler; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +import java.lang.reflect.Field; +import java.util.Date; +import java.util.List; +import java.util.Map; +import org.apache.airavata.credential.model.Credential; +import org.apache.airavata.credential.repository.CredentialReaderImpl; +import org.apache.airavata.credential.repository.CredentialStoreException; +import org.apache.airavata.credential.repository.SSHCredentialWriter; +import org.apache.airavata.model.credential.store.proto.*; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.MockedConstruction; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +class CredentialStoreServerHandlerTest { + + @Mock + CredentialReaderImpl credentialReader; + + @Mock + SSHCredentialWriter sshCredentialWriter; + + CredentialStoreServerHandler handler; + + @BeforeEach + void setUp() throws Exception { + // Use Mockito to create a spy-like instance without invoking the real constructor + // (which requires DB connections), then inject mocked dependencies via reflection + handler = mock(CredentialStoreServerHandler.class, withSettings().defaultAnswer(CALLS_REAL_METHODS)); + setField(handler, "credentialReader", credentialReader); + setField(handler, "sshCredentialWriter", sshCredentialWriter); + } + + private void setField(Object target, String fieldName, Object value) throws Exception { + Field field = findField(target.getClass(), fieldName); + field.setAccessible(true); + field.set(target, value); + } + + private Field findField(Class<?> clazz, String fieldName) throws NoSuchFieldException { + while (clazz != null) { + try { + return clazz.getDeclaredField(fieldName); + } catch (NoSuchFieldException e) { + clazz = clazz.getSuperclass(); + } + } + throw new NoSuchFieldException(fieldName); + } + + @Test + void getSSHCredential_returnsCredentialWhenFound() throws Exception { + org.apache.airavata.credential.model.SSHCredential modelCred = new org.apache.airavata.credential.model.SSHCredential(); + modelCred.setPortalUserName("testUser"); + modelCred.setGateway("gw1"); + modelCred.setPublicKey("ssh-rsa AAAA".getBytes()); + modelCred.setPrivateKey("-----BEGIN RSA PRIVATE KEY-----".getBytes()); + modelCred.setPassphrase("passphrase123"); + modelCred.setToken("token-1"); + modelCred.setCertificateRequestedTime(new Date(1000000)); + modelCred.setDescription("test key"); + + when(credentialReader.getCredential("gw1", "token-1")).thenReturn(modelCred); + + SSHCredential result = handler.getSSHCredential("token-1", "gw1"); + + assertNotNull(result); + assertEquals("testUser", result.getUsername()); + assertEquals("gw1", result.getGatewayId()); + assertEquals("ssh-rsa AAAA", result.getPublicKey()); + assertEquals("passphrase123", result.getPassphrase()); + assertEquals("token-1", result.getToken()); + assertEquals(1000000, result.getPersistedTime()); + assertEquals("test key", result.getDescription()); + } + + @Test + void getSSHCredential_returnsNullWhenNotSSHCredential() throws Exception { + org.apache.airavata.credential.model.PasswordCredential modelCred = + new org.apache.airavata.credential.model.PasswordCredential(); + modelCred.setToken("token-1"); + + when(credentialReader.getCredential("gw1", "token-1")).thenReturn(modelCred); + + SSHCredential result = handler.getSSHCredential("token-1", "gw1"); + + assertNull(result); + } + + @Test + void getSSHCredential_throwsOnReaderError() throws Exception { + when(credentialReader.getCredential("gw1", "token-1")) + .thenThrow(new CredentialStoreException("DB error")); + + assertThrows(CredentialStoreException.class, () -> handler.getSSHCredential("token-1", "gw1")); + } + + @Test + void getPasswordCredential_returnsCredentialWhenFound() throws Exception { + org.apache.airavata.credential.model.PasswordCredential modelCred = + new org.apache.airavata.credential.model.PasswordCredential(); + modelCred.setGateway("gw1"); + modelCred.setPortalUserName("portalUser"); + modelCred.setUserName("loginUser"); + modelCred.setPassword("secret"); + modelCred.setToken("pwd-token-1"); + modelCred.setCertificateRequestedTime(new Date(2000000)); + modelCred.setDescription("pwd cred"); + + when(credentialReader.getCredential("gw1", "pwd-token-1")).thenReturn(modelCred); + + PasswordCredential result = handler.getPasswordCredential("pwd-token-1", "gw1"); + + assertNotNull(result); + assertEquals("gw1", result.getGatewayId()); + assertEquals("portalUser", result.getPortalUserName()); + assertEquals("loginUser", result.getLoginUserName()); + assertEquals("secret", result.getPassword()); + assertEquals("pwd-token-1", result.getToken()); + } + + @Test + void getPasswordCredential_returnsNullWhenNotPasswordCredential() throws Exception { + org.apache.airavata.credential.model.SSHCredential modelCred = + new org.apache.airavata.credential.model.SSHCredential(); + modelCred.setToken("token-1"); + + when(credentialReader.getCredential("gw1", "token-1")).thenReturn(modelCred); + + PasswordCredential result = handler.getPasswordCredential("token-1", "gw1"); + + assertNull(result); + } + + @Test + void addSSHCredential_writesAndReturnsToken() throws Exception { + SSHCredential sshCredential = SSHCredential.newBuilder() + .setGatewayId("gw1") + .setUsername("testUser") + .setPublicKey("ssh-rsa AAAA") + .setPrivateKey("-----BEGIN RSA-----") + .setDescription("my key") + .build(); + + String token = handler.addSSHCredential(sshCredential); + + assertNotNull(token); + assertFalse(token.isEmpty()); + verify(sshCredentialWriter).writeCredentials(any(org.apache.airavata.credential.model.SSHCredential.class)); + } + + @Test + void addPasswordCredential_writesAndReturnsToken() throws Exception { + PasswordCredential pwdCredential = PasswordCredential.newBuilder() + .setGatewayId("gw1") + .setPortalUserName("portalUser") + .setLoginUserName("loginUser") + .setPassword("secret") + .setDescription("pwd cred") + .build(); + + String token = handler.addPasswordCredential(pwdCredential); + + assertNotNull(token); + assertFalse(token.isEmpty()); + verify(sshCredentialWriter).writeCredentials(any(org.apache.airavata.credential.model.PasswordCredential.class)); + } + + @Test + void getAllCredentialSummaries_sshType_filtersCorrectly() throws Exception { + org.apache.airavata.credential.model.SSHCredential sshCred = + new org.apache.airavata.credential.model.SSHCredential(); + sshCred.setPortalUserName("user1"); + sshCred.setGateway("gw1"); + sshCred.setPublicKey("ssh-rsa key".getBytes()); + sshCred.setToken("ssh-token-1"); + sshCred.setCertificateRequestedTime(new Date(1000000)); + + org.apache.airavata.credential.model.PasswordCredential pwdCred = + new org.apache.airavata.credential.model.PasswordCredential(); + pwdCred.setPortalUserName("user2"); + pwdCred.setGateway("gw1"); + pwdCred.setToken("pwd-token-1"); + pwdCred.setCertificateRequestedTime(new Date(2000000)); + + List<String> tokens = List.of("ssh-token-1", "pwd-token-1"); + when(credentialReader.getAllAccessibleCredentialsPerGateway("gw1", tokens)) + .thenReturn(List.of(sshCred, pwdCred)); + + List<CredentialSummary> result = handler.getAllCredentialSummaries(SummaryType.SSH, tokens, "gw1"); + + assertEquals(1, result.size()); + assertEquals(SummaryType.SSH, result.get(0).getType()); + assertEquals("ssh-token-1", result.get(0).getToken()); + } + + @Test + void getAllCredentialSummaries_passwdType_filtersCorrectly() throws Exception { + org.apache.airavata.credential.model.PasswordCredential pwdCred = + new org.apache.airavata.credential.model.PasswordCredential(); + pwdCred.setPortalUserName("user1"); + pwdCred.setGateway("gw1"); + pwdCred.setToken("pwd-token-1"); + pwdCred.setCertificateRequestedTime(new Date(1000000)); + + List<String> tokens = List.of("pwd-token-1"); + when(credentialReader.getAllAccessibleCredentialsPerGateway("gw1", tokens)) + .thenReturn(List.of(pwdCred)); + + List<CredentialSummary> result = handler.getAllCredentialSummaries(SummaryType.PASSWD, tokens, "gw1"); + + assertEquals(1, result.size()); + assertEquals(SummaryType.PASSWD, result.get(0).getType()); + } + + @Test + void deleteSSHCredential_delegatesToReader() throws Exception { + handler.deleteSSHCredential("token-1", "gw1"); + + verify(credentialReader).removeCredentials("gw1", "token-1"); + } + + @Test + void deleteSSHCredential_throwsOnError() throws Exception { + doThrow(new CredentialStoreException("DB error")).when(credentialReader).removeCredentials("gw1", "token-1"); + + assertThrows(CredentialStoreException.class, () -> handler.deleteSSHCredential("token-1", "gw1")); + } + + @Test + void getAllCredentialSummaryForGateway_sshType_returnsSSHOnly() throws Exception { + org.apache.airavata.credential.model.SSHCredential sshCred = + new org.apache.airavata.credential.model.SSHCredential(); + sshCred.setPortalUserName("user1"); + sshCred.setGateway("gw1"); + sshCred.setPublicKey("ssh-rsa key".getBytes()); + sshCred.setToken("ssh-token-1"); + sshCred.setCertificateRequestedTime(new Date(1000000)); + + when(credentialReader.getAllCredentialsPerGateway("gw1")).thenReturn(List.of(sshCred)); + + List<CredentialSummary> result = handler.getAllCredentialSummaryForGateway(SummaryType.SSH, "gw1"); + + assertEquals(1, result.size()); + assertEquals("ssh-token-1", result.get(0).getToken()); + } + + @Test + void getAllCredentialSummaryForGateway_nonSSHType_returnsEmpty() throws Exception { + List<CredentialSummary> result = handler.getAllCredentialSummaryForGateway(SummaryType.PASSWD, "gw1"); + + assertTrue(result.isEmpty()); + } + + @Test + void getAllPWDCredentialsForGateway_returnsPwdTokensAndDescriptions() throws Exception { + org.apache.airavata.credential.model.PasswordCredential pwdCred = + new org.apache.airavata.credential.model.PasswordCredential(); + pwdCred.setToken("pwd-token-1"); + pwdCred.setDescription("my password"); + pwdCred.setGateway("gw1"); + pwdCred.setPortalUserName("user1"); + pwdCred.setCertificateRequestedTime(new Date()); + + when(credentialReader.getAllCredentialsPerGateway("gw1")).thenReturn(List.of(pwdCred)); + + Map<String, String> result = handler.getAllPWDCredentialsForGateway("gw1"); + + assertEquals(1, result.size()); + assertEquals("my password", result.get("pwd-token-1")); + } +} diff --git a/airavata-api/src/test/java/org/apache/airavata/sharing/service/ResourceSharingServiceTest.java b/airavata-api/src/test/java/org/apache/airavata/sharing/service/ResourceSharingServiceTest.java index db0007912f..025d3eb54d 100644 --- a/airavata-api/src/test/java/org/apache/airavata/sharing/service/ResourceSharingServiceTest.java +++ b/airavata-api/src/test/java/org/apache/airavata/sharing/service/ResourceSharingServiceTest.java @@ -29,7 +29,9 @@ import org.apache.airavata.execution.service.RequestContext; import org.apache.airavata.execution.service.ServiceAuthorizationException; import org.apache.airavata.model.group.proto.ResourcePermissionType; import org.apache.airavata.sharing.handler.SharingRegistryServerHandler; +import org.apache.airavata.sharing.model.EntityEntity; import org.apache.airavata.sharing.model.UserEntity; +import org.apache.airavata.sharing.model.UserGroupEntity; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -137,4 +139,199 @@ class ResourceSharingServiceTest { .shareEntityWithGroups( eq("testGateway"), eq("resource-1"), anyList(), eq("testGateway:WRITE"), eq(true)); } + + @Test + void shareResourceWithGroups_nonOwnerWithoutSharingPermissionRejected() throws Exception { + when(sharingHandler.userHasAccess("testGateway", "testUser@testGateway", "resource-1", "testGateway:OWNER")) + .thenReturn(false); + when(sharingHandler.userHasAccess("testGateway", "testUser@testGateway", "resource-1", "testGateway:MANAGE_SHARING")) + .thenReturn(false); + + assertThrows( + ServiceAuthorizationException.class, + () -> resourceSharingService.shareResourceWithGroups( + ctx, "resource-1", Map.of("group-1", ResourcePermissionType.READ))); + } + + @Test + void shareResourceWithGroups_manageSharingUserCanShareRead() throws Exception { + when(sharingHandler.userHasAccess("testGateway", "testUser@testGateway", "resource-1", "testGateway:OWNER")) + .thenReturn(false); + when(sharingHandler.userHasAccess("testGateway", "testUser@testGateway", "resource-1", "testGateway:MANAGE_SHARING")) + .thenReturn(true); + + boolean result = resourceSharingService.shareResourceWithGroups( + ctx, "resource-1", Map.of("group-1", ResourcePermissionType.READ)); + + assertTrue(result); + verify(sharingHandler) + .shareEntityWithGroups( + eq("testGateway"), eq("resource-1"), anyList(), eq("testGateway:READ"), eq(true)); + } + + @Test + void revokeSharingOfResourceFromGroups_ownerCanRevoke() throws Exception { + when(sharingHandler.userHasAccess("testGateway", "testUser@testGateway", "resource-1", "testGateway:OWNER")) + .thenReturn(true); + EntityEntity entity = new EntityEntity(); + entity.setEntityTypeId("testGateway:OTHER"); + when(sharingHandler.getEntity("testGateway", "resource-1")).thenReturn(entity); + + boolean result = resourceSharingService.revokeSharingOfResourceFromGroups( + ctx, "resource-1", Map.of("group-1", ResourcePermissionType.WRITE)); + + assertTrue(result); + verify(sharingHandler) + .revokeEntitySharingFromUsers( + eq("testGateway"), eq("resource-1"), anyList(), eq("testGateway:WRITE")); + } + + @Test + void revokeSharingOfResourceFromGroups_nonOwnerWithoutSharingPermissionRejected() throws Exception { + when(sharingHandler.userHasAccess("testGateway", "testUser@testGateway", "resource-1", "testGateway:OWNER")) + .thenReturn(false); + when(sharingHandler.userHasAccess("testGateway", "testUser@testGateway", "resource-1", "testGateway:MANAGE_SHARING")) + .thenReturn(false); + + assertThrows( + ServiceAuthorizationException.class, + () -> resourceSharingService.revokeSharingOfResourceFromGroups( + ctx, "resource-1", Map.of("group-1", ResourcePermissionType.READ))); + } + + @Test + void getAllDirectlyAccessibleUsers_returnsUserIds() throws Exception { + UserEntity user1 = new UserEntity(); + user1.setUserId("user1@testGateway"); + when(sharingHandler.getListOfDirectlySharedUsers("testGateway", "resource-1", "testGateway:WRITE")) + .thenReturn(List.of(user1)); + UserEntity owner = new UserEntity(); + owner.setUserId("owner@testGateway"); + when(sharingHandler.getListOfDirectlySharedUsers("testGateway", "resource-1", "testGateway:OWNER")) + .thenReturn(List.of(owner)); + + List<String> result = + resourceSharingService.getAllDirectlyAccessibleUsers(ctx, "resource-1", ResourcePermissionType.WRITE); + + assertEquals(2, result.size()); + assertTrue(result.contains("user1@testGateway")); + assertTrue(result.contains("owner@testGateway")); + } + + @Test + void getAllAccessibleGroups_returnsGroupIds() throws Exception { + UserGroupEntity group1 = new UserGroupEntity(); + group1.setGroupId("group-1"); + when(sharingHandler.getListOfSharedGroups("testGateway", "resource-1", "testGateway:READ")) + .thenReturn(List.of(group1)); + + List<String> result = + resourceSharingService.getAllAccessibleGroups(ctx, "resource-1", ResourcePermissionType.READ); + + assertEquals(1, result.size()); + assertTrue(result.contains("group-1")); + } + + @Test + void getAllDirectlyAccessibleGroups_returnsGroupIds() throws Exception { + UserGroupEntity group1 = new UserGroupEntity(); + group1.setGroupId("group-1"); + when(sharingHandler.getListOfDirectlySharedGroups("testGateway", "resource-1", "testGateway:WRITE")) + .thenReturn(List.of(group1)); + + List<String> result = + resourceSharingService.getAllDirectlyAccessibleGroups(ctx, "resource-1", ResourcePermissionType.WRITE); + + assertEquals(1, result.size()); + assertTrue(result.contains("group-1")); + } + + @Test + void userHasAccess_writePermission_trueWhenWriteAccess() throws Exception { + when(sharingHandler.userHasAccess("testGateway", "testUser@testGateway", "resource-1", "testGateway:OWNER")) + .thenReturn(false); + when(sharingHandler.userHasAccess("testGateway", "testUser@testGateway", "resource-1", "testGateway:WRITE")) + .thenReturn(true); + + boolean result = resourceSharingService.userHasAccess(ctx, "resource-1", ResourcePermissionType.WRITE); + + assertTrue(result); + } + + @Test + void userHasAccess_manageSharingPermission() throws Exception { + when(sharingHandler.userHasAccess("testGateway", "testUser@testGateway", "resource-1", "testGateway:OWNER")) + .thenReturn(false); + when(sharingHandler.userHasAccess("testGateway", "testUser@testGateway", "resource-1", "testGateway:MANAGE_SHARING")) + .thenReturn(true); + + boolean result = resourceSharingService.userHasAccess(ctx, "resource-1", ResourcePermissionType.MANAGE_SHARING); + + assertTrue(result); + } + + @Test + void userHasAccess_ownerImpliesAllPermissions() throws Exception { + when(sharingHandler.userHasAccess("testGateway", "testUser@testGateway", "resource-1", "testGateway:OWNER")) + .thenReturn(true); + + assertTrue(resourceSharingService.userHasAccess(ctx, "resource-1", ResourcePermissionType.OWNER)); + assertTrue(resourceSharingService.userHasAccess(ctx, "resource-1", ResourcePermissionType.WRITE)); + assertTrue(resourceSharingService.userHasAccess(ctx, "resource-1", ResourcePermissionType.READ)); + assertTrue(resourceSharingService.userHasAccess(ctx, "resource-1", ResourcePermissionType.MANAGE_SHARING)); + } + + @Test + void shareResourceWithUsers_writePermission() throws Exception { + when(sharingHandler.userHasAccess("testGateway", "testUser@testGateway", "resource-1", "testGateway:OWNER")) + .thenReturn(true); + + boolean result = resourceSharingService.shareResourceWithUsers( + ctx, "resource-1", Map.of("otherUser", ResourcePermissionType.WRITE)); + + assertTrue(result); + verify(sharingHandler) + .shareEntityWithUsers(eq("testGateway"), eq("resource-1"), anyList(), eq("testGateway:WRITE"), eq(true)); + } + + @Test + void revokeSharingOfResourceFromUsers_readPermission() throws Exception { + when(sharingHandler.userHasAccess("testGateway", "testUser@testGateway", "resource-1", "testGateway:OWNER")) + .thenReturn(true); + + boolean result = resourceSharingService.revokeSharingOfResourceFromUsers( + ctx, "resource-1", Map.of("otherUser", ResourcePermissionType.READ)); + + assertTrue(result); + verify(sharingHandler) + .revokeEntitySharingFromUsers(eq("testGateway"), eq("resource-1"), anyList(), eq("testGateway:READ")); + } + + @Test + void getAllAccessibleUsers_ownerPermission_returnsOnlyOwners() throws Exception { + UserEntity owner = new UserEntity(); + owner.setUserId("owner@testGateway"); + when(sharingHandler.getListOfSharedUsers("testGateway", "resource-1", "testGateway:OWNER")) + .thenReturn(List.of(owner)); + + List<String> result = + resourceSharingService.getAllAccessibleUsers(ctx, "resource-1", ResourcePermissionType.OWNER); + + assertEquals(1, result.size()); + assertTrue(result.contains("owner@testGateway")); + } + + @Test + void getAllAccessibleGroups_manageSharingPermission() throws Exception { + UserGroupEntity group1 = new UserGroupEntity(); + group1.setGroupId("group-manage-1"); + when(sharingHandler.getListOfSharedGroups("testGateway", "resource-1", "testGateway:MANAGE_SHARING")) + .thenReturn(List.of(group1)); + + List<String> result = + resourceSharingService.getAllAccessibleGroups(ctx, "resource-1", ResourcePermissionType.MANAGE_SHARING); + + assertEquals(1, result.size()); + assertTrue(result.contains("group-manage-1")); + } } diff --git a/airavata-api/src/test/java/org/apache/airavata/sharing/service/SharingHelperTest.java b/airavata-api/src/test/java/org/apache/airavata/sharing/service/SharingHelperTest.java new file mode 100644 index 0000000000..68fbab96a5 --- /dev/null +++ b/airavata-api/src/test/java/org/apache/airavata/sharing/service/SharingHelperTest.java @@ -0,0 +1,193 @@ +/** +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ +package org.apache.airavata.sharing.service; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +import java.util.Arrays; +import org.apache.airavata.execution.handler.RegistryServerHandler; +import org.apache.airavata.model.appcatalog.gatewaygroups.proto.GatewayGroups; +import org.apache.airavata.model.group.proto.ResourcePermissionType; +import org.apache.airavata.sharing.handler.SharingRegistryServerHandler; +import org.apache.airavata.sharing.model.PermissionTypeEntity; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +class SharingHelperTest { + + @Mock + SharingRegistryServerHandler sharingHandler; + + @Mock + RegistryServerHandler registryHandler; + + @Test + void isSharingEnabled_returnsFalseWhenSettingsNotConfigured() { + // ServerSettings not configured in test context + assertFalse(SharingHelper.isSharingEnabled()); + } + + @Test + void userHasAccess_ownerPermission_returnsTrueWhenOwner() throws Exception { + when(sharingHandler.userHasAccess("gw1", "alice@gw1", "entity1", "gw1:OWNER")) + .thenReturn(true); + + boolean result = + SharingHelper.userHasAccess(sharingHandler, "gw1", "alice", "entity1", ResourcePermissionType.OWNER); + + assertTrue(result); + } + + @Test + void userHasAccess_ownerPermission_returnsFalseWhenNotOwner() throws Exception { + when(sharingHandler.userHasAccess("gw1", "alice@gw1", "entity1", "gw1:OWNER")) + .thenReturn(false); + + boolean result = + SharingHelper.userHasAccess(sharingHandler, "gw1", "alice", "entity1", ResourcePermissionType.OWNER); + + assertFalse(result); + } + + @Test + void userHasAccess_readPermission_returnsTrueWhenOwner() throws Exception { + when(sharingHandler.userHasAccess("gw1", "alice@gw1", "entity1", "gw1:OWNER")) + .thenReturn(true); + + boolean result = + SharingHelper.userHasAccess(sharingHandler, "gw1", "alice", "entity1", ResourcePermissionType.READ); + + assertTrue(result); + // should not check READ permission when OWNER already grants access + verify(sharingHandler, never()).userHasAccess("gw1", "alice@gw1", "entity1", "gw1:READ"); + } + + @Test + void userHasAccess_readPermission_fallsBackToReadWhenNotOwner() throws Exception { + when(sharingHandler.userHasAccess("gw1", "alice@gw1", "entity1", "gw1:OWNER")) + .thenReturn(false); + when(sharingHandler.userHasAccess("gw1", "alice@gw1", "entity1", "gw1:READ")) + .thenReturn(true); + + boolean result = + SharingHelper.userHasAccess(sharingHandler, "gw1", "alice", "entity1", ResourcePermissionType.READ); + + assertTrue(result); + } + + @Test + void userHasAccess_readPermission_returnsFalseWhenNeitherOwnerNorReader() throws Exception { + when(sharingHandler.userHasAccess("gw1", "alice@gw1", "entity1", "gw1:OWNER")) + .thenReturn(false); + when(sharingHandler.userHasAccess("gw1", "alice@gw1", "entity1", "gw1:READ")) + .thenReturn(false); + + boolean result = + SharingHelper.userHasAccess(sharingHandler, "gw1", "alice", "entity1", ResourcePermissionType.READ); + + assertFalse(result); + } + + @Test + void userHasAccess_qualifiedUserId_usedAsIs() throws Exception { + when(sharingHandler.userHasAccess("gw1", "alice@gw1", "entity1", "gw1:OWNER")) + .thenReturn(true); + + boolean result = SharingHelper.userHasAccess( + sharingHandler, "gw1", "alice@gw1", "entity1", ResourcePermissionType.OWNER); + + assertTrue(result); + } + + @Test + void userHasAccess_throwsRuntimeExceptionOnFailure() throws Exception { + when(sharingHandler.userHasAccess(anyString(), anyString(), anyString(), anyString())) + .thenThrow(new RuntimeException("DB error")); + + assertThrows( + RuntimeException.class, + () -> SharingHelper.userHasAccess( + sharingHandler, "gw1", "alice", "entity1", ResourcePermissionType.OWNER)); + } + + @Test + void retrieveGatewayGroups_returnsExistingGroups() throws Exception { + GatewayGroups groups = GatewayGroups.newBuilder() + .setGatewayId("gw1") + .setAdminsGroupId("admins") + .build(); + when(registryHandler.isGatewayGroupsExists("gw1")).thenReturn(true); + when(registryHandler.getGatewayGroups("gw1")).thenReturn(groups); + + GatewayGroups result = SharingHelper.retrieveGatewayGroups(registryHandler, "gw1"); + + assertEquals("gw1", result.getGatewayId()); + assertEquals("admins", result.getAdminsGroupId()); + } + + @Test + void createManageSharingPermissionTypeIfMissing_createsWhenNotExists() throws Exception { + when(sharingHandler.isPermissionExists("gw1", "gw1:MANAGE_SHARING")).thenReturn(false); + + SharingHelper.createManageSharingPermissionTypeIfMissing(sharingHandler, "gw1"); + + verify(sharingHandler).createPermissionType(argThat(pt -> pt.getPermissionTypeId().equals("gw1:MANAGE_SHARING") + && pt.getDomainId().equals("gw1") + && pt.getName().equals("MANAGE_SHARING"))); + } + + @Test + void createManageSharingPermissionTypeIfMissing_skipsWhenExists() throws Exception { + when(sharingHandler.isPermissionExists("gw1", "gw1:MANAGE_SHARING")).thenReturn(true); + + SharingHelper.createManageSharingPermissionTypeIfMissing(sharingHandler, "gw1"); + + verify(sharingHandler, never()).createPermissionType(any()); + } + + @Test + void shareEntityWithAdminGatewayGroups_sharesWithAdminAndReadOnlyGroups() throws Exception { + GatewayGroups groups = GatewayGroups.newBuilder() + .setGatewayId("gw1") + .setAdminsGroupId("admins") + .setReadOnlyAdminsGroupId("readOnlyAdmins") + .build(); + when(registryHandler.isGatewayGroupsExists("gw1")).thenReturn(true); + when(registryHandler.getGatewayGroups("gw1")).thenReturn(groups); + when(sharingHandler.isPermissionExists("gw1", "gw1:MANAGE_SHARING")).thenReturn(true); + + org.apache.airavata.sharing.model.EntityEntity entity = new org.apache.airavata.sharing.model.EntityEntity(); + entity.setDomainId("gw1"); + entity.setEntityId("entity1"); + + SharingHelper.shareEntityWithAdminGatewayGroups(sharingHandler, registryHandler, entity); + + verify(sharingHandler) + .shareEntityWithGroups("gw1", "entity1", Arrays.asList("admins"), "gw1:MANAGE_SHARING", true); + verify(sharingHandler).shareEntityWithGroups("gw1", "entity1", Arrays.asList("admins"), "gw1:WRITE", true); + verify(sharingHandler) + .shareEntityWithGroups( + "gw1", "entity1", Arrays.asList("admins", "readOnlyAdmins"), "gw1:READ", true); + } +} diff --git a/airavata-api/storage-service/src/test/java/org/apache/airavata/storage/service/AirvataFileServiceTest.java b/airavata-api/storage-service/src/test/java/org/apache/airavata/storage/service/AirvataFileServiceTest.java new file mode 100644 index 0000000000..33be634d89 --- /dev/null +++ b/airavata-api/storage-service/src/test/java/org/apache/airavata/storage/service/AirvataFileServiceTest.java @@ -0,0 +1,199 @@ +/** +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ +package org.apache.airavata.storage.service; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.nio.file.Path; +import java.util.List; +import org.apache.airavata.compute.util.AgentAdaptor; +import org.apache.airavata.execution.orchestrator.AdaptorSupport; +import org.apache.airavata.execution.handler.RegistryServerHandler; +import org.apache.airavata.storage.model.AiravataDirectory; +import org.apache.airavata.storage.model.AiravataFile; +import org.apache.airavata.storage.util.FileMetadata; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockedConstruction; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +class AirvataFileServiceTest { + + @Mock + AdaptorSupport adaptorSupport; + + @Mock + RegistryServerHandler registryClient; + + @Mock + AgentAdaptor agentAdaptor; + + @InjectMocks + AirvataFileService fileService; + + @Test + void getInfo_returnsFileMetadata() throws Exception { + FileMetadata metadata = new FileMetadata(); + metadata.setName("test.txt"); + metadata.setSize(1024); + + try (MockedConstruction<ProcessDataManager> mocked = mockConstruction(ProcessDataManager.class, (mock, ctx) -> { + when(mock.getAgentAdaptor()).thenReturn(agentAdaptor); + when(mock.getBaseDir()).thenReturn("/home/user/"); + })) { + when(agentAdaptor.getFileMetadata("/home/user/subdir/test.txt")).thenReturn(metadata); + + FileMetadata result = fileService.getInfo("proc-1", "subdir/test.txt"); + + assertEquals("test.txt", result.getName()); + assertEquals(1024, result.getSize()); + } + } + + @Test + void listDir_returnsDirectoryWithContents() throws Exception { + FileMetadata dirMeta = new FileMetadata(); + dirMeta.setName("workdir"); + dirMeta.setSize(4096); + dirMeta.setDirectory(true); + + FileMetadata fileMeta = new FileMetadata(); + fileMeta.setName("output.txt"); + fileMeta.setSize(512); + fileMeta.setDirectory(false); + + FileMetadata subDirMeta = new FileMetadata(); + subDirMeta.setName("logs"); + subDirMeta.setSize(4096); + subDirMeta.setDirectory(true); + + try (MockedConstruction<ProcessDataManager> mocked = mockConstruction(ProcessDataManager.class, (mock, ctx) -> { + when(mock.getAgentAdaptor()).thenReturn(agentAdaptor); + when(mock.getBaseDir()).thenReturn("/home/user/"); + })) { + when(agentAdaptor.getFileMetadata("/home/user/workdir")).thenReturn(dirMeta); + when(agentAdaptor.listDirectory("/home/user/workdir")).thenReturn(List.of("output.txt", "logs")); + when(agentAdaptor.getFileMetadata("/home/user/workdir/output.txt")).thenReturn(fileMeta); + when(agentAdaptor.getFileMetadata("/home/user/workdir/logs")).thenReturn(subDirMeta); + + AiravataDirectory result = fileService.listDir("proc-1", "workdir"); + + assertEquals("workdir", result.getDirectoryName()); + assertEquals(1, result.getInnerFiles().size()); + assertEquals("output.txt", result.getInnerFiles().get(0).getFileName()); + assertEquals(1, result.getInnerDirectories().size()); + assertEquals("logs", result.getInnerDirectories().get(0).getDirectoryName()); + } + } + + @Test + void listDir_throwsWhenPathIsNotDirectory() throws Exception { + FileMetadata fileMeta = new FileMetadata(); + fileMeta.setName("file.txt"); + fileMeta.setDirectory(false); + + try (MockedConstruction<ProcessDataManager> mocked = mockConstruction(ProcessDataManager.class, (mock, ctx) -> { + when(mock.getAgentAdaptor()).thenReturn(agentAdaptor); + when(mock.getBaseDir()).thenReturn("/home/user/"); + })) { + when(agentAdaptor.getFileMetadata("/home/user/file.txt")).thenReturn(fileMeta); + + assertThrows(Exception.class, () -> fileService.listDir("proc-1", "file.txt")); + } + } + + @Test + void listFile_returnsFileInfo() throws Exception { + FileMetadata fileMeta = new FileMetadata(); + fileMeta.setName("data.csv"); + fileMeta.setSize(2048); + fileMeta.setDirectory(false); + + try (MockedConstruction<ProcessDataManager> mocked = mockConstruction(ProcessDataManager.class, (mock, ctx) -> { + when(mock.getAgentAdaptor()).thenReturn(agentAdaptor); + when(mock.getBaseDir()).thenReturn("/home/user/"); + })) { + when(agentAdaptor.getFileMetadata("/home/user/data.csv")).thenReturn(fileMeta); + + AiravataFile result = fileService.listFile("proc-1", "data.csv"); + + assertEquals("data.csv", result.getFileName()); + assertEquals(2048, result.getFileSize()); + } + } + + @Test + void uploadFile_delegatesToAdaptor() throws Exception { + byte[] content = "hello world".getBytes(); + InputStream inputStream = new ByteArrayInputStream(content); + + try (MockedConstruction<ProcessDataManager> mocked = mockConstruction(ProcessDataManager.class, (mock, ctx) -> { + when(mock.getAgentAdaptor()).thenReturn(agentAdaptor); + when(mock.getBaseDir()).thenReturn("/home/user/"); + })) { + fileService.uploadFile("proc-1", "output/test.txt", inputStream, "test.txt", content.length); + + verify(agentAdaptor).createDirectory("/home/user/output", true); + verify(agentAdaptor).uploadFile(any(InputStream.class), any(FileMetadata.class), eq("/home/user/output/test.txt")); + } + } + + @Test + void downloadFile_returnsPathWhenFileExists() throws Exception { + try (MockedConstruction<ProcessDataManager> mocked = mockConstruction(ProcessDataManager.class, (mock, ctx) -> { + when(mock.getAgentAdaptor()).thenReturn(agentAdaptor); + when(mock.getBaseDir()).thenReturn("/home/user/"); + })) { + when(agentAdaptor.doesFileExist("/home/user/result.txt")).thenReturn(true); + doAnswer(invocation -> { + // Simulate download by creating the temp file content + String localPath = invocation.getArgument(1); + java.nio.file.Files.writeString(Path.of(localPath), "file content"); + return null; + }).when(agentAdaptor).downloadFile(eq("/home/user/result.txt"), anyString()); + + Path result = fileService.downloadFile("proc-1", "result.txt"); + + assertNotNull(result); + assertTrue(result.toFile().exists()); + // cleanup + result.toFile().delete(); + } + } + + @Test + void downloadFile_throwsWhenFileDoesNotExist() throws Exception { + try (MockedConstruction<ProcessDataManager> mocked = mockConstruction(ProcessDataManager.class, (mock, ctx) -> { + when(mock.getAgentAdaptor()).thenReturn(agentAdaptor); + when(mock.getBaseDir()).thenReturn("/home/user/"); + })) { + when(agentAdaptor.doesFileExist("/home/user/missing.txt")).thenReturn(false); + + assertThrows(Exception.class, () -> fileService.downloadFile("proc-1", "missing.txt")); + } + } +}
