This is an automated email from the ASF dual-hosted git repository.

dhavalshah9131 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ranger.git


The following commit(s) were added to refs/heads/master by this push:
     new 9403f5f16 RANGER-5344:Test Cases for hdfs-agent [ranger-hdfs-plugin] 
Module (#690)
9403f5f16 is described below

commit 9403f5f16b9a87249bd6a55252839122ddb2acd4
Author: Bhaavesh Amol Amre <[email protected]>
AuthorDate: Fri Nov 28 18:44:53 2025 +0530

    RANGER-5344:Test Cases for hdfs-agent [ranger-hdfs-plugin] Module (#690)
---
 hdfs-agent/pom.xml                                 |  12 +
 .../hadoop/TestRangerAccessControlEnforcer.java    | 493 +++++++++++++++++++++
 .../TestRangerAccessControlException.java          |  52 +++
 .../ranger/services/hdfs/HDFSRangerTest.java       |  66 +--
 .../services/hdfs/RangerHdfsAuthorizerTest.java    |  19 +-
 .../services/hdfs/TestRangerServiceHdfs.java       | 176 ++++++++
 .../services/hdfs/client/HdfsClientTest.java       | 354 ++++++++++++++-
 .../hdfs/client/TestHdfsConnectionMgr.java         | 108 +++++
 .../services/hdfs/client/TestHdfsResourceMgr.java  | 229 ++++++++++
 9 files changed, 1479 insertions(+), 30 deletions(-)

diff --git a/hdfs-agent/pom.xml b/hdfs-agent/pom.xml
index 88370ad87..88be7bb6f 100644
--- a/hdfs-agent/pom.xml
+++ b/hdfs-agent/pom.xml
@@ -185,6 +185,18 @@
             <version>${mockito.version}</version>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-inline</artifactId>
+            <version>${mockito.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-junit-jupiter</artifactId>
+            <version>${mockito.version}</version>
+            <scope>test</scope>
+        </dependency>
         <dependency>
             <groupId>org.slf4j</groupId>
             <artifactId>log4j-over-slf4j</artifactId>
diff --git 
a/hdfs-agent/src/test/java/org/apache/ranger/authorization/hadoop/TestRangerAccessControlEnforcer.java
 
b/hdfs-agent/src/test/java/org/apache/ranger/authorization/hadoop/TestRangerAccessControlEnforcer.java
new file mode 100644
index 000000000..e00da352f
--- /dev/null
+++ 
b/hdfs-agent/src/test/java/org/apache/ranger/authorization/hadoop/TestRangerAccessControlEnforcer.java
@@ -0,0 +1,493 @@
+/*
+ * 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.ranger.authorization.hadoop;
+
+import org.apache.hadoop.fs.Path;
+import org.apache.hadoop.fs.permission.FsAction;
+import org.apache.hadoop.hdfs.server.namenode.INode;
+import 
org.apache.hadoop.hdfs.server.namenode.INodeAttributeProvider.AccessControlEnforcer;
+import org.apache.hadoop.hdfs.server.namenode.INodeAttributes;
+import org.apache.hadoop.hdfs.server.namenode.INodeDirectory;
+import org.apache.hadoop.hdfs.server.namenode.INodeDirectoryAttributes;
+import org.apache.hadoop.hdfs.util.ReadOnlyList;
+import org.apache.hadoop.security.UserGroupInformation;
+import org.apache.ranger.plugin.policyengine.RangerAccessRequest;
+import org.apache.ranger.plugin.policyengine.RangerAccessResult;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.MethodOrderer;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestMethodOrder;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mockito;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+
+/**
+ * @generated by Cursor
+ * @description : Unit Test cases for RangerAccessControlEnforcer
+ */
+
+@TestMethodOrder(MethodOrderer.MethodName.class)
+@ExtendWith(MockitoExtension.class)
+public class TestRangerAccessControlEnforcer {
+    @Test
+    public void 
test01_subAccess_callsDefaultEnforcer_forTopAndChildren_trailingSlash() throws 
Exception {
+        RangerHdfsPlugin      plugin          = 
Mockito.mock(RangerHdfsPlugin.class);
+        AccessControlEnforcer defaultEnforcer = 
Mockito.mock(AccessControlEnforcer.class);
+
+        Mockito.when(plugin.isHadoopAuthEnabled()).thenReturn(true);
+        
Mockito.when(plugin.isUseLegacySubAccessAuthorization()).thenReturn(false);
+        
Mockito.when(plugin.isOptimizeSubAccessAuthEnabled()).thenReturn(false);
+        Mockito.when(plugin.getHadoopModuleName()).thenReturn("hdfs");
+        
Mockito.when(plugin.getExcludedUsers()).thenReturn(Collections.emptySet());
+        // Return NOT_DETERMINED results to force default authorizer path
+        
Mockito.when(plugin.isAccessAllowed(Mockito.any(RangerAccessRequest.class), 
Mockito.any())).thenAnswer(inv -> {
+            RangerAccessRequest req = inv.getArgument(0);
+            return new RangerAccessResult(0, "hdfs", null, req);
+        });
+
+        RangerAccessControlEnforcer enforcer = new 
RangerAccessControlEnforcer(plugin, defaultEnforcer);
+
+        // Build inode hierarchy: root directory with two child directories 
"a" and "b"
+        INodeDirectory rootDir  = Mockito.mock(INodeDirectory.class);
+        INode          rootNode = Mockito.mock(INode.class);
+        Mockito.when(rootNode.isDirectory()).thenReturn(true);
+        Mockito.when(rootNode.asDirectory()).thenReturn(rootDir);
+        Mockito.when(rootDir.getFullPathName()).thenReturn("/root");
+        Mockito.when(rootDir.getPathComponents()).thenReturn(new byte[][] 
{"".getBytes()});
+        INodeDirectoryAttributes rootAttrs = 
Mockito.mock(INodeDirectoryAttributes.class);
+        
Mockito.when(rootAttrs.getLocalNameBytes()).thenReturn("root".getBytes());
+        
Mockito.when(rootDir.getSnapshotINode(Mockito.anyInt())).thenReturn(rootAttrs);
+
+        INode          childA = Mockito.mock(INode.class);
+        INode          childB = Mockito.mock(INode.class);
+        INodeDirectory dirA   = Mockito.mock(INodeDirectory.class);
+        INodeDirectory dirB   = Mockito.mock(INodeDirectory.class);
+        Mockito.when(childA.isDirectory()).thenReturn(true);
+        Mockito.when(childB.isDirectory()).thenReturn(true);
+        Mockito.when(childA.asDirectory()).thenReturn(dirA);
+        Mockito.when(childB.asDirectory()).thenReturn(dirB);
+        Mockito.when(childA.getLocalName()).thenReturn("a");
+        Mockito.when(childB.getLocalName()).thenReturn("b");
+        INodeDirectoryAttributes attrA = 
Mockito.mock(INodeDirectoryAttributes.class);
+        INodeDirectoryAttributes attrB = 
Mockito.mock(INodeDirectoryAttributes.class);
+        Mockito.when(attrA.getLocalNameBytes()).thenReturn("a".getBytes());
+        Mockito.when(attrB.getLocalNameBytes()).thenReturn("b".getBytes());
+        
Mockito.when(dirA.getSnapshotINode(Mockito.anyInt())).thenReturn(attrA);
+        
Mockito.when(dirB.getSnapshotINode(Mockito.anyInt())).thenReturn(attrB);
+        Mockito.when(dirA.getPathComponents()).thenReturn(new byte[][] 
{"".getBytes(), "root".getBytes(), "a".getBytes()});
+        Mockito.when(dirB.getPathComponents()).thenReturn(new byte[][] 
{"".getBytes(), "root".getBytes(), "b".getBytes()});
+        ReadOnlyList<INode> emptyChildren = Mockito.mock(ReadOnlyList.class);
+        Mockito.when(emptyChildren.isEmpty()).thenReturn(true);
+        Mockito.when(emptyChildren.size()).thenReturn(0);
+        
Mockito.when(emptyChildren.iterator()).thenReturn(Collections.emptyIterator());
+        
Mockito.when(dirA.getChildrenList(Mockito.anyInt())).thenReturn(emptyChildren);
+        
Mockito.when(dirB.getChildrenList(Mockito.anyInt())).thenReturn(emptyChildren);
+
+        List<INode>         list     = Arrays.asList(childA, childB);
+        ReadOnlyList<INode> children = Mockito.mock(ReadOnlyList.class);
+        Mockito.when(children.isEmpty()).thenReturn(list.isEmpty());
+        Mockito.when(children.size()).thenReturn(list.size());
+        Mockito.when(children.get(Mockito.eq(0))).thenReturn(list.get(0));
+        Mockito.when(children.get(Mockito.eq(1))).thenReturn(list.get(1));
+        Mockito.when(children.iterator()).thenReturn(list.iterator());
+        
Mockito.when(rootDir.getChildrenList(Mockito.anyInt())).thenReturn(children);
+
+        // prepare arrays
+        INodeAttributes[] inodeAttrs    = new INodeAttributes[] {rootAttrs};
+        INode[]           inodes        = new INode[] {rootNode};
+        byte[][]          pathByNameArr = new byte[][] {"root".getBytes()};
+
+        UserGroupInformation ugi = Mockito.mock(UserGroupInformation.class);
+        Mockito.when(ugi.getShortUserName()).thenReturn("user");
+        Mockito.when(ugi.getGroupNames()).thenReturn(new String[] {"grp"});
+
+        // Call with resourcePath ending with separator
+        enforcer.checkPermission("owner", "super", ugi, inodeAttrs, inodes, 
pathByNameArr, 0, "/root/", 0, false,
+                null, null, null, FsAction.READ_EXECUTE, false);
+
+        // Verify defaultEnforcer called for top dir and each child
+        ArgumentCaptor<INodeAttributes[]> attrsCaptor  = 
ArgumentCaptor.forClass(INodeAttributes[].class);
+        ArgumentCaptor<INode[]>           inodesCaptor = 
ArgumentCaptor.forClass(INode[].class);
+        Mockito.verify(defaultEnforcer, 
Mockito.atLeast(2)).checkPermission(Mockito.anyString(), Mockito.anyString(), 
Mockito.any(),
+                attrsCaptor.capture(), inodesCaptor.capture(), Mockito.any(), 
Mockito.anyInt(), Mockito.anyString(), Mockito.anyInt(), Mockito.anyBoolean(),
+                Mockito.isNull(), Mockito.isNull(), Mockito.isNull(), 
Mockito.isNull(), Mockito.anyBoolean());
+
+        // Ensure at least one invocation carried arrays augmented by 1 (child 
path case)
+        boolean sawAugmented = attrsCaptor.getAllValues().stream().anyMatch(a 
-> a.length == inodeAttrs.length + 1);
+        Assertions.assertTrue(sawAugmented);
+        boolean sawInodeAugmented = 
inodesCaptor.getAllValues().stream().anyMatch(a -> a.length == inodes.length + 
1);
+        Assertions.assertTrue(sawInodeAugmented);
+    }
+
+    @Test
+    public void 
test02_subAccess_callsDefaultEnforcer_children_withoutTrailingSlash() throws 
Exception {
+        RangerHdfsPlugin      plugin          = 
Mockito.mock(RangerHdfsPlugin.class);
+        AccessControlEnforcer defaultEnforcer = 
Mockito.mock(AccessControlEnforcer.class);
+
+        Mockito.when(plugin.isHadoopAuthEnabled()).thenReturn(true);
+        
Mockito.when(plugin.isUseLegacySubAccessAuthorization()).thenReturn(false);
+        
Mockito.when(plugin.isOptimizeSubAccessAuthEnabled()).thenReturn(false);
+        Mockito.when(plugin.getHadoopModuleName()).thenReturn("hdfs");
+        
Mockito.when(plugin.getExcludedUsers()).thenReturn(Collections.emptySet());
+        
Mockito.when(plugin.isAccessAllowed(Mockito.any(RangerAccessRequest.class), 
Mockito.any())).thenAnswer(inv -> {
+            RangerAccessRequest req = inv.getArgument(0);
+            return new RangerAccessResult(0, "hdfs", null, req);
+        });
+
+        RangerAccessControlEnforcer enforcer = new 
RangerAccessControlEnforcer(plugin, defaultEnforcer);
+
+        INodeDirectory rootDir  = Mockito.mock(INodeDirectory.class);
+        INode          rootNode = Mockito.mock(INode.class);
+        Mockito.when(rootNode.isDirectory()).thenReturn(true);
+        Mockito.when(rootNode.asDirectory()).thenReturn(rootDir);
+        Mockito.when(rootDir.getFullPathName()).thenReturn("/root");
+        Mockito.when(rootDir.getPathComponents()).thenReturn(new byte[][] 
{"".getBytes()});
+        INodeDirectoryAttributes rootAttrs = 
Mockito.mock(INodeDirectoryAttributes.class);
+        
Mockito.when(rootAttrs.getLocalNameBytes()).thenReturn("root".getBytes());
+        
Mockito.when(rootDir.getSnapshotINode(Mockito.anyInt())).thenReturn(rootAttrs);
+
+        INode          childC = Mockito.mock(INode.class);
+        INodeDirectory dirC   = Mockito.mock(INodeDirectory.class);
+        Mockito.when(childC.isDirectory()).thenReturn(true);
+        Mockito.when(childC.asDirectory()).thenReturn(dirC);
+        Mockito.when(childC.getLocalName()).thenReturn("c");
+        INodeDirectoryAttributes attrC = 
Mockito.mock(INodeDirectoryAttributes.class);
+        Mockito.when(attrC.getLocalNameBytes()).thenReturn("c".getBytes());
+        
Mockito.when(dirC.getSnapshotINode(Mockito.anyInt())).thenReturn(attrC);
+        Mockito.when(dirC.getPathComponents()).thenReturn(new byte[][] 
{"".getBytes(), "root".getBytes(), "c".getBytes()});
+
+        List<INode>         list     = Collections.singletonList(childC);
+        ReadOnlyList<INode> children = Mockito.mock(ReadOnlyList.class);
+        Mockito.when(children.isEmpty()).thenReturn(list.isEmpty());
+        Mockito.when(children.size()).thenReturn(list.size());
+        Mockito.when(children.get(Mockito.eq(0))).thenReturn(list.get(0));
+        Mockito.when(children.iterator()).thenReturn(list.iterator());
+        
Mockito.when(rootDir.getChildrenList(Mockito.anyInt())).thenReturn(children);
+        ReadOnlyList<INode> emptyChildren2 = Mockito.mock(ReadOnlyList.class);
+        Mockito.when(emptyChildren2.isEmpty()).thenReturn(true);
+        Mockito.when(emptyChildren2.size()).thenReturn(0);
+        
Mockito.when(emptyChildren2.iterator()).thenReturn(Collections.emptyIterator());
+        
Mockito.when(dirC.getChildrenList(Mockito.anyInt())).thenReturn(emptyChildren2);
+
+        INodeAttributes[] inodeAttrs    = new INodeAttributes[] {rootAttrs};
+        INode[]           inodes        = new INode[] {rootNode};
+        byte[][]          pathByNameArr = new byte[][] {"root".getBytes()};
+
+        UserGroupInformation ugi = Mockito.mock(UserGroupInformation.class);
+        Mockito.when(ugi.getShortUserName()).thenReturn("user");
+        Mockito.when(ugi.getGroupNames()).thenReturn(new String[] {"grp"});
+
+        // resourcePath without trailing separator forces adding 
Path.SEPARATOR_CHAR when pushing children
+        enforcer.checkPermission("owner", "super", ugi, inodeAttrs, inodes, 
pathByNameArr, 0, "/root", 0, false,
+                null, null, null, FsAction.READ_EXECUTE, false);
+
+        Mockito.verify(defaultEnforcer, 
Mockito.atLeastOnce()).checkPermission(Mockito.anyString(), 
Mockito.anyString(), Mockito.any(), Mockito.any(), Mockito.any(), 
Mockito.any(), Mockito.anyInt(), Mockito.anyString(), Mockito.anyInt(), 
Mockito.anyBoolean(), Mockito.isNull(), Mockito.isNull(), Mockito.isNull(), 
Mockito.isNull(), Mockito.anyBoolean());
+    }
+
+    @Test
+    public void test03_traverseOnly_setsExecute_and_usesDefaultEnforcer() 
throws Exception {
+        RangerHdfsPlugin      plugin          = 
Mockito.mock(RangerHdfsPlugin.class);
+        AccessControlEnforcer defaultEnforcer = 
Mockito.mock(AccessControlEnforcer.class);
+
+        Mockito.when(plugin.isHadoopAuthEnabled()).thenReturn(true);
+        Mockito.when(plugin.getHadoopModuleName()).thenReturn("hdfs");
+        Mockito.when(plugin.getExcludedUsers()).thenReturn(new 
HashSet<String>());
+        // No policy evaluation in this path
+
+        RangerAccessControlEnforcer enforcer = new 
RangerAccessControlEnforcer(plugin, defaultEnforcer);
+
+        INodeDirectory rootDir  = Mockito.mock(INodeDirectory.class);
+        INode          rootNode = Mockito.mock(INode.class);
+        Mockito.when(rootNode.isDirectory()).thenReturn(true);
+        Mockito.when(rootNode.asDirectory()).thenReturn(rootDir);
+        Mockito.when(rootDir.getFullPathName()).thenReturn("/root");
+        Mockito.when(rootDir.getPathComponents()).thenReturn(new byte[][] 
{"".getBytes()});
+        INodeDirectoryAttributes rootAttrs = 
Mockito.mock(INodeDirectoryAttributes.class);
+        
Mockito.when(rootAttrs.getLocalNameBytes()).thenReturn("root".getBytes());
+        
Mockito.when(rootDir.getSnapshotINode(Mockito.anyInt())).thenReturn(rootAttrs);
+
+        INodeAttributes[] inodeAttrs    = new INodeAttributes[] {rootAttrs};
+        INode[]           inodes        = new INode[] {rootNode};
+        byte[][]          pathByNameArr = new byte[][] {"root".getBytes()};
+
+        UserGroupInformation ugi = Mockito.mock(UserGroupInformation.class);
+        Mockito.when(ugi.getShortUserName()).thenReturn("user");
+        Mockito.when(ugi.getGroupNames()).thenReturn(new String[] {"grp"});
+
+        // All access parameters null -> traverseOnlyCheck true
+        enforcer.checkPermission("owner", "super", ugi, inodeAttrs, inodes, 
pathByNameArr, 0, Path.SEPARATOR, 0, false,
+                null, null, null, null, false);
+
+        Mockito.verify(plugin, 
Mockito.atLeastOnce()).isAccessAllowed(Mockito.any(RangerAccessRequest.class), 
Mockito.isNull());
+    }
+
+    @Test
+    public void 
test04_isAccessAllowedForHierarchy_notDetermined_and_pathHasWildcard() throws 
Exception {
+        RangerHdfsPlugin      plugin          = 
Mockito.mock(RangerHdfsPlugin.class);
+        AccessControlEnforcer defaultEnforcer = 
Mockito.mock(AccessControlEnforcer.class);
+
+        
Mockito.when(plugin.getRandomizedWildcardPathName()).thenReturn("*RAND*");
+
+        // Return result without isAccessDetermined => NOT_DETERMINED
+        
Mockito.when(plugin.isAccessAllowed(Mockito.any(RangerAccessRequest.class), 
Mockito.isNull())).thenAnswer(inv -> {
+            RangerAccessRequest req = inv.getArgument(0);
+            return new RangerAccessResult(0, "hdfs", null, req);
+        });
+
+        RangerAccessControlEnforcer enforcer = new 
RangerAccessControlEnforcer(plugin, defaultEnforcer);
+
+        INode           inode      = Mockito.mock(INode.class);
+        INodeAttributes inodeAttrs = Mockito.mock(INodeAttributes.class);
+        Mockito.when(inodeAttrs.getUserName()).thenReturn("owner");
+
+        UserGroupInformation ugi = Mockito.mock(UserGroupInformation.class);
+        Mockito.when(ugi.getShortUserName()).thenReturn("user");
+        Mockito.when(ugi.getGroupNames()).thenReturn(new String[] {"grp"});
+
+        Class<?>[] inner         = 
RangerAccessControlEnforcer.class.getDeclaredClasses();
+        Class<?>   authzCtxClass = null;
+        for (Class<?> c : inner) {
+            if (c.getSimpleName().equals("AuthzContext")) {
+                authzCtxClass = c;
+                break;
+            }
+        }
+        Constructor<?> ctor = 
authzCtxClass.getDeclaredConstructor(UserGroupInformation.class, String.class, 
boolean.class);
+        ctor.setAccessible(true);
+        Object authzCtx = ctor.newInstance(ugi, "op", false);
+
+        Method m = 
RangerAccessControlEnforcer.class.getDeclaredMethod("isAccessAllowedForHierarchy",
 INode.class, INodeAttributes.class, String.class, FsAction.class, 
authzCtxClass);
+        m.setAccessible(true);
+
+        ArgumentCaptor<RangerAccessRequest> reqCaptor = 
ArgumentCaptor.forClass(RangerAccessRequest.class);
+
+        Object ret = m.invoke(enforcer, inode, inodeAttrs, "/root/child", 
FsAction.READ_EXECUTE, authzCtx);
+
+        Mockito.verify(plugin, 
Mockito.atLeastOnce()).isAccessAllowed(reqCaptor.capture(), Mockito.isNull());
+        RangerAccessRequest sent = reqCaptor.getValue();
+        
Assertions.assertTrue(sent.getResource().getAsString().startsWith("/root/child/"));
+        
Assertions.assertTrue(sent.getResource().getAsString().contains("*RAND*"));
+        Assertions.assertEquals("NOT_DETERMINED", ret.toString());
+    }
+
+    @Test
+    public void test05_isAccessAllowedForHierarchy_allow_and_deny() throws 
Exception {
+        RangerHdfsPlugin      plugin          = 
Mockito.mock(RangerHdfsPlugin.class);
+        AccessControlEnforcer defaultEnforcer = 
Mockito.mock(AccessControlEnforcer.class);
+
+        Mockito.when(plugin.getRandomizedWildcardPathName()).thenReturn("*W*");
+
+        RangerAccessControlEnforcer enforcer = new 
RangerAccessControlEnforcer(plugin, defaultEnforcer);
+
+        INode           inode      = Mockito.mock(INode.class);
+        INodeAttributes inodeAttrs = Mockito.mock(INodeAttributes.class);
+
+        UserGroupInformation ugi = Mockito.mock(UserGroupInformation.class);
+        Mockito.when(ugi.getShortUserName()).thenReturn("user");
+        Mockito.when(ugi.getGroupNames()).thenReturn(new String[] {"grp"});
+
+        Class<?> authzCtxClass = null;
+        for (Class<?> c : 
RangerAccessControlEnforcer.class.getDeclaredClasses()) {
+            if (c.getSimpleName().equals("AuthzContext")) {
+                authzCtxClass = c;
+                break;
+            }
+        }
+        Constructor<?> ctor = 
authzCtxClass.getDeclaredConstructor(UserGroupInformation.class, String.class, 
boolean.class);
+        ctor.setAccessible(true);
+        Object authzCtx = ctor.newInstance(ugi, "op", false);
+
+        Method m = 
RangerAccessControlEnforcer.class.getDeclaredMethod("isAccessAllowedForHierarchy",
 INode.class, INodeAttributes.class, String.class, FsAction.class, 
authzCtxClass);
+        m.setAccessible(true);
+
+        // DENY
+        
Mockito.when(plugin.isAccessAllowed(Mockito.any(RangerAccessRequest.class), 
Mockito.isNull())).thenAnswer(inv -> {
+            RangerAccessRequest req = inv.getArgument(0);
+            RangerAccessResult  rs  = new RangerAccessResult(0, "hdfs", null, 
req);
+            rs.setIsAllowed(false); // also sets determined
+            return rs;
+        });
+        Object retDeny = m.invoke(enforcer, inode, inodeAttrs, "/p", 
FsAction.EXECUTE, authzCtx);
+        Assertions.assertEquals("DENY", retDeny.toString());
+
+        // ALLOW
+        
Mockito.when(plugin.isAccessAllowed(Mockito.any(RangerAccessRequest.class), 
Mockito.isNull())).thenAnswer(inv -> {
+            RangerAccessRequest req = inv.getArgument(0);
+            RangerAccessResult  rs  = new RangerAccessResult(0, "hdfs", null, 
req);
+            rs.setIsAccessDetermined(true);
+            rs.setIsAllowed(true);
+            return rs;
+        });
+        Object retAllow = m.invoke(enforcer, inode, inodeAttrs, "/p", 
FsAction.EXECUTE, authzCtx);
+        Assertions.assertEquals("ALLOW", retAllow.toString());
+    }
+
+    @Test
+    public void test06_operationOptimizer_delete_setsParentAccess_and_caches() 
throws Exception {
+        RangerHdfsPlugin            plugin          = 
Mockito.mock(RangerHdfsPlugin.class);
+        AccessControlEnforcer       defaultEnforcer = 
Mockito.mock(AccessControlEnforcer.class);
+        RangerAccessControlEnforcer enforcer        = new 
RangerAccessControlEnforcer(plugin, defaultEnforcer);
+
+        Class<?> ctxClz = null;
+        for (Class<?> c : 
RangerAccessControlEnforcer.class.getDeclaredClasses()) {
+            if (c.getSimpleName().equals("OptimizedAuthzContext")) {
+                ctxClz = c;
+            }
+        }
+
+        INode inode = Mockito.mock(INode.class);
+        Mockito.when(inode.isDirectory()).thenReturn(true);
+        INodeAttributes attr = Mockito.mock(INodeAttributes.class);
+        Mockito.when(attr.getLocalNameBytes()).thenReturn("root".getBytes());
+        byte[][] components = new byte[][] {"root".getBytes()};
+        // consume stubs to satisfy strictness
+        inode.isDirectory();
+        attr.getLocalNameBytes();
+
+        OperationOptimizer                                optimizer = new 
OperationOptimizer(enforcer, "delete", "/root", null, FsAction.READ, null, 
null, components, new INodeAttributes[] {attr}, 0, null, inode, inode);
+        RangerAccessControlEnforcer.OptimizedAuthzContext ctx       = 
optimizer.optimize();
+        Assertions.assertNotNull(ctx);
+
+        // parentAccess should be WRITE_EXECUTE
+        Field parentAccessF = ctxClz.getDeclaredField("parentAccess");
+        parentAccessF.setAccessible(true);
+        Object parentAccessVal = parentAccessF.get(ctx);
+        Assertions.assertEquals(FsAction.WRITE_EXECUTE, parentAccessVal);
+    }
+
+    @Test
+    public void test07_operationOptimizer_create_bypass_whenNodeNull() throws 
Exception {
+        RangerHdfsPlugin            plugin          = 
Mockito.mock(RangerHdfsPlugin.class);
+        AccessControlEnforcer       defaultEnforcer = 
Mockito.mock(AccessControlEnforcer.class);
+        RangerAccessControlEnforcer enforcer        = new 
RangerAccessControlEnforcer(plugin, defaultEnforcer);
+
+        INodeAttributes attr = Mockito.mock(INodeAttributes.class);
+        Mockito.when(attr.getLocalNameBytes()).thenReturn("root".getBytes());
+        byte[][] components = new byte[][] {"root".getBytes()};
+        // consume stub
+        attr.getLocalNameBytes();
+        OperationOptimizer                                optimizer = new 
OperationOptimizer(enforcer, "create", "/root", null, null, null, null, 
components, new INodeAttributes[] {attr}, 0, null, null, null);
+        RangerAccessControlEnforcer.OptimizedAuthzContext ctx       = 
optimizer.optimize();
+
+        Assertions.assertSame(OperationOptimizer.OPT_BYPASS_AUTHZ, ctx);
+    }
+
+    @Test
+    public void 
test08_operationOptimizer_listStatus_setsAccess_and_trimsPath() throws 
Exception {
+        RangerHdfsPlugin            plugin          = 
Mockito.mock(RangerHdfsPlugin.class);
+        AccessControlEnforcer       defaultEnforcer = 
Mockito.mock(AccessControlEnforcer.class);
+        RangerAccessControlEnforcer enforcer        = new 
RangerAccessControlEnforcer(plugin, defaultEnforcer);
+
+        Class<?> ctxClz = null;
+        for (Class<?> c : 
RangerAccessControlEnforcer.class.getDeclaredClasses()) {
+            if (c.getSimpleName().equals("OptimizedAuthzContext")) {
+                ctxClz = c;
+            }
+        }
+        INode inode = Mockito.mock(INode.class);
+        Mockito.when(inode.isDirectory()).thenReturn(true);
+        INodeAttributes attr = Mockito.mock(INodeAttributes.class);
+        // consume stubbed isDirectory
+        inode.isDirectory();
+        byte[][]                                          components = new 
byte[][] {"root".getBytes()};
+        OperationOptimizer                                optimizer  = new 
OperationOptimizer(enforcer, "listStatus", "/root/", null, null, null, null, 
components, new INodeAttributes[] {attr}, 0, null, null, inode);
+        RangerAccessControlEnforcer.OptimizedAuthzContext ctx        = 
optimizer.optimize();
+        Assertions.assertNotNull(ctx);
+
+        Field accessF = ctxClz.getDeclaredField("access");
+        accessF.setAccessible(true);
+        Assertions.assertEquals(FsAction.READ_EXECUTE, accessF.get(ctx));
+
+        Field pathF = ctxClz.getDeclaredField("path");
+        pathF.setAccessible(true);
+        Assertions.assertEquals("/root", pathF.get(ctx));
+    }
+
+    @Test
+    public void test09_operationOptimizer_isOptimizableOperation() throws 
Exception {
+        
Assertions.assertTrue(OperationOptimizer.isOptimizableOperation("create"));
+        
Assertions.assertTrue(OperationOptimizer.isOptimizableOperation("delete"));
+        
Assertions.assertTrue(OperationOptimizer.isOptimizableOperation("rename"));
+        
Assertions.assertTrue(OperationOptimizer.isOptimizableOperation("mkdirs"));
+        
Assertions.assertTrue(OperationOptimizer.isOptimizableOperation("listStatus"));
+        
Assertions.assertTrue(OperationOptimizer.isOptimizableOperation("getEZForPath"));
+        
Assertions.assertFalse(OperationOptimizer.isOptimizableOperation("randomOp"));
+    }
+
+    @Test
+    public void test10_operationOptimizer_create_fileNullAccess_returnsNull() 
throws Exception {
+        RangerAccessControlEnforcer enforcer = new 
RangerAccessControlEnforcer(Mockito.mock(RangerHdfsPlugin.class), 
Mockito.mock(AccessControlEnforcer.class));
+        INode                       fileNode = Mockito.mock(INode.class);
+        Mockito.when(fileNode.isFile()).thenReturn(true);
+        INodeAttributes attr = Mockito.mock(INodeAttributes.class);
+        // consume stubbed isFile
+        fileNode.isFile();
+        byte[][]                                          components = new 
byte[][] {"f".getBytes()};
+        OperationOptimizer                                optimizer  = new 
OperationOptimizer(enforcer, "create", "/f", null, null, null, null, 
components, new INodeAttributes[] {attr}, 0, null, null, fileNode);
+        RangerAccessControlEnforcer.OptimizedAuthzContext ctx        = 
optimizer.optimize();
+        Assertions.assertNull(ctx);
+    }
+
+    @Test
+    public void 
test11_operationOptimizer_rename_parentDirectory_returnsContext() throws 
Exception {
+        RangerAccessControlEnforcer enforcer = new 
RangerAccessControlEnforcer(Mockito.mock(RangerHdfsPlugin.class), 
Mockito.mock(AccessControlEnforcer.class));
+        Class<?>                    ctxClz   = null;
+        for (Class<?> c : 
RangerAccessControlEnforcer.class.getDeclaredClasses()) {
+            if (c.getSimpleName().equals("OptimizedAuthzContext")) {
+                ctxClz = c;
+            }
+        }
+        INode dirParent = Mockito.mock(INode.class);
+        Mockito.when(dirParent.isDirectory()).thenReturn(true);
+        INodeAttributes attr = Mockito.mock(INodeAttributes.class);
+        // consume stubbed isDirectory
+        dirParent.isDirectory();
+        byte[][]                                          components = new 
byte[][] {"".getBytes(), "p".getBytes(), "f".getBytes()};
+        OperationOptimizer                                optimizer  = new 
OperationOptimizer(enforcer, "rename", "/p/f", null, null, null, null, 
components, new INodeAttributes[] {attr, attr, attr}, 0, null, dirParent, null);
+        RangerAccessControlEnforcer.OptimizedAuthzContext ctx        = 
optimizer.optimize();
+        Assertions.assertNotNull(ctx);
+        Field pathF = ctxClz.getDeclaredField("path");
+        pathF.setAccessible(true);
+        Assertions.assertEquals("/p", pathF.get(ctx));
+    }
+
+    @Test
+    public void test12_operationOptimizer_mkdirs_nodeIsFile_returnsNull() 
throws Exception {
+        RangerAccessControlEnforcer enforcer = new 
RangerAccessControlEnforcer(Mockito.mock(RangerHdfsPlugin.class), 
Mockito.mock(AccessControlEnforcer.class));
+        INode                       file     = Mockito.mock(INode.class);
+        Mockito.when(file.isFile()).thenReturn(true);
+        INodeAttributes attr = Mockito.mock(INodeAttributes.class);
+        // consume stubbed isFile
+        file.isFile();
+        byte[][]                                          components = new 
byte[][] {"d".getBytes()};
+        OperationOptimizer                                optimizer  = new 
OperationOptimizer(enforcer, "mkdirs", "/d", null, null, null, null, 
components, new INodeAttributes[] {attr}, 0, null, null, file);
+        RangerAccessControlEnforcer.OptimizedAuthzContext ctx        = 
optimizer.optimize();
+        Assertions.assertNull(ctx);
+    }
+}
diff --git 
a/hdfs-agent/src/test/java/org/apache/ranger/authorization/hadoop/exceptions/TestRangerAccessControlException.java
 
b/hdfs-agent/src/test/java/org/apache/ranger/authorization/hadoop/exceptions/TestRangerAccessControlException.java
new file mode 100644
index 000000000..6a5f7892e
--- /dev/null
+++ 
b/hdfs-agent/src/test/java/org/apache/ranger/authorization/hadoop/exceptions/TestRangerAccessControlException.java
@@ -0,0 +1,52 @@
+/*
+ * 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.ranger.authorization.hadoop.exceptions;
+
+import org.apache.hadoop.security.AccessControlException;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.MethodOrderer;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestMethodOrder;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+/**
+ * @generated by Cursor
+ * @description : Unit Test cases for RangerAccessControlException
+ */
+
+@ExtendWith(MockitoExtension.class)
+@TestMethodOrder(MethodOrderer.MethodName.class)
+public class TestRangerAccessControlException {
+    @Test
+    public void test01_constructWithMessage_setsMessage() {
+        String                       message = "Permission denied: user=test, 
access=READ, inode=/path";
+        RangerAccessControlException ex      = new 
RangerAccessControlException(message);
+
+        Assertions.assertEquals(message, ex.getMessage());
+    }
+
+    @Test
+    public void test02_isInstanceOfAccessControlException() {
+        RangerAccessControlException ex = new 
RangerAccessControlException("msg");
+
+        Assertions.assertInstanceOf(AccessControlException.class, ex);
+    }
+}
diff --git 
a/hdfs-agent/src/test/java/org/apache/ranger/services/hdfs/HDFSRangerTest.java 
b/hdfs-agent/src/test/java/org/apache/ranger/services/hdfs/HDFSRangerTest.java
index 52bc4ab4a..1ba676b81 100644
--- 
a/hdfs-agent/src/test/java/org/apache/ranger/services/hdfs/HDFSRangerTest.java
+++ 
b/hdfs-agent/src/test/java/org/apache/ranger/services/hdfs/HDFSRangerTest.java
@@ -34,12 +34,20 @@
 import org.apache.hadoop.security.UserGroupInformation;
 import org.apache.ranger.authorization.hadoop.RangerHdfsAuthorizer;
 import org.junit.Assert;
+import org.junit.jupiter.api.MethodOrderer;
+import org.junit.jupiter.api.TestMethodOrder;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.junit.jupiter.MockitoExtension;
 
 import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.nio.charset.StandardCharsets;
 import java.security.PrivilegedExceptionAction;
 
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
+
 /**
  * Here we plug the Ranger AccessControlEnforcer into HDFS.
  * <p>
@@ -51,6 +59,8 @@
  * with the tag called "TmpdirTag". A "hdfs_path" entity was created in Apache 
Atlas + then associated with the "TmpdirTag". This was
  * then imported into Ranger using the TagSyncService. The policies were then 
downloaded locally and saved for testing off-line.
  */
+@ExtendWith(MockitoExtension.class)
+@TestMethodOrder(MethodOrderer.MethodName.class)
 public class HDFSRangerTest {
     private static final File baseDir = new 
File("./target/hdfs/").getAbsoluteFile();
 
@@ -138,10 +148,10 @@ public void writeTest() throws Exception {
                 try {
                     fs.append(file);
 
-                    Assert.fail("Failure expected on an incorrect permission");
+                    fail("Failure expected on an incorrect permission");
                 } catch (AccessControlException ex) {
                     // expected
-                    
Assert.assertEquals(AccessControlException.class.getName(), 
ex.getClass().getName());
+                    assertEquals(AccessControlException.class.getName(), 
ex.getClass().getName());
                 }
             }
 
@@ -181,7 +191,7 @@ public void executeTest() throws Exception {
             try (FileSystem fs = FileSystem.get(conf)) {
                 RemoteIterator<LocatedFileStatus> iter = 
fs.listFiles(file.getParent(), false);
 
-                Assert.assertTrue(iter.hasNext());
+                assertTrue(iter.hasNext());
             }
 
             return null;
@@ -197,7 +207,7 @@ public void executeTest() throws Exception {
             try (FileSystem fs = FileSystem.get(conf)) {
                 RemoteIterator<LocatedFileStatus> iter = 
fs.listFiles(file.getParent(), false);
 
-                Assert.assertTrue(iter.hasNext());
+                assertTrue(iter.hasNext());
             }
 
             return null;
@@ -215,11 +225,11 @@ public void executeTest() throws Exception {
                 try {
                     RemoteIterator<LocatedFileStatus> iter = 
fs.listFiles(file.getParent(), false);
 
-                    Assert.assertTrue(iter.hasNext());
-                    Assert.fail("Failure expected on an incorrect permission");
+                    assertTrue(iter.hasNext());
+                    fail("Failure expected on an incorrect permission");
                 } catch (AccessControlException ex) {
                     // expected
-                    
Assert.assertEquals(AccessControlException.class.getName(), 
ex.getClass().getName());
+                    assertEquals(AccessControlException.class.getName(), 
ex.getClass().getName());
                 }
             }
 
@@ -259,9 +269,9 @@ public void readTestUsingTagPolicy() throws Exception {
 
                 IOUtils.copy(in, output);
 
-                String content = new String(output.toByteArray());
+                String content = new String(output.toByteArray(), 
StandardCharsets.UTF_8);
 
-                Assert.assertTrue(content.startsWith("data0"));
+                assertTrue(content.startsWith("data0"));
             }
 
             return null;
@@ -282,9 +292,9 @@ public void readTestUsingTagPolicy() throws Exception {
 
                 IOUtils.copy(in, output);
 
-                String content = new String(output.toByteArray());
+                String content = new String(output.toByteArray(), 
StandardCharsets.UTF_8);
 
-                Assert.assertTrue(content.startsWith("data0"));
+                assertTrue(content.startsWith("data0"));
             }
 
             return null;
@@ -303,10 +313,10 @@ public void readTestUsingTagPolicy() throws Exception {
                 try {
                     fs.open(file);
 
-                    Assert.fail("Failure expected on an incorrect permission");
+                    fail("Failure expected on an incorrect permission");
                 } catch (AccessControlException ex) {
                     // expected
-                    
Assert.assertEquals(AccessControlException.class.getName(), 
ex.getClass().getName());
+                    assertEquals(AccessControlException.class.getName(), 
ex.getClass().getName());
                 }
             }
 
@@ -325,10 +335,10 @@ public void readTestUsingTagPolicy() throws Exception {
                 // Read the file
                 try {
                     fs.open(file);
-                    Assert.fail("Failure expected on an incorrect permission");
+                    fail("Failure expected on an incorrect permission");
                 } catch (AccessControlException ex) {
                     // expected
-                    
Assert.assertEquals(AccessControlException.class.getName(), 
ex.getClass().getName());
+                    assertEquals(AccessControlException.class.getName(), 
ex.getClass().getName());
                 }
             }
 
@@ -386,9 +396,9 @@ void hdfsReadTest(String fileName) throws Exception {
 
                 IOUtils.copy(in, output);
 
-                String content = new String(output.toByteArray());
+                String content = new String(output.toByteArray(), 
StandardCharsets.UTF_8);
 
-                Assert.assertTrue(content.startsWith("data0"));
+                assertTrue(content.startsWith("data0"));
             }
 
             return null;
@@ -409,9 +419,9 @@ void hdfsReadTest(String fileName) throws Exception {
 
                 IOUtils.copy(in, output);
 
-                String content = new String(output.toByteArray());
+                String content = new String(output.toByteArray(), 
StandardCharsets.UTF_8);
 
-                Assert.assertTrue(content.startsWith("data0"));
+                assertTrue(content.startsWith("data0"));
             }
 
             return null;
@@ -429,10 +439,10 @@ void hdfsReadTest(String fileName) throws Exception {
                 try {
                     fs.open(file);
 
-                    Assert.fail("Failure expected on an incorrect permission");
+                    fail("Failure expected on an incorrect permission");
                 } catch (AccessControlException ex) {
                     // expected
-                    
Assert.assertEquals(AccessControlException.class.getName(), 
ex.getClass().getName());
+                    assertEquals(AccessControlException.class.getName(), 
ex.getClass().getName());
                 }
             }
 
@@ -469,10 +479,10 @@ void hdfsReadFailTest(String fileName) throws Exception {
                 try {
                     fs.open(file);
 
-                    Assert.fail("Failure expected on an incorrect permission");
+                    fail("Failure expected on an incorrect permission");
                 } catch (AccessControlException ex) {
                     // expected
-                    
Assert.assertEquals(AccessControlException.class.getName(), 
ex.getClass().getName());
+                    assertEquals(AccessControlException.class.getName(), 
ex.getClass().getName());
                 }
             }
 
@@ -492,10 +502,10 @@ void hdfsReadFailTest(String fileName) throws Exception {
                 try {
                     fs.open(file);
 
-                    Assert.fail("Failure expected on an incorrect permission");
+                    fail("Failure expected on an incorrect permission");
                 } catch (AccessControlException ex) {
                     // expected
-                    
Assert.assertEquals(AccessControlException.class.getName(), 
ex.getClass().getName());
+                    assertEquals(AccessControlException.class.getName(), 
ex.getClass().getName());
                 }
             }
 
@@ -514,10 +524,10 @@ void hdfsReadFailTest(String fileName) throws Exception {
                 // Read the file
                 try {
                     fs.open(file);
-                    Assert.fail("Failure expected on an incorrect permission");
+                    fail("Failure expected on an incorrect permission");
                 } catch (AccessControlException ex) {
                     // expected
-                    
Assert.assertEquals(AccessControlException.class.getName(), 
ex.getClass().getName());
+                    assertEquals(AccessControlException.class.getName(), 
ex.getClass().getName());
                 }
             }
 
@@ -547,7 +557,7 @@ void hdfsGetContentSummary(final String dirName) throws 
Exception {
 
                     Assert.assertEquals("Found unexpected number of 
directories; expected-count=3, actual-count=" + directoryCount, 3, 
directoryCount);
                 } catch (Exception e) {
-                    Assert.fail("Failed to getContentSummary, exception=" + e);
+                    fail("Failed to getContentSummary, exception=" + e);
                 }
             }
 
diff --git 
a/hdfs-agent/src/test/java/org/apache/ranger/services/hdfs/RangerHdfsAuthorizerTest.java
 
b/hdfs-agent/src/test/java/org/apache/ranger/services/hdfs/RangerHdfsAuthorizerTest.java
index 68884d811..11a1b739e 100644
--- 
a/hdfs-agent/src/test/java/org/apache/ranger/services/hdfs/RangerHdfsAuthorizerTest.java
+++ 
b/hdfs-agent/src/test/java/org/apache/ranger/services/hdfs/RangerHdfsAuthorizerTest.java
@@ -17,7 +17,7 @@
 
 package org.apache.ranger.services.hdfs;
 
-import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang.StringUtils;
 import org.apache.hadoop.fs.permission.FsAction;
 import org.apache.hadoop.fs.permission.FsPermission;
 import org.apache.hadoop.hdfs.server.namenode.INode;
@@ -62,6 +62,8 @@ public static void setup() {
 
             file.deleteOnExit();
 
+            String resourceDir = 
RangerHdfsAuthorizerTest.class.getResource("/hdfs_version_3.0").getPath();
+
             try (FileOutputStream outStream = new FileOutputStream(file);
                     OutputStreamWriter writer = new 
OutputStreamWriter(outStream, StandardCharsets.UTF_8)) {
                 writer.write("<configuration>\n" +
@@ -73,6 +75,18 @@ public static void setup() {
                         "                
<name>xasecure.add-hadoop-authorization</name>\n" +
                         "                <value>true</value>\n" +
                         "        </property>\n" +
+                        "        <property>\n" +
+                        "                
<name>ranger.plugin.hdfs.service.name</name>\n" +
+                        "                <value>cl1_hadoop</value>\n" +
+                        "        </property>\n" +
+                        "        <property>\n" +
+                        "                
<name>ranger.plugin.hdfs.policy.source.impl</name>\n" +
+                        "                
<value>org.apache.ranger.services.hdfs.RangerAdminClientImpl</value>\n" +
+                        "        </property>\n" +
+                        "        <property>\n" +
+                        "                
<name>ranger.plugin.hdfs.policy.cache.dir</name>\n" +
+                        "                <value>" + resourceDir + "</value>\n" 
+
+                        "        </property>\n" +
                         "</configuration>\n");
             }
 
@@ -86,6 +100,8 @@ public static void setup() {
         AccessControlEnforcer accessControlEnforcer = null;
 
         rangerControlEnforcer = 
authorizer.getExternalAccessControlEnforcer(accessControlEnforcer);
+
+        Assert.assertNotNull("rangerControlEnforcer should not be null", 
rangerControlEnforcer);
     }
 
     @AfterClass
@@ -213,6 +229,7 @@ private static INode createNode(String[] pathSegments, int 
i, String owner, Stri
         INode  mock     = Mockito.mock(INode.class);
 
         
when(mock.getLocalNameBytes()).thenReturn(name.getBytes(StandardCharsets.UTF_8));
+        when(mock.getLocalName()).thenReturn(name);
         when(mock.getUserName()).thenReturn(owner);
         when(mock.getGroupName()).thenReturn(group);
         when(mock.getFullPathName()).thenReturn(fullPath);
diff --git 
a/hdfs-agent/src/test/java/org/apache/ranger/services/hdfs/TestRangerServiceHdfs.java
 
b/hdfs-agent/src/test/java/org/apache/ranger/services/hdfs/TestRangerServiceHdfs.java
new file mode 100644
index 000000000..63b1d34df
--- /dev/null
+++ 
b/hdfs-agent/src/test/java/org/apache/ranger/services/hdfs/TestRangerServiceHdfs.java
@@ -0,0 +1,176 @@
+/*
+ * 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.ranger.services.hdfs;
+
+import org.apache.ranger.plugin.model.RangerPolicy;
+import org.apache.ranger.plugin.model.RangerService;
+import org.apache.ranger.plugin.model.RangerServiceDef;
+import org.apache.ranger.plugin.model.RangerServiceDef.RangerAccessTypeDef;
+import org.apache.ranger.plugin.model.RangerServiceDef.RangerResourceDef;
+import org.apache.ranger.plugin.resourcematcher.RangerAbstractResourceMatcher;
+import org.apache.ranger.plugin.resourcematcher.RangerPathResourceMatcher;
+import org.apache.ranger.plugin.service.ResourceLookupContext;
+import org.apache.ranger.services.hdfs.client.HdfsResourceMgr;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.MethodOrderer;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestMethodOrder;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.MockedStatic;
+import org.mockito.Mockito;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @generated by Cursor
+ * @description : Unit Test cases for RangerServiceHdfs
+ */
+
+@ExtendWith(MockitoExtension.class)
+@TestMethodOrder(MethodOrderer.MethodName.class)
+public class TestRangerServiceHdfs {
+    @Test
+    public void test01_validateConfig_delegatesToConnectionTest() throws 
Exception {
+        RangerServiceHdfs svc     = new RangerServiceHdfs();
+        RangerServiceDef  def     = buildMinimalServiceDef();
+        RangerService     service = buildService("hdfs-test", "hdfs", new 
HashMap<>());
+        svc.init(def, service);
+
+        Map<String, Object> expected = new HashMap<>();
+        expected.put("status", "ok");
+
+        try (MockedStatic<HdfsResourceMgr> mocked = 
Mockito.mockStatic(HdfsResourceMgr.class)) {
+            mocked.when(() -> 
HdfsResourceMgr.connectionTest(Mockito.eq("hdfs-test"), 
Mockito.anyMap())).thenReturn(expected);
+
+            Map<String, Object> ret = svc.validateConfig();
+            Assertions.assertEquals(expected, ret);
+            mocked.verify(() -> 
HdfsResourceMgr.connectionTest(Mockito.eq("hdfs-test"), Mockito.anyMap()));
+        }
+    }
+
+    @Test
+    public void test02_lookupResource_delegatesToGetHdfsResources() throws 
Exception {
+        RangerServiceHdfs svc     = new RangerServiceHdfs();
+        RangerServiceDef  def     = buildMinimalServiceDef();
+        RangerService     service = buildService("hdfs-svc", "hdfs", new 
HashMap<>());
+        svc.init(def, service);
+
+        ResourceLookupContext ctx = new ResourceLookupContext();
+        ctx.setUserInput("/tmp/");
+        ctx.setResourceName("path");
+        Map<String, List<String>> res = new HashMap<>();
+        res.put(HdfsResourceMgr.PATH, Collections.singletonList("/tmp"));
+        ctx.setResources(res);
+
+        List<String> expected = Arrays.asList("/tmp/a", "/tmp/b");
+
+        try (MockedStatic<HdfsResourceMgr> mocked = 
Mockito.mockStatic(HdfsResourceMgr.class)) {
+            mocked.when(() -> 
HdfsResourceMgr.getHdfsResources(Mockito.eq("hdfs-svc"), Mockito.eq("hdfs"), 
Mockito.anyMap(), Mockito.eq(ctx))).thenReturn(expected);
+
+            List<String> ret = svc.lookupResource(ctx);
+            Assertions.assertEquals(expected, ret);
+            mocked.verify(() -> 
HdfsResourceMgr.getHdfsResources(Mockito.eq("hdfs-svc"), Mockito.eq("hdfs"), 
Mockito.anyMap(), Mockito.eq(ctx)));
+        }
+    }
+
+    @Test
+    public void 
test03_getDefaultRangerPolicies_setsPathWildcard_andAddsAuditPolicies() throws 
Exception {
+        RangerServiceHdfs   svc = new RangerServiceHdfs();
+        RangerServiceDef    def = buildMinimalServiceDef();
+        Map<String, String> cfg = new HashMap<>();
+        cfg.put("setup.additional.default.policies", "false");
+        RangerService service = buildService("hdfs-def", "hdfs", cfg);
+        svc.init(def, service);
+
+        List<RangerPolicy> policies = svc.getDefaultRangerPolicies();
+        Assertions.assertFalse(policies.isEmpty());
+
+        boolean foundAll          = false;
+        boolean foundKms          = false;
+        boolean foundHbaseArchive = false;
+
+        for (RangerPolicy p : policies) {
+            if (p.getName().contains("all")) {
+                RangerPolicy.RangerPolicyResource r = 
p.getResources().get("path");
+                Assertions.assertNotNull(r);
+                Assertions.assertEquals(Collections.singletonList("/*"), 
r.getValues());
+                foundAll = true;
+            }
+            if ("kms-audit-path".equals(p.getName())) {
+                
Assertions.assertEquals(Collections.singletonList("/ranger/audit/kms"), 
p.getResources().get("path").getValues());
+                Assertions.assertTrue(p.getPolicyItems().stream().anyMatch(it 
-> it.getUsers() != null && it.getUsers().contains("keyadmin")));
+                foundKms = true;
+            }
+            if ("hbase-archive".equals(p.getName())) {
+                
Assertions.assertEquals(Collections.singletonList("/hbase/archive"), 
p.getResources().get("path").getValues());
+                Assertions.assertTrue(p.getPolicyItems().stream().anyMatch(it 
-> it.getUsers() != null && it.getUsers().contains("hbase")));
+                foundHbaseArchive = true;
+            }
+        }
+
+        Assertions.assertTrue(foundAll);
+        Assertions.assertTrue(foundKms);
+        Assertions.assertTrue(foundHbaseArchive);
+    }
+
+    private RangerService buildService(String name, String type, Map<String, 
String> cfg) {
+        RangerService svc = new RangerService();
+        svc.setName(name);
+        svc.setType(type);
+        svc.setConfigs(cfg);
+        return svc;
+    }
+
+    private RangerServiceDef buildMinimalServiceDef() {
+        RangerServiceDef def = new RangerServiceDef();
+        def.setName("hdfs");
+
+        RangerAccessTypeDef read = new RangerAccessTypeDef();
+        read.setName("read");
+        RangerAccessTypeDef write = new RangerAccessTypeDef();
+        write.setName("write");
+        def.setAccessTypes(Arrays.asList(read, write));
+
+        Map<String, String> options = new HashMap<>();
+        options.put("create.default.policy.per.hierarchy", "true");
+        def.setOptions(options);
+
+        RangerResourceDef path = new RangerResourceDef();
+        path.setName("path");
+        path.setMandatory(true);
+        path.setRecursiveSupported(true);
+        path.setMatcher(RangerPathResourceMatcher.class.getName());
+        Map<String, String> matcherOptions = new HashMap<>();
+        matcherOptions.put(RangerPathResourceMatcher.OPTION_PATH_SEPARATOR, 
"/");
+        matcherOptions.put(RangerAbstractResourceMatcher.OPTION_WILD_CARD, 
"true");
+        path.setMatcherOptions(matcherOptions);
+
+        List<RangerResourceDef> resources = new ArrayList<>();
+        resources.add(path);
+        def.setResources(resources);
+        return def;
+    }
+}
diff --git 
a/hdfs-agent/src/test/java/org/apache/ranger/services/hdfs/client/HdfsClientTest.java
 
b/hdfs-agent/src/test/java/org/apache/ranger/services/hdfs/client/HdfsClientTest.java
index dbb8c4883..2026ab2e9 100644
--- 
a/hdfs-agent/src/test/java/org/apache/ranger/services/hdfs/client/HdfsClientTest.java
+++ 
b/hdfs-agent/src/test/java/org/apache/ranger/services/hdfs/client/HdfsClientTest.java
@@ -19,11 +19,41 @@
 
 package org.apache.ranger.services.hdfs.client;
 
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.fs.FileStatus;
+import org.apache.hadoop.fs.FileSystem;
+import org.apache.hadoop.fs.Path;
+import org.apache.hadoop.security.SecureClientLogin;
+import org.apache.hadoop.security.UserGroupInformation;
+import org.apache.ranger.plugin.client.HadoopException;
 import org.junit.Test;
-
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.MethodOrderer;
+import org.junit.jupiter.api.TestMethodOrder;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.MockedConstruction;
+import org.mockito.MockedStatic;
+import org.mockito.Mockito;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import javax.security.auth.Subject;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 
+/**
+ * @generated by Cursor
+ * @description : Unit Test cases for HdfsClient
+ */
+
+@TestMethodOrder(MethodOrderer.MethodName.class)
+@ExtendWith(MockitoExtension.class)
 public class HdfsClientTest {
     @Test(expected = IllegalArgumentException.class)
     public void testUsernameNotSpecified() throws IllegalArgumentException {
@@ -178,4 +208,326 @@ public void testValidHaConfig() throws 
IllegalArgumentException {
 
         HdfsClient.validateConnectionConfigs(configs);
     }
+
+    // ===== JUnit 5 additional tests appended (preserving existing code 
above) =====
+
+    @Test
+    public void test_validate_valid_multi_nn_transforms_config() {
+        Map<String, String> configs = new HashMap<>();
+        configs.put("username", "hdfsuser");
+        configs.put("password", "hdfsuser");
+        configs.put("hadoop.security.authentication", "simple");
+        configs.put("fs.default.name", 
"node-1.example.com:8020,node-2.example.com:8020");
+        HdfsClient.validateConnectionConfigs(configs);
+        Assertions.assertEquals("hdfscluster", 
configs.get("dfs.nameservices"));
+        Assertions.assertEquals("hdfs://hdfscluster", 
configs.get("fs.default.name"));
+        
Assertions.assertEquals("org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider",
 configs.get("dfs.client.failover.proxy.provider.hdfscluster"));
+        Assertions.assertEquals("namenode1,namenode2", 
configs.get("dfs.ha.namenodes.hdfscluster"));
+        Assertions.assertEquals("node-1.example.com:8020", 
configs.get("dfs.namenode.rpc-address.hdfscluster.namenode1"));
+        Assertions.assertEquals("node-2.example.com:8020", 
configs.get("dfs.namenode.rpc-address.hdfscluster.namenode2"));
+    }
+
+    @Test
+    public void test01_validate_username_missing() {
+        Map<String, String>      configs = new HashMap<>();
+        IllegalArgumentException ex      = 
Assertions.assertThrows(IllegalArgumentException.class, () -> 
HdfsClient.validateConnectionConfigs(configs));
+        Assertions.assertTrue(ex.getMessage().contains("username"));
+    }
+
+    @Test
+    public void test02_validate_password_missing() {
+        Map<String, String> configs = new HashMap<>();
+        configs.put("username", "hdfsuser");
+        IllegalArgumentException ex = 
Assertions.assertThrows(IllegalArgumentException.class, () -> 
HdfsClient.validateConnectionConfigs(configs));
+        Assertions.assertTrue(ex.getMessage().contains("password"));
+    }
+
+    @Test
+    public void test03_validate_auth_missing() {
+        Map<String, String> configs = new HashMap<>();
+        configs.put("username", "hdfsuser");
+        configs.put("password", "hdfsuser");
+        IllegalArgumentException ex = 
Assertions.assertThrows(IllegalArgumentException.class, () -> 
HdfsClient.validateConnectionConfigs(configs));
+        
Assertions.assertTrue(ex.getMessage().contains("hadoop.security.authentication"));
+    }
+
+    @Test
+    public void test04_validate_fsdefault_missing() {
+        Map<String, String> configs = new HashMap<>();
+        configs.put("username", "hdfsuser");
+        configs.put("password", "hdfsuser");
+        configs.put("hadoop.security.authentication", "simple");
+        IllegalArgumentException ex = 
Assertions.assertThrows(IllegalArgumentException.class, () -> 
HdfsClient.validateConnectionConfigs(configs));
+        Assertions.assertTrue(ex.getMessage().contains("fs.default.name"));
+    }
+
+    @Test
+    public void test05_validate_proxyProvider_missing() {
+        Map<String, String> configs = new HashMap<>();
+        configs.put("username", "hdfsuser");
+        configs.put("password", "hdfsuser");
+        configs.put("hadoop.security.authentication", "simple");
+        configs.put("fs.default.name", "hdfs://hwqe-1425428405");
+        configs.put("dfs.nameservices", "hwqe-1425428405");
+        IllegalArgumentException ex = 
Assertions.assertThrows(IllegalArgumentException.class, () -> 
HdfsClient.validateConnectionConfigs(configs));
+        
Assertions.assertTrue(ex.getMessage().contains("dfs.client.failover.proxy.provider.hwqe-1425428405"));
+    }
+
+    @Test
+    public void test06_validate_nnElements_missing() {
+        Map<String, String> configs = new HashMap<>();
+        configs.put("username", "hdfsuser");
+        configs.put("password", "hdfsuser");
+        configs.put("hadoop.security.authentication", "simple");
+        configs.put("fs.default.name", "hdfs://hwqe-1425428405");
+        configs.put("dfs.nameservices", "hwqe-1425428405");
+        configs.put("dfs.client.failover.proxy.provider.hwqe-1425428405", 
"org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider");
+        IllegalArgumentException ex = 
Assertions.assertThrows(IllegalArgumentException.class, () -> 
HdfsClient.validateConnectionConfigs(configs));
+        
Assertions.assertTrue(ex.getMessage().contains("dfs.ha.namenodes.hwqe-1425428405"));
+    }
+
+    @Test
+    public void test07_validate_nn1_missing() {
+        Map<String, String> configs = new HashMap<>();
+        configs.put("username", "hdfsuser");
+        configs.put("password", "hdfsuser");
+        configs.put("hadoop.security.authentication", "simple");
+        configs.put("fs.default.name", "hdfs://hwqe-1425428405");
+        configs.put("dfs.nameservices", "hwqe-1425428405");
+        configs.put("dfs.client.failover.proxy.provider.hwqe-1425428405", 
"org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider");
+        configs.put("dfs.ha.namenodes.hwqe-1425428405", "nn1,nn2");
+        configs.put("dfs.namenode.rpc-address.hwqe-1425428405.nn2", 
"node-2.example.com:8020");
+        IllegalArgumentException ex = 
Assertions.assertThrows(IllegalArgumentException.class, () -> 
HdfsClient.validateConnectionConfigs(configs));
+        
Assertions.assertTrue(ex.getMessage().contains("dfs.namenode.rpc-address.hwqe-1425428405.nn1"));
+    }
+
+    @Test
+    public void test08_validate_nn2_missing() {
+        Map<String, String> configs = new HashMap<>();
+        configs.put("username", "hdfsuser");
+        configs.put("password", "hdfsuser");
+        configs.put("hadoop.security.authentication", "simple");
+        configs.put("fs.default.name", "hdfs://hwqe-1425428405");
+        configs.put("dfs.nameservices", "hwqe-1425428405");
+        configs.put("dfs.client.failover.proxy.provider.hwqe-1425428405", 
"org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider");
+        configs.put("dfs.ha.namenodes.hwqe-1425428405", "nn1,nn2");
+        configs.put("dfs.namenode.rpc-address.hwqe-1425428405.nn1", 
"node-1.example.com:8020");
+        IllegalArgumentException ex = 
Assertions.assertThrows(IllegalArgumentException.class, () -> 
HdfsClient.validateConnectionConfigs(configs));
+        
Assertions.assertTrue(ex.getMessage().contains("dfs.namenode.rpc-address.hwqe-1425428405.nn2"));
+    }
+
+    @Test
+    public void test09_validate_valid_non_ha() {
+        Map<String, String> configs = new HashMap<>();
+        configs.put("username", "hdfsuser");
+        configs.put("password", "hdfsuser");
+        configs.put("hadoop.security.authentication", "simple");
+        configs.put("fs.default.name", "hdfs://node-2.example.com:8020");
+        HdfsClient.validateConnectionConfigs(configs);
+    }
+
+    @Test
+    public void test10_validate_valid_multi_nn_transforms_config() {
+        Map<String, String> configs = new HashMap<>();
+        configs.put("username", "hdfsuser");
+        configs.put("password", "hdfsuser");
+        configs.put("hadoop.security.authentication", "simple");
+        configs.put("fs.default.name", 
"node-1.example.com:8020,node-2.example.com:8020");
+        HdfsClient.validateConnectionConfigs(configs);
+        Assertions.assertEquals("hdfscluster", 
configs.get("dfs.nameservices"));
+        Assertions.assertEquals("hdfs://hdfscluster", 
configs.get("fs.default.name"));
+        
Assertions.assertEquals("org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider",
 configs.get("dfs.client.failover.proxy.provider.hdfscluster"));
+        Assertions.assertEquals("namenode1,namenode2", 
configs.get("dfs.ha.namenodes.hdfscluster"));
+        Assertions.assertEquals("node-1.example.com:8020", 
configs.get("dfs.namenode.rpc-address.hdfscluster.namenode1"));
+        Assertions.assertEquals("node-2.example.com:8020", 
configs.get("dfs.namenode.rpc-address.hdfscluster.namenode2"));
+    }
+
+    @Test
+    public void test11_connectionTest_success() throws Exception {
+        Map<String, String> cfg = new HashMap<>();
+        cfg.put("username", "u");
+        cfg.put("password", "p");
+        cfg.put("hadoop.security.authentication", "simple");
+        cfg.put("fs.default.name", "hdfs://node-1:8020");
+
+        try (MockedStatic<SecureClientLogin> mockedLogin = 
Mockito.mockStatic(SecureClientLogin.class);
+                MockedStatic<UserGroupInformation> mockedUGI = 
Mockito.mockStatic(UserGroupInformation.class);
+                MockedStatic<FileSystem> mockedFS = 
Mockito.mockStatic(FileSystem.class)) {
+            mockedLogin.when(() -> 
SecureClientLogin.getPrincipal(Mockito.anyString(), 
Mockito.anyString())).thenReturn(null);
+            mockedLogin.when(() -> 
SecureClientLogin.login(Mockito.anyString())).thenReturn(new Subject());
+
+            try (MockedConstruction<HdfsClient> mockedConstruct = 
Mockito.mockConstruction(HdfsClient.class, (mock, ctx) -> {
+                Mockito.when(mock.listFiles(Mockito.eq("/"), Mockito.isNull(), 
Mockito.isNull())).thenReturn(Collections.singletonList("/a"));
+            })) {
+                Map<String, Object> ret = HdfsClient.connectionTest("svc", 
cfg);
+                Assertions.assertEquals(Boolean.TRUE, 
ret.get("connectivityStatus"));
+            }
+        }
+    }
+
+    @Test
+    public void test12_connectionTest_unableToListFiles() throws Exception {
+        Map<String, String> cfg = new HashMap<>();
+        cfg.put("username", "u");
+        cfg.put("password", "p");
+        cfg.put("hadoop.security.authentication", "simple");
+        cfg.put("fs.default.name", "hdfs://node-1:8020");
+
+        try (MockedStatic<SecureClientLogin> mockedLogin = 
Mockito.mockStatic(SecureClientLogin.class);
+                MockedStatic<UserGroupInformation> mockedUGI = 
Mockito.mockStatic(UserGroupInformation.class);
+                MockedStatic<FileSystem> mockedFS = 
Mockito.mockStatic(FileSystem.class)) {
+            mockedLogin.when(() -> 
SecureClientLogin.getPrincipal(Mockito.anyString(), 
Mockito.anyString())).thenReturn(null);
+            mockedLogin.when(() -> 
SecureClientLogin.login(Mockito.anyString())).thenReturn(new Subject());
+
+            try (MockedConstruction<HdfsClient> mockedConstruct = 
Mockito.mockConstruction(HdfsClient.class, (mock, ctx) -> {
+                Mockito.when(mock.listFiles(Mockito.eq("/"), Mockito.isNull(), 
Mockito.isNull())).thenReturn(Collections.emptyList());
+            })) {
+                Map<String, Object> ret = HdfsClient.connectionTest("svc", 
cfg);
+                Assertions.assertEquals(Boolean.FALSE, 
ret.get("connectivityStatus"));
+            }
+        }
+    }
+
+    @Test
+    public void test13_connectionTest_propagates_hadoop_exception() throws 
Exception {
+        Map<String, String> cfg = new HashMap<>();
+        cfg.put("username", "u");
+        cfg.put("password", "p");
+        cfg.put("hadoop.security.authentication", "simple");
+        cfg.put("fs.default.name", "hdfs://node-1:8020");
+
+        try (MockedStatic<SecureClientLogin> mockedLogin = 
Mockito.mockStatic(SecureClientLogin.class);
+                MockedStatic<UserGroupInformation> mockedUGI = 
Mockito.mockStatic(UserGroupInformation.class);
+                MockedStatic<FileSystem> mockedFS = 
Mockito.mockStatic(FileSystem.class)) {
+            mockedLogin.when(() -> 
SecureClientLogin.getPrincipal(Mockito.anyString(), 
Mockito.anyString())).thenReturn(null);
+            mockedLogin.when(() -> 
SecureClientLogin.login(Mockito.anyString())).thenReturn(new Subject());
+
+            try (MockedConstruction<HdfsClient> mockedConstruct = 
Mockito.mockConstruction(HdfsClient.class, (mock, ctx) -> {
+                Mockito.when(mock.listFiles(Mockito.eq("/"), Mockito.isNull(), 
Mockito.isNull())).thenThrow(new HadoopException("x", new 
RuntimeException("y")));
+            })) {
+                Assertions.assertThrows(HadoopException.class, () -> 
HdfsClient.connectionTest("svc", cfg));
+            }
+        }
+    }
+
+    @Test
+    public void test14_listFiles_returns_base_when_empty_listing_and_exists() 
throws Exception {
+        Map<String, String> cfg = baseCfg();
+        try (MockedStatic<SecureClientLogin> mockedLogin = 
Mockito.mockStatic(SecureClientLogin.class);
+                MockedStatic<UserGroupInformation> mockedUGI = 
Mockito.mockStatic(UserGroupInformation.class);
+                MockedStatic<FileSystem> mockedFS = 
Mockito.mockStatic(FileSystem.class)) {
+            mockedLogin.when(() -> 
SecureClientLogin.getPrincipal(Mockito.anyString(), 
Mockito.anyString())).thenReturn(null);
+            mockedLogin.when(() -> 
SecureClientLogin.login(Mockito.anyString())).thenReturn(new Subject());
+
+            FileSystem fs = Mockito.mock(FileSystem.class);
+            mockedFS.when(() -> 
FileSystem.get(Mockito.any(Configuration.class))).thenReturn(fs);
+
+            Path base = new Path("/base");
+            Mockito.when(fs.listStatus(Mockito.eq(base))).thenReturn(new 
FileStatus[0]);
+            Mockito.when(fs.exists(Mockito.eq(base))).thenReturn(true);
+
+            HdfsClient   client = new HdfsClient("svc", cfg);
+            List<String> out    = client.listFiles("/base", null, null);
+            Assertions.assertEquals(Collections.singletonList("/base"), out);
+        }
+    }
+
+    @Test
+    public void test15_listFiles_filters_and_skips_duplicates() throws 
Exception {
+        Map<String, String> cfg = baseCfg();
+        try (MockedStatic<SecureClientLogin> mockedLogin = 
Mockito.mockStatic(SecureClientLogin.class);
+                MockedStatic<UserGroupInformation> mockedUGI = 
Mockito.mockStatic(UserGroupInformation.class);
+                MockedStatic<FileSystem> mockedFS = 
Mockito.mockStatic(FileSystem.class)) {
+            mockedLogin.when(() -> 
SecureClientLogin.getPrincipal(Mockito.anyString(), 
Mockito.anyString())).thenReturn(null);
+            mockedLogin.when(() -> 
SecureClientLogin.login(Mockito.anyString())).thenReturn(new Subject());
+
+            FileSystem fs = Mockito.mock(FileSystem.class);
+            mockedFS.when(() -> 
FileSystem.get(Mockito.any(Configuration.class))).thenReturn(fs);
+
+            Path       base = new Path("/base");
+            FileStatus stA  = Mockito.mock(FileStatus.class);
+            FileStatus stB  = Mockito.mock(FileStatus.class);
+            Mockito.when(stA.getPath()).thenReturn(new Path("/base/a"));
+            Mockito.when(stB.getPath()).thenReturn(new Path("/base/b"));
+            Mockito.when(fs.listStatus(Mockito.eq(base))).thenReturn(new 
FileStatus[] {stA, stB});
+            Mockito.when(fs.exists(Mockito.eq(base))).thenReturn(true);
+
+            HdfsClient   client      = new HdfsClient("svc", cfg);
+            List<String> outNoFilter = client.listFiles("/base", null, 
Collections.singletonList("/base/a"));
+            Assertions.assertEquals(Collections.singletonList("/base/b"), 
outNoFilter);
+
+            List<String> outWithFilter = client.listFiles("/base", "*.txt", 
new ArrayList<>());
+            // for filter, set statuses to .txt and .log
+            FileStatus stTxt = Mockito.mock(FileStatus.class);
+            FileStatus stLog = Mockito.mock(FileStatus.class);
+            Mockito.when(stTxt.getPath()).thenReturn(new 
Path("/base/file.txt"));
+            Mockito.when(stLog.getPath()).thenReturn(new 
Path("/base/file.log"));
+            Mockito.when(fs.listStatus(Mockito.eq(base))).thenReturn(new 
FileStatus[] {stTxt, stLog});
+            List<String> filtered = client.listFiles("/base", "*.txt", new 
ArrayList<>());
+            
Assertions.assertEquals(Collections.singletonList("/base/file.txt"), filtered);
+        }
+    }
+
+    @Test
+    public void test16_listFiles_wraps_unknown_host() throws Exception {
+        Map<String, String> cfg = baseCfg();
+        try (MockedStatic<SecureClientLogin> mockedLogin = 
Mockito.mockStatic(SecureClientLogin.class);
+                MockedStatic<UserGroupInformation> mockedUGI = 
Mockito.mockStatic(UserGroupInformation.class);
+                MockedStatic<FileSystem> mockedFS = 
Mockito.mockStatic(FileSystem.class)) {
+            mockedLogin.when(() -> 
SecureClientLogin.getPrincipal(Mockito.anyString(), 
Mockito.anyString())).thenReturn(null);
+            mockedLogin.when(() -> 
SecureClientLogin.login(Mockito.anyString())).thenReturn(new Subject());
+
+            mockedFS.when(() -> 
FileSystem.get(Mockito.any(Configuration.class))).thenThrow(new 
UnknownHostException("unresolvable"));
+
+            HdfsClient client = new HdfsClient("svc", cfg);
+            Assertions.assertThrows(HadoopException.class, () -> 
client.listFiles("/base", null, null));
+        }
+    }
+
+    @Test
+    public void test17_listFiles_wraps_file_not_found() throws Exception {
+        Map<String, String> cfg = baseCfg();
+        try (MockedStatic<SecureClientLogin> mockedLogin = 
Mockito.mockStatic(SecureClientLogin.class);
+                MockedStatic<UserGroupInformation> mockedUGI = 
Mockito.mockStatic(UserGroupInformation.class);
+                MockedStatic<FileSystem> mockedFS = 
Mockito.mockStatic(FileSystem.class)) {
+            mockedLogin.when(() -> 
SecureClientLogin.getPrincipal(Mockito.anyString(), 
Mockito.anyString())).thenReturn(null);
+            mockedLogin.when(() -> 
SecureClientLogin.login(Mockito.anyString())).thenReturn(new Subject());
+
+            FileSystem fs = Mockito.mock(FileSystem.class);
+            mockedFS.when(() -> 
FileSystem.get(Mockito.any(Configuration.class))).thenReturn(fs);
+
+            Mockito.when(fs.listStatus(Mockito.any(Path.class))).thenThrow(new 
FileNotFoundException("missing"));
+
+            HdfsClient client = new HdfsClient("svc", cfg);
+            Assertions.assertThrows(HadoopException.class, () -> 
client.listFiles("/base", null, null));
+        }
+    }
+
+    @Test
+    public void test18_listFiles_wraps_io_exception() throws Exception {
+        Map<String, String> cfg = baseCfg();
+        try (MockedStatic<SecureClientLogin> mockedLogin = 
Mockito.mockStatic(SecureClientLogin.class);
+                MockedStatic<UserGroupInformation> mockedUGI = 
Mockito.mockStatic(UserGroupInformation.class);
+                MockedStatic<FileSystem> mockedFS = 
Mockito.mockStatic(FileSystem.class)) {
+            mockedLogin.when(() -> 
SecureClientLogin.getPrincipal(Mockito.anyString(), 
Mockito.anyString())).thenReturn(null);
+            mockedLogin.when(() -> 
SecureClientLogin.login(Mockito.anyString())).thenReturn(new Subject());
+
+            FileSystem fs = Mockito.mock(FileSystem.class);
+            mockedFS.when(() -> 
FileSystem.get(Mockito.any(Configuration.class))).thenReturn(fs);
+
+            Mockito.when(fs.listStatus(Mockito.any(Path.class))).thenThrow(new 
IOException("io"));
+
+            HdfsClient client = new HdfsClient("svc", cfg);
+            Assertions.assertThrows(HadoopException.class, () -> 
client.listFiles("/base", null, null));
+        }
+    }
+
+    private Map<String, String> baseCfg() {
+        Map<String, String> cfg = new HashMap<>();
+        cfg.put("username", "u");
+        cfg.put("password", "p");
+        cfg.put("hadoop.security.authentication", "simple");
+        cfg.put("fs.default.name", "hdfs://node-1:8020");
+        return cfg;
+    }
 }
diff --git 
a/hdfs-agent/src/test/java/org/apache/ranger/services/hdfs/client/TestHdfsConnectionMgr.java
 
b/hdfs-agent/src/test/java/org/apache/ranger/services/hdfs/client/TestHdfsConnectionMgr.java
new file mode 100644
index 000000000..796a2daee
--- /dev/null
+++ 
b/hdfs-agent/src/test/java/org/apache/ranger/services/hdfs/client/TestHdfsConnectionMgr.java
@@ -0,0 +1,108 @@
+/*
+ * 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.ranger.services.hdfs.client;
+
+import org.apache.ranger.plugin.util.TimedEventUtil;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.MethodOrderer;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestMethodOrder;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.MockedConstruction;
+import org.mockito.MockedStatic;
+import org.mockito.Mockito;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.Callable;
+import java.util.concurrent.TimeUnit;
+
+import static org.mockito.Mockito.eq;
+
+/**
+ * @generated by Cursor
+ * @description : Unit Test cases for HdfsConnectionMgr
+ */
+
+@ExtendWith(MockitoExtension.class)
+@TestMethodOrder(MethodOrderer.MethodName.class)
+public class TestHdfsConnectionMgr {
+    @Test
+    public void test01_cacheMiss_withConfigs_constructsClient() throws 
Exception {
+        Map<String, String> cfg = new HashMap<>();
+        cfg.put("username", "u");
+        cfg.put("password", "p");
+        HdfsConnectionMgr mgr = new HdfsConnectionMgr();
+
+        try (MockedStatic<TimedEventUtil> timed = 
Mockito.mockStatic(TimedEventUtil.class);
+                MockedConstruction<HdfsClient> constructed = 
Mockito.mockConstruction(HdfsClient.class, (mock, ctx) -> {
+                    Mockito.when(mock.listFiles(eq("/"), eq("*"), 
Mockito.isNull())).thenReturn(Collections.singletonList("/a"));
+                })) {
+            timed.when(() -> 
TimedEventUtil.timedTask(Mockito.any(Callable.class), eq(5L), 
eq(TimeUnit.SECONDS))).then(inv -> {
+                Callable<HdfsClient> c = inv.getArgument(0);
+                return c.call();
+            });
+
+            HdfsClient client = mgr.getHadoopConnection("svc", "hdfs", cfg);
+            Assertions.assertNotNull(client);
+        }
+    }
+
+    @Test
+    public void test02_cacheHit_successfulListFiles() throws Exception {
+        Map<String, String> cfg = new HashMap<>();
+        HdfsConnectionMgr   mgr = new HdfsConnectionMgr();
+
+        try (MockedStatic<TimedEventUtil> timed = 
Mockito.mockStatic(TimedEventUtil.class);
+                MockedConstruction<HdfsClient> constructed = 
Mockito.mockConstruction(HdfsClient.class, (mock, ctx) -> {
+                    Mockito.when(mock.listFiles(eq("/"), eq("*"), 
Mockito.isNull())).thenReturn(Collections.singletonList("/a"));
+                })) {
+            timed.when(() -> 
TimedEventUtil.timedTask(Mockito.any(Callable.class), Mockito.anyLong(), 
Mockito.eq(TimeUnit.SECONDS))).then(inv -> {
+                Callable<HdfsClient> c = inv.getArgument(0);
+                return c.call();
+            });
+
+            HdfsClient first  = mgr.getHadoopConnection("svc2", "hdfs", cfg);
+            HdfsClient second = mgr.getHadoopConnection("svc2", "hdfs", cfg);
+            Assertions.assertSame(first, second);
+        }
+    }
+
+    @Test
+    public void test03_cacheHit_nullListFiles_triggersReconnection() throws 
Exception {
+        Map<String, String> cfg = new HashMap<>();
+        HdfsConnectionMgr   mgr = new HdfsConnectionMgr();
+
+        try (MockedStatic<TimedEventUtil> timed = 
Mockito.mockStatic(TimedEventUtil.class);
+                MockedConstruction<HdfsClient> constructed = 
Mockito.mockConstruction(HdfsClient.class, (mock, ctx) -> {
+                    Mockito.when(mock.listFiles(eq("/"), eq("*"), 
Mockito.isNull())).thenReturn(null).thenReturn(Collections.singletonList("/a"));
+                })) {
+            timed.when(() -> 
TimedEventUtil.timedTask(Mockito.any(Callable.class), Mockito.anyLong(), 
Mockito.eq(TimeUnit.SECONDS))).then(inv -> {
+                Callable<HdfsClient> c = inv.getArgument(0);
+                return c.call();
+            });
+
+            HdfsClient client = mgr.getHadoopConnection("svc3", "hdfs", cfg);
+            Assertions.assertNotNull(client);
+        }
+    }
+}
diff --git 
a/hdfs-agent/src/test/java/org/apache/ranger/services/hdfs/client/TestHdfsResourceMgr.java
 
b/hdfs-agent/src/test/java/org/apache/ranger/services/hdfs/client/TestHdfsResourceMgr.java
new file mode 100644
index 000000000..0c8bd39e4
--- /dev/null
+++ 
b/hdfs-agent/src/test/java/org/apache/ranger/services/hdfs/client/TestHdfsResourceMgr.java
@@ -0,0 +1,229 @@
+/*
+ * 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.ranger.services.hdfs.client;
+
+import org.apache.ranger.plugin.client.HadoopException;
+import org.apache.ranger.plugin.service.ResourceLookupContext;
+import org.apache.ranger.plugin.util.TimedEventUtil;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.MethodOrderer;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestMethodOrder;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.MockedConstruction;
+import org.mockito.MockedStatic;
+import org.mockito.Mockito;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Callable;
+import java.util.concurrent.TimeUnit;
+
+import static org.mockito.Mockito.eq;
+
+/**
+ * @generated by Cursor
+ * @description : Unit Test cases for HdfsResourceMgr
+ */
+
+@ExtendWith(MockitoExtension.class)
+@TestMethodOrder(MethodOrderer.MethodName.class)
+public class TestHdfsResourceMgr {
+    @Test
+    public void test01_connectionTest_delegatesToHdfsClient() throws Exception 
{
+        Map<String, String> cfg = new HashMap<>();
+        cfg.put("username", "u");
+
+        Map<String, Object> expected = new HashMap<>();
+        expected.put("connectivityStatus", true);
+
+        try (MockedStatic<HdfsClient> mocked = 
Mockito.mockStatic(HdfsClient.class)) {
+            mocked.when(() -> HdfsClient.connectionTest(eq("svc"), 
eq(cfg))).thenReturn(expected);
+
+            Map<String, Object> ret = HdfsResourceMgr.connectionTest("svc", 
cfg);
+            Assertions.assertEquals(expected, ret);
+            mocked.verify(() -> HdfsClient.connectionTest(eq("svc"), eq(cfg)));
+        }
+    }
+
+    @Test
+    public void test02_getHdfsResources_userInputNoSlash() throws Exception {
+        ResourceLookupContext ctx = new ResourceLookupContext();
+        ctx.setUserInput("abc");
+        ctx.setResourceName("path");
+        Map<String, List<String>> res = new HashMap<>();
+        res.put(HdfsResourceMgr.PATH, Collections.singletonList("/alt/skip"));
+        ctx.setResources(res);
+
+        Map<String, String> cfg      = new HashMap<>();
+        HdfsClient          client   = Mockito.mock(HdfsClient.class);
+        List<String>        expected = Arrays.asList("/x", "/y");
+        Mockito.when(client.listFiles(eq("/"), eq("abc*"), 
eq(Collections.singletonList("/alt/skip")))).thenReturn(expected);
+
+        try (MockedStatic<TimedEventUtil> timed = 
Mockito.mockStatic(TimedEventUtil.class);
+                MockedConstruction<HdfsConnectionMgr> conn = 
Mockito.mockConstruction(HdfsConnectionMgr.class, (mock, context) -> {
+                    Mockito.when(mock.getHadoopConnection(eq("svc"), 
eq("hdfs"), eq(cfg))).thenReturn(client);
+                })) {
+            timed.when(() -> 
TimedEventUtil.timedTask(Mockito.any(Callable.class), eq(5L), 
eq(TimeUnit.SECONDS))).then(inv -> {
+                Callable<List<String>> c = inv.getArgument(0);
+                return c.call();
+            });
+
+            List<String> out = HdfsResourceMgr.getHdfsResources("svc", "hdfs", 
cfg, ctx);
+            Assertions.assertEquals(expected, out);
+        }
+    }
+
+    @Test
+    public void test03_getHdfsResources_userInputRootOnly() throws Exception {
+        ResourceLookupContext ctx = new ResourceLookupContext();
+        ctx.setUserInput("/");
+        ctx.setResourceName("path");
+        ctx.setResources(Collections.singletonMap(HdfsResourceMgr.PATH, 
Collections.singletonList("/p")));
+
+        Map<String, String> cfg      = new HashMap<>();
+        HdfsClient          client   = Mockito.mock(HdfsClient.class);
+        List<String>        expected = Collections.singletonList("/a");
+        Mockito.when(client.listFiles(eq("/"), Mockito.isNull(), 
eq(Collections.singletonList("/p")))).thenReturn(expected);
+
+        try (MockedStatic<TimedEventUtil> timed = 
Mockito.mockStatic(TimedEventUtil.class);
+                MockedConstruction<HdfsConnectionMgr> conn = 
Mockito.mockConstruction(HdfsConnectionMgr.class, (mock, context) -> {
+                    Mockito.when(mock.getHadoopConnection(eq("svc"), 
eq("hdfs"), eq(cfg))).thenReturn(client);
+                })) {
+            timed.when(() -> 
TimedEventUtil.timedTask(Mockito.any(Callable.class), eq(5L), 
eq(TimeUnit.SECONDS))).then(inv -> ((Callable<List<String>>) 
inv.getArgument(0)).call());
+
+            List<String> out = HdfsResourceMgr.getHdfsResources("svc", "hdfs", 
cfg, ctx);
+            Assertions.assertEquals(expected, out);
+        }
+    }
+
+    @Test
+    public void test04_getHdfsResources_userInputTrailingSlash() throws 
Exception {
+        ResourceLookupContext ctx = new ResourceLookupContext();
+        ctx.setUserInput("/tmp/");
+        ctx.setResourceName("path");
+        ctx.setResources(Collections.singletonMap(HdfsResourceMgr.PATH, 
Collections.singletonList("/ignore")));
+
+        Map<String, String> cfg      = new HashMap<>();
+        HdfsClient          client   = Mockito.mock(HdfsClient.class);
+        List<String>        expected = Arrays.asList("/tmp/a", "/tmp/b");
+        Mockito.when(client.listFiles(eq("/tmp/"), Mockito.isNull(), 
eq(Collections.singletonList("/ignore")))).thenReturn(expected);
+
+        try (MockedStatic<TimedEventUtil> timed = 
Mockito.mockStatic(TimedEventUtil.class);
+                MockedConstruction<HdfsConnectionMgr> conn = 
Mockito.mockConstruction(HdfsConnectionMgr.class, (mock, context) -> {
+                    Mockito.when(mock.getHadoopConnection(eq("svc"), 
eq("hdfs"), eq(cfg))).thenReturn(client);
+                })) {
+            timed.when(() -> 
TimedEventUtil.timedTask(Mockito.any(Callable.class), eq(5L), 
eq(TimeUnit.SECONDS))).then(inv -> ((Callable<List<String>>) 
inv.getArgument(0)).call());
+
+            List<String> out = HdfsResourceMgr.getHdfsResources("svc", "hdfs", 
cfg, ctx);
+            Assertions.assertEquals(expected, out);
+        }
+    }
+
+    @Test
+    public void test05_getHdfsResources_userInputWithComponent() throws 
Exception {
+        ResourceLookupContext ctx = new ResourceLookupContext();
+        ctx.setUserInput("/tmp/dir");
+        ctx.setResourceName("path");
+        ctx.setResources(Collections.singletonMap(HdfsResourceMgr.PATH, 
Collections.singletonList("/tmp/a")));
+
+        Map<String, String> cfg      = new HashMap<>();
+        HdfsClient          client   = Mockito.mock(HdfsClient.class);
+        List<String>        expected = Arrays.asList("/tmp/dir1", "/tmp/dir2");
+        Mockito.when(client.listFiles(eq("/tmp/"), eq("dir*"), 
eq(Collections.singletonList("/tmp/a")))).thenReturn(expected);
+
+        try (MockedStatic<TimedEventUtil> timed = 
Mockito.mockStatic(TimedEventUtil.class);
+                MockedConstruction<HdfsConnectionMgr> conn = 
Mockito.mockConstruction(HdfsConnectionMgr.class, (mock, context) -> {
+                    Mockito.when(mock.getHadoopConnection(eq("svc"), 
eq("hdfs"), eq(cfg))).thenReturn(client);
+                })) {
+            timed.when(() -> 
TimedEventUtil.timedTask(Mockito.any(Callable.class), eq(5L), 
eq(TimeUnit.SECONDS))).then(inv -> ((Callable<List<String>>) 
inv.getArgument(0)).call());
+
+            List<String> out = HdfsResourceMgr.getHdfsResources("svc", "hdfs", 
cfg, ctx);
+            Assertions.assertEquals(expected, out);
+        }
+    }
+
+    @Test
+    public void test06_getHdfsResources_nullServiceOrUserInput_returnsNull() 
throws Exception {
+        ResourceLookupContext ctx1 = new ResourceLookupContext();
+        ctx1.setUserInput("/tmp");
+        ctx1.setResourceName("path");
+        ctx1.setResources(Collections.singletonMap(HdfsResourceMgr.PATH, 
Collections.singletonList("/p")));
+
+        ResourceLookupContext ctx2 = new ResourceLookupContext();
+        ctx2.setUserInput(null);
+        ctx2.setResourceName("path");
+        ctx2.setResources(Collections.singletonMap(HdfsResourceMgr.PATH, 
Collections.singletonList("/p")));
+
+        Map<String, String> cfg = new HashMap<>();
+
+        Assertions.assertNull(HdfsResourceMgr.getHdfsResources(null, "hdfs", 
cfg, ctx1));
+        Assertions.assertNull(HdfsResourceMgr.getHdfsResources("svc", "hdfs", 
cfg, ctx2));
+    }
+
+    @Test
+    public void test07_getHdfsResources_returnsNullWhenHdfsClientNull() throws 
Exception {
+        ResourceLookupContext ctx = new ResourceLookupContext();
+        ctx.setUserInput("/tmp");
+        ctx.setResourceName("path");
+        ctx.setResources(Collections.singletonMap(HdfsResourceMgr.PATH, 
Collections.singletonList("/p")));
+
+        Map<String, String> cfg = new HashMap<>();
+
+        try (MockedStatic<TimedEventUtil> timed = 
Mockito.mockStatic(TimedEventUtil.class);
+                MockedConstruction<HdfsConnectionMgr> conn = 
Mockito.mockConstruction(HdfsConnectionMgr.class, (mock, context) -> {
+                    Mockito.when(mock.getHadoopConnection(eq("svc"), 
eq("hdfs"), eq(cfg))).thenReturn(null);
+                })) {
+            // timedTask should not be called when client is null; but we 
still set a safe default
+            timed.when(() -> 
TimedEventUtil.timedTask(Mockito.any(Callable.class), Mockito.anyLong(), 
Mockito.any(TimeUnit.class))).thenThrow(new AssertionError("timedTask should 
not be invoked"));
+
+            List<String> out = HdfsResourceMgr.getHdfsResources("svc", "hdfs", 
cfg, ctx);
+            Assertions.assertNull(out);
+        }
+    }
+
+    @Test
+    public void test08_getHdfsResources_propagatesHadoopException() throws 
Exception {
+        ResourceLookupContext ctx = new ResourceLookupContext();
+        ctx.setUserInput("/tmp");
+        ctx.setResourceName("path");
+        ctx.setResources(Collections.singletonMap(HdfsResourceMgr.PATH, 
Collections.singletonList("/p")));
+
+        Map<String, String> cfg    = new HashMap<>();
+        HdfsClient          client = Mockito.mock(HdfsClient.class);
+        Mockito.when(client.listFiles(eq("/"), eq("tmp*"), 
eq(Collections.singletonList("/p")))).thenThrow(new HadoopException("boom", new 
RuntimeException("x")));
+
+        try (MockedStatic<TimedEventUtil> timed = 
Mockito.mockStatic(TimedEventUtil.class);
+                MockedConstruction<HdfsConnectionMgr> conn = 
Mockito.mockConstruction(HdfsConnectionMgr.class, (mock, context) -> {
+                    Mockito.when(mock.getHadoopConnection(eq("svc"), 
eq("hdfs"), eq(cfg))).thenReturn(client);
+                })) {
+            timed.when(() -> 
TimedEventUtil.timedTask(Mockito.any(Callable.class), eq(5L), 
eq(TimeUnit.SECONDS))).then(inv -> {
+                Callable<List<String>> c = inv.getArgument(0);
+                return c.call();
+            });
+
+            Assertions.assertThrows(HadoopException.class, () -> 
HdfsResourceMgr.getHdfsResources("svc", "hdfs", cfg, ctx));
+        }
+    }
+}


Reply via email to