You are viewing a plain text version of this content. The canonical link for it is here.
Posted to issues@airavata.apache.org by "Copilot (via GitHub)" <gi...@apache.org> on 2025/11/29 16:05:10 UTC

Re: [PR] Experiment cleanup support test cases [airavata]

Copilot commented on code in PR #583:
URL: https://github.com/apache/airavata/pull/583#discussion_r2573076995


##########
airavata-api/src/main/java/org/apache/airavata/helix/adaptor/SSHJAgentAdaptor.java:
##########
@@ -277,6 +277,51 @@ public void createDirectory(String path, boolean recursive) throws AgentExceptio
         }
     }
 
+
+    private void deleteDirectoryRecursively(SFTPClientWrapper sftpClient, String path) throws IOException {
+        FileAttributes lstat = sftpClient.lstat(path);
+        if (lstat.getMode().getType() == Type.DIRECTORY) {
+            List<RemoteResourceInfo> ls = sftpClient.ls(path);
+            if (ls == null || ls.isEmpty()) {
+                sftpClient.rmdir(path);
+            } else {
+                for (RemoteResourceInfo r : ls) {

Review Comment:
   Missing filter for special directory entries. The `ls` command typically returns "." and ".." entries in directory listings. The recursive delete function doesn't filter these out, which could cause issues or inefficiency. Consider filtering out these entries before processing: `for (RemoteResourceInfo r : ls) { if (".".equals(r.getName()) || "..".equals(r.getName())) continue; ... }`
   ```suggestion
                   for (RemoteResourceInfo r : ls) {
                       if (".".equals(r.getName()) || "..".equals(r.getName())) {
                           continue;
                       }
   ```



##########
airavata-api/src/test/java/org/apache/airavata/helix/SFTPDeleteDirTest.java:
##########
@@ -0,0 +1,173 @@
+package org.apache.airavata.helix;
+
+import org.apache.airavata.helix.adaptor.SSHJAgentAdaptor;
+import org.apache.sshd.common.file.virtualfs.VirtualFileSystemFactory;
+import org.apache.sshd.server.SshServer;
+import org.apache.sshd.server.config.keys.AuthorizedKeysAuthenticator;
+import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider;
+import org.apache.sshd.sftp.server.SftpSubsystemFactory;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Collections;
+import java.util.List;
+
+public class SFTPDeleteDirTest {
+
+    private SshServer sshd;
+    private int port;
+    private Path sftpRootDir;
+    private int sftpPort = 52122;
+
+    private String privateKey = "-----BEGIN OPENSSH PRIVATE KEY-----\n" +
+            "b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABCHHZONdz\n" +
+            "yrWLbnw4nyEw3BAAAAGAAAAAEAAAGXAAAAB3NzaC1yc2EAAAADAQABAAABgQCxhKaGaUlU\n" +
+            "Znlr6OtFQ7hjVceZsWLBaIWB5NUwp45IoWLm7Hnor+Y8J/SwLBgdUSsjxkMUQMMbJdY/rP\n" +
+            "Gwc8aW0K1JMjSNhv03dBxvXHdY+NSd24WjSezD89l6v78lGhVQ5g3rI4eTFsfPy2WSxZw1\n" +
+            "Fo0UUDVzzBtLuvC9ZCWd3nsT8Ox4LnZWLHrrRxGX2eCotEEO6CT1+wmk4szIkeDDmX79Tb\n" +
+            "KatcN2vv7H6WjsoGH1bhc1QwS2/hmOdBwqGfm+sE0BI3VgMJ1NVDQrnt0IXlMtLH9Feg1y\n" +
+            "tdagCzaHulQ9lHn1wBzSARP/NqYzu2vNwpWSSJeafClHpA8yF/9FW9gOi4k+Oo949b+Xd1\n" +
+            "NHqjt+7lnlsepm0IsgfJ9Gr/0sweYfUSsTfGZGMstSRMu8V+bD1BaVqXQKZ80XoCm0NMnR\n" +
+            "Chm109wXtt5+0atDmIFiy1Byr8QjwjqsIap1j93R/8R3L3mhUmLruSl7IPKPhjShEIL253\n" +
+            "GpoHiSENae9e0AAAWgEaos8m239pnUDpWU3N9VtUvg3XVh9WC1YwL9wg1rnl+uW3ygA4Xq\n" +
+            "VvGUEc5Xx5AR3buKaYGI7+Tb4RAwQL8HkQS78mDtmSiNKJbxmUWkLIWERBe/OZGO/HYPSl\n" +
+            "WS3nkXogcYy5Q/9Fy4U35Trg82yq/kaSjIneJAGLz0ShbQNgWBtnpzK8eHqceoMFYQsvZ+\n" +
+            "eaK3JWTwQPgXinj2E37OU5N0y5ncZ8yQ5bKEbOBZ62uYdZFnIgQhz9oNVS8ShIVZtBC0h4\n" +
+            "ytl45Tdsd4H8cy2RMzzvvLtsfnvA6EOzj5exSNrtsbjZMFvK7f1oatKkm71IknvieGr0nh\n" +
+            "qvmR+qc15wwnmmFus9MFpqxsOKdPzkeSvBjhe9Oj5Qc9g9ecNHuSuS7MTRcx6UFmB9tvo+\n" +
+            "iLW0uEzIguQSyaAo1VBNgbr+wz11TaB+rhi2krdUc59skS6/mrah7gJr0kGAJowLR+YGjN\n" +
+            "/UTJpaEhMWkktuAznY56qs7AlHqKzcNq+258LpIOQJzN9/gw9IB2rz0PNnA+NqDCHttQLw\n" +
+            "0dZe/oPHJQ6vI/5ykakSas5GJZOph5udSz05ndM5kRoMOGHhi8WeYA0vFBed3BH+lkZ59K\n" +
+            "z+vjf4sGmOb0ptW95QA9ZeMN899QvuCYOgnuyCPguVL3SsRkQ9AXmOrLT4oPTSUOY3t7vv\n" +
+            "GI5WN5ZN9zYtT21bOMqYi+cHlIhnaqz+GjRpEfGaqJFPLcj1tVznHbi+2HHCG0M+NTjw9G\n" +
+            "JRjAjqOfkJZ0/7KmfBT7lGWNPPNgXtYPDdYRHHiIeDMLu4s2gBbqn8pmIdG14K4IqLl7uC\n" +
+            "payMNJxmQ75oRFpv3Vtf31FlpnsT762iS0e7P0CwBxVZyjdCYet9IVjw6MJC62svnTDznn\n" +
+            "0ZxPdz78acoXlBkH67zDH69LyPGZlZ9e7HeKrMbOTU5mnUfSiHc3mk8PYEuphnKXFd8Zzi\n" +
+            "bc/SfaxLbf19MsuqlM+gqKR9hVqDn6Ri9JAmHJBgFNc5hdLSKucunNFFamCslCXRkB3TNl\n" +
+            "pbPxSLMJ9UDTcrRnzgi5zyQxSe3K8tspqhXQ6ek5Z2sZ+zZuFzcKzgUcd8fpYxC9lZvJ1b\n" +
+            "pS8OCuGUI6KHHmGJmNKBTbxvp0B4EjRIy3lDJDBMap/GN9GsgqscrvYPIfqlnVR7GXN+qj\n" +
+            "MgOsue1jtVzG1SBAmBxcctEFLzBsr4k/fNNTXPt/mPKeO3w59zt1OSPyNx63NbNmo/uWO1\n" +
+            "8P24MBcO5crhlYa5ptb6Fvi1/j6Yrg1NYDPutRopcZNemEFPkR4dqW5AhJwT8L8hqZmmhs\n" +
+            "DH97qNiqkqyVmrRIygnVMdYqXsn/uV8yEb5mgRw8C6fJ7OZsvwsSfy052tBKJhj/63Ay/S\n" +
+            "wJ+HxQ/8vthvEkXsaJWiQ2RwatZIoVpOhYEpKwSDuBHMKrnMiCow13+pAq9Gf/CbXUd/Ko\n" +
+            "xNQ8RZ8lkreUDjJJhTXRRcpufJChL6zQj9bat6E9QBq4l1XjGDhAqgfvQT/1fDataZW3vW\n" +
+            "skze0s7diqtYIWNlx2+4vGxL38pSCSqtOWjHS6Rbjf37ERKQMH57z4w3aEiahtBcgKTWBy\n" +
+            "n4UD18TfLGd2i7jtENLxOcWBFzRxtIbFnKGiktLcp0XILs/lOhtRF+K2abiif26rDx++jI\n" +
+            "4iQCet6ltdeQJLekjmNh4/9Y4hCf5yx9lKuGbzGeZPI66ClbY+R2l29ZXUNUxZmVKM4BDw\n" +
+            "2LqMlVLcM1Nzg6ftQ09Dku1ApX/uKeOaf0I0DPaBwVD+iTGCeZWuOg5b1LZUuxxYT4ZB6F\n" +
+            "hoZ8/1mt5gTzo4XdZCmJ7jCOqEc75JV2NEfcIwpy6TOZPVMMWFYT88OgkF86Vxx8GR0FQU\n" +
+            "CLSDGVZjFU7kv1eKpDJ0oETyGBELC1PPMpm90nxCkzCx7uQw\n" +
+            "-----END OPENSSH PRIVATE KEY-----\n";
+
+    private String publicKey = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCxhKaGaUlUZnlr6OtFQ7hjVceZsWLBaIWB5NUwp45IoWLm7Hnor+Y8J/SwLBgdUSsjxkMUQMMbJdY/rPGwc8aW0K1JMjSNhv03dBxvXHdY+NSd24WjSezD89l6v78lGhVQ5g3rI4eTFsfPy2WSxZw1Fo0UUDVzzBtLuvC9ZCWd3nsT8Ox4LnZWLHrrRxGX2eCotEEO6CT1+wmk4szIkeDDmX79TbKatcN2vv7H6WjsoGH1bhc1QwS2/hmOdBwqGfm+sE0BI3VgMJ1NVDQrnt0IXlMtLH9Feg1ytdagCzaHulQ9lHn1wBzSARP/NqYzu2vNwpWSSJeafClHpA8yF/9FW9gOi4k+Oo949b+Xd1NHqjt+7lnlsepm0IsgfJ9Gr/0sweYfUSsTfGZGMstSRMu8V+bD1BaVqXQKZ80XoCm0NMnRChm109wXtt5+0atDmIFiy1Byr8QjwjqsIap1j93R/8R3L3mhUmLruSl7IPKPhjShEIL253GpoHiSENae9e0= dwannipu@Dimuthus-MacBook-Pro.local";
+    private String passphrase = "airavata";
+
+    @BeforeEach
+    void setUp() throws Exception {
+        sftpRootDir = Files.createTempDirectory("sftp-root-");
+        sftpRootDir.toFile().deleteOnExit();
+
+        Path authorizedKeysFile = Files.createTempFile("authorized_keys-", "");
+        Files.write(authorizedKeysFile, publicKey.getBytes(StandardCharsets.UTF_8));
+        authorizedKeysFile.toFile().deleteOnExit();
+
+        sshd = SshServer.setUpDefaultServer();
+        sshd.setHost("localhost");
+        sshd.setPort(sftpPort);
+
+        // Host key (for the server itself, unrelated to client auth)
+        sshd.setKeyPairProvider(new SimpleGeneratorHostKeyProvider());
+
+        // SFTP subsystem
+        sshd.setSubsystemFactories(
+                Collections.singletonList(new SftpSubsystemFactory.Builder().build())
+        );
+
+        // Virtual root
+        sshd.setFileSystemFactory(new VirtualFileSystemFactory(sftpRootDir));
+
+        // *** AUTH CONFIG ***
+
+        // 1) Disable password auth (only key-based logins allowed)
+        sshd.setPasswordAuthenticator(null);
+
+        sshd.setPublickeyAuthenticator(new AuthorizedKeysAuthenticator(authorizedKeysFile));
+
+        sshd.start();
+        port = sshd.getPort();
+    }
+
+    @AfterEach
+    void tearDown() throws Exception {
+        if (sshd != null && !sshd.isClosed()) {
+            sshd.stop(true);
+        }
+    }
+
+    public void createFilesInDir(Path root) throws IOException {
+        Path dir1 = Files.createDirectory(root.resolve("dir1"));
+        dir1.toFile().deleteOnExit();
+        Path dir2 = Files.createDirectory(root.resolve("dir2"));
+        dir2.toFile().deleteOnExit();
+
+        Path file1 = Files.createFile(root.resolve("file1.txt"));
+        file1.toFile().deleteOnExit();
+        Path file2 = Files.createFile(root.resolve("file2.txt"));
+        file2.toFile().deleteOnExit();
+
+        Path file3 = Files.createFile(dir1.resolve("file3.txt"));
+        file3.toFile().deleteOnExit();
+        Path file4 = Files.createFile(dir1.resolve("file4.txt"));
+        file4.toFile().deleteOnExit();
+
+        Files.writeString(file1, "Hello from file1\n");
+        Files.writeString(file2, "Hello from file2\n");
+        Files.writeString(file3, "Hello from file3\n");
+        Files.writeString(file4, "Hello from file4\n");
+    }
+
+    @Test
+    public void deleteNonEmptyDir() throws Exception {
+        System.out.printf("Root dir: %s\n", sftpRootDir);
+
+        Path dir1 = Files.createDirectory(sftpRootDir.resolve("dir1"));
+        dir1.toFile().deleteOnExit();
+        Path dir2 = Files.createDirectory(sftpRootDir.resolve("dir2"));
+        dir2.toFile().deleteOnExit();
+
+        createFilesInDir(dir1);
+        createFilesInDir(dir2);
+
+        SSHJAgentAdaptor adaptor = new SSHJAgentAdaptor();
+        adaptor.init("testuser", "localhost", sftpPort, publicKey, privateKey, passphrase);
+
+        List<String> itemsBefore = adaptor.listDirectory("/");
+        adaptor.deleteDirectory("dir1");
+        List<String> itemsAfter = adaptor.listDirectory("/");
+        System.out.printf("Before: %s\n", itemsBefore);
+        System.out.printf("After: %s\n", itemsAfter);
+
+    }

Review Comment:
   Missing assertions in test method. The test `deleteNonEmptyDir` verifies that a non-empty directory can be deleted but doesn't include any assertions to validate the expected behavior. The test should verify that 'dir1' is actually removed and no longer appears in `itemsAfter`.



##########
airavata-api/src/main/java/org/apache/airavata/registry/core/entities/expcatalog/ExperimentEntity.java:
##########
@@ -50,7 +50,7 @@ public class ExperimentEntity implements Serializable {
 
     @Column(name = "CLEANUP_STRATEGY")
     @Enumerated(EnumType.STRING)
-    public ExperimentCleanupStrategy cleanupStrategy;
+    public ExperimentCleanupStrategy cleanUpStrategy;

Review Comment:
   [nitpick] Inconsistent naming: The field name has been changed from `cleanupStrategy` to `cleanUpStrategy`. While "cleanup" is often written as one word in technical contexts, this change should be consistent with the column name "CLEANUP_STRATEGY" (which uses the single-word form). Consider whether this naming inconsistency between the Java field and database column is intentional.
   ```suggestion
       public ExperimentCleanupStrategy cleanupStrategy;
   ```



##########
airavata-api/src/main/java/org/apache/airavata/helix/adaptor/SSHJAgentAdaptor.java:
##########
@@ -277,6 +277,51 @@ public void createDirectory(String path, boolean recursive) throws AgentExceptio
         }
     }
 
+
+    private void deleteDirectoryRecursively(SFTPClientWrapper sftpClient, String path) throws IOException {
+        FileAttributes lstat = sftpClient.lstat(path);
+        if (lstat.getMode().getType() == Type.DIRECTORY) {
+            List<RemoteResourceInfo> ls = sftpClient.ls(path);
+            if (ls == null || ls.isEmpty()) {
+                sftpClient.rmdir(path);
+            } else {
+                for (RemoteResourceInfo r : ls) {
+                    deleteDirectoryRecursively(sftpClient, path + "/" + r.getName());

Review Comment:
   Path concatenation issue: Using string concatenation with "/" to build paths (e.g., `path + "/" + r.getName()`) may cause issues on Windows systems or when dealing with paths that already end with a separator. Consider using proper path handling utilities or checking if the path already ends with a separator before concatenating.



##########
airavata-api/src/test/java/org/apache/airavata/helix/SFTPDeleteDirTest.java:
##########
@@ -0,0 +1,173 @@
+package org.apache.airavata.helix;
+
+import org.apache.airavata.helix.adaptor.SSHJAgentAdaptor;
+import org.apache.sshd.common.file.virtualfs.VirtualFileSystemFactory;
+import org.apache.sshd.server.SshServer;
+import org.apache.sshd.server.config.keys.AuthorizedKeysAuthenticator;
+import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider;
+import org.apache.sshd.sftp.server.SftpSubsystemFactory;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Collections;
+import java.util.List;
+
+public class SFTPDeleteDirTest {
+
+    private SshServer sshd;
+    private int port;
+    private Path sftpRootDir;
+    private int sftpPort = 52122;
+
+    private String privateKey = "-----BEGIN OPENSSH PRIVATE KEY-----\n" +
+            "b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABCHHZONdz\n" +
+            "yrWLbnw4nyEw3BAAAAGAAAAAEAAAGXAAAAB3NzaC1yc2EAAAADAQABAAABgQCxhKaGaUlU\n" +
+            "Znlr6OtFQ7hjVceZsWLBaIWB5NUwp45IoWLm7Hnor+Y8J/SwLBgdUSsjxkMUQMMbJdY/rP\n" +
+            "Gwc8aW0K1JMjSNhv03dBxvXHdY+NSd24WjSezD89l6v78lGhVQ5g3rI4eTFsfPy2WSxZw1\n" +
+            "Fo0UUDVzzBtLuvC9ZCWd3nsT8Ox4LnZWLHrrRxGX2eCotEEO6CT1+wmk4szIkeDDmX79Tb\n" +
+            "KatcN2vv7H6WjsoGH1bhc1QwS2/hmOdBwqGfm+sE0BI3VgMJ1NVDQrnt0IXlMtLH9Feg1y\n" +
+            "tdagCzaHulQ9lHn1wBzSARP/NqYzu2vNwpWSSJeafClHpA8yF/9FW9gOi4k+Oo949b+Xd1\n" +
+            "NHqjt+7lnlsepm0IsgfJ9Gr/0sweYfUSsTfGZGMstSRMu8V+bD1BaVqXQKZ80XoCm0NMnR\n" +
+            "Chm109wXtt5+0atDmIFiy1Byr8QjwjqsIap1j93R/8R3L3mhUmLruSl7IPKPhjShEIL253\n" +
+            "GpoHiSENae9e0AAAWgEaos8m239pnUDpWU3N9VtUvg3XVh9WC1YwL9wg1rnl+uW3ygA4Xq\n" +
+            "VvGUEc5Xx5AR3buKaYGI7+Tb4RAwQL8HkQS78mDtmSiNKJbxmUWkLIWERBe/OZGO/HYPSl\n" +
+            "WS3nkXogcYy5Q/9Fy4U35Trg82yq/kaSjIneJAGLz0ShbQNgWBtnpzK8eHqceoMFYQsvZ+\n" +
+            "eaK3JWTwQPgXinj2E37OU5N0y5ncZ8yQ5bKEbOBZ62uYdZFnIgQhz9oNVS8ShIVZtBC0h4\n" +
+            "ytl45Tdsd4H8cy2RMzzvvLtsfnvA6EOzj5exSNrtsbjZMFvK7f1oatKkm71IknvieGr0nh\n" +
+            "qvmR+qc15wwnmmFus9MFpqxsOKdPzkeSvBjhe9Oj5Qc9g9ecNHuSuS7MTRcx6UFmB9tvo+\n" +
+            "iLW0uEzIguQSyaAo1VBNgbr+wz11TaB+rhi2krdUc59skS6/mrah7gJr0kGAJowLR+YGjN\n" +
+            "/UTJpaEhMWkktuAznY56qs7AlHqKzcNq+258LpIOQJzN9/gw9IB2rz0PNnA+NqDCHttQLw\n" +
+            "0dZe/oPHJQ6vI/5ykakSas5GJZOph5udSz05ndM5kRoMOGHhi8WeYA0vFBed3BH+lkZ59K\n" +
+            "z+vjf4sGmOb0ptW95QA9ZeMN899QvuCYOgnuyCPguVL3SsRkQ9AXmOrLT4oPTSUOY3t7vv\n" +
+            "GI5WN5ZN9zYtT21bOMqYi+cHlIhnaqz+GjRpEfGaqJFPLcj1tVznHbi+2HHCG0M+NTjw9G\n" +
+            "JRjAjqOfkJZ0/7KmfBT7lGWNPPNgXtYPDdYRHHiIeDMLu4s2gBbqn8pmIdG14K4IqLl7uC\n" +
+            "payMNJxmQ75oRFpv3Vtf31FlpnsT762iS0e7P0CwBxVZyjdCYet9IVjw6MJC62svnTDznn\n" +
+            "0ZxPdz78acoXlBkH67zDH69LyPGZlZ9e7HeKrMbOTU5mnUfSiHc3mk8PYEuphnKXFd8Zzi\n" +
+            "bc/SfaxLbf19MsuqlM+gqKR9hVqDn6Ri9JAmHJBgFNc5hdLSKucunNFFamCslCXRkB3TNl\n" +
+            "pbPxSLMJ9UDTcrRnzgi5zyQxSe3K8tspqhXQ6ek5Z2sZ+zZuFzcKzgUcd8fpYxC9lZvJ1b\n" +
+            "pS8OCuGUI6KHHmGJmNKBTbxvp0B4EjRIy3lDJDBMap/GN9GsgqscrvYPIfqlnVR7GXN+qj\n" +
+            "MgOsue1jtVzG1SBAmBxcctEFLzBsr4k/fNNTXPt/mPKeO3w59zt1OSPyNx63NbNmo/uWO1\n" +
+            "8P24MBcO5crhlYa5ptb6Fvi1/j6Yrg1NYDPutRopcZNemEFPkR4dqW5AhJwT8L8hqZmmhs\n" +
+            "DH97qNiqkqyVmrRIygnVMdYqXsn/uV8yEb5mgRw8C6fJ7OZsvwsSfy052tBKJhj/63Ay/S\n" +
+            "wJ+HxQ/8vthvEkXsaJWiQ2RwatZIoVpOhYEpKwSDuBHMKrnMiCow13+pAq9Gf/CbXUd/Ko\n" +
+            "xNQ8RZ8lkreUDjJJhTXRRcpufJChL6zQj9bat6E9QBq4l1XjGDhAqgfvQT/1fDataZW3vW\n" +
+            "skze0s7diqtYIWNlx2+4vGxL38pSCSqtOWjHS6Rbjf37ERKQMH57z4w3aEiahtBcgKTWBy\n" +
+            "n4UD18TfLGd2i7jtENLxOcWBFzRxtIbFnKGiktLcp0XILs/lOhtRF+K2abiif26rDx++jI\n" +
+            "4iQCet6ltdeQJLekjmNh4/9Y4hCf5yx9lKuGbzGeZPI66ClbY+R2l29ZXUNUxZmVKM4BDw\n" +
+            "2LqMlVLcM1Nzg6ftQ09Dku1ApX/uKeOaf0I0DPaBwVD+iTGCeZWuOg5b1LZUuxxYT4ZB6F\n" +
+            "hoZ8/1mt5gTzo4XdZCmJ7jCOqEc75JV2NEfcIwpy6TOZPVMMWFYT88OgkF86Vxx8GR0FQU\n" +
+            "CLSDGVZjFU7kv1eKpDJ0oETyGBELC1PPMpm90nxCkzCx7uQw\n" +
+            "-----END OPENSSH PRIVATE KEY-----\n";
+
+    private String publicKey = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCxhKaGaUlUZnlr6OtFQ7hjVceZsWLBaIWB5NUwp45IoWLm7Hnor+Y8J/SwLBgdUSsjxkMUQMMbJdY/rPGwc8aW0K1JMjSNhv03dBxvXHdY+NSd24WjSezD89l6v78lGhVQ5g3rI4eTFsfPy2WSxZw1Fo0UUDVzzBtLuvC9ZCWd3nsT8Ox4LnZWLHrrRxGX2eCotEEO6CT1+wmk4szIkeDDmX79TbKatcN2vv7H6WjsoGH1bhc1QwS2/hmOdBwqGfm+sE0BI3VgMJ1NVDQrnt0IXlMtLH9Feg1ytdagCzaHulQ9lHn1wBzSARP/NqYzu2vNwpWSSJeafClHpA8yF/9FW9gOi4k+Oo949b+Xd1NHqjt+7lnlsepm0IsgfJ9Gr/0sweYfUSsTfGZGMstSRMu8V+bD1BaVqXQKZ80XoCm0NMnRChm109wXtt5+0atDmIFiy1Byr8QjwjqsIap1j93R/8R3L3mhUmLruSl7IPKPhjShEIL253GpoHiSENae9e0= dwannipu@Dimuthus-MacBook-Pro.local";
+    private String passphrase = "airavata";
+
+    @BeforeEach
+    void setUp() throws Exception {
+        sftpRootDir = Files.createTempDirectory("sftp-root-");
+        sftpRootDir.toFile().deleteOnExit();
+
+        Path authorizedKeysFile = Files.createTempFile("authorized_keys-", "");
+        Files.write(authorizedKeysFile, publicKey.getBytes(StandardCharsets.UTF_8));
+        authorizedKeysFile.toFile().deleteOnExit();
+
+        sshd = SshServer.setUpDefaultServer();
+        sshd.setHost("localhost");
+        sshd.setPort(sftpPort);
+
+        // Host key (for the server itself, unrelated to client auth)
+        sshd.setKeyPairProvider(new SimpleGeneratorHostKeyProvider());
+
+        // SFTP subsystem
+        sshd.setSubsystemFactories(
+                Collections.singletonList(new SftpSubsystemFactory.Builder().build())
+        );
+
+        // Virtual root
+        sshd.setFileSystemFactory(new VirtualFileSystemFactory(sftpRootDir));
+
+        // *** AUTH CONFIG ***
+
+        // 1) Disable password auth (only key-based logins allowed)
+        sshd.setPasswordAuthenticator(null);
+
+        sshd.setPublickeyAuthenticator(new AuthorizedKeysAuthenticator(authorizedKeysFile));
+
+        sshd.start();
+        port = sshd.getPort();
+    }
+
+    @AfterEach
+    void tearDown() throws Exception {
+        if (sshd != null && !sshd.isClosed()) {
+            sshd.stop(true);
+        }
+    }
+
+    public void createFilesInDir(Path root) throws IOException {
+        Path dir1 = Files.createDirectory(root.resolve("dir1"));
+        dir1.toFile().deleteOnExit();
+        Path dir2 = Files.createDirectory(root.resolve("dir2"));
+        dir2.toFile().deleteOnExit();
+
+        Path file1 = Files.createFile(root.resolve("file1.txt"));
+        file1.toFile().deleteOnExit();
+        Path file2 = Files.createFile(root.resolve("file2.txt"));
+        file2.toFile().deleteOnExit();
+
+        Path file3 = Files.createFile(dir1.resolve("file3.txt"));
+        file3.toFile().deleteOnExit();
+        Path file4 = Files.createFile(dir1.resolve("file4.txt"));
+        file4.toFile().deleteOnExit();
+
+        Files.writeString(file1, "Hello from file1\n");
+        Files.writeString(file2, "Hello from file2\n");
+        Files.writeString(file3, "Hello from file3\n");
+        Files.writeString(file4, "Hello from file4\n");
+    }
+
+    @Test
+    public void deleteNonEmptyDir() throws Exception {
+        System.out.printf("Root dir: %s\n", sftpRootDir);
+
+        Path dir1 = Files.createDirectory(sftpRootDir.resolve("dir1"));
+        dir1.toFile().deleteOnExit();
+        Path dir2 = Files.createDirectory(sftpRootDir.resolve("dir2"));
+        dir2.toFile().deleteOnExit();
+
+        createFilesInDir(dir1);
+        createFilesInDir(dir2);
+
+        SSHJAgentAdaptor adaptor = new SSHJAgentAdaptor();
+        adaptor.init("testuser", "localhost", sftpPort, publicKey, privateKey, passphrase);
+
+        List<String> itemsBefore = adaptor.listDirectory("/");
+        adaptor.deleteDirectory("dir1");
+        List<String> itemsAfter = adaptor.listDirectory("/");
+        System.out.printf("Before: %s\n", itemsBefore);
+        System.out.printf("After: %s\n", itemsAfter);
+
+    }
+
+    @Test
+    public void deleteEmptyDir() throws Exception {
+        Path dir1 = Files.createDirectory(sftpRootDir.resolve("dir1"));
+        dir1.toFile().deleteOnExit();
+
+        SSHJAgentAdaptor adaptor = new SSHJAgentAdaptor();
+        adaptor.init("testuser", "localhost", sftpPort, publicKey, privateKey, passphrase);
+        List<String> itemsBefore = adaptor.listDirectory("/");
+        adaptor.deleteDirectory("dir1");
+        List<String> itemsAfter = adaptor.listDirectory("/");
+
+        Assertions.assertTrue(itemsBefore.get(0).equals("dir1"));

Review Comment:
   Unsafe assertion that assumes ordering. The assertion `itemsBefore.get(0).equals("dir1")` assumes that "dir1" will be the first item in the list, but directory listings may not have a guaranteed order. Consider using `itemsBefore.contains("dir1")` or sorting the list before checking the first element.
   ```suggestion
           Assertions.assertTrue(itemsBefore.contains("dir1"));
   ```



##########
airavata-api/src/test/java/org/apache/airavata/helix/SFTPDeleteDirTest.java:
##########
@@ -0,0 +1,173 @@
+package org.apache.airavata.helix;
+
+import org.apache.airavata.helix.adaptor.SSHJAgentAdaptor;
+import org.apache.sshd.common.file.virtualfs.VirtualFileSystemFactory;
+import org.apache.sshd.server.SshServer;
+import org.apache.sshd.server.config.keys.AuthorizedKeysAuthenticator;
+import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider;
+import org.apache.sshd.sftp.server.SftpSubsystemFactory;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Collections;
+import java.util.List;
+
+public class SFTPDeleteDirTest {
+
+    private SshServer sshd;
+    private int port;
+    private Path sftpRootDir;
+    private int sftpPort = 52122;
+
+    private String privateKey = "-----BEGIN OPENSSH PRIVATE KEY-----\n" +
+            "b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABCHHZONdz\n" +
+            "yrWLbnw4nyEw3BAAAAGAAAAAEAAAGXAAAAB3NzaC1yc2EAAAADAQABAAABgQCxhKaGaUlU\n" +
+            "Znlr6OtFQ7hjVceZsWLBaIWB5NUwp45IoWLm7Hnor+Y8J/SwLBgdUSsjxkMUQMMbJdY/rP\n" +
+            "Gwc8aW0K1JMjSNhv03dBxvXHdY+NSd24WjSezD89l6v78lGhVQ5g3rI4eTFsfPy2WSxZw1\n" +
+            "Fo0UUDVzzBtLuvC9ZCWd3nsT8Ox4LnZWLHrrRxGX2eCotEEO6CT1+wmk4szIkeDDmX79Tb\n" +
+            "KatcN2vv7H6WjsoGH1bhc1QwS2/hmOdBwqGfm+sE0BI3VgMJ1NVDQrnt0IXlMtLH9Feg1y\n" +
+            "tdagCzaHulQ9lHn1wBzSARP/NqYzu2vNwpWSSJeafClHpA8yF/9FW9gOi4k+Oo949b+Xd1\n" +
+            "NHqjt+7lnlsepm0IsgfJ9Gr/0sweYfUSsTfGZGMstSRMu8V+bD1BaVqXQKZ80XoCm0NMnR\n" +
+            "Chm109wXtt5+0atDmIFiy1Byr8QjwjqsIap1j93R/8R3L3mhUmLruSl7IPKPhjShEIL253\n" +
+            "GpoHiSENae9e0AAAWgEaos8m239pnUDpWU3N9VtUvg3XVh9WC1YwL9wg1rnl+uW3ygA4Xq\n" +
+            "VvGUEc5Xx5AR3buKaYGI7+Tb4RAwQL8HkQS78mDtmSiNKJbxmUWkLIWERBe/OZGO/HYPSl\n" +
+            "WS3nkXogcYy5Q/9Fy4U35Trg82yq/kaSjIneJAGLz0ShbQNgWBtnpzK8eHqceoMFYQsvZ+\n" +
+            "eaK3JWTwQPgXinj2E37OU5N0y5ncZ8yQ5bKEbOBZ62uYdZFnIgQhz9oNVS8ShIVZtBC0h4\n" +
+            "ytl45Tdsd4H8cy2RMzzvvLtsfnvA6EOzj5exSNrtsbjZMFvK7f1oatKkm71IknvieGr0nh\n" +
+            "qvmR+qc15wwnmmFus9MFpqxsOKdPzkeSvBjhe9Oj5Qc9g9ecNHuSuS7MTRcx6UFmB9tvo+\n" +
+            "iLW0uEzIguQSyaAo1VBNgbr+wz11TaB+rhi2krdUc59skS6/mrah7gJr0kGAJowLR+YGjN\n" +
+            "/UTJpaEhMWkktuAznY56qs7AlHqKzcNq+258LpIOQJzN9/gw9IB2rz0PNnA+NqDCHttQLw\n" +
+            "0dZe/oPHJQ6vI/5ykakSas5GJZOph5udSz05ndM5kRoMOGHhi8WeYA0vFBed3BH+lkZ59K\n" +
+            "z+vjf4sGmOb0ptW95QA9ZeMN899QvuCYOgnuyCPguVL3SsRkQ9AXmOrLT4oPTSUOY3t7vv\n" +
+            "GI5WN5ZN9zYtT21bOMqYi+cHlIhnaqz+GjRpEfGaqJFPLcj1tVznHbi+2HHCG0M+NTjw9G\n" +
+            "JRjAjqOfkJZ0/7KmfBT7lGWNPPNgXtYPDdYRHHiIeDMLu4s2gBbqn8pmIdG14K4IqLl7uC\n" +
+            "payMNJxmQ75oRFpv3Vtf31FlpnsT762iS0e7P0CwBxVZyjdCYet9IVjw6MJC62svnTDznn\n" +
+            "0ZxPdz78acoXlBkH67zDH69LyPGZlZ9e7HeKrMbOTU5mnUfSiHc3mk8PYEuphnKXFd8Zzi\n" +
+            "bc/SfaxLbf19MsuqlM+gqKR9hVqDn6Ri9JAmHJBgFNc5hdLSKucunNFFamCslCXRkB3TNl\n" +
+            "pbPxSLMJ9UDTcrRnzgi5zyQxSe3K8tspqhXQ6ek5Z2sZ+zZuFzcKzgUcd8fpYxC9lZvJ1b\n" +
+            "pS8OCuGUI6KHHmGJmNKBTbxvp0B4EjRIy3lDJDBMap/GN9GsgqscrvYPIfqlnVR7GXN+qj\n" +
+            "MgOsue1jtVzG1SBAmBxcctEFLzBsr4k/fNNTXPt/mPKeO3w59zt1OSPyNx63NbNmo/uWO1\n" +
+            "8P24MBcO5crhlYa5ptb6Fvi1/j6Yrg1NYDPutRopcZNemEFPkR4dqW5AhJwT8L8hqZmmhs\n" +
+            "DH97qNiqkqyVmrRIygnVMdYqXsn/uV8yEb5mgRw8C6fJ7OZsvwsSfy052tBKJhj/63Ay/S\n" +
+            "wJ+HxQ/8vthvEkXsaJWiQ2RwatZIoVpOhYEpKwSDuBHMKrnMiCow13+pAq9Gf/CbXUd/Ko\n" +
+            "xNQ8RZ8lkreUDjJJhTXRRcpufJChL6zQj9bat6E9QBq4l1XjGDhAqgfvQT/1fDataZW3vW\n" +
+            "skze0s7diqtYIWNlx2+4vGxL38pSCSqtOWjHS6Rbjf37ERKQMH57z4w3aEiahtBcgKTWBy\n" +
+            "n4UD18TfLGd2i7jtENLxOcWBFzRxtIbFnKGiktLcp0XILs/lOhtRF+K2abiif26rDx++jI\n" +
+            "4iQCet6ltdeQJLekjmNh4/9Y4hCf5yx9lKuGbzGeZPI66ClbY+R2l29ZXUNUxZmVKM4BDw\n" +
+            "2LqMlVLcM1Nzg6ftQ09Dku1ApX/uKeOaf0I0DPaBwVD+iTGCeZWuOg5b1LZUuxxYT4ZB6F\n" +
+            "hoZ8/1mt5gTzo4XdZCmJ7jCOqEc75JV2NEfcIwpy6TOZPVMMWFYT88OgkF86Vxx8GR0FQU\n" +
+            "CLSDGVZjFU7kv1eKpDJ0oETyGBELC1PPMpm90nxCkzCx7uQw\n" +
+            "-----END OPENSSH PRIVATE KEY-----\n";
+
+    private String publicKey = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCxhKaGaUlUZnlr6OtFQ7hjVceZsWLBaIWB5NUwp45IoWLm7Hnor+Y8J/SwLBgdUSsjxkMUQMMbJdY/rPGwc8aW0K1JMjSNhv03dBxvXHdY+NSd24WjSezD89l6v78lGhVQ5g3rI4eTFsfPy2WSxZw1Fo0UUDVzzBtLuvC9ZCWd3nsT8Ox4LnZWLHrrRxGX2eCotEEO6CT1+wmk4szIkeDDmX79TbKatcN2vv7H6WjsoGH1bhc1QwS2/hmOdBwqGfm+sE0BI3VgMJ1NVDQrnt0IXlMtLH9Feg1ytdagCzaHulQ9lHn1wBzSARP/NqYzu2vNwpWSSJeafClHpA8yF/9FW9gOi4k+Oo949b+Xd1NHqjt+7lnlsepm0IsgfJ9Gr/0sweYfUSsTfGZGMstSRMu8V+bD1BaVqXQKZ80XoCm0NMnRChm109wXtt5+0atDmIFiy1Byr8QjwjqsIap1j93R/8R3L3mhUmLruSl7IPKPhjShEIL253GpoHiSENae9e0= dwannipu@Dimuthus-MacBook-Pro.local";
+    private String passphrase = "airavata";
+
+    @BeforeEach
+    void setUp() throws Exception {
+        sftpRootDir = Files.createTempDirectory("sftp-root-");
+        sftpRootDir.toFile().deleteOnExit();
+
+        Path authorizedKeysFile = Files.createTempFile("authorized_keys-", "");
+        Files.write(authorizedKeysFile, publicKey.getBytes(StandardCharsets.UTF_8));
+        authorizedKeysFile.toFile().deleteOnExit();
+
+        sshd = SshServer.setUpDefaultServer();
+        sshd.setHost("localhost");
+        sshd.setPort(sftpPort);
+
+        // Host key (for the server itself, unrelated to client auth)
+        sshd.setKeyPairProvider(new SimpleGeneratorHostKeyProvider());
+
+        // SFTP subsystem
+        sshd.setSubsystemFactories(
+                Collections.singletonList(new SftpSubsystemFactory.Builder().build())
+        );
+
+        // Virtual root
+        sshd.setFileSystemFactory(new VirtualFileSystemFactory(sftpRootDir));
+
+        // *** AUTH CONFIG ***
+
+        // 1) Disable password auth (only key-based logins allowed)
+        sshd.setPasswordAuthenticator(null);
+
+        sshd.setPublickeyAuthenticator(new AuthorizedKeysAuthenticator(authorizedKeysFile));
+
+        sshd.start();
+        port = sshd.getPort();
+    }
+
+    @AfterEach
+    void tearDown() throws Exception {
+        if (sshd != null && !sshd.isClosed()) {
+            sshd.stop(true);
+        }
+    }
+
+    public void createFilesInDir(Path root) throws IOException {
+        Path dir1 = Files.createDirectory(root.resolve("dir1"));
+        dir1.toFile().deleteOnExit();
+        Path dir2 = Files.createDirectory(root.resolve("dir2"));
+        dir2.toFile().deleteOnExit();
+
+        Path file1 = Files.createFile(root.resolve("file1.txt"));
+        file1.toFile().deleteOnExit();
+        Path file2 = Files.createFile(root.resolve("file2.txt"));
+        file2.toFile().deleteOnExit();
+
+        Path file3 = Files.createFile(dir1.resolve("file3.txt"));
+        file3.toFile().deleteOnExit();
+        Path file4 = Files.createFile(dir1.resolve("file4.txt"));
+        file4.toFile().deleteOnExit();
+
+        Files.writeString(file1, "Hello from file1\n");
+        Files.writeString(file2, "Hello from file2\n");
+        Files.writeString(file3, "Hello from file3\n");
+        Files.writeString(file4, "Hello from file4\n");
+    }
+
+    @Test
+    public void deleteNonEmptyDir() throws Exception {
+        System.out.printf("Root dir: %s\n", sftpRootDir);
+
+        Path dir1 = Files.createDirectory(sftpRootDir.resolve("dir1"));
+        dir1.toFile().deleteOnExit();
+        Path dir2 = Files.createDirectory(sftpRootDir.resolve("dir2"));
+        dir2.toFile().deleteOnExit();
+
+        createFilesInDir(dir1);
+        createFilesInDir(dir2);
+
+        SSHJAgentAdaptor adaptor = new SSHJAgentAdaptor();
+        adaptor.init("testuser", "localhost", sftpPort, publicKey, privateKey, passphrase);

Review Comment:
   Resource leak: The `SSHJAgentAdaptor` instance is created but never properly closed. The adaptor has a `destroy()` method that should be called to clean up SSH connections. Consider adding `adaptor.destroy()` in a finally block or use a try-with-resources pattern if the class implements AutoCloseable.



##########
airavata-api/src/test/java/org/apache/airavata/helix/SFTPDeleteDirTest.java:
##########
@@ -0,0 +1,173 @@
+package org.apache.airavata.helix;
+
+import org.apache.airavata.helix.adaptor.SSHJAgentAdaptor;
+import org.apache.sshd.common.file.virtualfs.VirtualFileSystemFactory;
+import org.apache.sshd.server.SshServer;
+import org.apache.sshd.server.config.keys.AuthorizedKeysAuthenticator;
+import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider;
+import org.apache.sshd.sftp.server.SftpSubsystemFactory;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Collections;
+import java.util.List;
+
+public class SFTPDeleteDirTest {
+
+    private SshServer sshd;
+    private int port;
+    private Path sftpRootDir;
+    private int sftpPort = 52122;
+
+    private String privateKey = "-----BEGIN OPENSSH PRIVATE KEY-----\n" +
+            "b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABCHHZONdz\n" +
+            "yrWLbnw4nyEw3BAAAAGAAAAAEAAAGXAAAAB3NzaC1yc2EAAAADAQABAAABgQCxhKaGaUlU\n" +
+            "Znlr6OtFQ7hjVceZsWLBaIWB5NUwp45IoWLm7Hnor+Y8J/SwLBgdUSsjxkMUQMMbJdY/rP\n" +
+            "Gwc8aW0K1JMjSNhv03dBxvXHdY+NSd24WjSezD89l6v78lGhVQ5g3rI4eTFsfPy2WSxZw1\n" +
+            "Fo0UUDVzzBtLuvC9ZCWd3nsT8Ox4LnZWLHrrRxGX2eCotEEO6CT1+wmk4szIkeDDmX79Tb\n" +
+            "KatcN2vv7H6WjsoGH1bhc1QwS2/hmOdBwqGfm+sE0BI3VgMJ1NVDQrnt0IXlMtLH9Feg1y\n" +
+            "tdagCzaHulQ9lHn1wBzSARP/NqYzu2vNwpWSSJeafClHpA8yF/9FW9gOi4k+Oo949b+Xd1\n" +
+            "NHqjt+7lnlsepm0IsgfJ9Gr/0sweYfUSsTfGZGMstSRMu8V+bD1BaVqXQKZ80XoCm0NMnR\n" +
+            "Chm109wXtt5+0atDmIFiy1Byr8QjwjqsIap1j93R/8R3L3mhUmLruSl7IPKPhjShEIL253\n" +
+            "GpoHiSENae9e0AAAWgEaos8m239pnUDpWU3N9VtUvg3XVh9WC1YwL9wg1rnl+uW3ygA4Xq\n" +
+            "VvGUEc5Xx5AR3buKaYGI7+Tb4RAwQL8HkQS78mDtmSiNKJbxmUWkLIWERBe/OZGO/HYPSl\n" +
+            "WS3nkXogcYy5Q/9Fy4U35Trg82yq/kaSjIneJAGLz0ShbQNgWBtnpzK8eHqceoMFYQsvZ+\n" +
+            "eaK3JWTwQPgXinj2E37OU5N0y5ncZ8yQ5bKEbOBZ62uYdZFnIgQhz9oNVS8ShIVZtBC0h4\n" +
+            "ytl45Tdsd4H8cy2RMzzvvLtsfnvA6EOzj5exSNrtsbjZMFvK7f1oatKkm71IknvieGr0nh\n" +
+            "qvmR+qc15wwnmmFus9MFpqxsOKdPzkeSvBjhe9Oj5Qc9g9ecNHuSuS7MTRcx6UFmB9tvo+\n" +
+            "iLW0uEzIguQSyaAo1VBNgbr+wz11TaB+rhi2krdUc59skS6/mrah7gJr0kGAJowLR+YGjN\n" +
+            "/UTJpaEhMWkktuAznY56qs7AlHqKzcNq+258LpIOQJzN9/gw9IB2rz0PNnA+NqDCHttQLw\n" +
+            "0dZe/oPHJQ6vI/5ykakSas5GJZOph5udSz05ndM5kRoMOGHhi8WeYA0vFBed3BH+lkZ59K\n" +
+            "z+vjf4sGmOb0ptW95QA9ZeMN899QvuCYOgnuyCPguVL3SsRkQ9AXmOrLT4oPTSUOY3t7vv\n" +
+            "GI5WN5ZN9zYtT21bOMqYi+cHlIhnaqz+GjRpEfGaqJFPLcj1tVznHbi+2HHCG0M+NTjw9G\n" +
+            "JRjAjqOfkJZ0/7KmfBT7lGWNPPNgXtYPDdYRHHiIeDMLu4s2gBbqn8pmIdG14K4IqLl7uC\n" +
+            "payMNJxmQ75oRFpv3Vtf31FlpnsT762iS0e7P0CwBxVZyjdCYet9IVjw6MJC62svnTDznn\n" +
+            "0ZxPdz78acoXlBkH67zDH69LyPGZlZ9e7HeKrMbOTU5mnUfSiHc3mk8PYEuphnKXFd8Zzi\n" +
+            "bc/SfaxLbf19MsuqlM+gqKR9hVqDn6Ri9JAmHJBgFNc5hdLSKucunNFFamCslCXRkB3TNl\n" +
+            "pbPxSLMJ9UDTcrRnzgi5zyQxSe3K8tspqhXQ6ek5Z2sZ+zZuFzcKzgUcd8fpYxC9lZvJ1b\n" +
+            "pS8OCuGUI6KHHmGJmNKBTbxvp0B4EjRIy3lDJDBMap/GN9GsgqscrvYPIfqlnVR7GXN+qj\n" +
+            "MgOsue1jtVzG1SBAmBxcctEFLzBsr4k/fNNTXPt/mPKeO3w59zt1OSPyNx63NbNmo/uWO1\n" +
+            "8P24MBcO5crhlYa5ptb6Fvi1/j6Yrg1NYDPutRopcZNemEFPkR4dqW5AhJwT8L8hqZmmhs\n" +
+            "DH97qNiqkqyVmrRIygnVMdYqXsn/uV8yEb5mgRw8C6fJ7OZsvwsSfy052tBKJhj/63Ay/S\n" +
+            "wJ+HxQ/8vthvEkXsaJWiQ2RwatZIoVpOhYEpKwSDuBHMKrnMiCow13+pAq9Gf/CbXUd/Ko\n" +
+            "xNQ8RZ8lkreUDjJJhTXRRcpufJChL6zQj9bat6E9QBq4l1XjGDhAqgfvQT/1fDataZW3vW\n" +
+            "skze0s7diqtYIWNlx2+4vGxL38pSCSqtOWjHS6Rbjf37ERKQMH57z4w3aEiahtBcgKTWBy\n" +
+            "n4UD18TfLGd2i7jtENLxOcWBFzRxtIbFnKGiktLcp0XILs/lOhtRF+K2abiif26rDx++jI\n" +
+            "4iQCet6ltdeQJLekjmNh4/9Y4hCf5yx9lKuGbzGeZPI66ClbY+R2l29ZXUNUxZmVKM4BDw\n" +
+            "2LqMlVLcM1Nzg6ftQ09Dku1ApX/uKeOaf0I0DPaBwVD+iTGCeZWuOg5b1LZUuxxYT4ZB6F\n" +
+            "hoZ8/1mt5gTzo4XdZCmJ7jCOqEc75JV2NEfcIwpy6TOZPVMMWFYT88OgkF86Vxx8GR0FQU\n" +
+            "CLSDGVZjFU7kv1eKpDJ0oETyGBELC1PPMpm90nxCkzCx7uQw\n" +
+            "-----END OPENSSH PRIVATE KEY-----\n";
+
+    private String publicKey = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCxhKaGaUlUZnlr6OtFQ7hjVceZsWLBaIWB5NUwp45IoWLm7Hnor+Y8J/SwLBgdUSsjxkMUQMMbJdY/rPGwc8aW0K1JMjSNhv03dBxvXHdY+NSd24WjSezD89l6v78lGhVQ5g3rI4eTFsfPy2WSxZw1Fo0UUDVzzBtLuvC9ZCWd3nsT8Ox4LnZWLHrrRxGX2eCotEEO6CT1+wmk4szIkeDDmX79TbKatcN2vv7H6WjsoGH1bhc1QwS2/hmOdBwqGfm+sE0BI3VgMJ1NVDQrnt0IXlMtLH9Feg1ytdagCzaHulQ9lHn1wBzSARP/NqYzu2vNwpWSSJeafClHpA8yF/9FW9gOi4k+Oo949b+Xd1NHqjt+7lnlsepm0IsgfJ9Gr/0sweYfUSsTfGZGMstSRMu8V+bD1BaVqXQKZ80XoCm0NMnRChm109wXtt5+0atDmIFiy1Byr8QjwjqsIap1j93R/8R3L3mhUmLruSl7IPKPhjShEIL253GpoHiSENae9e0= dwannipu@Dimuthus-MacBook-Pro.local";
+    private String passphrase = "airavata";
+
+    @BeforeEach
+    void setUp() throws Exception {
+        sftpRootDir = Files.createTempDirectory("sftp-root-");
+        sftpRootDir.toFile().deleteOnExit();
+
+        Path authorizedKeysFile = Files.createTempFile("authorized_keys-", "");
+        Files.write(authorizedKeysFile, publicKey.getBytes(StandardCharsets.UTF_8));
+        authorizedKeysFile.toFile().deleteOnExit();
+
+        sshd = SshServer.setUpDefaultServer();
+        sshd.setHost("localhost");
+        sshd.setPort(sftpPort);
+
+        // Host key (for the server itself, unrelated to client auth)
+        sshd.setKeyPairProvider(new SimpleGeneratorHostKeyProvider());
+
+        // SFTP subsystem
+        sshd.setSubsystemFactories(
+                Collections.singletonList(new SftpSubsystemFactory.Builder().build())
+        );
+
+        // Virtual root
+        sshd.setFileSystemFactory(new VirtualFileSystemFactory(sftpRootDir));
+
+        // *** AUTH CONFIG ***
+
+        // 1) Disable password auth (only key-based logins allowed)
+        sshd.setPasswordAuthenticator(null);
+
+        sshd.setPublickeyAuthenticator(new AuthorizedKeysAuthenticator(authorizedKeysFile));
+
+        sshd.start();
+        port = sshd.getPort();
+    }
+
+    @AfterEach
+    void tearDown() throws Exception {
+        if (sshd != null && !sshd.isClosed()) {
+            sshd.stop(true);
+        }
+    }
+
+    public void createFilesInDir(Path root) throws IOException {
+        Path dir1 = Files.createDirectory(root.resolve("dir1"));
+        dir1.toFile().deleteOnExit();
+        Path dir2 = Files.createDirectory(root.resolve("dir2"));
+        dir2.toFile().deleteOnExit();
+
+        Path file1 = Files.createFile(root.resolve("file1.txt"));
+        file1.toFile().deleteOnExit();
+        Path file2 = Files.createFile(root.resolve("file2.txt"));
+        file2.toFile().deleteOnExit();
+
+        Path file3 = Files.createFile(dir1.resolve("file3.txt"));
+        file3.toFile().deleteOnExit();
+        Path file4 = Files.createFile(dir1.resolve("file4.txt"));
+        file4.toFile().deleteOnExit();
+
+        Files.writeString(file1, "Hello from file1\n");
+        Files.writeString(file2, "Hello from file2\n");
+        Files.writeString(file3, "Hello from file3\n");
+        Files.writeString(file4, "Hello from file4\n");
+    }
+
+    @Test
+    public void deleteNonEmptyDir() throws Exception {
+        System.out.printf("Root dir: %s\n", sftpRootDir);
+
+        Path dir1 = Files.createDirectory(sftpRootDir.resolve("dir1"));
+        dir1.toFile().deleteOnExit();
+        Path dir2 = Files.createDirectory(sftpRootDir.resolve("dir2"));
+        dir2.toFile().deleteOnExit();
+
+        createFilesInDir(dir1);
+        createFilesInDir(dir2);
+
+        SSHJAgentAdaptor adaptor = new SSHJAgentAdaptor();
+        adaptor.init("testuser", "localhost", sftpPort, publicKey, privateKey, passphrase);
+
+        List<String> itemsBefore = adaptor.listDirectory("/");
+        adaptor.deleteDirectory("dir1");
+        List<String> itemsAfter = adaptor.listDirectory("/");
+        System.out.printf("Before: %s\n", itemsBefore);
+        System.out.printf("After: %s\n", itemsAfter);
+
+    }
+
+    @Test
+    public void deleteEmptyDir() throws Exception {
+        Path dir1 = Files.createDirectory(sftpRootDir.resolve("dir1"));
+        dir1.toFile().deleteOnExit();
+
+        SSHJAgentAdaptor adaptor = new SSHJAgentAdaptor();
+        adaptor.init("testuser", "localhost", sftpPort, publicKey, privateKey, passphrase);

Review Comment:
   Resource leak: The `SSHJAgentAdaptor` instance is created but never properly closed. The adaptor has a `destroy()` method that should be called to clean up SSH connections. Consider adding `adaptor.destroy()` in a finally block or use a try-with-resources pattern if the class implements AutoCloseable.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: issues-unsubscribe@airavata.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org