You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ranger.apache.org by dh...@apache.org on 2026/08/15 19:04:16 UTC

(ranger) branch master updated: RANGER-5349: Test Cases for agents-common Module: Packages (plugin.service, plugin.store) (#762)

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 8cf92131c RANGER-5349: Test Cases for agents-common Module: Packages (plugin.service, plugin.store) (#762)
8cf92131c is described below

commit 8cf92131c8189e777f80573da40ca26ba6ce3057
Author: Rakesh Gupta <ra...@gmail.com>
AuthorDate: Sun Aug 16 00:34:10 2026 +0530

    RANGER-5349: Test Cases for agents-common Module: Packages (plugin.service, plugin.store) (#762)
---
 .../plugin/service/TestRangerAuthContext.java      |  136 +++
 .../plugin/service/TestRangerBasePlugin.java       |  475 ++++++++-
 .../plugin/service/TestRangerBaseService.java      |  265 +++++
 .../plugin/service/TestRangerChainedPlugin.java    |  127 +++
 .../service/TestRangerDefaultRequestProcessor.java |  335 +++++++
 .../plugin/service/TestRangerDefaultService.java   |   53 +
 .../plugin/service/TestResourceLookupContext.java  |   64 ++
 .../ranger/plugin/store/TestAbstractGdsStore.java  |  151 +++
 .../plugin/store/TestAbstractPredicateUtil.java    |  573 +++++++++++
 .../plugin/store/TestAbstractServiceStore.java     | 1047 ++++++++++++++++++++
 .../ranger/plugin/store/TestAbstractTagStore.java  |  541 ++++++++++
 .../plugin/store/TestEmbeddedServiceDefsUtil.java  |  347 +++++++
 .../org/apache/ranger/plugin/store/TestPList.java  |  101 ++
 .../store/TestRangerServiceResourceSignature.java  |  116 +++
 .../ranger/plugin/store/TestRolePredicateUtil.java |  123 +++
 .../store/TestSecurityZonePredicateUtil.java       |   67 ++
 .../plugin/store/TestServicePredicateUtil.java     |  331 +++++++
 .../plugin/store/TestStoredServiceResource.java    |   61 ++
 .../ranger/plugin/store/TestTagPredicateUtil.java  |  106 ++
 .../ranger/plugin/store/TestTagValidator.java      |  315 ++++++
 .../store/file/TestGeolocationFileStore.java       |  258 +++++
 21 files changed, 5590 insertions(+), 2 deletions(-)

diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/service/TestRangerAuthContext.java b/agents-common/src/test/java/org/apache/ranger/plugin/service/TestRangerAuthContext.java
new file mode 100644
index 000000000..ecfa4352d
--- /dev/null
+++ b/agents-common/src/test/java/org/apache/ranger/plugin/service/TestRangerAuthContext.java
@@ -0,0 +1,136 @@
+/*
+ * 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.plugin.service;
+
+import org.apache.ranger.plugin.model.RangerRole;
+import org.apache.ranger.plugin.policyengine.RangerPolicyEngine;
+import org.apache.ranger.plugin.util.RangerCommonConstants;
+import org.apache.ranger.plugin.util.RangerRoles;
+import org.apache.ranger.plugin.util.RangerUserStore;
+import org.apache.ranger.ugsyncutil.transform.Mapper;
+import org.apache.ranger.ugsyncutil.util.UgsyncCommonConstants;
+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;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+* @generated by Cursor
+* @description <Unit Test for RangerAuthContext class>
+*/
+@ExtendWith(MockitoExtension.class)
+@TestMethodOrder(MethodOrderer.MethodName.class)
+public class TestRangerAuthContext {
+    public static class NoopMapper implements Mapper {
+        @Override
+        public void init(String baseProperty, List<String> regexPatterns, String regexSeparator) {
+        }
+
+        @Override
+        public String transform(String attrValue) {
+            return attrValue;
+        }
+    }
+
+    @Test
+    public void test1_RoleAggregationForUserAndGroups() {
+        RangerRole roleA = new RangerRole();
+        roleA.setName("roleA");
+        roleA.setUsers(Collections.singletonList(new RangerRole.RoleMember("u1", false)));
+
+        RangerRole roleB = new RangerRole();
+        roleB.setName("roleB");
+        roleB.setGroups(Collections.singletonList(new RangerRole.RoleMember("g1", false)));
+
+        RangerRole roleC = new RangerRole();
+        roleC.setName("roleC");
+        roleC.setGroups(Collections.singletonList(new RangerRole.RoleMember(RangerPolicyEngine.GROUP_PUBLIC, false)));
+
+        Set<RangerRole> rset = new HashSet<>();
+        rset.add(roleA);
+        rset.add(roleB);
+        rset.add(roleC);
+
+        RangerRoles roles = new RangerRoles();
+        roles.setRoleVersion(5L);
+        roles.setRangerRoles(rset);
+
+        RangerAuthContext ctx = new RangerAuthContext(new HashMap<>(), null, roles, new RangerUserStore());
+
+        Set<String> rolesOut = ctx.getRolesForUserAndGroups("u1", Collections.singleton("g1"));
+        assertTrue(rolesOut.contains("roleA"));
+        assertTrue(rolesOut.contains("roleB"));
+        assertTrue(rolesOut.contains("roleC"));
+        assertEquals(5L, ctx.getRoleVersion());
+    }
+
+    @Test
+    public void test2_OnServiceConfigsUpdateSetsTransformers() {
+        RangerAuthContext ctx = new RangerAuthContext(new HashMap<>(), null, new RangerRoles(), new RangerUserStore());
+        Map<String, String> cfg = new HashMap<>();
+        cfg.put(RangerCommonConstants.PLUGINS_CONF_USERNAME_CASE_CONVERSION_PARAM, "lower");
+        cfg.put(RangerCommonConstants.PLUGINS_CONF_GROUPNAME_CASE_CONVERSION_PARAM, "upper");
+        ctx.onServiceConfigsUpdate(cfg);
+        assertNotNull(ctx.getUserNameCaseConversion());
+        assertNotNull(ctx.getGroupNameCaseConversion());
+        assertEquals(UgsyncCommonConstants.CaseConversion.TO_LOWER, ctx.getUserNameCaseConversion());
+        assertEquals(UgsyncCommonConstants.CaseConversion.TO_UPPER, ctx.getGroupNameCaseConversion());
+    }
+
+    @Test
+    public void test3_OnServiceConfigsUpdateRegexPatternsAggregation() {
+        RangerAuthContext ctx = new RangerAuthContext(new HashMap<>(), null, new RangerRoles(), new RangerUserStore());
+        Map<String, String> cfg = new HashMap<>();
+        cfg.put(RangerCommonConstants.PLUGINS_CONF_MAPPING_USERNAME_HANDLER, this.getClass().getName() + "$NoopMapper");
+        cfg.put(RangerCommonConstants.PLUGINS_CONF_MAPPING_USERNAME, "^(.+)@example\\.com$");
+        cfg.put(RangerCommonConstants.PLUGINS_CONF_MAPPING_USERNAME + ".1", "^user_(.+)$");
+        cfg.put(RangerCommonConstants.PLUGINS_CONF_MAPPING_SEPARATOR, ":");
+        cfg.put(RangerCommonConstants.PLUGINS_CONF_MAPPING_GROUPNAME_HANDLER, this.getClass().getName() + "$NoopMapper");
+        cfg.put(RangerCommonConstants.PLUGINS_CONF_MAPPING_GROUPNAME, "^grp_(.+)$");
+        cfg.put(RangerCommonConstants.PLUGINS_CONF_MAPPING_GROUPNAME + ".1", "^team_(.+)$");
+        cfg.put(RangerCommonConstants.PLUGINS_CONF_MAPPING_SEPARATOR, ":");
+
+        ctx.onServiceConfigsUpdate(cfg);
+
+        // handlers should be set
+        assertNotNull(ctx.getUserNameTransformer());
+        assertNotNull(ctx.getGroupNameTransformer());
+    }
+
+    @Test
+    public void test4_UserStoreVersionPassThrough() {
+        RangerUserStore us = new RangerUserStore();
+        us.setUserStoreVersion(42L);
+        RangerAuthContext ctx = new RangerAuthContext(new HashMap<>(), null, new RangerRoles(), us);
+        assertEquals(42L, ctx.getUserStoreVersion());
+    }
+}
diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/service/TestRangerBasePlugin.java b/agents-common/src/test/java/org/apache/ranger/plugin/service/TestRangerBasePlugin.java
index cd56715cc..674a870e2 100644
--- a/agents-common/src/test/java/org/apache/ranger/plugin/service/TestRangerBasePlugin.java
+++ b/agents-common/src/test/java/org/apache/ranger/plugin/service/TestRangerBasePlugin.java
@@ -26,17 +26,23 @@
 import com.google.gson.JsonElement;
 import com.google.gson.JsonParseException;
 import org.apache.commons.lang3.StringUtils;
+import org.apache.ranger.audit.provider.AuditHandler;
 import org.apache.ranger.authorization.hadoop.config.RangerPluginConfig;
 import org.apache.ranger.plugin.audit.RangerDefaultAuditHandler;
+import org.apache.ranger.plugin.model.RangerPolicy;
+import org.apache.ranger.plugin.model.RangerRole;
 import org.apache.ranger.plugin.model.RangerServiceDef;
 import org.apache.ranger.plugin.policyengine.RangerAccessRequest;
 import org.apache.ranger.plugin.policyengine.RangerAccessRequestImpl;
 import org.apache.ranger.plugin.policyengine.RangerAccessResource;
 import org.apache.ranger.plugin.policyengine.RangerAccessResourceImpl;
 import org.apache.ranger.plugin.policyengine.RangerAccessResult;
+import org.apache.ranger.plugin.policyengine.RangerAccessResultProcessor;
 import org.apache.ranger.plugin.policyengine.RangerPolicyEngine;
 import org.apache.ranger.plugin.policyengine.RangerPolicyEngineOptions;
 import org.apache.ranger.plugin.policyengine.RangerResourceACLs;
+import org.apache.ranger.plugin.policyengine.RangerResourceACLs.AccessResult;
+import org.apache.ranger.plugin.policyevaluator.RangerPolicyEvaluator;
 import org.apache.ranger.plugin.store.EmbeddedServiceDefsUtil;
 import org.apache.ranger.plugin.util.RangerAccessRequestUtil;
 import org.apache.ranger.plugin.util.RangerRoles;
@@ -45,15 +51,24 @@
 import org.apache.ranger.plugin.util.ServiceGdsInfo;
 import org.apache.ranger.plugin.util.ServicePolicies;
 import org.apache.ranger.plugin.util.ServiceTags;
+import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.BeforeAll;
+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.Mockito;
+import org.mockito.junit.jupiter.MockitoExtension;
 
 import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.io.Reader;
 import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.Date;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
@@ -62,7 +77,16 @@
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
-
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+/**
+* @generated by Cursor
+* @description <Unit Test for RangerBasePlugin class>
+*/
+@ExtendWith(MockitoExtension.class)
+@TestMethodOrder(MethodOrderer.MethodName.class)
 public class TestRangerBasePlugin {
     static Gson                      gsonBuilder;
     static RangerPolicyEngineOptions peOptions;
@@ -84,10 +108,457 @@ public static void setUpBeforeClass() throws Exception {
     }
 
     @Test
-    public void testBasePluginHive() throws Exception {
+    public void test1_BasePluginHive() throws Exception {
         runTestsFromResourceFile("/plugin/test_base_plugin_hive.json");
     }
 
+    @Test
+    public void test2_MergedResourceACLs() {
+        RangerResourceACLs base = new RangerResourceACLs();
+        RangerResourceACLs chained = new RangerResourceACLs();
+
+        // craft simple ACL entries to verify merge logic does not throw and returns base reference
+        base.setUserAccessInfo("u1", "select", 1, null);
+        chained.setUserAccessInfo("u2", "select", 1, null);
+        base.getDatasets().add("d1");
+        chained.getDatasets().add("d2");
+        base.getProjects().add("p1");
+        chained.getProjects().add("p2");
+
+        RangerResourceACLs merged = RangerBasePlugin.getMergedResourceACLs(base, chained);
+        assertNotNull(merged);
+        assertEquals(base, merged);
+        assertEquals(2, merged.getDatasets().size());
+        assertEquals(2, merged.getProjects().size());
+        assertNotNull(merged.getUserACLs().get("u1"));
+    }
+
+    @Test
+    public void test3_CreateAdminClient_UsesCustomImplIfConfiguredElseFallsBack() {
+        RangerPolicyEngineOptions peOptionsLocal = new RangerPolicyEngineOptions();
+        peOptionsLocal.disablePolicyRefresher = true;
+        peOptionsLocal.disableTagRetriever = true;
+        peOptionsLocal.disableUserStoreRetriever = true;
+        peOptionsLocal.disableGdsInfoRetriever = true;
+
+        RangerPluginConfig cfg = new RangerPluginConfig("hbase", "svc", "app", null, null, peOptionsLocal);
+
+        // Provide REST URL to avoid empty URLs list in REST client
+        cfg.set("ranger.plugin.hbase.policy.rest.url", "http://localhost:6080");
+
+        // invalid class first -> fallback to REST client
+        cfg.set("ranger.plugin.hbase.policy.source.impl", "com.example.DoesNotExist");
+        assertNotNull(RangerBasePlugin.createAdminClient(cfg));
+
+        // set to a valid test implementation
+        cfg.set("ranger.plugin.hbase.policy.source.impl", "org.apache.ranger.plugin.policyengine.RangerAdminClientImpl");
+        assertNotNull(RangerBasePlugin.createAdminClient(cfg));
+    }
+
+    @Test
+    public void test4_GetServiceDefIdWhenNull() {
+        RangerPolicyEngineOptions peOptionsLocal = new RangerPolicyEngineOptions();
+        peOptionsLocal.disablePolicyRefresher = true;
+        RangerPluginConfig pluginConfig = new RangerPluginConfig("dummy", "svc", "app", null, null, peOptionsLocal);
+        RangerBasePlugin plugin = new RangerBasePlugin(pluginConfig);
+        // don't initialize policy engine; getServiceDef() returns null
+        assertEquals(-1, plugin.getServiceDefId());
+    }
+
+    @Test
+    public void test5_LogErrorMessageRateLimit() {
+        RangerPolicyEngineOptions opts = new RangerPolicyEngineOptions();
+        opts.disablePolicyRefresher = true;
+        opts.disableTagRetriever = true;
+        opts.disableUserStoreRetriever = true;
+        opts.disableGdsInfoRetriever = true;
+        RangerPluginConfig cfg = new RangerPluginConfig("dummy", "svc", "app", null, null, opts);
+        RangerBasePlugin plugin = new RangerBasePlugin(cfg);
+
+        boolean first = plugin.logErrorMessage("E1");
+        boolean second = plugin.logErrorMessage("E1");
+
+        assertEquals(true, first);
+        assertEquals(false, second);
+    }
+
+    @Test
+    public void test6_UGILogin_KeytabMissingThrows() {
+        RangerPolicyEngineOptions opts = new RangerPolicyEngineOptions();
+        RangerPluginConfig cfg = new RangerPluginConfig("dummy", "svc", "app", null, null, opts);
+        String prefix = cfg.getPropertyPrefix() + ".ugi";
+        cfg.set(prefix + ".initialize", "true");
+        cfg.set(prefix + ".login.type", "keytab");
+        // Intentionally do not set principal/keytab to trigger exception
+        assertThrows(RuntimeException.class, () -> new RangerBasePlugin(cfg));
+    }
+
+    @Test
+    public void test7_UGILogin_JaasMissingThrows() {
+        RangerPolicyEngineOptions opts = new RangerPolicyEngineOptions();
+        RangerPluginConfig cfg = new RangerPluginConfig("dummy", "svc", "app", null, null, opts);
+        String prefix = cfg.getPropertyPrefix() + ".ugi";
+        cfg.set(prefix + ".initialize", "true");
+        cfg.set(prefix + ".login.type", "jaas");
+        // Intentionally do not set jaas appconfig to trigger exception
+        assertThrows(RuntimeException.class, () -> new RangerBasePlugin(cfg));
+    }
+
+    @Test
+    public void test8_UGILogin_InvalidTypeNoThrow() {
+        RangerPolicyEngineOptions opts = new RangerPolicyEngineOptions();
+        opts.disablePolicyRefresher = true;
+        opts.disableTagRetriever = true;
+        opts.disableUserStoreRetriever = true;
+        opts.disableGdsInfoRetriever = true;
+        RangerPluginConfig cfg = new RangerPluginConfig("dummy", "svc", "app", null, null, opts);
+        String prefix = cfg.getPropertyPrefix() + ".ugi";
+        cfg.set(prefix + ".initialize", "true");
+        cfg.set(prefix + ".login.type", "unknown");
+        new RangerBasePlugin(cfg); // should not throw
+    }
+
+    @Test
+    public void test9_IsAccessAllowedCollection_SynchronousRefreshAndResultProcessorCalled() throws Exception {
+        RangerPolicyEngineOptions opts = new RangerPolicyEngineOptions();
+        opts.disablePolicyRefresher = true;
+        opts.disableTagRetriever = true;
+        opts.disableUserStoreRetriever = true;
+        opts.disableGdsInfoRetriever = true;
+        RangerPluginConfig cfg = new RangerPluginConfig("hive", "svc", "app", null, null, opts);
+        // Build minimal policies with serviceDef and serviceConfig to enable synchronous refresh
+        ServicePolicies policies = new ServicePolicies();
+        policies.setServiceName("svc");
+        policies.setServiceDef(EmbeddedServiceDefsUtil.instance().getEmbeddedServiceDef(EmbeddedServiceDefsUtil.EMBEDDED_SERVICEDEF_HIVE_NAME));
+        HashMap<String, String> sc = new HashMap<>();
+        sc.put(cfg.getPropertyPrefix() + ".policy.refresh.synchronous", "true");
+        policies.setServiceConfig(sc);
+
+        RangerBasePlugin plugin = new RangerBasePlugin(cfg);
+        plugin.setPolicies(policies);
+
+        RangerAccessResourceImpl res = new RangerAccessResourceImpl();
+        res.setValue("database", "db1");
+        RangerAccessRequestImpl r1 = new RangerAccessRequestImpl();
+        r1.setResource(res);
+        r1.setAccessType("select");
+        RangerAccessRequestImpl r2 = new RangerAccessRequestImpl();
+        r2.setResource(res);
+        r2.setAccessType("update");
+
+        RangerAccessResultProcessor rp = Mockito.mock(RangerAccessResultProcessor.class);
+        List<RangerAccessRequest> reqs = new ArrayList<>();
+        reqs.add(r1);
+        reqs.add(r2);
+
+        Collection<RangerAccessResult> results = plugin.isAccessAllowed(reqs, rp);
+        assertNotNull(results);
+        verify(rp, times(1)).processResults(results);
+    }
+
+    @Test
+    public void test10_MergedACLs_PriorityAndDenyWins() {
+        RangerResourceACLs base = new RangerResourceACLs();
+        RangerResourceACLs chained = new RangerResourceACLs();
+
+        // Prepare policies with different priorities
+        RangerPolicy basePolicyNormal = new RangerPolicy();
+        basePolicyNormal.setPolicyPriority(RangerPolicy.POLICY_PRIORITY_NORMAL);
+        RangerPolicy chainedHigher = new RangerPolicy();
+        chainedHigher.setPolicyPriority(RangerPolicy.POLICY_PRIORITY_OVERRIDE);
+
+        // Case 1: base missing access -> chained should be used
+        chained.setUserAccessInfo("uA", "select", RangerPolicyEvaluator.ACCESS_ALLOWED, chainedHigher);
+        RangerBasePlugin.getMergedResourceACLs(base, chained);
+        AccessResult ar1 = base.getUserACLs().get("uA").get("select");
+        assertNotNull(ar1);
+        assertEquals(RangerPolicyEvaluator.ACCESS_ALLOWED, ar1.getResult());
+
+        // Case 2: both present, chained lower priority and denied -> lower priority should not override higher-priority base allow
+        RangerPolicy basePolicyOverride = new RangerPolicy();
+        basePolicyOverride.setPolicyPriority(RangerPolicy.POLICY_PRIORITY_OVERRIDE);
+        base.setGroupAccessInfo("g1", "update", RangerPolicyEvaluator.ACCESS_ALLOWED, basePolicyOverride);
+        RangerPolicy chainedLower = new RangerPolicy();
+        chainedLower.setPolicyPriority(RangerPolicy.POLICY_PRIORITY_NORMAL);
+        chained.setGroupAccessInfo("g1", "update", RangerPolicyEvaluator.ACCESS_DENIED, chainedLower);
+        RangerBasePlugin.getMergedResourceACLs(base, chained);
+        AccessResult ar2 = base.getGroupACLs().get("g1").get("update");
+        assertEquals(RangerPolicyEvaluator.ACCESS_ALLOWED, ar2.getResult());
+
+        // Case 3: same priority and both allowed -> chained used per code path
+        base.setRoleAccessInfo("r1", "create", RangerPolicyEvaluator.ACCESS_ALLOWED, basePolicyNormal);
+        chained.setRoleAccessInfo("r1", "create", RangerPolicyEvaluator.ACCESS_ALLOWED, chainedLower);
+        RangerBasePlugin.getMergedResourceACLs(base, chained);
+        AccessResult ar3 = base.getRoleACLs().get("r1").get("create");
+        assertEquals(RangerPolicyEvaluator.ACCESS_ALLOWED, ar3.getResult());
+
+        // Case 4: same priority but chained denies and base allowed -> deny should override
+        base.setUserAccessInfo("uB", "drop", RangerPolicyEvaluator.ACCESS_ALLOWED, basePolicyNormal);
+        chained.setUserAccessInfo("uB", "drop", RangerPolicyEvaluator.ACCESS_DENIED, chainedLower);
+        RangerBasePlugin.getMergedResourceACLs(base, chained);
+        AccessResult ar4 = base.getUserACLs().get("uB").get("drop");
+        assertEquals(RangerPolicyEvaluator.ACCESS_DENIED, ar4.getResult());
+    }
+
+    @Test
+    public void test11_RegisterAuthContextListener_NotifiesOnAuthContextChange() throws Exception {
+        RangerPolicyEngineOptions opts = new RangerPolicyEngineOptions();
+        opts.disablePolicyRefresher = true;
+        opts.disableTagRetriever = true;
+        opts.disableUserStoreRetriever = true;
+        opts.disableGdsInfoRetriever = true;
+        RangerPluginConfig cfg = new RangerPluginConfig("hive", "svc", "app", null, null, opts);
+
+        ServicePolicies policies = new ServicePolicies();
+        policies.setServiceName("svc");
+        policies.setServiceDef(EmbeddedServiceDefsUtil.instance().getEmbeddedServiceDef(EmbeddedServiceDefsUtil.EMBEDDED_SERVICEDEF_HIVE_NAME));
+
+        RangerBasePlugin plugin = new RangerBasePlugin(cfg);
+
+        // simple listener that flips a flag when notified
+        final boolean[] notified = new boolean[] {false};
+        plugin.registerAuthContextEventListener(new RangerAuthContextListener() {
+            @Override
+            public void contextChanged() {
+                notified[0] = true;
+            }
+        });
+
+        plugin.setPolicies(policies);
+        assertEquals(true, notified[0]);
+    }
+
+    @Test
+    public void test12_GettersDefaultValuesWithoutPolicyEngine() {
+        RangerPolicyEngineOptions opts = new RangerPolicyEngineOptions();
+        opts.disablePolicyRefresher = true;
+        RangerPluginConfig cfg = new RangerPluginConfig("dummy", "svc", "app", null, null, opts);
+        RangerBasePlugin plugin = new RangerBasePlugin(cfg);
+
+        assertEquals(-1L, plugin.getPoliciesVersion());
+        assertEquals(-1L, plugin.getTagsVersion());
+        assertEquals(-1L, plugin.getRolesVersion());
+        assertEquals(-1L, plugin.getUserStoreVersion());
+        assertEquals(null, plugin.getResourceAccessInfo(null));
+        assertEquals(null, plugin.getResourceACLs(null));
+        assertEquals(null, plugin.getRolesFromUserAndGroups("u", new HashSet<>()));
+        assertEquals(null, plugin.getRangerRoles());
+        assertEquals(false, plugin.isServiceAdmin("u"));
+    }
+
+    @Test
+    public void test13_EnrichersReturnNullByDefault() {
+        RangerPolicyEngineOptions opts = new RangerPolicyEngineOptions();
+        opts.disablePolicyRefresher = true;
+        RangerPluginConfig cfg = new RangerPluginConfig("dummy", "svc", "app", null, null, opts);
+        RangerBasePlugin plugin = new RangerBasePlugin(cfg);
+
+        assertEquals(null, plugin.getTagEnricher());
+        assertEquals(null, plugin.getUserStoreEnricher());
+        assertEquals(null, plugin.getGdsEnricher());
+        assertEquals(null, plugin.getGdsPolicyEngine());
+    }
+
+    @Test
+    public void test14_IsAccessAllowedSingleRequest_ProcessorIsCalled() throws Exception {
+        RangerPolicyEngineOptions opts = new RangerPolicyEngineOptions();
+        opts.disablePolicyRefresher = true;
+        opts.disableTagRetriever = true;
+        opts.disableUserStoreRetriever = true;
+        opts.disableGdsInfoRetriever = true;
+        RangerPluginConfig cfg = new RangerPluginConfig("hive", "svc", "app", null, null, opts);
+
+        ServicePolicies policies = new ServicePolicies();
+        policies.setServiceName("svc");
+        policies.setServiceDef(EmbeddedServiceDefsUtil.instance().getEmbeddedServiceDef(EmbeddedServiceDefsUtil.EMBEDDED_SERVICEDEF_HIVE_NAME));
+
+        RangerBasePlugin plugin = new RangerBasePlugin(cfg);
+        plugin.setPolicies(policies);
+
+        RangerAccessResourceImpl res = new RangerAccessResourceImpl();
+        res.setValue("database", "db1");
+        RangerAccessRequestImpl req = new RangerAccessRequestImpl();
+        req.setResource(res);
+        req.setAccessType("select");
+
+        RangerAccessResultProcessor rp = Mockito.mock(RangerAccessResultProcessor.class);
+        RangerAccessResult ret = plugin.isAccessAllowed(req, rp);
+        assertNotNull(ret);
+        verify(rp, times(1)).processResult(Mockito.any(RangerAccessResult.class));
+    }
+
+    @Test
+    public void test15_GetResourceAccessInfoNonNullWhenInitialized() throws Exception {
+        RangerPolicyEngineOptions opts = new RangerPolicyEngineOptions();
+        opts.disablePolicyRefresher = true;
+        opts.disableTagRetriever = true;
+        opts.disableUserStoreRetriever = true;
+        opts.disableGdsInfoRetriever = true;
+        RangerPluginConfig cfg = new RangerPluginConfig("hive", "svc", "app", null, null, opts);
+
+        ServicePolicies policies = new ServicePolicies();
+        policies.setServiceName("svc");
+        policies.setServiceDef(EmbeddedServiceDefsUtil.instance().getEmbeddedServiceDef(EmbeddedServiceDefsUtil.EMBEDDED_SERVICEDEF_HIVE_NAME));
+
+        RangerBasePlugin plugin = new RangerBasePlugin(cfg);
+        plugin.setPolicies(policies);
+
+        RangerAccessResourceImpl res = new RangerAccessResourceImpl();
+        res.setValue("database", "db1");
+        RangerAccessRequestImpl req = new RangerAccessRequestImpl();
+        req.setResource(res);
+        req.setAccessType("select");
+
+        assertNotNull(plugin.getResourceAccessInfo(req));
+    }
+
+    @Test
+    public void test16_GetAuditProviderAlwaysNonNull() {
+        AuditHandler h = RangerBasePlugin.getAuditProvider("svc");
+        assertNotNull(h);
+    }
+
+    @Test
+    public void test17_GetServiceConfigsEmptyByDefault() throws Exception {
+        RangerPolicyEngineOptions opts = new RangerPolicyEngineOptions();
+        opts.disablePolicyRefresher = true;
+        opts.disableTagRetriever = true;
+        opts.disableUserStoreRetriever = true;
+        opts.disableGdsInfoRetriever = true;
+        RangerPluginConfig cfg = new RangerPluginConfig("hive", "svc", "app", null, null, opts);
+
+        ServicePolicies policies = new ServicePolicies();
+        policies.setServiceName("svc");
+        policies.setServiceDef(EmbeddedServiceDefsUtil.instance().getEmbeddedServiceDef(EmbeddedServiceDefsUtil.EMBEDDED_SERVICEDEF_HIVE_NAME));
+
+        RangerBasePlugin plugin = new RangerBasePlugin(cfg);
+        plugin.setPolicies(policies);
+        assertEquals(0, plugin.getServiceConfigs().size());
+    }
+
+    @Test
+    public void test18_SetPoliciesNullUsesDefaultPoliciesCreatesEngine() {
+        RangerPolicyEngineOptions opts = new RangerPolicyEngineOptions();
+        opts.disablePolicyRefresher = true;
+        opts.disableTagRetriever = true;
+        opts.disableUserStoreRetriever = true;
+        opts.disableGdsInfoRetriever = true;
+        RangerPluginConfig cfg = new RangerPluginConfig("hive", "svc", "app", null, null, opts);
+
+        RangerBasePlugin plugin = new RangerBasePlugin(cfg);
+        // passing null should use default embedded service def for given serviceType
+        plugin.setPolicies(null);
+        assertNotNull(plugin.getServiceDef());
+        assertEquals("svc", plugin.getServiceName());
+    }
+
+    @Test
+    public void test19_RolesMappingAPIsReturnExpected() throws Exception {
+        RangerPolicyEngineOptions opts = new RangerPolicyEngineOptions();
+        opts.disablePolicyRefresher = true;
+        opts.disableTagRetriever = true;
+        opts.disableUserStoreRetriever = true;
+        opts.disableGdsInfoRetriever = true;
+        RangerPluginConfig cfg = new RangerPluginConfig("hive", "svc", "app", null, null, opts);
+
+        ServicePolicies policies = new ServicePolicies();
+        policies.setServiceName("svc");
+        policies.setServiceDef(EmbeddedServiceDefsUtil.instance().getEmbeddedServiceDef(EmbeddedServiceDefsUtil.EMBEDDED_SERVICEDEF_HIVE_NAME));
+
+        // build roles: role1 with user u1 and group g1
+        RangerRole role1 = new RangerRole();
+        role1.setName("role1");
+        List<RangerRole.RoleMember> users = new ArrayList<>();
+        users.add(new RangerRole.RoleMember("u1", false));
+        role1.setUsers(users);
+        List<RangerRole.RoleMember> groups = new ArrayList<>();
+        groups.add(new RangerRole.RoleMember("g1", false));
+        role1.setGroups(groups);
+
+        RangerRoles rangerRoles = new RangerRoles();
+        Set<RangerRole> rrset = new HashSet<>();
+        rrset.add(role1);
+        rangerRoles.setRangerRoles(rrset);
+
+        RangerBasePlugin plugin = new RangerBasePlugin(cfg);
+        plugin.setPolicies(policies);
+        plugin.setRoles(rangerRoles);
+
+        Set<String> groupsInput = new HashSet<>();
+        groupsInput.add("gX");
+        Set<String> rolesFromUser = plugin.getRolesFromUserAndGroups("u1", groupsInput);
+        assertNotNull(rolesFromUser);
+        // u1 maps to role1 via users
+        Assertions.assertTrue(rolesFromUser.contains("role1"));
+
+        Set<String> rolesFromGroup = plugin.getRolesFromUserAndGroups(null, new HashSet<>(Arrays.asList("g1")));
+        assertNotNull(rolesFromGroup);
+        Assertions.assertTrue(rolesFromGroup.contains("role1"));
+
+        Set<RangerRole> byUser = plugin.getRangerRoleForPrincipal("u1", "USER");
+        assertNotNull(byUser);
+        Assertions.assertTrue(byUser.stream().anyMatch(r -> "role1".equals(r.getName())));
+
+        Set<RangerRole> byGroup = plugin.getRangerRoleForPrincipal("g1", "GROUP");
+        assertNotNull(byGroup);
+        Assertions.assertTrue(byGroup.stream().anyMatch(r -> "role1".equals(r.getName())));
+    }
+
+    @Test
+    public void test20_EvalAuditPolicies_NoThrowOnNullOrRealResult() throws Exception {
+        RangerPolicyEngineOptions opts = new RangerPolicyEngineOptions();
+        opts.disablePolicyRefresher = true;
+        opts.disableTagRetriever = true;
+        opts.disableUserStoreRetriever = true;
+        opts.disableGdsInfoRetriever = true;
+        RangerPluginConfig cfg = new RangerPluginConfig("hive", "svc", "app", null, null, opts);
+
+        ServicePolicies policies = new ServicePolicies();
+        policies.setServiceName("svc");
+        policies.setServiceDef(EmbeddedServiceDefsUtil.instance().getEmbeddedServiceDef(EmbeddedServiceDefsUtil.EMBEDDED_SERVICEDEF_HIVE_NAME));
+
+        RangerBasePlugin plugin = new RangerBasePlugin(cfg);
+        plugin.setPolicies(policies);
+
+        RangerAccessResourceImpl res = new RangerAccessResourceImpl();
+        res.setValue("database", "db1");
+        RangerAccessRequestImpl req = new RangerAccessRequestImpl();
+        req.setResource(res);
+        req.setAccessType("select");
+
+        RangerAccessResult result = plugin.isAccessAllowed(req);
+        plugin.evalAuditPolicies(result);
+    }
+
+    @Test
+    public void test21_BasicGettersNonNull() {
+        RangerPolicyEngineOptions opts = new RangerPolicyEngineOptions();
+        RangerPluginConfig cfg = new RangerPluginConfig("svcType", "svc", "appId", null, null, opts);
+        RangerBasePlugin plugin = new RangerBasePlugin(cfg);
+        assertEquals("svcType", plugin.getServiceType());
+        assertEquals("appId", plugin.getAppId());
+        assertNotNull(plugin.getConfig());
+    }
+
+    @Test
+    public void test22_CreateRangerAuthContextNotNullAfterPolicies() throws Exception {
+        RangerPolicyEngineOptions opts = new RangerPolicyEngineOptions();
+        opts.disablePolicyRefresher = true;
+        opts.disableTagRetriever = true;
+        opts.disableUserStoreRetriever = true;
+        opts.disableGdsInfoRetriever = true;
+        RangerPluginConfig cfg = new RangerPluginConfig("hive", "svc", "app", null, null, opts);
+
+        ServicePolicies policies = new ServicePolicies();
+        policies.setServiceName("svc");
+        policies.setServiceDef(EmbeddedServiceDefsUtil.instance().getEmbeddedServiceDef(EmbeddedServiceDefsUtil.EMBEDDED_SERVICEDEF_HIVE_NAME));
+
+        RangerBasePlugin plugin = new RangerBasePlugin(cfg);
+        plugin.setPolicies(policies);
+        assertNotNull(plugin.createRangerAuthContext());
+        assertNotNull(plugin.getCurrentRangerAuthContext());
+    }
+
     private void runTestsFromResourceFile(String resourceFile) throws Exception {
         InputStream       inStream = this.getClass().getResourceAsStream(resourceFile);
         InputStreamReader reader   = new InputStreamReader(inStream);
diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/service/TestRangerBaseService.java b/agents-common/src/test/java/org/apache/ranger/plugin/service/TestRangerBaseService.java
new file mode 100644
index 000000000..9dd1255f8
--- /dev/null
+++ b/agents-common/src/test/java/org/apache/ranger/plugin/service/TestRangerBaseService.java
@@ -0,0 +1,265 @@
+/*
+ * 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.plugin.service;
+
+import org.apache.ranger.authorization.hadoop.config.RangerAdminConfig;
+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.model.validation.RangerServiceDefHelper;
+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;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+* @generated by Cursor
+* @description <Unit Test for RangerBaseService class>
+*/
+@ExtendWith(MockitoExtension.class)
+@TestMethodOrder(MethodOrderer.MethodName.class)
+public class TestRangerBaseService {
+    public static class DummyService extends RangerBaseService {
+        @Override
+        public Map<String, Object> validateConfig() {
+            Map<String, Object> m = new HashMap<>();
+            m.put("ok", Boolean.TRUE);
+            return m;
+        }
+
+        @Override
+        public List<String> lookupResource(ResourceLookupContext context) {
+            return Collections.singletonList("X");
+        }
+    }
+
+    private RangerServiceDef buildServiceDef() {
+        RangerServiceDef def = new RangerServiceDef();
+        def.setId(1L);
+        def.setName("dummy");
+
+        List<RangerResourceDef> resources = new ArrayList<>();
+        RangerResourceDef db = new RangerResourceDef();
+        db.setName("database");
+        db.setLevel(1);
+        db.setRecursiveSupported(Boolean.TRUE);
+        db.setMandatory(Boolean.TRUE);
+        db.setIsValidLeaf(Boolean.FALSE);
+        RangerResourceDef table = new RangerResourceDef();
+        table.setName("table");
+        table.setLevel(2);
+        table.setParent("database");
+        table.setRecursiveSupported(Boolean.TRUE);
+        table.setMandatory(Boolean.TRUE);
+        table.setIsValidLeaf(Boolean.TRUE);
+        resources.add(db);
+        resources.add(table);
+        def.setResources(resources);
+
+        List<RangerAccessTypeDef> accessTypes = new ArrayList<>();
+        RangerAccessTypeDef select = new RangerAccessTypeDef();
+        select.setName("select");
+        RangerAccessTypeDef update = new RangerAccessTypeDef();
+        update.setName("update");
+        accessTypes.add(select);
+        accessTypes.add(update);
+        def.setAccessTypes(accessTypes);
+
+        Map<String, String> options = new HashMap<>();
+        options.put("create.default.policy.per.hierarchy", "true");
+        def.setOptions(options);
+
+        return def;
+    }
+
+    private RangerService buildService(Map<String, String> configs) {
+        RangerService svc = new RangerService();
+        svc.setName("svc1");
+        svc.setType("dummy");
+        svc.setConfigs(configs);
+        return svc;
+    }
+
+    @Test
+    public void test1_DefaultPoliciesCreatedPerHierarchy() throws Exception {
+        DummyService svc = new DummyService();
+        RangerServiceDef def = buildServiceDef();
+        RangerService service = buildService(new LinkedHashMap<>());
+
+        svc.init(def, service);
+
+        List<RangerPolicy> policies = svc.getDefaultRangerPolicies();
+        assertNotNull(policies);
+        assertTrue(policies.size() >= 1);
+        RangerPolicy p0 = policies.get(0);
+        assertEquals("svc1", p0.getService());
+        assertTrue(p0.getResources().containsKey("database"));
+        assertTrue(p0.getResources().containsKey("table"));
+        assertTrue(p0.getPolicyItems().get(0).getAccesses().stream().anyMatch(a -> "select".equals(a.getType())));
+    }
+
+    @Test
+    public void test2_CreateDefaultPolicyResourceAndAllowedAccesses() {
+        DummyService svc = new DummyService();
+        RangerServiceDef def = buildServiceDef();
+        RangerService service = buildService(new LinkedHashMap<>());
+        svc.init(def, service);
+
+        RangerServiceDefHelper helper = new RangerServiceDefHelper(def);
+        Set<List<RangerResourceDef>> hierarchies = helper.filterHierarchies_containsOnlyMandatoryResources(RangerPolicy.POLICY_TYPE_ACCESS);
+        assertTrue(!hierarchies.isEmpty());
+        List<RangerResourceDef> first = hierarchies.iterator().next();
+
+        Map<String, RangerPolicy.RangerPolicyResource> res = svc.createDefaultPolicyResource(first);
+        assertTrue(res.containsKey("database"));
+        assertTrue(res.get("database").getIsRecursive());
+
+        List<RangerPolicy.RangerPolicyItemAccess> accesses = svc.getAllowedAccesses(res);
+        assertTrue(accesses.stream().anyMatch(a -> "select".equals(a.getType())));
+        assertTrue(accesses.stream().anyMatch(a -> "update".equals(a.getType())));
+    }
+
+    @Test
+    public void test3_CustomDefaultPoliciesFromConfigs() throws Exception {
+        DummyService svc = new DummyService();
+        RangerServiceDef def = buildServiceDef();
+        Map<String, String> cfg = new LinkedHashMap<>();
+        cfg.put("default-policy.1.name", "p-custom");
+        cfg.put("default-policy.1.description", "desc");
+        cfg.put("default-policy.1.isDenyAllElse", "true");
+        cfg.put("default-policy.1.resource.database", "db1,db2");
+        cfg.put("default-policy.1.resource.table", "t1");
+        cfg.put("default-policy.1.policyItem.1.users", "u1,u2");
+        cfg.put("default-policy.1.policyItem.1.groups", "g1");
+        cfg.put("default-policy.1.policyItem.1.roles", "r1");
+        cfg.put("default-policy.1.policyItem.1.accessTypes", "select,update");
+        cfg.put("default-policy.1.policyItem.1.isDelegateAdmin", "true");
+
+        RangerService service = buildService(cfg);
+        svc.init(def, service);
+
+        List<RangerPolicy> policies = svc.getDefaultRangerPolicies();
+        assertNotNull(policies);
+        boolean found = policies.stream().anyMatch(p -> p.getResources().containsKey("database")
+                && p.getResources().containsKey("table")
+                && p.getPolicyItems() != null
+                && p.getPolicyItems().stream().anyMatch(pi -> pi.getAccesses() != null && pi.getAccesses().stream().anyMatch(a -> "select".equals(a.getType()) || "update".equals(a.getType()))));
+        assertTrue(found);
+    }
+
+    @Test
+    public void test4_GetLookupUserKerberosBranches() throws IOException {
+        DummyService svc = new DummyService();
+        RangerAdminConfig rac = RangerAdminConfig.getInstance();
+        String user = svc.getLookupUser("kerberos", rac.get("ranger.lookup.kerberos.principal"), rac.get("ranger.lookup.kerberos.keytab"));
+        assertEquals(null, user);
+    }
+
+    @Test
+    public void test5_CreateDefaultPolicyPerHierarchy_FromServiceAndServiceDefOptions() throws Exception {
+        DummyService svc = new DummyService();
+        RangerServiceDef def = buildServiceDef();
+        // remove option from service-def to test default true
+        def.setOptions(new HashMap<>());
+        Map<String, String> cfg1 = new LinkedHashMap<>();
+        // no explicit config -> should fallback to default true
+        RangerService service1 = buildService(cfg1);
+        svc.init(def, service1);
+        List<RangerPolicy> p1 = svc.getDefaultRangerPolicies();
+        assertFalse(p1.isEmpty());
+
+        // now set service config to false to suppress creation
+        Map<String, String> cfg2 = new LinkedHashMap<>();
+        cfg2.put("create.default.policy.per.hierarchy", "false");
+        RangerService service2 = buildService(cfg2);
+        svc.init(def, service2);
+        List<RangerPolicy> p2 = svc.getDefaultRangerPolicies();
+        assertTrue(p2.isEmpty());
+    }
+
+    @Test
+    public void test6_GetResourcesForPrefix_WithFlags() throws Exception {
+        DummyService svc = new DummyService();
+        RangerServiceDef def = buildServiceDef();
+        Map<String, String> cfg = new LinkedHashMap<>();
+        cfg.put("default-policy.1.name", "p1");
+        cfg.put("default-policy.1.resource.database", "db1,db2");
+        cfg.put("default-policy.1.resource.database.is-excludes", "true");
+        cfg.put("default-policy.1.resource.database.is-recursive", "true");
+        cfg.put("default-policy.1.policyItem.1.users", "u1");
+        cfg.put("default-policy.1.policyItem.1.accessTypes", "select");
+        cfg.put("setup.additional.default.policies", "true");
+        RangerService service = buildService(cfg);
+        svc.init(def, service);
+        List<RangerPolicy> policies = svc.getDefaultRangerPolicies();
+        assertTrue(policies.stream().anyMatch(p -> Boolean.TRUE.equals(p.getResources().get("database").getIsExcludes())
+                && Boolean.TRUE.equals(p.getResources().get("database").getIsRecursive())));
+    }
+
+    @Test
+    public void test7_GetUserAndGroupLists_MergesServiceConfigs() {
+        DummyService svc = new DummyService();
+        RangerServiceDef def = buildServiceDef();
+        Map<String, String> cfg = new LinkedHashMap<>();
+        cfg.put("username", "svcUser");
+        cfg.put("default.policy.users", "u1,u2");
+        cfg.put("default.policy.groups", "g1,g2");
+        RangerService service = buildService(cfg);
+        svc.init(def, service);
+
+        // access private via public API: getDefaultRangerPolicies should include these in first policy item
+        List<RangerPolicy> policies;
+        try {
+            policies = svc.getDefaultRangerPolicies();
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+        RangerPolicy first = policies.get(0);
+        List<String> users = first.getPolicyItems().get(0).getUsers();
+        List<String> groups = first.getPolicyItems().get(0).getGroups();
+        assertTrue(users.contains("svcUser"));
+        assertTrue(users.contains("u1") && users.contains("u2"));
+        assertTrue(groups.contains("g1") && groups.contains("g2"));
+    }
+
+    @Test
+    public void test8_GetLookupUser_SimpleAuthReturnsNull() throws IOException {
+        DummyService svc = new DummyService();
+        String user = svc.getLookupUser("simple", null, null);
+        assertEquals(null, user);
+    }
+}
diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/service/TestRangerChainedPlugin.java b/agents-common/src/test/java/org/apache/ranger/plugin/service/TestRangerChainedPlugin.java
new file mode 100644
index 000000000..e463d34bd
--- /dev/null
+++ b/agents-common/src/test/java/org/apache/ranger/plugin/service/TestRangerChainedPlugin.java
@@ -0,0 +1,127 @@
+/*
+ * 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.plugin.service;
+
+import org.apache.ranger.authorization.hadoop.config.RangerPluginConfig;
+import org.apache.ranger.plugin.policyengine.RangerAccessRequest;
+import org.apache.ranger.plugin.policyengine.RangerAccessResult;
+import org.apache.ranger.plugin.policyengine.RangerPolicyEngineOptions;
+import org.apache.ranger.plugin.policyengine.RangerResourceACLs;
+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;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+/**
+* @generated by Cursor
+* @description <Unit Test for RangerChainedPlugin class>
+*/
+@ExtendWith(MockitoExtension.class)
+@TestMethodOrder(MethodOrderer.MethodName.class)
+public class TestRangerChainedPlugin {
+    private static class NoopChainedPlugin extends RangerChainedPlugin {
+        protected NoopChainedPlugin(RangerBasePlugin rootPlugin, String serviceType, String serviceName) {
+            super(rootPlugin, serviceType, serviceName);
+        }
+
+        @Override
+        public RangerAccessResult isAccessAllowed(RangerAccessRequest request) {
+            return null;
+        }
+
+        @Override
+        public Collection<RangerAccessResult> isAccessAllowed(Collection<RangerAccessRequest> requests) {
+            return new ArrayList<>();
+        }
+
+        @Override
+        public RangerResourceACLs getResourceACLs(RangerAccessRequest request) {
+            return null;
+        }
+
+        @Override
+        public RangerResourceACLs getResourceACLs(RangerAccessRequest request, Integer policyType) {
+            return null;
+        }
+
+        @Override
+        protected RangerBasePlugin buildChainedPlugin(String serviceType, String serviceName, String appId) {
+            return new RangerBasePlugin(new RangerPluginConfig(serviceType, serviceName, appId, null, null, new RangerPolicyEngineOptions())) {
+                @Override
+                public void init() {
+                    // no-op to avoid threads
+                }
+            };
+        }
+    }
+
+    @Test
+    public void test1_InitAndBypassFlagDefaultFalse() {
+        // Disable background components via policy engine options in config
+        RangerPolicyEngineOptions opts = new RangerPolicyEngineOptions();
+        opts.disablePolicyRefresher = true;
+        opts.disableTagRetriever = true;
+        opts.disableUserStoreRetriever = true;
+        opts.disableGdsInfoRetriever = true;
+        RangerPluginConfig cfg = new RangerPluginConfig("dummy", "svc", "app", null, null, opts);
+        RangerBasePlugin root = new RangerBasePlugin(cfg) {
+            @Override
+            public void init() {
+                // no-op
+            }
+        };
+        NoopChainedPlugin cp = new NoopChainedPlugin(root, "dummy", "svc2");
+        // Do not call cp.init() to avoid starting threads; just assert defaults and no-ops
+        assertEquals(false, cp.skipAccessCheckIfAlreadyDetermined);
+        assertNull(cp.evalDataMaskPolicies(null));
+        assertNull(cp.evalRowFilterPolicies(null));
+        assertNotNull(cp.isAccessAllowed(new ArrayList<>()));
+    }
+
+    @Test
+    public void test2_IsAuthorizeOnlyWithChainedPluginDefaultFalseAndInitDelegation() {
+        RangerPolicyEngineOptions opts = new RangerPolicyEngineOptions();
+        opts.disablePolicyRefresher = true;
+        opts.disableTagRetriever = true;
+        opts.disableUserStoreRetriever = true;
+        opts.disableGdsInfoRetriever = true;
+        RangerPluginConfig cfg = new RangerPluginConfig("dummy", "svc", "app", null, null, opts);
+
+        RangerBasePlugin root = new RangerBasePlugin(cfg) {
+            @Override
+            public void init() {
+                // no-op
+            }
+        };
+        NoopChainedPlugin cp = new NoopChainedPlugin(root, "dummy", "svc2");
+
+        // call init which should delegate to inner plugin.init() but overridden to no-op
+        cp.init();
+        assertEquals(false, cp.isAuthorizeOnlyWithChainedPlugin());
+    }
+}
diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/service/TestRangerDefaultRequestProcessor.java b/agents-common/src/test/java/org/apache/ranger/plugin/service/TestRangerDefaultRequestProcessor.java
new file mode 100644
index 000000000..9b4411722
--- /dev/null
+++ b/agents-common/src/test/java/org/apache/ranger/plugin/service/TestRangerDefaultRequestProcessor.java
@@ -0,0 +1,335 @@
+/*
+ * 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.plugin.service;
+
+import org.apache.ranger.authorization.hadoop.config.RangerPluginConfig;
+import org.apache.ranger.plugin.model.RangerRole;
+import org.apache.ranger.plugin.model.RangerServiceDef;
+import org.apache.ranger.plugin.policyengine.PolicyEngine;
+import org.apache.ranger.plugin.policyengine.RangerAccessRequestImpl;
+import org.apache.ranger.plugin.policyengine.RangerAccessResource;
+import org.apache.ranger.plugin.policyengine.RangerMutableResource;
+import org.apache.ranger.plugin.policyengine.RangerPluginContext;
+import org.apache.ranger.plugin.policyengine.RangerPolicyEngineOptions;
+import org.apache.ranger.plugin.util.RangerAccessRequestUtil;
+import org.apache.ranger.plugin.util.RangerCommonConstants;
+import org.apache.ranger.plugin.util.RangerRoles;
+import org.apache.ranger.plugin.util.RangerUserStore;
+import org.apache.ranger.ugsyncutil.transform.Mapper;
+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.Mockito;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+* @generated by Cursor
+* @description <Unit Test for RangerDefaultRequestProcessor class>
+*/
+@ExtendWith(MockitoExtension.class)
+@TestMethodOrder(MethodOrderer.MethodName.class)
+public class TestRangerDefaultRequestProcessor {
+    private static class TestResource implements RangerMutableResource {
+        private RangerServiceDef serviceDef;
+
+        @Override
+        public void setOwnerUser(String ownerUser) {
+        }
+
+        @Override
+        public void setValue(String type, Object value) {
+        }
+
+        @Override
+        public void setServiceDef(RangerServiceDef serviceDef) {
+            this.serviceDef = serviceDef;
+        }
+
+        @Override
+        public RangerServiceDef getServiceDef() {
+            return serviceDef;
+        }
+
+        @Override
+        public Map<String, Object> getAsMap() {
+            return new HashMap<>();
+        }
+
+        @Override
+        public String getAsString() {
+            return "";
+        }
+
+        @Override
+        public Set<String> getKeys() {
+            return new HashSet<>();
+        }
+
+        @Override
+        public Object getValue(String resourceElement) {
+            return null;
+        }
+
+        @Override
+        public String getOwnerUser() {
+            return null;
+        }
+
+        @Override
+        public boolean exists(String name) {
+            return false;
+        }
+
+        @Override
+        public String getLeafName() {
+            return null;
+        }
+
+        @Override
+        public String getCacheKey() {
+            return "";
+        }
+
+        @Override
+        public RangerAccessResource getReadOnlyCopy() {
+            return this;
+        }
+    }
+
+    public static class NoopMapper implements Mapper {
+        @Override
+        public void init(String baseProperty, List<String> regexPatterns, String regexSeparator) {
+        }
+
+        @Override
+        public String transform(String attrValue) {
+            return attrValue;
+        }
+    }
+
+    @Test
+    public void test1_PreProcessSetsClusterAndEmailConversionAndGroups() {
+        RangerPolicyEngineOptions opts = new RangerPolicyEngineOptions();
+        opts.disablePolicyRefresher = true;
+
+        RangerPluginConfig mockCfg = Mockito.mock(RangerPluginConfig.class);
+        Mockito.when(mockCfg.isUseRangerGroups()).thenReturn(true);
+        Mockito.when(mockCfg.isUseOnlyRangerGroups()).thenReturn(false);
+        Mockito.when(mockCfg.isConvertEmailToUsername()).thenReturn(false);
+
+        RangerPluginContext mockPluginCtx = Mockito.mock(RangerPluginContext.class);
+        Mockito.when(mockPluginCtx.getConfig()).thenReturn(mockCfg);
+        Mockito.when(mockPluginCtx.getClusterName()).thenReturn("clX");
+        Mockito.when(mockPluginCtx.getClusterType()).thenReturn("typeY");
+
+        RangerServiceDef sd = new RangerServiceDef();
+        sd.setName("dummy");
+
+        PolicyEngine pe = Mockito.mock(PolicyEngine.class);
+        Mockito.when(pe.getPluginContext()).thenReturn(mockPluginCtx);
+        Mockito.when(pe.getUseForwardedIPAddress()).thenReturn(false);
+        Mockito.when(pe.getTrustedProxyAddresses()).thenReturn(null);
+
+        RangerUserStore us = new RangerUserStore();
+        Map<String, Map<String, String>> userAttrs = new HashMap<>();
+        Map<String, String> attrs = new HashMap<>();
+        attrs.put(RangerCommonConstants.SCRIPT_FIELD__EMAIL_ADDRESS, "user1@example.com");
+        userAttrs.put("user1", attrs);
+        us.setUserAttrMapping(userAttrs);
+        Map<String, Set<String>> ug = new HashMap<>();
+        ug.put("user1@example.com", new HashSet<>(Collections.singletonList("rg1")));
+        us.setUserGroupMapping(ug);
+
+        RangerAuthContext ac = new RangerAuthContext(new HashMap<>(), null, new RangerRoles(), us);
+        Mockito.when(mockPluginCtx.getAuthContext()).thenReturn(ac);
+
+        RangerDefaultRequestProcessor rp = new RangerDefaultRequestProcessor(pe);
+
+        RangerAccessRequestImpl req = new RangerAccessRequestImpl();
+        TestResource tr = new TestResource();
+        tr.setServiceDef(sd);
+        req.setResource(tr);
+        req.setUser("user1@example.com");
+        req.setUserGroups(new HashSet<>(Collections.singletonList("g0")));
+
+        rp.preProcess(req);
+
+        assertEquals("clX", req.getClusterName());
+        assertEquals("typeY", req.getClusterType());
+        assertEquals("user1@example.com", req.getUser());
+        assertNotNull(req.getUserGroups());
+        assertTrue(req.getUserGroups().contains("g0"));
+        assertEquals("user1@example.com", RangerAccessRequestUtil.getCurrentUserFromContext(req.getContext()));
+    }
+
+    @Test
+    public void test2_PreProcessConvertsEmailAndReplacesGroupsAndSetsRoles() {
+        RangerPolicyEngineOptions opts = new RangerPolicyEngineOptions();
+        opts.disablePolicyRefresher = true;
+
+        RangerPluginConfig mockCfg = Mockito.mock(RangerPluginConfig.class);
+        Mockito.when(mockCfg.isUseRangerGroups()).thenReturn(true);
+        Mockito.when(mockCfg.isUseOnlyRangerGroups()).thenReturn(true);
+        Mockito.when(mockCfg.isConvertEmailToUsername()).thenReturn(true);
+
+        // Enable name transformation feature flag to allow transformation steps
+        Mockito.when(mockCfg.getPropertyPrefix()).thenReturn("ranger.plugin.dummy");
+        Mockito.when(mockCfg.getBoolean("ranger.plugin.dummy" + RangerCommonConstants.PLUGIN_CONFIG_SUFFIX_NAME_TRANSFORMATION, false)).thenReturn(true);
+
+        RangerPluginContext mockPluginCtx = Mockito.mock(RangerPluginContext.class);
+        Mockito.when(mockPluginCtx.getConfig()).thenReturn(mockCfg);
+        Mockito.when(mockPluginCtx.getClusterName()).thenReturn("clA");
+        Mockito.when(mockPluginCtx.getClusterType()).thenReturn("typeB");
+
+        RangerServiceDef sd = new RangerServiceDef();
+        sd.setName("dummy");
+
+        PolicyEngine pe = Mockito.mock(PolicyEngine.class);
+        Mockito.when(pe.getPluginContext()).thenReturn(mockPluginCtx);
+        Mockito.when(pe.getUseForwardedIPAddress()).thenReturn(false);
+        Mockito.when(pe.getTrustedProxyAddresses()).thenReturn(null);
+        Mockito.when(pe.getServiceDef()).thenReturn(sd);
+
+        // Prepare user store mapping for email->username and ranger groups for username
+        RangerUserStore us = new RangerUserStore();
+        Map<String, Map<String, String>> userAttrs = new HashMap<>();
+        Map<String, String> attrs = new HashMap<>();
+        attrs.put(RangerCommonConstants.SCRIPT_FIELD__EMAIL_ADDRESS, "u2@example.com");
+        userAttrs.put("u2", attrs);
+        us.setUserAttrMapping(userAttrs);
+        Map<String, Set<String>> ug = new HashMap<>();
+        ug.put("u2", new HashSet<>(Collections.singletonList("rg9")));
+        us.setUserGroupMapping(ug);
+
+        // Set roles so that roles are computed and populated onto the request when empty
+        RangerRoles roles = new RangerRoles();
+        Set<RangerRole> roleSet = new HashSet<>();
+        RangerRole r = new RangerRole();
+        r.setName("roleX");
+        r.setUsers(Collections.singletonList(new RangerRole.RoleMember("u2", false)));
+        roleSet.add(r);
+        roles.setRangerRoles(roleSet);
+
+        RangerAuthContext ac = new RangerAuthContext(new HashMap<>(), null, roles, us);
+        Mockito.when(mockPluginCtx.getAuthContext()).thenReturn(ac);
+
+        RangerDefaultRequestProcessor rp = new RangerDefaultRequestProcessor(pe);
+
+        RangerAccessRequestImpl req = new RangerAccessRequestImpl();
+        TestResource tr = new TestResource();
+        req.setResource(tr); // serviceDef is null initially to exercise setResourceServiceDef
+        req.setUser("u2@example.com");
+        req.setUserGroups(new HashSet<>(Collections.singletonList("g1")));
+
+        rp.preProcess(req);
+
+        assertEquals("u2", req.getUser()); // email converted to username via user store
+        assertNotNull(req.getUserGroups());
+        assertEquals(1, req.getUserGroups().size());
+        assertTrue(req.getUserGroups().contains("rg9")); // replaced with only ranger groups
+        assertNotNull(req.getUserRoles());
+        assertTrue(req.getUserRoles().contains("roleX"));
+        assertNotNull(((RangerMutableResource) req.getResource()).getServiceDef());
+    }
+
+    @Test
+    public void test3_PreProcessEarlyReturnWhenAlreadyPreprocessed() {
+        RangerPluginContext mockPluginCtx = Mockito.mock(RangerPluginContext.class);
+        RangerPluginConfig mockCfg = Mockito.mock(RangerPluginConfig.class);
+        Mockito.when(mockPluginCtx.getConfig()).thenReturn(mockCfg);
+
+        PolicyEngine pe = Mockito.mock(PolicyEngine.class);
+        Mockito.when(pe.getPluginContext()).thenReturn(mockPluginCtx);
+
+        RangerDefaultRequestProcessor rp = new RangerDefaultRequestProcessor(pe);
+
+        RangerAccessRequestImpl req = new RangerAccessRequestImpl();
+        TestResource tr = new TestResource();
+        req.setResource(tr);
+
+        // mark as already preprocessed
+        RangerAccessRequestUtil.setIsRequestPreprocessed(req.getContext(), Boolean.TRUE);
+
+        rp.preProcess(req);
+
+        // cluster should remain unset due to early return
+        assertEquals(null, req.getClusterName());
+        assertEquals(null, req.getClusterType());
+    }
+
+    @Test
+    public void test4_NameAndGroupCaseConversionWithoutMapper() {
+        RangerPluginConfig mockCfg = Mockito.mock(RangerPluginConfig.class);
+        Mockito.when(mockCfg.isUseRangerGroups()).thenReturn(false);
+        Mockito.when(mockCfg.isUseOnlyRangerGroups()).thenReturn(false);
+        Mockito.when(mockCfg.isConvertEmailToUsername()).thenReturn(false);
+        Mockito.when(mockCfg.getPropertyPrefix()).thenReturn("ranger.plugin.svc");
+        Mockito.when(mockCfg.getBoolean("ranger.plugin.svc" + RangerCommonConstants.PLUGIN_CONFIG_SUFFIX_NAME_TRANSFORMATION, false)).thenReturn(true);
+
+        RangerPluginContext mockPluginCtx = Mockito.mock(RangerPluginContext.class);
+        Mockito.when(mockPluginCtx.getConfig()).thenReturn(mockCfg);
+        Mockito.when(mockPluginCtx.getClusterName()).thenReturn("clZ");
+        Mockito.when(mockPluginCtx.getClusterType()).thenReturn("typeZ");
+
+        PolicyEngine pe = Mockito.mock(PolicyEngine.class);
+        Mockito.when(pe.getPluginContext()).thenReturn(mockPluginCtx);
+        Mockito.when(pe.getUseForwardedIPAddress()).thenReturn(false);
+        Mockito.when(pe.getTrustedProxyAddresses()).thenReturn(null);
+
+        RangerAuthContext ac = new RangerAuthContext(new HashMap<>(), null, new RangerRoles(), new RangerUserStore());
+        Map<String, String> cfg = new HashMap<>();
+        cfg.put(RangerCommonConstants.PLUGINS_CONF_USERNAME_CASE_CONVERSION_PARAM, "lower");
+        cfg.put(RangerCommonConstants.PLUGINS_CONF_GROUPNAME_CASE_CONVERSION_PARAM, "upper");
+        cfg.put(RangerCommonConstants.PLUGINS_CONF_MAPPING_GROUPNAME_HANDLER, this.getClass().getName() + "$NoopMapper");
+        cfg.put(RangerCommonConstants.PLUGINS_CONF_MAPPING_GROUPNAME, "(.*)");
+        cfg.put(RangerCommonConstants.PLUGINS_CONF_MAPPING_SEPARATOR, ":");
+        ac.onServiceConfigsUpdate(cfg);
+        Mockito.when(mockPluginCtx.getAuthContext()).thenReturn(ac);
+
+        RangerDefaultRequestProcessor rp = new RangerDefaultRequestProcessor(pe);
+
+        RangerAccessRequestImpl req = new RangerAccessRequestImpl();
+        TestResource tr = new TestResource();
+        req.setResource(tr);
+        req.setUser("USERX");
+        Set<String> groups = new HashSet<>();
+        groups.add("grp");
+        groups.add("Grp2");
+        req.setUserGroups(groups);
+
+        rp.preProcess(req);
+
+        assertEquals("userx", req.getUser());
+        assertTrue(req.getUserGroups().contains("GRP"));
+        assertTrue(req.getUserGroups().contains("GRP2"));
+    }
+}
diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/service/TestRangerDefaultService.java b/agents-common/src/test/java/org/apache/ranger/plugin/service/TestRangerDefaultService.java
new file mode 100644
index 000000000..b09dd9dd4
--- /dev/null
+++ b/agents-common/src/test/java/org/apache/ranger/plugin/service/TestRangerDefaultService.java
@@ -0,0 +1,53 @@
+/*
+ * 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.plugin.service;
+
+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;
+
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+/**
+* @generated by Cursor
+* @description <Unit Test for RangerDefaultService class>
+*/
+@ExtendWith(MockitoExtension.class)
+@TestMethodOrder(MethodOrderer.MethodName.class)
+public class TestRangerDefaultService {
+    @Test
+    public void test1_validateConfigThrows() {
+        RangerDefaultService svc = new RangerDefaultService();
+        Exception ex = assertThrows(Exception.class, svc::validateConfig);
+        assertEquals(RangerDefaultService.ERROR_MSG_VALIDATE_CONFIG_NOT_IMPLEMENTED, ex.getMessage());
+    }
+
+    @Test
+    public void test2_lookupResourceReturnsEmptyList() throws Exception {
+        RangerDefaultService svc = new RangerDefaultService();
+        List<String> res = svc.lookupResource(new ResourceLookupContext());
+        assertEquals(0, res.size());
+    }
+}
diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/service/TestResourceLookupContext.java b/agents-common/src/test/java/org/apache/ranger/plugin/service/TestResourceLookupContext.java
new file mode 100644
index 000000000..eb463303b
--- /dev/null
+++ b/agents-common/src/test/java/org/apache/ranger/plugin/service/TestResourceLookupContext.java
@@ -0,0 +1,64 @@
+/*
+ * 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.plugin.service;
+
+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;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+* @generated by Cursor
+* @description <Unit Test for ResourceLookupContext class>
+*/
+@ExtendWith(MockitoExtension.class)
+@TestMethodOrder(MethodOrderer.MethodName.class)
+public class TestResourceLookupContext {
+    @Test
+    public void test1_GettersSettersAndToString() {
+        ResourceLookupContext ctx = new ResourceLookupContext();
+        ctx.setUserInput("abc");
+        ctx.setResourceName("table");
+
+        Map<String, List<String>> resources = new HashMap<>();
+        List<String> vals = new ArrayList<>();
+        vals.add("v1");
+        resources.put("key", vals);
+        ctx.setResources(resources);
+
+        assertEquals("abc", ctx.getUserInput());
+        assertEquals("table", ctx.getResourceName());
+        assertEquals(1, ctx.getResources().get("key").size());
+
+        String s = ctx.toString();
+        assertTrue(s.contains("resourceName=table"));
+        assertTrue(s.contains("userInput=abc"));
+        assertTrue(s.contains("resources={key=[v1]"));
+    }
+}
diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/store/TestAbstractGdsStore.java b/agents-common/src/test/java/org/apache/ranger/plugin/store/TestAbstractGdsStore.java
new file mode 100644
index 000000000..cc0cf1c5c
--- /dev/null
+++ b/agents-common/src/test/java/org/apache/ranger/plugin/store/TestAbstractGdsStore.java
@@ -0,0 +1,151 @@
+/*
+ * 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.plugin.store;
+
+import org.apache.ranger.plugin.model.RangerGds;
+import org.apache.ranger.plugin.model.RangerPolicy;
+import org.apache.ranger.plugin.util.SearchFilter;
+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;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * @generated by Cursor
+ * @description <Unit Test for AbstractGdsStore class>
+ */
+@ExtendWith(MockitoExtension.class)
+@TestMethodOrder(MethodOrderer.MethodName.class)
+public class TestAbstractGdsStore {
+    private static class MyGdsStore extends AbstractGdsStore {
+        @Override
+        public void deleteAllGdsObjectsForService(Long serviceId) {
+        }
+
+        @Override
+        public void deleteAllGdsObjectsForSecurityZone(Long zoneId) {
+        }
+
+        @Override
+        public void onSecurityZoneUpdate(Long zoneId, Collection<String> updatedServices,
+                Collection<String> removedServices) {
+        }
+
+        @Override
+        public List<RangerPolicy> getProjectPolicies(Long projectId) throws Exception {
+            return null;
+        }
+
+        @Override
+        public RangerPolicy addDatasetPolicy(Long datasetId, RangerPolicy policy) throws Exception {
+            return null;
+        }
+
+        @Override
+        public RangerPolicy updateDatasetPolicy(Long datasetId, RangerPolicy policy) throws Exception {
+            return null;
+        }
+
+        @Override
+        public void deleteDatasetPolicy(Long datasetId, Long policyId) throws Exception {
+        }
+
+        @Override
+        public void deleteDatasetPolicies(Long datasetId) throws Exception {
+        }
+
+        @Override
+        public RangerPolicy getDatasetPolicy(Long datasetId, Long policyId) throws Exception {
+            return null;
+        }
+
+        @Override
+        public List<RangerPolicy> getDatasetPolicies(Long datasetId) throws Exception {
+            return null;
+        }
+
+        @Override
+        public RangerPolicy addProjectPolicy(Long projectId, RangerPolicy policy) throws Exception {
+            return null;
+        }
+
+        @Override
+        public RangerPolicy updateProjectPolicy(Long projectId, RangerPolicy policy) throws Exception {
+            return null;
+        }
+
+        @Override
+        public void deleteProjectPolicy(Long projectId, Long policyId) throws Exception {
+        }
+
+        @Override
+        public void deleteProjectPolicies(Long projectId) throws Exception {
+        }
+
+        @Override
+        public RangerPolicy getProjectPolicy(Long projectId, Long policyId) throws Exception {
+            return null;
+        }
+    }
+
+    @Test
+    public void test01_defaultReturnsNullsAndNoThrow() throws Exception {
+        MyGdsStore s = new MyGdsStore();
+        Assertions.assertNull(s.createDataset(new RangerGds.RangerDataset()));
+        Assertions.assertNull(s.updateDataset(new RangerGds.RangerDataset()));
+        s.deleteDataset(1L, true);
+        Assertions.assertNull(s.getDataset(1L));
+        Assertions.assertNull(s.getDatasetByName("n"));
+        Assertions.assertNull(s.getDatasetNames(new SearchFilter()));
+        Assertions.assertNull(s.searchDatasets(new SearchFilter()));
+        Assertions.assertNull(s.createProject(new RangerGds.RangerProject()));
+        Assertions.assertNull(s.updateProject(new RangerGds.RangerProject()));
+        s.deleteProject(1L, true);
+        Assertions.assertNull(s.getProject(1L));
+        Assertions.assertNull(s.getProjectByName("p"));
+        Assertions.assertNull(s.getProjectNames(new SearchFilter()));
+        Assertions.assertNull(s.searchProjects(new SearchFilter()));
+        Assertions.assertNull(s.createDataShare(new RangerGds.RangerDataShare()));
+        Assertions.assertNull(s.updateDataShare(new RangerGds.RangerDataShare()));
+        s.deleteDataShare(1L, true);
+        Assertions.assertNull(s.getDataShare(1L));
+        Assertions.assertNull(s.searchDataShares(new SearchFilter()));
+        Assertions.assertNull(s.addSharedResources(null));
+        Assertions.assertNull(s.updateSharedResource(new RangerGds.RangerSharedResource()));
+        s.removeSharedResources(null);
+        Assertions.assertNull(s.getSharedResource(1L));
+        Assertions.assertNull(s.searchSharedResources(new SearchFilter()));
+        Assertions.assertNull(s.addDataShareInDataset(new RangerGds.RangerDataShareInDataset()));
+        Assertions.assertNull(s.updateDataShareInDataset(new RangerGds.RangerDataShareInDataset()));
+        s.removeDataShareInDataset(1L);
+        Assertions.assertNull(s.getDataShareInDataset(1L));
+        Assertions.assertNull(s.searchDataShareInDatasets(new SearchFilter()));
+        Assertions.assertNull(s.addDatasetInProject(new RangerGds.RangerDatasetInProject()));
+        Assertions.assertNull(s.updateDatasetInProject(new RangerGds.RangerDatasetInProject()));
+        s.removeDatasetInProject(1L);
+        Assertions.assertNull(s.getDatasetInProject(1L));
+        Assertions.assertNull(s.searchDatasetInProjects(new SearchFilter()));
+    }
+}
diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/store/TestAbstractPredicateUtil.java b/agents-common/src/test/java/org/apache/ranger/plugin/store/TestAbstractPredicateUtil.java
new file mode 100644
index 000000000..9af269dab
--- /dev/null
+++ b/agents-common/src/test/java/org/apache/ranger/plugin/store/TestAbstractPredicateUtil.java
@@ -0,0 +1,573 @@
+/*
+ * 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.plugin.store;
+
+import org.apache.ranger.plugin.model.RangerPolicy;
+import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyItem;
+import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyResource;
+import org.apache.ranger.plugin.model.RangerSecurityZone;
+import org.apache.ranger.plugin.model.RangerService;
+import org.apache.ranger.plugin.model.RangerServiceDef;
+import org.apache.ranger.plugin.util.SearchFilter;
+import org.apache.ranger.plugin.util.ServicePolicies;
+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;
+
+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 for AbstractPredicateUtil class>
+ */
+@ExtendWith(MockitoExtension.class)
+@TestMethodOrder(MethodOrderer.MethodName.class)
+public class TestAbstractPredicateUtil {
+    @Test
+    public void test01_basicFilterByServiceAndPolicy() {
+        RangerPolicy p1 = new RangerPolicy();
+        p1.setId(1L);
+        p1.setService("svc1");
+        p1.setName("read-policy");
+
+        RangerPolicy p2 = new RangerPolicy();
+        p2.setId(2L);
+        p2.setService("svc2");
+        p2.setName("write-policy");
+
+        List<RangerPolicy> list = new ArrayList<>(Arrays.asList(p1, p2));
+
+        AbstractPredicateUtil util = new AbstractPredicateUtil();
+        SearchFilter f = new SearchFilter();
+        f.setParam(SearchFilter.SERVICE_NAME, "svc1");
+        f.setParam(SearchFilter.POLICY_NAME_PARTIAL, "read");
+        util.applyFilter(list, f);
+
+        Assertions.assertEquals(1, list.size());
+        Assertions.assertEquals("svc1", list.get(0).getService());
+    }
+
+    @Test
+    public void test02_sortingByPolicyIdDesc() {
+        RangerPolicy p1 = new RangerPolicy();
+        p1.setId(1L);
+        RangerPolicy p2 = new RangerPolicy();
+        p2.setId(3L);
+        RangerPolicy p3 = new RangerPolicy();
+        p3.setId(2L);
+        List<RangerPolicy> list = new ArrayList<>(Arrays.asList(p1, p2, p3));
+
+        AbstractPredicateUtil util = new AbstractPredicateUtil();
+        SearchFilter f = new SearchFilter();
+        f.setSortBy(SearchFilter.POLICY_ID);
+        f.setSortType("desc");
+        util.applyFilter(list, f);
+        Assertions.assertEquals(3L, list.get(0).getId().longValue());
+        Assertions.assertEquals(1L, list.get(2).getId().longValue());
+    }
+
+    @Test
+    public void test03_filterResourcesWildcardMatch() {
+        RangerPolicy p = new RangerPolicy();
+        Map<String, RangerPolicyResource> res = new HashMap<>();
+        res.put("path", new RangerPolicyResource("/data/*"));
+        p.setResources(res);
+        List<RangerPolicy> list = new ArrayList<>(Collections.singletonList(p));
+
+        AbstractPredicateUtil util = new AbstractPredicateUtil();
+        SearchFilter f = new SearchFilter();
+        f.setParam(SearchFilter.RESOURCE_PREFIX + "path", "/data/2024");
+        util.applyFilter(list, f);
+        Assertions.assertEquals(1, list.size());
+    }
+
+    @Test
+    public void test04_rolePredicateUtilByUserGroupRole() {
+        RangerPolicyItem item = new RangerPolicyItem();
+        item.addUser("alice");
+        item.addGroup("analytics");
+        item.addRole("analyst");
+        RangerPolicy p = new RangerPolicy();
+        p.addPolicyItem(item);
+        List<RangerPolicy> policies = new ArrayList<>(Collections.singletonList(p));
+
+        AbstractPredicateUtil util = new AbstractPredicateUtil();
+        SearchFilter f = new SearchFilter();
+        f.setParam(SearchFilter.USER, "ali");
+        f.setParam(SearchFilter.GROUP, "lyt");
+        f.setParam(SearchFilter.ROLE, "ana");
+        util.applyFilter(policies, f);
+        Assertions.assertEquals(1, policies.size());
+    }
+
+    @Test
+    public void test05_servicePredicateUtilUsesServiceStore() throws Exception {
+        ServiceStore stub = new ServiceStore() {
+            @Override
+            public void init() {
+            }
+
+            @Override
+            public RangerServiceDef createServiceDef(RangerServiceDef serviceDef) {
+                return null;
+            }
+
+            @Override
+            public RangerServiceDef updateServiceDef(RangerServiceDef serviceDef) {
+                return null;
+            }
+
+            @Override
+            public void deleteServiceDef(Long id, Boolean forceDelete) {
+            }
+
+            @Override
+            public void updateTagServiceDefForAccessTypes() {
+            }
+
+            @Override
+            public RangerServiceDef getServiceDef(Long id) {
+                return null;
+            }
+
+            @Override
+            public RangerServiceDef getServiceDefByName(String name) {
+                return null;
+            }
+
+            @Override
+            public RangerServiceDef getServiceDefByDisplayName(String name) {
+                return null;
+            }
+
+            @Override
+            public List<RangerServiceDef> getServiceDefs(SearchFilter filter) {
+                return null;
+            }
+
+            @Override
+            public PList<RangerServiceDef> getPaginatedServiceDefs(SearchFilter filter) {
+                return null;
+            }
+
+            @Override
+            public RangerService createService(RangerService service) {
+                return null;
+            }
+
+            @Override
+            public RangerService updateService(RangerService service, Map<String, Object> options) {
+                return null;
+            }
+
+            @Override
+            public void deleteService(Long id) {
+            }
+
+            @Override
+            public boolean serviceExists(String name) {
+                return false;
+            }
+
+            @Override
+            public RangerService getService(Long id) {
+                return null;
+            }
+
+            @Override
+            public RangerService getServiceByName(String name) {
+                RangerService s = new RangerService();
+                s.setName(name);
+                s.setId(100L);
+                s.setType("hive");
+                return s;
+            }
+
+            @Override
+            public RangerService getServiceByDisplayName(String displayName) {
+                return null;
+            }
+
+            @Override
+            public List<RangerService> getServices(SearchFilter filter) {
+                return null;
+            }
+
+            @Override
+            public PList<RangerService> getPaginatedServices(SearchFilter filter) {
+                return null;
+            }
+
+            @Override
+            public RangerPolicy createPolicy(RangerPolicy policy) {
+                return null;
+            }
+
+            @Override
+            public RangerPolicy createDefaultPolicy(RangerPolicy policy) {
+                return null;
+            }
+
+            @Override
+            public RangerPolicy updatePolicy(RangerPolicy policy) {
+                return null;
+            }
+
+            @Override
+            public void deletePolicy(RangerPolicy policy, RangerService service) {
+            }
+
+            @Override
+            public void deletePolicy(RangerPolicy policy) {
+            }
+
+            @Override
+            public boolean policyExists(Long id) {
+                return false;
+            }
+
+            @Override
+            public RangerPolicy getPolicy(Long id) {
+                return null;
+            }
+
+            @Override
+            public List<RangerPolicy> getPolicies(SearchFilter filter) {
+                return null;
+            }
+
+            @Override
+            public Long getPolicyId(Long serviceId, String policyName, Long zoneId) {
+                return null;
+            }
+
+            @Override
+            public PList<RangerPolicy> getPaginatedPolicies(SearchFilter filter) {
+                return null;
+            }
+
+            @Override
+            public List<RangerPolicy> getPoliciesByResourceSignature(String serviceName, String policySignature,
+                    Boolean isPolicyEnabled) {
+                return null;
+            }
+
+            @Override
+            public List<RangerPolicy> getServicePolicies(Long serviceId, SearchFilter filter) {
+                return null;
+            }
+
+            @Override
+            public PList<RangerPolicy> getPaginatedServicePolicies(Long serviceId, SearchFilter filter) {
+                return null;
+            }
+
+            @Override
+            public List<RangerPolicy> getServicePolicies(String serviceName, SearchFilter filter) {
+                return null;
+            }
+
+            @Override
+            public PList<RangerPolicy> getPaginatedServicePolicies(String serviceName, SearchFilter filter) {
+                return null;
+            }
+
+            @Override
+            public ServicePolicies getServicePoliciesIfUpdated(String serviceName,
+                    Long lastKnownVersion, boolean needsBackwardCompatibility) {
+                return null;
+            }
+
+            @Override
+            public Long getServicePolicyVersion(String serviceName) {
+                return null;
+            }
+
+            @Override
+            public ServicePolicies getServicePolicyDeltasOrPolicies(String serviceName,
+                    Long lastKnownVersion) {
+                return null;
+            }
+
+            @Override
+            public ServicePolicies getServicePolicyDeltas(String serviceName,
+                    Long lastKnownVersion, Long cachedPolicyVersion) {
+                return null;
+            }
+
+            @Override
+            public ServicePolicies getServicePolicies(String serviceName,
+                    Long lastKnownVersion) {
+                return null;
+            }
+
+            @Override
+            public RangerPolicy getPolicyFromEventTime(String eventTimeStr, Long policyId) {
+                return null;
+            }
+
+            @Override
+            public Boolean getPopulateExistingBaseFields() {
+                return null;
+            }
+
+            @Override
+            public void setPopulateExistingBaseFields(Boolean populateExistingBaseFields) {
+            }
+
+            @Override
+            public RangerSecurityZone getSecurityZone(Long id) {
+                return null;
+            }
+
+            @Override
+            public RangerSecurityZone getSecurityZone(String name) {
+                return null;
+            }
+
+            @Override
+            public long getPoliciesCount(String serviceName) {
+                return 0;
+            }
+
+            @Override
+            public Map<String, String> getServiceConfigForPlugin(Long serviceId) {
+                return null;
+            }
+
+            @Override
+            public List<RangerPolicy> getPoliciesWithMetaAttributes(List<RangerPolicy> policies) {
+                return null;
+            }
+        };
+
+        ServicePredicateUtil util = new ServicePredicateUtil(stub);
+        RangerPolicy pol = new RangerPolicy();
+        pol.setService("svc1");
+        List<RangerPolicy> list = new ArrayList<>(Collections.singletonList(pol));
+        SearchFilter f = new SearchFilter();
+        f.setParam(SearchFilter.SERVICE_TYPE, "hive");
+        util.applyFilter(list, f);
+        Assertions.assertEquals(1, list.size());
+    }
+
+    @Test
+    public void test06_securityZonePredicateUtil() {
+        RangerSecurityZone z = new RangerSecurityZone();
+        z.setId(1L);
+        z.setName("zone-a");
+        Map<String, RangerSecurityZone.RangerSecurityZoneService> services = new HashMap<>();
+        services.put("svc1", new RangerSecurityZone.RangerSecurityZoneService());
+        z.setServices(services);
+        z.setCreatedBy("admin");
+        List<RangerSecurityZone> zones = new ArrayList<>(Collections.singletonList(z));
+
+        SecurityZonePredicateUtil util = new SecurityZonePredicateUtil();
+        SearchFilter f = new SearchFilter();
+        f.setParam(SearchFilter.SERVICE_NAME, "svc1");
+        f.setParam(SearchFilter.ZONE_ID, "1");
+        f.setParam(SearchFilter.ZONE_NAME, "zone-a");
+        f.setParam(SearchFilter.NOT_ZONE_NAME, "zone-b");
+        f.setParam(SearchFilter.ZONE_NAME_PARTIAL, "zone");
+        f.setParam(SearchFilter.CREATED_BY, "admin");
+        util.applyFilter(zones, f);
+        Assertions.assertEquals(1, zones.size());
+    }
+
+    @Test
+    public void test07_isRecursivePredicate() {
+        RangerPolicy p = new RangerPolicy();
+        Map<String, RangerPolicyResource> res = new HashMap<>();
+        RangerPolicyResource r = new RangerPolicyResource("/a", false, true);
+        res.put("path", r);
+        p.setResources(res);
+        List<RangerPolicy> list = new ArrayList<>(Collections.singletonList(p));
+
+        AbstractPredicateUtil util = new AbstractPredicateUtil();
+        SearchFilter f = new SearchFilter();
+        f.setParam(SearchFilter.IS_RECURSIVE, "true");
+        util.applyFilter(list, f);
+        Assertions.assertEquals(1, list.size());
+
+        // Now set recursive to false and ensure mismatch when filter is true
+        res.put("path", new RangerPolicyResource("/a", false, false));
+        p.setResources(res);
+        list = new ArrayList<>(Collections.singletonList(p));
+        util.applyFilter(list, f);
+        Assertions.assertEquals(0, list.size());
+    }
+
+    @Test
+    public void test08_policyPriorityFilter() {
+        RangerPolicy p = new RangerPolicy();
+        p.setPolicyPriority(RangerPolicy.POLICY_PRIORITY_OVERRIDE);
+        List<RangerPolicy> list = new ArrayList<>(Collections.singletonList(p));
+
+        AbstractPredicateUtil util = new AbstractPredicateUtil();
+        SearchFilter f = new SearchFilter();
+        f.setParam(SearchFilter.POLICY_PRIORITY, RangerPolicy.POLICY_PRIORITY_NAME_OVERRIDE);
+        util.applyFilter(list, f);
+        Assertions.assertEquals(1, list.size());
+
+        f.setParam(SearchFilter.POLICY_PRIORITY, RangerPolicy.POLICY_PRIORITY_NAME_NORMAL);
+        list = new ArrayList<>(Collections.singletonList(p));
+        util.applyFilter(list, f);
+        Assertions.assertEquals(0, list.size());
+    }
+
+    @Test
+    public void test09_zoneNamePredicateForPolicy() {
+        RangerPolicy p = new RangerPolicy();
+        p.setZoneName("zone-x");
+        List<RangerPolicy> list = new ArrayList<>(Collections.singletonList(p));
+
+        AbstractPredicateUtil util = new AbstractPredicateUtil();
+        SearchFilter f = new SearchFilter();
+        f.setParam(SearchFilter.ZONE_NAME, "zone-x");
+        util.applyFilter(list, f);
+        Assertions.assertEquals(1, list.size());
+
+        f.setParam(SearchFilter.ZONE_NAME, "other");
+        list = new ArrayList<>(Collections.singletonList(p));
+        util.applyFilter(list, f);
+        Assertions.assertEquals(0, list.size());
+    }
+
+    @Test
+    public void test10_tagServiceNamePredicateForService() {
+        RangerService s = new RangerService();
+        s.setTagService("tagsvc");
+        List<RangerService> list = new ArrayList<>(Collections.singletonList(s));
+
+        AbstractPredicateUtil util = new AbstractPredicateUtil();
+        SearchFilter f = new SearchFilter();
+        f.setParam(SearchFilter.TAG_SERVICE_NAME, "tagsvc");
+        util.applyFilter(list, f);
+        Assertions.assertEquals(1, list.size());
+
+        f.setParam(SearchFilter.TAG_SERVICE_NAME, "other");
+        list = new ArrayList<>(Collections.singletonList(s));
+        util.applyFilter(list, f);
+        Assertions.assertEquals(0, list.size());
+    }
+
+    @Test
+    public void test11_reverseOrderWhenNoSorterButDesc() {
+        RangerPolicy p1 = new RangerPolicy();
+        p1.setId(1L);
+        RangerPolicy p2 = new RangerPolicy();
+        p2.setId(2L);
+        List<RangerPolicy> list = new ArrayList<>(Arrays.asList(p1, p2));
+
+        AbstractPredicateUtil util = new AbstractPredicateUtil();
+        SearchFilter f = new SearchFilter();
+        f.setSortType("desc"); // no sortBy
+        util.applyFilter(list, f);
+        Assertions.assertEquals(2L, list.get(0).getId().longValue());
+    }
+
+    @Test
+    public void test12_isEnabledPredicateTrueAndFalse() {
+        RangerPolicy enabled = new RangerPolicy();
+        enabled.setIsEnabled(true);
+        RangerPolicy disabled = new RangerPolicy();
+        disabled.setIsEnabled(false);
+        List<RangerPolicy> list = new ArrayList<>(Arrays.asList(enabled, disabled));
+
+        AbstractPredicateUtil util = new AbstractPredicateUtil();
+        SearchFilter f = new SearchFilter();
+        f.setParam(SearchFilter.IS_ENABLED, "true");
+        List<RangerPolicy> copy = new ArrayList<>(list);
+        util.applyFilter(copy, f);
+        Assertions.assertEquals(1, copy.size());
+        Assertions.assertTrue(copy.get(0).getIsEnabled());
+
+        f.setParam(SearchFilter.IS_ENABLED, "false");
+        copy = new ArrayList<>(list);
+        util.applyFilter(copy, f);
+        Assertions.assertEquals(1, copy.size());
+        Assertions.assertFalse(copy.get(0).getIsEnabled());
+    }
+
+    @Test
+    public void test13_policyResourceContainsMatch() {
+        RangerPolicy p = new RangerPolicy();
+        Map<String, RangerPolicyResource> res = new HashMap<>();
+        res.put("db", new RangerPolicyResource("sales"));
+        p.setResources(res);
+        List<RangerPolicy> list = new ArrayList<>(Collections.singletonList(p));
+
+        AbstractPredicateUtil util = new AbstractPredicateUtil();
+        SearchFilter f = new SearchFilter();
+        f.setParam(SearchFilter.POL_RESOURCE, "ale"); // partial in 'sales'
+        util.applyFilter(list, f);
+        Assertions.assertEquals(1, list.size());
+    }
+
+    @Test
+    public void test14_resourceSignaturePredicate() {
+        RangerPolicy p = new RangerPolicy();
+        p.setResourceSignature("sig-123");
+        List<RangerPolicy> list = new ArrayList<>(Collections.singletonList(p));
+
+        AbstractPredicateUtil util = new AbstractPredicateUtil();
+        SearchFilter f = new SearchFilter();
+        f.setParam(SearchFilter.RESOURCE_SIGNATURE, "sig-123");
+        util.applyFilter(list, f);
+        Assertions.assertEquals(1, list.size());
+
+        list = new ArrayList<>(Collections.singletonList(p));
+        f.setParam(SearchFilter.RESOURCE_SIGNATURE, "other");
+        util.applyFilter(list, f);
+        Assertions.assertEquals(0, list.size());
+    }
+
+    @Test
+    public void test15_sorterMapOtherSorters() {
+        RangerServiceDef sd1 = new RangerServiceDef();
+        sd1.setName("a");
+        RangerServiceDef sd2 = new RangerServiceDef();
+        sd2.setName("b");
+        List<RangerServiceDef> defs = new ArrayList<>(Arrays.asList(sd2, sd1));
+        AbstractPredicateUtil util = new AbstractPredicateUtil();
+        SearchFilter f = new SearchFilter();
+        f.setSortBy(SearchFilter.SERVICE_TYPE);
+        util.applyFilter(defs, f);
+        Assertions.assertEquals("a", defs.get(0).getName());
+
+        RangerPolicy p1 = new RangerPolicy();
+        p1.setName("alpha");
+        RangerPolicy p2 = new RangerPolicy();
+        p2.setName("beta");
+        List<RangerPolicy> policies = new ArrayList<>(Arrays.asList(p2, p1));
+        f = new SearchFilter();
+        f.setSortBy(SearchFilter.POLICY_NAME);
+        util.applyFilter(policies, f);
+        Assertions.assertEquals("alpha", policies.get(0).getName());
+    }
+}
diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/store/TestAbstractServiceStore.java b/agents-common/src/test/java/org/apache/ranger/plugin/store/TestAbstractServiceStore.java
new file mode 100644
index 000000000..3f3710ac0
--- /dev/null
+++ b/agents-common/src/test/java/org/apache/ranger/plugin/store/TestAbstractServiceStore.java
@@ -0,0 +1,1047 @@
+/*
+ * 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.plugin.store;
+
+import org.apache.ranger.authorization.hadoop.config.RangerAdminConfig;
+import org.apache.ranger.plugin.model.RangerPolicy;
+import org.apache.ranger.plugin.model.RangerSecurityZone;
+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.util.SearchFilter;
+import org.apache.ranger.plugin.util.ServicePolicies;
+import org.apache.ranger.services.tag.RangerServiceTag;
+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;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @generated by Cursor
+ * @description <Unit Test for AbstractServiceStore class>
+ */
+@ExtendWith(MockitoExtension.class)
+@TestMethodOrder(MethodOrderer.MethodName.class)
+public class TestAbstractServiceStore {
+    public static class MyServiceStore extends AbstractServiceStore {
+        private final List<RangerServiceDef> sds;
+        private final List<RangerService> svcs;
+        private final List<RangerPolicy> pols;
+
+        MyServiceStore(List<RangerServiceDef> sds, List<RangerService> svcs, List<RangerPolicy> pols) {
+            this.sds = sds;
+            this.svcs = svcs;
+            this.pols = pols;
+        }
+
+        @Override
+        protected void updateServicesForServiceDefUpdate(RangerServiceDef serviceDef) {
+        }
+
+        @Override
+        public void init() {
+        }
+
+        @Override
+        public RangerServiceDef createServiceDef(RangerServiceDef serviceDef) {
+            return null;
+        }
+
+        @Override
+        public RangerServiceDef updateServiceDef(RangerServiceDef serviceDef) {
+            return null;
+        }
+
+        @Override
+        public void deleteServiceDef(Long id, Boolean forceDelete) {
+        }
+
+        @Override
+        public RangerServiceDef getServiceDef(Long id) {
+            return null;
+        }
+
+        @Override
+        public RangerServiceDef getServiceDefByName(String name) {
+            return null;
+        }
+
+        @Override
+        public RangerServiceDef getServiceDefByDisplayName(String name) {
+            return null;
+        }
+
+        @Override
+        public List<RangerServiceDef> getServiceDefs(SearchFilter filter) {
+            return sds;
+        }
+
+        @Override
+        public PList<RangerServiceDef> getPaginatedServiceDefs(SearchFilter filter) throws Exception {
+            return super.getPaginatedServiceDefs(filter);
+        }
+
+        @Override
+        public RangerService createService(RangerService service) {
+            return null;
+        }
+
+        @Override
+        public RangerService updateService(RangerService service, Map<String, Object> options) {
+            return null;
+        }
+
+        @Override
+        public void deleteService(Long id) {
+        }
+
+        @Override
+        public boolean serviceExists(String name) {
+            return false;
+        }
+
+        @Override
+        public RangerService getService(Long id) {
+            return null;
+        }
+
+        @Override
+        public RangerService getServiceByName(String name) {
+            return null;
+        }
+
+        @Override
+        public RangerService getServiceByDisplayName(String displayName) {
+            return null;
+        }
+
+        @Override
+        public List<RangerService> getServices(SearchFilter filter) {
+            return svcs;
+        }
+
+        @Override
+        public PList<RangerService> getPaginatedServices(SearchFilter filter) throws Exception {
+            return super.getPaginatedServices(filter);
+        }
+
+        @Override
+        public RangerPolicy createPolicy(RangerPolicy policy) {
+            return null;
+        }
+
+        @Override
+        public RangerPolicy createDefaultPolicy(RangerPolicy policy) {
+            return null;
+        }
+
+        @Override
+        public RangerPolicy updatePolicy(RangerPolicy policy) {
+            return null;
+        }
+
+        @Override
+        public void deletePolicy(RangerPolicy policy, RangerService service) {
+        }
+
+        @Override
+        public void deletePolicy(RangerPolicy policy) {
+        }
+
+        @Override
+        public boolean policyExists(Long id) {
+            return false;
+        }
+
+        @Override
+        public RangerPolicy getPolicy(Long id) {
+            return null;
+        }
+
+        @Override
+        public List<RangerPolicy> getPolicies(SearchFilter filter) {
+            return pols;
+        }
+
+        @Override
+        public PList<RangerPolicy> getPaginatedPolicies(SearchFilter filter) throws Exception {
+            return super.getPaginatedPolicies(filter);
+        }
+
+        @Override
+        public Long getPolicyId(Long serviceId, String policyName, Long zoneId) {
+            return null;
+        }
+
+        @Override
+        public List<RangerPolicy> getServicePolicies(Long serviceId, SearchFilter filter) {
+            return pols;
+        }
+
+        @Override
+        public PList<RangerPolicy> getPaginatedServicePolicies(Long serviceId, SearchFilter filter) throws Exception {
+            return super.getPaginatedServicePolicies(serviceId, filter);
+        }
+
+        @Override
+        public List<RangerPolicy> getServicePolicies(String serviceName, SearchFilter filter) {
+            return pols;
+        }
+
+        @Override
+        public PList<RangerPolicy> getPaginatedServicePolicies(String serviceName, SearchFilter filter)
+                throws Exception {
+            return super.getPaginatedServicePolicies(serviceName, filter);
+        }
+
+        @Override
+        public ServicePolicies getServicePoliciesIfUpdated(String serviceName, Long lastKnownVersion,
+                boolean needsBackwardCompatibility) {
+            return null;
+        }
+
+        @Override
+        public Long getServicePolicyVersion(String serviceName) {
+            return super.getServicePolicyVersion(serviceName);
+        }
+
+        @Override
+        public ServicePolicies getServicePolicyDeltasOrPolicies(String serviceName, Long lastKnownVersion) {
+            return null;
+        }
+
+        @Override
+        public ServicePolicies getServicePolicyDeltas(String serviceName, Long lastKnownVersion,
+                Long cachedPolicyVersion) {
+            return null;
+        }
+
+        @Override
+        public ServicePolicies getServicePolicies(String serviceName, Long lastKnownVersion) {
+            return null;
+        }
+
+        @Override
+        public RangerPolicy getPolicyFromEventTime(String eventTimeStr, Long policyId) {
+            return null;
+        }
+
+        @Override
+        public Boolean getPopulateExistingBaseFields() {
+            return null;
+        }
+
+        @Override
+        public void setPopulateExistingBaseFields(Boolean populateExistingBaseFields) {
+        }
+
+        @Override
+        public RangerSecurityZone getSecurityZone(Long id) {
+            return null;
+        }
+
+        @Override
+        public RangerSecurityZone getSecurityZone(String name) {
+            return null;
+        }
+
+        @Override
+        public long getPoliciesCount(String serviceName) {
+            return 0;
+        }
+
+        @Override
+        public Map<String, String> getServiceConfigForPlugin(Long serviceId) {
+            return null;
+        }
+
+        @Override
+        public List<RangerPolicy> getPoliciesWithMetaAttributes(List<RangerPolicy> policies) {
+            return policies;
+        }
+
+        @Override
+        public List<RangerPolicy> getPoliciesByResourceSignature(String serviceName, String policySignature,
+                Boolean isPolicyEnabled) {
+            return pols;
+        }
+    }
+
+    public static class UpdateStore extends MyServiceStore {
+        public RangerServiceDef lastUpdated;
+        private final Long tagId;
+        private final RangerServiceDef tagRef;
+
+        public UpdateStore(List<RangerServiceDef> sds, RangerServiceDef tagRef) {
+            super(sds, new ArrayList<>(), new ArrayList<>());
+            this.tagRef = tagRef;
+            this.tagId = tagRef.getId();
+        }
+
+        @Override
+        public RangerServiceDef getServiceDef(Long id) {
+            return id != null && id.equals(tagId) ? tagRef : null;
+        }
+
+        @Override
+        public RangerServiceDef updateServiceDef(RangerServiceDef serviceDef) {
+            this.lastUpdated = serviceDef;
+            return serviceDef;
+        }
+    }
+
+    public static class CapturingStore extends MyServiceStore {
+        public RangerServiceDef lastUpdated;
+
+        public CapturingStore(List<RangerServiceDef> sds) {
+            super(sds, new ArrayList<>(), new ArrayList<>());
+        }
+
+        @Override
+        public RangerServiceDef updateServiceDef(RangerServiceDef serviceDef) {
+            this.lastUpdated = serviceDef;
+            return serviceDef;
+        }
+    }
+
+    public static class ThrowingServiceStore extends MyServiceStore {
+        public ThrowingServiceStore(List<RangerServiceDef> sds, List<RangerService> svcs, List<RangerPolicy> pols) {
+            super(sds, svcs, pols);
+        }
+
+        @Override
+        public RangerService getServiceByName(String name) {
+            throw new RuntimeException("boom");
+        }
+    }
+
+    public static class DeleteStore extends MyServiceStore {
+        public RangerServiceDef lastUpdated;
+        private final Long tagId;
+        private final RangerServiceDef tagRef;
+
+        public DeleteStore(List<RangerServiceDef> sds, RangerServiceDef tagRef) {
+            super(sds, new ArrayList<>(), new ArrayList<>());
+            this.tagRef = tagRef;
+            this.tagId = tagRef.getId();
+        }
+
+        @Override
+        public RangerServiceDef getServiceDef(Long id) {
+            return id != null && id.equals(tagId) ? tagRef : null;
+        }
+
+        @Override
+        public RangerServiceDef updateServiceDef(RangerServiceDef serviceDef) {
+            this.lastUpdated = serviceDef;
+            return serviceDef;
+        }
+    }
+
+    public static class PostUpdateStore extends UpdateStore {
+        public boolean servicesUpdated;
+
+        public PostUpdateStore(List<RangerServiceDef> sds, RangerServiceDef tagRef) {
+            super(sds, tagRef);
+        }
+
+        @Override
+        protected void updateServicesForServiceDefUpdate(RangerServiceDef serviceDef) {
+            servicesUpdated = true;
+        }
+    }
+
+    @Test
+    public void test01_getNextVersionAndPaginatedHelpers() throws Exception {
+        Assertions.assertEquals(1L, AbstractServiceStore.getNextVersion(null));
+        Assertions.assertEquals(6L, AbstractServiceStore.getNextVersion(5L));
+
+        List<RangerServiceDef> sds = new ArrayList<>(Collections.singletonList(new RangerServiceDef()));
+        List<RangerService> svcs = new ArrayList<>(Collections.singletonList(new RangerService()));
+        List<RangerPolicy> pols = new ArrayList<>(Collections.singletonList(new RangerPolicy()));
+        MyServiceStore store = new MyServiceStore(sds, svcs, pols);
+
+        PList<RangerServiceDef> p1 = store.getPaginatedServiceDefs(new SearchFilter());
+        Assertions.assertEquals(1, p1.getListSize());
+        PList<RangerService> p2 = store.getPaginatedServices(new SearchFilter());
+        Assertions.assertEquals(1, p2.getListSize());
+        PList<RangerPolicy> p3 = store.getPaginatedPolicies(new SearchFilter());
+        Assertions.assertEquals(1, p3.getListSize());
+
+        PList<RangerPolicy> p4 = store.getPaginatedServicePolicies(1L, new SearchFilter());
+        Assertions.assertEquals(1, p4.getListSize());
+        PList<RangerPolicy> p5 = store.getPaginatedServicePolicies("svc", new SearchFilter());
+        Assertions.assertEquals(1, p5.getListSize());
+    }
+
+    @Test
+    public void test02_getServicePolicyVersionHandlesNullService() {
+        List<RangerServiceDef> sds = new ArrayList<>();
+        List<RangerService> svcs = new ArrayList<>();
+        List<RangerPolicy> pols = new ArrayList<>();
+        MyServiceStore store = new MyServiceStore(sds, svcs, pols);
+
+        // getServiceByName returns null in stub; should return null and not throw
+        Assertions.assertNull(store.getServicePolicyVersion("missing"));
+    }
+
+    @Test
+    public void test03_getPaginatedServicePoliciesByNameAndId() throws Exception {
+        List<RangerServiceDef> sds = new ArrayList<>();
+        List<RangerService> svcs = new ArrayList<>();
+        List<RangerPolicy> pols = new ArrayList<>();
+        pols.add(new RangerPolicy());
+        MyServiceStore store = new MyServiceStore(sds, svcs, pols);
+
+        PList<RangerPolicy> pById = store.getPaginatedServicePolicies(5L, new SearchFilter());
+        Assertions.assertEquals(1, pById.getList().size());
+        PList<RangerPolicy> pByName = store.getPaginatedServicePolicies("svc1", new SearchFilter());
+        Assertions.assertEquals(1, pByName.getList().size());
+    }
+
+    @Test
+    public void test04_getServicePolicyVersion_whenGetServiceByNameThrows_returnsNull() {
+        List<RangerServiceDef> sds = new ArrayList<>();
+        List<RangerService> svcs = new ArrayList<>();
+        List<RangerPolicy> pols = new ArrayList<>();
+        ThrowingServiceStore store = new ThrowingServiceStore(sds, svcs, pols);
+
+        Assertions.assertNull(store.getServicePolicyVersion("svc"));
+    }
+
+    @Test
+    public void test05_getPaginatedEmptyLists() throws Exception {
+        List<RangerServiceDef> sds = new ArrayList<>();
+        List<RangerService> svcs = new ArrayList<>();
+        List<RangerPolicy> pols = new ArrayList<>();
+        MyServiceStore store = new MyServiceStore(sds, svcs, pols);
+
+        PList<RangerServiceDef> p1 = store.getPaginatedServiceDefs(new SearchFilter());
+        Assertions.assertEquals(0, p1.getListSize());
+        PList<RangerService> p2 = store.getPaginatedServices(new SearchFilter());
+        Assertions.assertEquals(0, p2.getListSize());
+        PList<RangerPolicy> p3 = store.getPaginatedPolicies(new SearchFilter());
+        Assertions.assertEquals(0, p3.getListSize());
+
+        PList<RangerPolicy> p4 = store.getPaginatedServicePolicies(1L, new SearchFilter());
+        Assertions.assertEquals(0, p4.getListSize());
+        PList<RangerPolicy> p5 = store.getPaginatedServicePolicies("svc", new SearchFilter());
+        Assertions.assertEquals(0, p5.getListSize());
+    }
+
+    @Test
+    public void test06_updateTagServiceDefForAccessTypes_updatesAccessTypesMaskAndRowFilter() throws Exception {
+        // Enable row-filter auto-propagation before store construction
+        RangerAdminConfig.getInstance().set(AbstractServiceStore.AUTOPROPAGATE_ROWFILTERDEF_TO_TAG_PROP, "true");
+
+        // Prepare service def "hdfs" with accessTypes, dataMask and rowFilter
+        RangerServiceDef svcDef = new RangerServiceDef();
+        svcDef.setId(2L);
+        svcDef.setName("hdfs");
+        List<RangerAccessTypeDef> svcAccess = new ArrayList<>();
+        svcAccess.add(new RangerAccessTypeDef(1L, "read", "read", null, null));
+        svcDef.setAccessTypes(svcAccess);
+
+        RangerServiceDef.RangerDataMaskDef svcMaskDef = new RangerServiceDef.RangerDataMaskDef();
+        List<RangerServiceDef.RangerDataMaskTypeDef> maskTypes = new ArrayList<>();
+        maskTypes.add(new RangerServiceDef.RangerDataMaskTypeDef(1L, "MASK", "MASK", null, null, null, null, null));
+        svcMaskDef.setMaskTypes(maskTypes);
+        List<RangerAccessTypeDef> maskAccess = new ArrayList<>();
+        maskAccess.add(new RangerAccessTypeDef(2L, "read", "read", null, null));
+        svcMaskDef.setAccessTypes(maskAccess);
+        svcDef.setDataMaskDef(svcMaskDef);
+
+        RangerServiceDef.RangerRowFilterDef svcRowDef = new RangerServiceDef.RangerRowFilterDef();
+        List<RangerAccessTypeDef> rowAccess = new ArrayList<>();
+        rowAccess.add(new RangerAccessTypeDef(3L, "select", "select", null, null));
+        svcRowDef.setAccessTypes(rowAccess);
+        svcDef.setRowFilterDef(svcRowDef);
+
+        // Prepare tag service-def with TAG resource
+        RangerServiceDef tagDef = new RangerServiceDef();
+        tagDef.setId(500L);
+        tagDef.setName(RangerServiceTag.TAG_RESOURCE_NAME);
+        List<RangerServiceDef.RangerResourceDef> tagResources = new ArrayList<>();
+        RangerServiceDef.RangerResourceDef tagRes = new RangerServiceDef.RangerResourceDef();
+        tagRes.setName(RangerServiceTag.TAG_RESOURCE_NAME);
+        tagResources.add(tagRes);
+        tagDef.setResources(tagResources);
+
+        List<RangerServiceDef> sds = new ArrayList<>();
+        sds.add(svcDef);
+        UpdateStore store = new UpdateStore(sds, tagDef);
+
+        // Wire EmbeddedServiceDefsUtil.tagServiceDef via reflection so
+        // getTagServiceDefId() matches
+        Field f = EmbeddedServiceDefsUtil.class.getDeclaredField("tagServiceDef");
+        f.setAccessible(true);
+        f.set(EmbeddedServiceDefsUtil.instance(), tagDef);
+
+        store.updateTagServiceDefForAccessTypes();
+
+        Assertions.assertNotNull(store.lastUpdated);
+        // Access types propagated with prefix
+        Assertions.assertTrue(tagDef.getAccessTypes().stream().anyMatch(a -> "hdfs:read".equals(a.getName())));
+        // Data mask: mask type added with prefixed name and itemId offset 2*(1000+1)+1
+        // = 2003
+        RangerServiceDef.RangerDataMaskDef updatedMaskDef = tagDef.getDataMaskDef();
+        Assertions.assertTrue(updatedMaskDef.getMaskTypes().stream().anyMatch(m -> "hdfs:MASK".equals(m.getName())));
+        Assertions.assertEquals(1, updatedMaskDef.getResources().size());
+        Assertions.assertEquals(RangerServiceTag.TAG_RESOURCE_NAME, updatedMaskDef.getResources().get(0).getName());
+        // Row filter propagated and resources set due to auto-propagation
+        RangerServiceDef.RangerRowFilterDef updatedRowDef = tagDef.getRowFilterDef();
+        Assertions.assertTrue(updatedRowDef.getAccessTypes().stream().anyMatch(a -> "hdfs:select".equals(a.getName())));
+        Assertions.assertEquals(1, updatedRowDef.getResources().size());
+        Assertions.assertEquals(RangerServiceTag.TAG_RESOURCE_NAME, updatedRowDef.getResources().get(0).getName());
+    }
+
+    @Test
+    public void test07_updateTagServiceDefForAccessTypes_tagServiceDefMissing_noUpdate() throws Exception {
+        // Ensure EmbeddedServiceDefsUtil has no tag service-def
+        Field f = EmbeddedServiceDefsUtil.class.getDeclaredField("tagServiceDef");
+        f.setAccessible(true);
+        f.set(EmbeddedServiceDefsUtil.instance(), null);
+
+        RangerServiceDef svcDef = new RangerServiceDef();
+        svcDef.setId(1L);
+        svcDef.setName("hdfs");
+        svcDef.setAccessTypes(new ArrayList<>());
+
+        List<RangerServiceDef> sds = new ArrayList<>();
+        sds.add(svcDef);
+        CapturingStore store = new CapturingStore(sds);
+
+        store.updateTagServiceDefForAccessTypes();
+
+        Assertions.assertNull(store.lastUpdated);
+    }
+
+    @Test
+    public void test08_postDelete_removesPrefixedAccessTypesAndClearsResourcesWhenEmpty() throws Exception {
+        // Prepare tag service-def with resources and some prefixed entries
+        RangerServiceDef tagDef = new RangerServiceDef();
+        tagDef.setId(600L);
+        tagDef.setName(RangerServiceTag.TAG_RESOURCE_NAME);
+        List<RangerServiceDef.RangerResourceDef> tagResources = new ArrayList<>();
+        RangerServiceDef.RangerResourceDef tagRes = new RangerServiceDef.RangerResourceDef();
+        tagRes.setName(RangerServiceTag.TAG_RESOURCE_NAME);
+        tagResources.add(tagRes);
+        tagDef.setResources(tagResources);
+
+        List<RangerAccessTypeDef> tagAccess = new ArrayList<>();
+        tagAccess.add(new RangerAccessTypeDef(1L, "hdfs:read", "hdfs:read", null, null));
+        tagAccess.add(new RangerAccessTypeDef(2L, "kafka:publish", "kafka:publish", null, null));
+        tagAccess.add(new RangerAccessTypeDef(3L, "hdfs:write", "hdfs:write", null, null));
+        tagDef.setAccessTypes(tagAccess);
+
+        RangerServiceDef.RangerDataMaskDef tagMask = new RangerServiceDef.RangerDataMaskDef();
+        List<RangerAccessTypeDef> maskAccess = new ArrayList<>();
+        maskAccess.add(new RangerAccessTypeDef(10L, "hdfs:read", "hdfs:read", null, null));
+        tagMask.setAccessTypes(maskAccess);
+        List<RangerServiceDef.RangerDataMaskTypeDef> maskTypes = new ArrayList<>();
+        maskTypes.add(new RangerServiceDef.RangerDataMaskTypeDef(11L, "hdfs:MASK", "hdfs:MASK", null, null, null, null,
+                null));
+        tagMask.setMaskTypes(maskTypes);
+        List<RangerServiceDef.RangerResourceDef> maskRes = new ArrayList<>();
+        maskRes.add(tagRes);
+        tagMask.setResources(maskRes);
+        tagDef.setDataMaskDef(tagMask);
+
+        RangerServiceDef.RangerRowFilterDef tagRow = new RangerServiceDef.RangerRowFilterDef();
+        List<RangerAccessTypeDef> rowAccess = new ArrayList<>();
+        rowAccess.add(new RangerAccessTypeDef(20L, "hdfs:select", "hdfs:select", null, null));
+        tagRow.setAccessTypes(rowAccess);
+        List<RangerServiceDef.RangerResourceDef> rowRes = new ArrayList<>();
+        rowRes.add(tagRes);
+        tagRow.setResources(rowRes);
+        tagDef.setRowFilterDef(tagRow);
+
+        // Set tag service-def in EmbeddedServiceDefsUtil
+        Field f = EmbeddedServiceDefsUtil.class.getDeclaredField("tagServiceDef");
+        f.setAccessible(true);
+        f.set(EmbeddedServiceDefsUtil.instance(), tagDef);
+
+        RangerServiceDef svcDef = new RangerServiceDef();
+        svcDef.setId(2L);
+        svcDef.setName("hdfs");
+
+        List<RangerServiceDef> sds = new ArrayList<>();
+        sds.add(svcDef);
+        DeleteStore store = new DeleteStore(sds, tagDef);
+
+        store.postDelete(svcDef);
+
+        Assertions.assertNotNull(store.lastUpdated);
+        // Prefixed access types removed
+        Assertions.assertTrue(tagDef.getAccessTypes().stream().noneMatch(a -> a.getName().startsWith("hdfs:")));
+        Assertions.assertTrue(tagDef.getAccessTypes().stream().anyMatch(a -> a.getName().equals("kafka:publish")));
+        // Data mask and row filter entries for hdfs removed; since accessTypes become
+        // empty, resources should be cleared by updateResourceInTagServiceDef
+        Assertions.assertTrue(tagDef.getDataMaskDef().getAccessTypes().isEmpty());
+        Assertions.assertTrue(tagDef.getDataMaskDef().getMaskTypes().isEmpty());
+        Assertions.assertTrue(tagDef.getDataMaskDef().getResources().isEmpty());
+        Assertions.assertTrue(tagDef.getRowFilterDef().getAccessTypes().isEmpty());
+        Assertions.assertTrue(tagDef.getRowFilterDef().getResources().isEmpty());
+    }
+
+    @Test
+    public void test09_postCreate_updatesTagServiceDef_whenServiceDefCreated() throws Exception {
+        RangerServiceDef svcDef = new RangerServiceDef();
+        svcDef.setId(3L);
+        svcDef.setName("hdfs");
+        List<RangerAccessTypeDef> svcAccess = new ArrayList<>();
+        svcAccess.add(new RangerAccessTypeDef(1L, "read", "read", null, null));
+        svcDef.setAccessTypes(svcAccess);
+
+        RangerServiceDef tagDef = new RangerServiceDef();
+        tagDef.setId(700L);
+        tagDef.setName(RangerServiceTag.TAG_RESOURCE_NAME);
+        List<RangerServiceDef.RangerResourceDef> tagResources = new ArrayList<>();
+        RangerServiceDef.RangerResourceDef tagRes = new RangerServiceDef.RangerResourceDef();
+        tagRes.setName(RangerServiceTag.TAG_RESOURCE_NAME);
+        tagResources.add(tagRes);
+        tagDef.setResources(tagResources);
+
+        List<RangerServiceDef> sds = new ArrayList<>();
+        sds.add(svcDef);
+        UpdateStore store = new UpdateStore(sds, tagDef);
+
+        Field f = EmbeddedServiceDefsUtil.class.getDeclaredField("tagServiceDef");
+        f.setAccessible(true);
+        f.set(EmbeddedServiceDefsUtil.instance(), tagDef);
+
+        store.postCreate(svcDef);
+
+        Assertions.assertNotNull(store.lastUpdated);
+        Assertions.assertTrue(tagDef.getAccessTypes().stream().anyMatch(a -> "hdfs:read".equals(a.getName())));
+    }
+
+    @Test
+    public void test10_postCreate_nonServiceDef_noop() throws Exception {
+        RangerServiceDef tagDef = new RangerServiceDef();
+        tagDef.setId(701L);
+        tagDef.setName(RangerServiceTag.TAG_RESOURCE_NAME);
+
+        List<RangerServiceDef> sds = new ArrayList<>();
+        UpdateStore store = new UpdateStore(sds, tagDef);
+
+        Field f = EmbeddedServiceDefsUtil.class.getDeclaredField("tagServiceDef");
+        f.setAccessible(true);
+        f.set(EmbeddedServiceDefsUtil.instance(), tagDef);
+
+        store.postCreate(new RangerPolicy());
+        Assertions.assertNull(store.lastUpdated);
+    }
+
+    @Test
+    public void test11_postUpdate_updatesTagServiceDef_andInvokesServicesUpdate() throws Exception {
+        RangerServiceDef svcDef = new RangerServiceDef();
+        svcDef.setId(4L);
+        svcDef.setName("kafka");
+        List<RangerAccessTypeDef> svcAccess = new ArrayList<>();
+        svcAccess.add(new RangerAccessTypeDef(1L, "publish", "publish", null, null));
+        svcDef.setAccessTypes(svcAccess);
+
+        RangerServiceDef tagDef = new RangerServiceDef();
+        tagDef.setId(702L);
+        tagDef.setName(RangerServiceTag.TAG_RESOURCE_NAME);
+
+        List<RangerServiceDef> sds = new ArrayList<>();
+        sds.add(svcDef);
+        PostUpdateStore store = new PostUpdateStore(sds, tagDef);
+
+        Field f = EmbeddedServiceDefsUtil.class.getDeclaredField("tagServiceDef");
+        f.setAccessible(true);
+        f.set(EmbeddedServiceDefsUtil.instance(), tagDef);
+
+        store.postUpdate(svcDef);
+
+        Assertions.assertNotNull(store.lastUpdated);
+        Assertions.assertTrue(store.servicesUpdated);
+        Assertions.assertTrue(tagDef.getAccessTypes().stream().anyMatch(a -> "kafka:publish".equals(a.getName())));
+    }
+
+    @Test
+    public void test12_postUpdate_nonServiceDef_noop() throws Exception {
+        RangerServiceDef tagDef = new RangerServiceDef();
+        tagDef.setId(703L);
+        tagDef.setName(RangerServiceTag.TAG_RESOURCE_NAME);
+
+        List<RangerServiceDef> sds = new ArrayList<>();
+        PostUpdateStore store = new PostUpdateStore(sds, tagDef);
+
+        Field f = EmbeddedServiceDefsUtil.class.getDeclaredField("tagServiceDef");
+        f.setAccessible(true);
+        f.set(EmbeddedServiceDefsUtil.instance(), tagDef);
+
+        store.postUpdate(new RangerPolicy());
+        Assertions.assertNull(store.lastUpdated);
+        Assertions.assertFalse(store.servicesUpdated);
+    }
+
+    @Test
+    public void test13_getServicePolicyVersion_success() {
+        List<RangerServiceDef> sds = new ArrayList<>();
+        List<RangerPolicy> pols = new ArrayList<>();
+        List<RangerService> svcs = new ArrayList<>();
+
+        MyServiceStore store = new MyServiceStore(sds, svcs, pols) {
+            @Override
+            public Long getServicePolicyVersion(String serviceName) {
+                return super.getServicePolicyVersion(serviceName);
+            }
+
+            @Override
+            public RangerService getServiceByName(String name) {
+                RangerService svc = new RangerService();
+                svc.setPolicyVersion(9L);
+                return svc;
+            }
+        };
+
+        Assertions.assertEquals(9L, store.getServicePolicyVersion("svc"));
+    }
+
+    @Test
+    public void test14_updateTagAccessTypeDef_noChange_returnsFalse() throws Exception {
+        MyServiceStore store = new MyServiceStore(new ArrayList<>(), new ArrayList<>(), new ArrayList<>());
+
+        RangerAccessTypeDef svcAccess = new RangerAccessTypeDef(1L, "read", "lbl", "rb", null);
+        List<String> svcImplied = new ArrayList<>();
+        svcImplied.add("write");
+        svcAccess.setImpliedGrants(svcImplied);
+
+        RangerAccessTypeDef tagAccess = new RangerAccessTypeDef(1001L, "hdfs:read", "lbl", "rb", null);
+        List<String> tagImplied = new ArrayList<>();
+        tagImplied.add("hdfs:write");
+        tagAccess.setImpliedGrants(tagImplied);
+
+        Method m = AbstractServiceStore.class.getDeclaredMethod("updateTagAccessTypeDef",
+                RangerAccessTypeDef.class,
+                RangerAccessTypeDef.class,
+                String.class);
+        m.setAccessible(true);
+
+        Boolean updated = (Boolean) m.invoke(store, tagAccess, svcAccess, "hdfs:");
+        Assertions.assertFalse(updated);
+        Assertions.assertEquals("hdfs:read", tagAccess.getName());
+        Assertions.assertEquals("lbl", tagAccess.getLabel());
+        Assertions.assertEquals("rb", tagAccess.getRbKeyLabel());
+        Assertions.assertTrue(tagAccess.getImpliedGrants().contains("hdfs:write"));
+    }
+
+    @Test
+    public void test15_updateTagAccessTypeDef_nameDiff_updatesAndPrefixesImpliedGrants() throws Exception {
+        MyServiceStore store = new MyServiceStore(new ArrayList<>(), new ArrayList<>(), new ArrayList<>());
+
+        RangerAccessTypeDef svcAccess = new RangerAccessTypeDef(1L, "read", "lbl2", "rb2", null);
+        List<String> svcImplied = new ArrayList<>();
+        svcImplied.add("write");
+        svcImplied.add("execute");
+        svcAccess.setImpliedGrants(svcImplied);
+
+        RangerAccessTypeDef tagAccess = new RangerAccessTypeDef(1001L, "other", "old", "oldrb", null);
+        List<String> tagImplied = new ArrayList<>();
+        tagImplied.add("hdfs:old");
+        tagAccess.setImpliedGrants(tagImplied);
+
+        Method m = AbstractServiceStore.class.getDeclaredMethod("updateTagAccessTypeDef",
+                RangerAccessTypeDef.class,
+                RangerAccessTypeDef.class,
+                String.class);
+        m.setAccessible(true);
+
+        Boolean updated = (Boolean) m.invoke(store, tagAccess, svcAccess, "hdfs:");
+        Assertions.assertTrue(updated);
+        Assertions.assertEquals("hdfs:read", tagAccess.getName());
+        Assertions.assertEquals("lbl2", tagAccess.getLabel());
+        Assertions.assertEquals("rb2", tagAccess.getRbKeyLabel());
+        Assertions.assertTrue(tagAccess.getImpliedGrants().contains("hdfs:write"));
+        Assertions.assertTrue(tagAccess.getImpliedGrants().contains("hdfs:execute"));
+    }
+
+    @Test
+    public void test16_updateTagAccessTypeDef_impliedGrantMissing_updates() throws Exception {
+        MyServiceStore store = new MyServiceStore(new ArrayList<>(), new ArrayList<>(), new ArrayList<>());
+
+        RangerAccessTypeDef svcAccess = new RangerAccessTypeDef(1L, "read", "lbl", "rb", null);
+        List<String> svcImplied = new ArrayList<>();
+        svcImplied.add("write");
+        svcImplied.add("execute");
+        svcAccess.setImpliedGrants(svcImplied);
+
+        // tag has correct name/labels but only one implied grant; missing one should trigger update via inner loop
+        RangerAccessTypeDef tagAccess = new RangerAccessTypeDef(1001L, "hdfs:read", "lbl", "rb", null);
+        List<String> tagImplied = new ArrayList<>();
+        tagImplied.add("hdfs:write");
+        tagAccess.setImpliedGrants(tagImplied);
+
+        Method m = AbstractServiceStore.class.getDeclaredMethod("updateTagAccessTypeDef",
+                RangerAccessTypeDef.class,
+                RangerAccessTypeDef.class,
+                String.class);
+        m.setAccessible(true);
+
+        Boolean updated = (Boolean) m.invoke(store, tagAccess, svcAccess, "hdfs:");
+        Assertions.assertTrue(updated);
+        Assertions.assertTrue(tagAccess.getImpliedGrants().contains("hdfs:write"));
+        Assertions.assertTrue(tagAccess.getImpliedGrants().contains("hdfs:execute"));
+    }
+
+    @Test
+    public void test17_updateTagAccessTypeDef_labelOrRbKeyDiff_updates() throws Exception {
+        MyServiceStore store = new MyServiceStore(new ArrayList<>(), new ArrayList<>(), new ArrayList<>());
+
+        // Same name once prefix is removed, but label differs
+        RangerAccessTypeDef svcAccess = new RangerAccessTypeDef(1L, "read", "newLbl", "newRb", null);
+        List<String> svcImplied = new ArrayList<>();
+        svcImplied.add("write");
+        svcAccess.setImpliedGrants(svcImplied);
+
+        RangerAccessTypeDef tagAccess = new RangerAccessTypeDef(1001L, "hdfs:read", "oldLbl", "oldRb", null);
+        List<String> tagImplied = new ArrayList<>();
+        tagImplied.add("hdfs:write");
+        tagAccess.setImpliedGrants(tagImplied);
+
+        Method m = AbstractServiceStore.class.getDeclaredMethod("updateTagAccessTypeDef",
+                RangerAccessTypeDef.class,
+                RangerAccessTypeDef.class,
+                String.class);
+        m.setAccessible(true);
+
+        Boolean updated = (Boolean) m.invoke(store, tagAccess, svcAccess, "hdfs:");
+        Assertions.assertTrue(updated);
+        Assertions.assertEquals("hdfs:read", tagAccess.getName());
+        Assertions.assertEquals("newLbl", tagAccess.getLabel());
+        Assertions.assertEquals("newRb", tagAccess.getRbKeyLabel());
+        Assertions.assertTrue(tagAccess.getImpliedGrants().contains("hdfs:write"));
+    }
+
+    @Test
+    void test18_findAccessTypeDef_found() throws Exception {
+        AbstractServiceStore store = new MyServiceStore(new ArrayList<>(), new ArrayList<>(), new ArrayList<>());
+
+        List<RangerAccessTypeDef> list = new ArrayList<>();
+        list.add(new RangerAccessTypeDef(1L, "a", "a", null, null));
+        list.add(new RangerAccessTypeDef(2L, "b", "b", null, null));
+
+        Method m = AbstractServiceStore.class.getDeclaredMethod("findAccessTypeDef", long.class, List.class);
+        m.setAccessible(true);
+
+        Object ret = m.invoke(store, 2L, list);
+        Assertions.assertNotNull(ret);
+        Assertions.assertEquals(2L, ((RangerAccessTypeDef) ret).getItemId());
+    }
+
+    @Test
+    void test19_findAccessTypeDef_notFound() throws Exception {
+        AbstractServiceStore store = new MyServiceStore(new ArrayList<>(), new ArrayList<>(), new ArrayList<>());
+
+        List<RangerAccessTypeDef> list = new ArrayList<>();
+        list.add(new RangerAccessTypeDef(3L, "a", "a", null, null));
+        list.add(new RangerAccessTypeDef(4L, "b", "b", null, null));
+
+        Method m = AbstractServiceStore.class.getDeclaredMethod("findAccessTypeDef", long.class, List.class);
+        m.setAccessible(true);
+
+        Object ret = m.invoke(store, 2L, list);
+        Assertions.assertNull(ret);
+    }
+
+    @Test
+    void test20_updateTagAccessTypeDefs_addsMissingFromSvc() throws Exception {
+        AbstractServiceStore store = new MyServiceStore(new ArrayList<>(), new ArrayList<>(), new ArrayList<>());
+
+        List<RangerAccessTypeDef> svc = new ArrayList<>();
+        RangerAccessTypeDef svcRead = new RangerAccessTypeDef(1L, "read", "lbl", "rb", null);
+        List<String> svcImpl = new ArrayList<>();
+        svcImpl.add("write");
+        svcRead.setImpliedGrants(svcImpl);
+        svc.add(svcRead);
+
+        List<RangerAccessTypeDef> tag = new ArrayList<>();
+
+        Method m = AbstractServiceStore.class.getDeclaredMethod("updateTagAccessTypeDefs", List.class, List.class, long.class, String.class);
+        m.setAccessible(true);
+
+        Boolean updated = (Boolean) m.invoke(store, svc, tag, 100L, "hdfs:");
+        Assertions.assertTrue(updated);
+        Assertions.assertEquals(1, tag.size());
+        Assertions.assertEquals(Long.valueOf(101L), tag.get(0).getItemId());
+        Assertions.assertEquals("hdfs:read", tag.get(0).getName());
+        Assertions.assertTrue(tag.get(0).getImpliedGrants().contains("hdfs:write"));
+    }
+
+    @Test
+    void test21_updateTagAccessTypeDefs_updatesChangedEntries() throws Exception {
+        AbstractServiceStore store = new MyServiceStore(new ArrayList<>(), new ArrayList<>(), new ArrayList<>());
+
+        List<RangerAccessTypeDef> svc = new ArrayList<>();
+        RangerAccessTypeDef svcRead = new RangerAccessTypeDef(2L, "read", "newLbl", "newRb", null);
+        List<String> svcImpl = new ArrayList<>();
+        svcImpl.add("execute");
+        svcRead.setImpliedGrants(svcImpl);
+        svc.add(svcRead);
+
+        List<RangerAccessTypeDef> tag = new ArrayList<>();
+        RangerAccessTypeDef tagRead = new RangerAccessTypeDef(1002L, "hdfs:old", "oldLbl", "oldRb", null);
+        List<String> tagImpl = new ArrayList<>();
+        tagImpl.add("hdfs:write");
+        tagRead.setImpliedGrants(tagImpl);
+        tag.add(tagRead);
+
+        Method m = AbstractServiceStore.class.getDeclaredMethod("updateTagAccessTypeDefs", List.class, List.class, long.class, String.class);
+        m.setAccessible(true);
+
+        Boolean updated = (Boolean) m.invoke(store, svc, tag, 100L, "hdfs:");
+        Assertions.assertTrue(updated);
+        Assertions.assertEquals(1, tag.size());
+        Assertions.assertEquals("hdfs:read", tag.get(0).getName());
+        Assertions.assertEquals("newLbl", tag.get(0).getLabel());
+        Assertions.assertEquals("newRb", tag.get(0).getRbKeyLabel());
+        Assertions.assertTrue(tag.get(0).getImpliedGrants().contains("hdfs:execute"));
+    }
+
+    @Test
+    void test22_updateTagAccessTypeDefs_deletesRemovedFromSvc() throws Exception {
+        AbstractServiceStore store = new MyServiceStore(new ArrayList<>(), new ArrayList<>(), new ArrayList<>());
+
+        List<RangerAccessTypeDef> svc = new ArrayList<>();
+        // svc intentionally empty to trigger deletion of prefixed tag entry
+
+        List<RangerAccessTypeDef> tag = new ArrayList<>();
+        RangerAccessTypeDef tagWrite = new RangerAccessTypeDef(1003L, "hdfs:write", "write", null, null);
+        tag.add(tagWrite);
+
+        Method m = AbstractServiceStore.class.getDeclaredMethod("updateTagAccessTypeDefs", List.class, List.class, long.class, String.class);
+        m.setAccessible(true);
+
+        Boolean updated = (Boolean) m.invoke(store, svc, tag, 100L, "hdfs:");
+        Assertions.assertTrue(updated);
+        Assertions.assertTrue(tag.isEmpty());
+    }
+
+    @Test
+    void test23_updateTagAccessTypeDefs_noChanges_returnsFalse() throws Exception {
+        AbstractServiceStore store = new MyServiceStore(new ArrayList<>(), new ArrayList<>(), new ArrayList<>());
+
+        List<RangerAccessTypeDef> svc = new ArrayList<>();
+        RangerAccessTypeDef svcRead = new RangerAccessTypeDef(1L, "read", "lbl", "rb", null);
+        List<String> svcImpl = new ArrayList<>();
+        svcImpl.add("write");
+        svcRead.setImpliedGrants(svcImpl);
+        svc.add(svcRead);
+
+        List<RangerAccessTypeDef> tag = new ArrayList<>();
+        RangerAccessTypeDef tagRead = new RangerAccessTypeDef(101L, "hdfs:read", "lbl", "rb", null);
+        List<String> tagImpl = new ArrayList<>();
+        tagImpl.add("hdfs:write");
+        tagRead.setImpliedGrants(tagImpl);
+        tag.add(tagRead);
+
+        Method m = AbstractServiceStore.class.getDeclaredMethod("updateTagAccessTypeDefs", List.class, List.class, long.class, String.class);
+        m.setAccessible(true);
+
+        Boolean updated = (Boolean) m.invoke(store, svc, tag, 100L, "hdfs:");
+        Assertions.assertFalse(updated);
+        Assertions.assertEquals(1, tag.size());
+        Assertions.assertEquals("hdfs:read", tag.get(0).getName());
+    }
+
+    private RangerServiceDef makeSvcWithMask(long svcId, String svcName, List<RangerServiceDef.RangerDataMaskTypeDef> maskTypes, List<RangerAccessTypeDef> maskAccess) {
+        RangerServiceDef svc = new RangerServiceDef();
+        svc.setId(svcId);
+        svc.setName(svcName);
+        RangerServiceDef.RangerDataMaskDef maskDef = new RangerServiceDef.RangerDataMaskDef();
+        maskDef.setMaskTypes(maskTypes);
+        maskDef.setAccessTypes(maskAccess);
+        svc.setDataMaskDef(maskDef);
+        return svc;
+    }
+
+    private RangerServiceDef makeTagWithMask(List<RangerServiceDef.RangerDataMaskTypeDef> maskTypes, List<RangerAccessTypeDef> maskAccess) {
+        RangerServiceDef tag = new RangerServiceDef();
+        tag.setId(999L);
+        tag.setName(RangerServiceTag.TAG_RESOURCE_NAME);
+        RangerServiceDef.RangerDataMaskDef maskDef = new RangerServiceDef.RangerDataMaskDef();
+        maskDef.setMaskTypes(maskTypes);
+        maskDef.setAccessTypes(maskAccess);
+        tag.setDataMaskDef(maskDef);
+        return tag;
+    }
+
+    @Test
+    void test24_updateTagServiceDefForUpdatingDataMaskDef_add_update_delete_and_nochange() throws Exception {
+        AbstractServiceStore store = new MyServiceStore(new ArrayList<>(), new ArrayList<>(), new ArrayList<>());
+
+        // Add scenario: tag has none, svc has one mask type and one access type
+        List<RangerServiceDef.RangerDataMaskTypeDef> svcMaskTypesAdd = new ArrayList<>();
+        svcMaskTypesAdd.add(new RangerServiceDef.RangerDataMaskTypeDef(1L, "MASK", "MASK", null, null, null, null, null));
+        List<RangerAccessTypeDef> svcMaskAccessAdd = new ArrayList<>();
+        svcMaskAccessAdd.add(new RangerAccessTypeDef(2L, "read", "read", null, null));
+        RangerServiceDef svcAdd = makeSvcWithMask(5L, "hdfs", svcMaskTypesAdd, svcMaskAccessAdd);
+
+        RangerServiceDef tagAdd = makeTagWithMask(new ArrayList<>(), new ArrayList<>());
+
+        Method m = AbstractServiceStore.class.getDeclaredMethod("updateTagServiceDefForUpdatingDataMaskDef", RangerServiceDef.class, RangerServiceDef.class, long.class, String.class);
+        m.setAccessible(true);
+
+        Boolean updatedAdd = (Boolean) m.invoke(store, tagAdd, svcAdd, 5L * 1001L, "hdfs:");
+        Assertions.assertTrue(updatedAdd);
+        Assertions.assertTrue(tagAdd.getDataMaskDef().getMaskTypes().stream().anyMatch(mt -> "hdfs:MASK".equals(mt.getName())));
+        Assertions.assertTrue(tagAdd.getDataMaskDef().getAccessTypes().stream().anyMatch(a -> "hdfs:read".equals(a.getName())));
+
+        // Update scenario: change label/desc in svc; existing tag has matching itemId, prefixed name
+        List<RangerServiceDef.RangerDataMaskTypeDef> svcMaskTypesUpd = new ArrayList<>();
+        RangerServiceDef.RangerDataMaskTypeDef svcMaskUpd = new RangerServiceDef.RangerDataMaskTypeDef(1L, "MASK", "NEWLBL", null, null, null, null, null);
+        svcMaskTypesUpd.add(svcMaskUpd);
+        RangerServiceDef svcUpd = makeSvcWithMask(5L, "hdfs", svcMaskTypesUpd, svcMaskAccessAdd);
+
+        RangerServiceDef tagUpd = makeTagWithMask(new ArrayList<>(tagAdd.getDataMaskDef().getMaskTypes()), new ArrayList<>(tagAdd.getDataMaskDef().getAccessTypes()));
+
+        Boolean updatedUpd = (Boolean) m.invoke(store, tagUpd, svcUpd, 5L * 1001L, "hdfs:");
+        Assertions.assertTrue(updatedUpd);
+        Assertions.assertTrue(tagUpd.getDataMaskDef().getMaskTypes().stream().anyMatch(mt -> "hdfs:MASK".equals(mt.getName())));
+
+        // Delete scenario: svc has no mask types; tag has one prefixed type -> should delete
+        List<RangerServiceDef.RangerDataMaskTypeDef> svcMaskTypesDel = new ArrayList<>();
+        RangerServiceDef svcDel = makeSvcWithMask(5L, "hdfs", svcMaskTypesDel, svcMaskAccessAdd);
+        RangerServiceDef tagDel = makeTagWithMask(new ArrayList<>(tagAdd.getDataMaskDef().getMaskTypes()), new ArrayList<>(tagAdd.getDataMaskDef().getAccessTypes()));
+
+        Boolean updatedDel = (Boolean) m.invoke(store, tagDel, svcDel, 5L * 1001L, "hdfs:");
+        Assertions.assertTrue(updatedDel);
+        Assertions.assertTrue(tagDel.getDataMaskDef().getMaskTypes().isEmpty());
+
+        // No-change scenario: identical svc and tag -> expect false
+        RangerServiceDef svcSame = makeSvcWithMask(6L, "kafka", new ArrayList<>(), new ArrayList<>());
+        RangerServiceDef tagSame = makeTagWithMask(new ArrayList<>(), new ArrayList<>());
+        Boolean updatedSame = (Boolean) m.invoke(store, tagSame, svcSame, 6L * 1001L, "kafka:");
+        Assertions.assertFalse(updatedSame);
+    }
+}
diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/store/TestAbstractTagStore.java b/agents-common/src/test/java/org/apache/ranger/plugin/store/TestAbstractTagStore.java
new file mode 100644
index 000000000..e327fa229
--- /dev/null
+++ b/agents-common/src/test/java/org/apache/ranger/plugin/store/TestAbstractTagStore.java
@@ -0,0 +1,541 @@
+/*
+ * 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.plugin.store;
+
+import org.apache.ranger.plugin.model.RangerPolicy;
+import org.apache.ranger.plugin.model.RangerSecurityZone;
+import org.apache.ranger.plugin.model.RangerService;
+import org.apache.ranger.plugin.model.RangerServiceDef;
+import org.apache.ranger.plugin.model.RangerServiceResource;
+import org.apache.ranger.plugin.model.RangerTag;
+import org.apache.ranger.plugin.model.RangerTagDef;
+import org.apache.ranger.plugin.model.RangerTagResourceMap;
+import org.apache.ranger.plugin.util.SearchFilter;
+import org.apache.ranger.plugin.util.ServicePolicies;
+import org.apache.ranger.plugin.util.ServiceTags;
+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;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @generated by Cursor
+ * @description <Unit Test for AbstractTagStore class>
+ */
+@ExtendWith(MockitoExtension.class)
+@TestMethodOrder(MethodOrderer.MethodName.class)
+public class TestAbstractTagStore {
+    private static class MyTagStore extends AbstractTagStore {
+        @Override
+        public boolean isInPlaceTagUpdateSupported() {
+            return true;
+        }
+
+        @Override
+        public void deleteAllTagObjectsForService(String serviceName) {
+        }
+
+        @Override
+        public Long getTagVersion(String serviceName) {
+            return 0L;
+        }
+
+        @Override
+        public ServiceTags getServiceTagsDelta(String serviceName, Long lastKnownVersion) {
+            return null;
+        }
+
+        @Override
+        public ServiceTags getServiceTags(String serviceName, Long lastKnownVersion) {
+            return null;
+        }
+
+        @Override
+        public ServiceTags getServiceTagsIfUpdated(String serviceName, Long lastKnownVersion,
+                boolean needsBackwardCompatibility) {
+            return null;
+        }
+
+        @Override
+        public RangerTagDef createTagDef(RangerTagDef tagDef) {
+            return null;
+        }
+
+        @Override
+        public RangerTagDef updateTagDef(RangerTagDef tagDef) {
+            return null;
+        }
+
+        @Override
+        public void deleteTagDefByName(String name) {
+        }
+
+        @Override
+        public void deleteTagDef(Long id) {
+        }
+
+        @Override
+        public RangerTagDef getTagDef(Long id) {
+            return null;
+        }
+
+        @Override
+        public RangerTagDef getTagDefByGuid(String guid) {
+            return null;
+        }
+
+        @Override
+        public RangerTagDef getTagDefByName(String name) {
+            return null;
+        }
+
+        @Override
+        public List<RangerTagDef> getTagDefs(SearchFilter filter) {
+            return null;
+        }
+
+        @Override
+        public PList<RangerTagDef> getPaginatedTagDefs(SearchFilter filter) {
+            return null;
+        }
+
+        @Override
+        public List<String> getTagTypes() {
+            return null;
+        }
+
+        @Override
+        public RangerTag createTag(RangerTag tag) {
+            return null;
+        }
+
+        @Override
+        public RangerTag updateTag(RangerTag tag) {
+            return null;
+        }
+
+        @Override
+        public void deleteTag(Long id) {
+        }
+
+        @Override
+        public RangerTag getTag(Long id) {
+            return null;
+        }
+
+        @Override
+        public RangerTag getTagByGuid(String guid) {
+            return null;
+        }
+
+        @Override
+        public List<Long> getTagIdsForResourceId(Long resourceId) {
+            return null;
+        }
+
+        @Override
+        public List<RangerTag> getTagsByType(String name) {
+            return null;
+        }
+
+        @Override
+        public List<RangerTag> getTagsForResourceId(Long resourceId) {
+            return null;
+        }
+
+        @Override
+        public List<RangerTag> getTagsForResourceGuid(String resourceGuid) {
+            return null;
+        }
+
+        @Override
+        public List<RangerTag> getTags(SearchFilter filter) {
+            return null;
+        }
+
+        @Override
+        public PList<RangerTag> getPaginatedTags(SearchFilter filter) {
+            return null;
+        }
+
+        @Override
+        public RangerServiceResource createServiceResource(RangerServiceResource resource) {
+            return null;
+        }
+
+        @Override
+        public RangerServiceResource updateServiceResource(RangerServiceResource resource) {
+            return null;
+        }
+
+        @Override
+        public void refreshServiceResource(Long resourceId) {
+        }
+
+        @Override
+        public void deleteServiceResource(Long id) {
+        }
+
+        @Override
+        public void deleteServiceResourceByGuid(String guid) {
+        }
+
+        @Override
+        public RangerServiceResource getServiceResource(Long id) {
+            return null;
+        }
+
+        @Override
+        public RangerServiceResource getServiceResourceByGuid(String guid) {
+            return null;
+        }
+
+        @Override
+        public List<RangerServiceResource> getServiceResourcesByService(String serviceName) {
+            return null;
+        }
+
+        @Override
+        public List<String> getServiceResourceGuidsByService(String serviceName) {
+            return null;
+        }
+
+        @Override
+        public RangerServiceResource getServiceResourceByServiceAndResourceSignature(String serviceName,
+                String resourceSignature) {
+            return null;
+        }
+
+        @Override
+        public List<RangerServiceResource> getServiceResources(SearchFilter filter) {
+            return null;
+        }
+
+        @Override
+        public PList<RangerServiceResource> getPaginatedServiceResources(SearchFilter filter) {
+            return null;
+        }
+
+        @Override
+        public RangerTagResourceMap createTagResourceMap(RangerTagResourceMap tagResourceMap) {
+            return null;
+        }
+
+        @Override
+        public void deleteTagResourceMap(Long id) {
+        }
+
+        @Override
+        public RangerTagResourceMap getTagResourceMap(Long id) {
+            return null;
+        }
+
+        @Override
+        public RangerTagResourceMap getTagResourceMapByGuid(String guid) {
+            return null;
+        }
+
+        @Override
+        public List<RangerTagResourceMap> getTagResourceMapsForTagId(Long tagId) {
+            return null;
+        }
+
+        @Override
+        public List<RangerTagResourceMap> getTagResourceMapsForTagGuid(String tagGuid) {
+            return null;
+        }
+
+        @Override
+        public List<RangerTagResourceMap> getTagResourceMapsForResourceId(Long resourceId) {
+            return null;
+        }
+
+        @Override
+        public List<RangerTagResourceMap> getTagResourceMapsForResourceGuid(String resourceGuid) {
+            return null;
+        }
+
+        @Override
+        public RangerTagResourceMap getTagResourceMapForTagAndResourceId(Long tagId, Long resourceId) {
+            return null;
+        }
+
+        @Override
+        public RangerTagResourceMap getTagResourceMapForTagAndResourceGuid(String tagGuid, String resourceGuid) {
+            return null;
+        }
+
+        @Override
+        public List<RangerTagResourceMap> getTagResourceMaps(SearchFilter filter) {
+            return null;
+        }
+
+        @Override
+        public PList<RangerTagResourceMap> getPaginatedTagResourceMaps(SearchFilter filter) {
+            return null;
+        }
+    }
+
+    @Test
+    public void test01_getSetServiceStore_andInitNoop() throws Exception {
+        MyTagStore s = new MyTagStore();
+        Assertions.assertNull(s.getServiceStore());
+        ServiceStore ss = new ServiceStore() {
+            @Override
+            public void init() {
+            }
+
+            @Override
+            public RangerServiceDef createServiceDef(RangerServiceDef serviceDef) {
+                return null;
+            }
+
+            @Override
+            public RangerServiceDef updateServiceDef(RangerServiceDef serviceDef) {
+                return null;
+            }
+
+            @Override
+            public void deleteServiceDef(Long id, Boolean forceDelete) {
+            }
+
+            @Override
+            public void updateTagServiceDefForAccessTypes() {
+            }
+
+            @Override
+            public RangerServiceDef getServiceDef(Long id) {
+                return null;
+            }
+
+            @Override
+            public RangerServiceDef getServiceDefByName(String name) {
+                return null;
+            }
+
+            @Override
+            public RangerServiceDef getServiceDefByDisplayName(String name) {
+                return null;
+            }
+
+            @Override
+            public List<RangerServiceDef> getServiceDefs(SearchFilter filter) {
+                return null;
+            }
+
+            @Override
+            public PList<RangerServiceDef> getPaginatedServiceDefs(SearchFilter filter) {
+                return null;
+            }
+
+            @Override
+            public RangerService createService(RangerService service) {
+                return null;
+            }
+
+            @Override
+            public RangerService updateService(RangerService service, Map<String, Object> options) {
+                return null;
+            }
+
+            @Override
+            public void deleteService(Long id) {
+            }
+
+            @Override
+            public boolean serviceExists(String name) {
+                return false;
+            }
+
+            @Override
+            public RangerService getService(Long id) {
+                return null;
+            }
+
+            @Override
+            public RangerService getServiceByName(String name) {
+                return null;
+            }
+
+            @Override
+            public RangerService getServiceByDisplayName(String displayName) {
+                return null;
+            }
+
+            @Override
+            public List<RangerService> getServices(SearchFilter filter) {
+                return null;
+            }
+
+            @Override
+            public PList<RangerService> getPaginatedServices(SearchFilter filter) {
+                return null;
+            }
+
+            @Override
+            public RangerPolicy createPolicy(RangerPolicy policy) {
+                return null;
+            }
+
+            @Override
+            public RangerPolicy createDefaultPolicy(RangerPolicy policy) {
+                return null;
+            }
+
+            @Override
+            public RangerPolicy updatePolicy(RangerPolicy policy) {
+                return null;
+            }
+
+            @Override
+            public void deletePolicy(RangerPolicy policy, RangerService service) {
+            }
+
+            @Override
+            public void deletePolicy(RangerPolicy policy) {
+            }
+
+            @Override
+            public boolean policyExists(Long id) {
+                return false;
+            }
+
+            @Override
+            public RangerPolicy getPolicy(Long id) {
+                return null;
+            }
+
+            @Override
+            public List<RangerPolicy> getPolicies(SearchFilter filter) {
+                return null;
+            }
+
+            @Override
+            public Long getPolicyId(Long serviceId, String policyName, Long zoneId) {
+                return null;
+            }
+
+            @Override
+            public PList<RangerPolicy> getPaginatedPolicies(SearchFilter filter) {
+                return null;
+            }
+
+            @Override
+            public List<RangerPolicy> getPoliciesByResourceSignature(String serviceName, String policySignature,
+                    Boolean isPolicyEnabled) {
+                return null;
+            }
+
+            @Override
+            public List<RangerPolicy> getServicePolicies(Long serviceId, SearchFilter filter) {
+                return null;
+            }
+
+            @Override
+            public PList<RangerPolicy> getPaginatedServicePolicies(Long serviceId, SearchFilter filter) {
+                return null;
+            }
+
+            @Override
+            public List<RangerPolicy> getServicePolicies(String serviceName, SearchFilter filter) {
+                return null;
+            }
+
+            @Override
+            public PList<RangerPolicy> getPaginatedServicePolicies(String serviceName, SearchFilter filter) {
+                return null;
+            }
+
+            @Override
+            public ServicePolicies getServicePoliciesIfUpdated(String serviceName, Long lastKnownVersion,
+                    boolean needsBackwardCompatibility) {
+                return null;
+            }
+
+            @Override
+            public Long getServicePolicyVersion(String serviceName) {
+                return null;
+            }
+
+            @Override
+            public ServicePolicies getServicePolicyDeltasOrPolicies(String serviceName, Long lastKnownVersion) {
+                return null;
+            }
+
+            @Override
+            public ServicePolicies getServicePolicyDeltas(String serviceName, Long lastKnownVersion,
+                    Long cachedPolicyVersion) {
+                return null;
+            }
+
+            @Override
+            public ServicePolicies getServicePolicies(String serviceName, Long lastKnownVersion) {
+                return null;
+            }
+
+            @Override
+            public RangerPolicy getPolicyFromEventTime(String eventTimeStr, Long policyId) {
+                return null;
+            }
+
+            @Override
+            public Boolean getPopulateExistingBaseFields() {
+                return null;
+            }
+
+            @Override
+            public void setPopulateExistingBaseFields(Boolean populateExistingBaseFields) {
+            }
+
+            @Override
+            public RangerSecurityZone getSecurityZone(Long id) {
+                return null;
+            }
+
+            @Override
+            public RangerSecurityZone getSecurityZone(String name) {
+                return null;
+            }
+
+            @Override
+            public long getPoliciesCount(String serviceName) {
+                return 0;
+            }
+
+            @Override
+            public Map<String, String> getServiceConfigForPlugin(Long serviceId) {
+                return null;
+            }
+
+            @Override
+            public List<RangerPolicy> getPoliciesWithMetaAttributes(List<RangerPolicy> policies) {
+                return null;
+            }
+        };
+        s.setServiceStore(ss);
+        Assertions.assertNotNull(s.getServiceStore());
+        s.init();
+    }
+}
diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/store/TestEmbeddedServiceDefsUtil.java b/agents-common/src/test/java/org/apache/ranger/plugin/store/TestEmbeddedServiceDefsUtil.java
new file mode 100644
index 000000000..11207ac7f
--- /dev/null
+++ b/agents-common/src/test/java/org/apache/ranger/plugin/store/TestEmbeddedServiceDefsUtil.java
@@ -0,0 +1,347 @@
+/*
+ * 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.plugin.store;
+
+import org.apache.ranger.authorization.hadoop.config.RangerAdminConfig;
+import org.apache.ranger.plugin.model.RangerPolicy;
+import org.apache.ranger.plugin.model.RangerSecurityZone;
+import org.apache.ranger.plugin.model.RangerService;
+import org.apache.ranger.plugin.model.RangerServiceDef;
+import org.apache.ranger.plugin.policyengine.gds.GdsPolicyEngine;
+import org.apache.ranger.plugin.util.SearchFilter;
+import org.apache.ranger.plugin.util.ServicePolicies;
+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;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @generated by Cursor
+ * @description <Unit Test for EmbeddedServiceDefsUtil class>
+ */
+@ExtendWith(MockitoExtension.class)
+@TestMethodOrder(MethodOrderer.MethodName.class)
+public class TestEmbeddedServiceDefsUtil {
+    public static class CapturingServiceStore implements ServiceStore {
+        private final Map<String, RangerServiceDef> nameToServiceDef = new HashMap<>();
+        private final Map<Long, RangerServiceDef> idToServiceDef = new HashMap<>();
+        private final Map<String, RangerService> nameToService = new HashMap<>();
+        private final List<String> createdServiceDefNames = new ArrayList<>();
+        private int idSeq = 1;
+        private boolean populateExistingBaseFields;
+
+        @Override
+        public void init() {
+        }
+
+        @Override
+        public RangerServiceDef createServiceDef(RangerServiceDef serviceDef) {
+            if (serviceDef.getId() == null) {
+                serviceDef.setId((long) idSeq++);
+            }
+            nameToServiceDef.put(serviceDef.getName(), serviceDef);
+            idToServiceDef.put(serviceDef.getId(), serviceDef);
+            createdServiceDefNames.add(serviceDef.getName());
+            return serviceDef;
+        }
+
+        @Override
+        public RangerServiceDef updateServiceDef(RangerServiceDef serviceDef) {
+            nameToServiceDef.put(serviceDef.getName(), serviceDef);
+            idToServiceDef.put(serviceDef.getId(), serviceDef);
+            return serviceDef;
+        }
+
+        @Override
+        public void deleteServiceDef(Long id, Boolean forceDelete) {
+            RangerServiceDef sd = idToServiceDef.remove(id);
+            if (sd != null) {
+                nameToServiceDef.remove(sd.getName());
+            }
+        }
+
+        @Override
+        public void updateTagServiceDefForAccessTypes() {
+        }
+
+        @Override
+        public RangerServiceDef getServiceDef(Long id) {
+            return idToServiceDef.get(id);
+        }
+
+        @Override
+        public RangerServiceDef getServiceDefByName(String name) {
+            return nameToServiceDef.get(name);
+        }
+
+        @Override
+        public RangerServiceDef getServiceDefByDisplayName(String name) {
+            return null;
+        }
+
+        @Override
+        public List<RangerServiceDef> getServiceDefs(SearchFilter filter) {
+            return new ArrayList<>(nameToServiceDef.values());
+        }
+
+        @Override
+        public PList<RangerServiceDef> getPaginatedServiceDefs(SearchFilter filter) {
+            return null;
+        }
+
+        @Override
+        public RangerService createService(RangerService service) {
+            if (service.getId() == null) {
+                service.setId((long) idSeq++);
+            }
+            nameToService.put(service.getName(), service);
+            return service;
+        }
+
+        @Override
+        public RangerService updateService(RangerService service, Map<String, Object> options) {
+            nameToService.put(service.getName(), service);
+            return service;
+        }
+
+        @Override
+        public void deleteService(Long id) {
+        }
+
+        @Override
+        public boolean serviceExists(String name) {
+            return nameToService.containsKey(name);
+        }
+
+        @Override
+        public RangerService getService(Long id) {
+            return null;
+        }
+
+        @Override
+        public RangerService getServiceByName(String name) {
+            return nameToService.get(name);
+        }
+
+        @Override
+        public RangerService getServiceByDisplayName(String displayName) {
+            return null;
+        }
+
+        @Override
+        public List<RangerService> getServices(SearchFilter filter) {
+            return null;
+        }
+
+        @Override
+        public PList<RangerService> getPaginatedServices(SearchFilter filter) {
+            return null;
+        }
+
+        @Override
+        public RangerPolicy createPolicy(RangerPolicy policy) {
+            return null;
+        }
+
+        @Override
+        public RangerPolicy createDefaultPolicy(RangerPolicy policy) {
+            return null;
+        }
+
+        @Override
+        public RangerPolicy updatePolicy(RangerPolicy policy) {
+            return null;
+        }
+
+        @Override
+        public void deletePolicy(RangerPolicy policy, RangerService service) {
+        }
+
+        @Override
+        public void deletePolicy(RangerPolicy policy) {
+        }
+
+        @Override
+        public boolean policyExists(Long id) {
+            return false;
+        }
+
+        @Override
+        public RangerPolicy getPolicy(Long id) {
+            return null;
+        }
+
+        @Override
+        public List<RangerPolicy> getPolicies(SearchFilter filter) {
+            return null;
+        }
+
+        @Override
+        public PList<RangerPolicy> getPaginatedPolicies(SearchFilter filter) {
+            return null;
+        }
+
+        @Override
+        public Long getPolicyId(Long serviceId, String policyName, Long zoneId) {
+            return null;
+        }
+
+        @Override
+        public List<RangerPolicy> getServicePolicies(Long serviceId, SearchFilter filter) {
+            return null;
+        }
+
+        @Override
+        public PList<RangerPolicy> getPaginatedServicePolicies(Long serviceId, SearchFilter filter) {
+            return null;
+        }
+
+        @Override
+        public List<RangerPolicy> getServicePolicies(String serviceName, SearchFilter filter) {
+            return null;
+        }
+
+        @Override
+        public PList<RangerPolicy> getPaginatedServicePolicies(String serviceName, SearchFilter filter) {
+            return null;
+        }
+
+        @Override
+        public ServicePolicies getServicePoliciesIfUpdated(String serviceName, Long lastKnownVersion, boolean needsBackwardCompatibility) {
+            return null;
+        }
+
+        @Override
+        public Long getServicePolicyVersion(String serviceName) {
+            return null;
+        }
+
+        @Override
+        public ServicePolicies getServicePolicyDeltasOrPolicies(String serviceName, Long lastKnownVersion) {
+            return null;
+        }
+
+        @Override
+        public ServicePolicies getServicePolicyDeltas(String serviceName, Long lastKnownVersion, Long cachedPolicyVersion) {
+            return null;
+        }
+
+        @Override
+        public ServicePolicies getServicePolicies(String serviceName, Long lastKnownVersion) {
+            return null;
+        }
+
+        @Override
+        public RangerPolicy getPolicyFromEventTime(String eventTimeStr, Long policyId) {
+            return null;
+        }
+
+        @Override
+        public Boolean getPopulateExistingBaseFields() {
+            return populateExistingBaseFields;
+        }
+
+        @Override
+        public void setPopulateExistingBaseFields(Boolean populateExistingBaseFields) {
+            this.populateExistingBaseFields = populateExistingBaseFields != null && populateExistingBaseFields;
+        }
+
+        @Override
+        public RangerSecurityZone getSecurityZone(Long id) {
+            return null;
+        }
+
+        @Override
+        public RangerSecurityZone getSecurityZone(String name) {
+            return null;
+        }
+
+        @Override
+        public long getPoliciesCount(String serviceName) {
+            return 0L;
+        }
+
+        @Override
+        public Map<String, String> getServiceConfigForPlugin(Long serviceId) {
+            return null;
+        }
+
+        @Override
+        public List<RangerPolicy> getPoliciesWithMetaAttributes(List<RangerPolicy> policies) {
+            return policies;
+        }
+
+        @Override
+        public List<RangerPolicy> getPoliciesByResourceSignature(String serviceName, String policySignature, Boolean isPolicyEnabled) {
+            return null;
+        }
+    }
+
+    @Test
+    public void test01_getEmbeddedServiceDef_tagLoadsFromClasspath() throws Exception {
+        RangerServiceDef tag = EmbeddedServiceDefsUtil.instance().getEmbeddedServiceDef(EmbeddedServiceDefsUtil.EMBEDDED_SERVICEDEF_TAG_NAME);
+        Assertions.assertNotNull(tag);
+        Assertions.assertEquals(EmbeddedServiceDefsUtil.EMBEDDED_SERVICEDEF_TAG_NAME, tag.getName());
+        Assertions.assertEquals(tag.getName(), tag.getDisplayName());
+    }
+
+    @Test
+    public void test02_init_createsTagAndGdsAndCreatesGdsService() {
+        RangerAdminConfig.getInstance().setBoolean(EmbeddedServiceDefsUtil.PROPERTY_CREATE_EMBEDDED_SERVICE_DEFS, true);
+
+        CapturingServiceStore store = new CapturingServiceStore();
+        EmbeddedServiceDefsUtil.instance().init(store);
+
+        Assertions.assertTrue(store.createdServiceDefNames.contains(EmbeddedServiceDefsUtil.EMBEDDED_SERVICEDEF_TAG_NAME));
+        Assertions.assertTrue(store.createdServiceDefNames.contains(EmbeddedServiceDefsUtil.EMBEDDED_SERVICEDEF_GDS_NAME));
+        RangerService gdsSvc = store.getServiceByName(GdsPolicyEngine.GDS_SERVICE_NAME);
+        Assertions.assertNotNull(gdsSvc);
+        Assertions.assertEquals(EmbeddedServiceDefsUtil.EMBEDDED_SERVICEDEF_GDS_NAME, gdsSvc.getType());
+    }
+
+    @Test
+    public void test03_isRecursiveEnabled_checksResourceDefFlag() {
+        RangerServiceDef def = new RangerServiceDef();
+        List<RangerServiceDef.RangerResourceDef> resources = new ArrayList<>();
+        RangerServiceDef.RangerResourceDef r = new RangerServiceDef.RangerResourceDef();
+        r.setName("path");
+        r.setLevel(1);
+        r.setRecursiveSupported(true);
+        resources.add(r);
+        def.setResources(resources);
+
+        boolean recursive = EmbeddedServiceDefsUtil.isRecursiveEnabled(def, "path");
+        Assertions.assertTrue(recursive);
+    }
+
+    @Test
+    public void test04_getEmbeddedServiceDef_unknownOrEmpty() throws Exception {
+        Assertions.assertNull(EmbeddedServiceDefsUtil.instance().getEmbeddedServiceDef("unknown-type"));
+        Assertions.assertNull(EmbeddedServiceDefsUtil.instance().getEmbeddedServiceDef(null));
+        Assertions.assertNull(EmbeddedServiceDefsUtil.instance().getEmbeddedServiceDef(""));
+    }
+}
diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/store/TestPList.java b/agents-common/src/test/java/org/apache/ranger/plugin/store/TestPList.java
new file mode 100644
index 000000000..2f28e6610
--- /dev/null
+++ b/agents-common/src/test/java/org/apache/ranger/plugin/store/TestPList.java
@@ -0,0 +1,101 @@
+/*
+ * 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.plugin.store;
+
+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;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+/**
+* @generated by Cursor
+* @description <Unit Test for PList class>
+*/
+@ExtendWith(MockitoExtension.class)
+@TestMethodOrder(MethodOrderer.MethodName.class)
+public class TestPList {
+    @Test
+    public void test01_defaultConstructorAndGetters() {
+        PList<String> p = new PList<>();
+        Assertions.assertEquals(0, p.getStartIndex());
+        Assertions.assertEquals(0, p.getPageSize());
+        Assertions.assertEquals(0, p.getTotalCount());
+        Assertions.assertEquals(0, p.getResultSize());
+        Assertions.assertNull(p.getSortType());
+        Assertions.assertNull(p.getSortBy());
+        Assertions.assertEquals(0, p.getListSize());
+        Assertions.assertTrue(p.toString().contains("PList"));
+    }
+
+    @Test
+    public void test02_paramConstructorAndSize() {
+        List<String> list = new ArrayList<>(Arrays.asList("a", "b", "c"));
+        PList<String> p = new PList<>(list, 1, 10, 3, 3, "asc", "name");
+        Assertions.assertEquals(list, p.getList());
+        Assertions.assertEquals(1, p.getStartIndex());
+        Assertions.assertEquals(10, p.getPageSize());
+        Assertions.assertEquals(3, p.getTotalCount());
+        Assertions.assertEquals(3, p.getResultSize());
+        Assertions.assertEquals("asc", p.getSortType());
+        Assertions.assertEquals("name", p.getSortBy());
+        Assertions.assertEquals(3, p.getListSize());
+    }
+
+    @Test
+    public void test03_copyConstructorFromNull() {
+        PList<String> p = new PList<>((PList<String>) null);
+        Assertions.assertEquals(0, p.getStartIndex());
+        Assertions.assertEquals(0, p.getPageSize());
+        Assertions.assertEquals(0, p.getTotalCount());
+        Assertions.assertEquals(0, p.getResultSize());
+        Assertions.assertNull(p.getSortType());
+        Assertions.assertNull(p.getSortBy());
+        Assertions.assertEquals(0, p.getListSize());
+    }
+
+    @Test
+    public void test04_settersAffectValues() {
+        PList<Integer> p = new PList<>();
+        p.setList(new ArrayList<>(Collections.singletonList(42)));
+        p.setStartIndex(5);
+        p.setPageSize(7);
+        p.setTotalCount(11);
+        p.setResultSize(1);
+        p.setSortType("desc");
+        p.setSortBy("id");
+        p.setQueryTimeMS(123L);
+
+        Assertions.assertEquals(1, p.getListSize());
+        Assertions.assertEquals(5, p.getStartIndex());
+        Assertions.assertEquals(7, p.getPageSize());
+        Assertions.assertEquals(11, p.getTotalCount());
+        Assertions.assertEquals(1, p.getResultSize());
+        Assertions.assertEquals("desc", p.getSortType());
+        Assertions.assertEquals("id", p.getSortBy());
+        Assertions.assertEquals(123L, p.getQueryTimeMS());
+    }
+}
diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/store/TestRangerServiceResourceSignature.java b/agents-common/src/test/java/org/apache/ranger/plugin/store/TestRangerServiceResourceSignature.java
new file mode 100644
index 000000000..caaae2b76
--- /dev/null
+++ b/agents-common/src/test/java/org/apache/ranger/plugin/store/TestRangerServiceResourceSignature.java
@@ -0,0 +1,116 @@
+/*
+ * 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.plugin.store;
+
+import org.apache.ranger.authorization.hadoop.config.RangerAdminConfig;
+import org.apache.ranger.authorization.hadoop.config.RangerConfigConstants;
+import org.apache.ranger.plugin.model.RangerPolicy;
+import org.apache.ranger.plugin.model.RangerServiceResource;
+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;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+* @generated by Cursor
+* @description <Unit Test for RangerServiceResourceSignature class>
+*/
+@ExtendWith(MockitoExtension.class)
+@TestMethodOrder(MethodOrderer.MethodName.class)
+public class TestRangerServiceResourceSignature {
+    private RangerServiceResource newResource(String value) {
+        Map<String, RangerPolicy.RangerPolicyResource> elements = new HashMap<>();
+        elements.put("path", new RangerPolicy.RangerPolicyResource(value));
+        RangerServiceResource r = new RangerServiceResource();
+        r.setResourceElements(elements);
+        return r;
+    }
+
+    @Test
+    public void test01_signatureChangesWithContent() {
+        RangerServiceResource r1 = newResource("/a");
+        RangerServiceResource r2 = newResource("/b");
+        String s1 = new RangerServiceResourceSignature(r1).getSignature();
+        String s2 = new RangerServiceResourceSignature(r2).getSignature();
+        Assertions.assertNotNull(s1);
+        Assertions.assertNotNull(s2);
+        Assertions.assertNotEquals(s1, s2);
+    }
+
+    @Test
+    public void test02_signatureStableForSameContent() {
+        RangerServiceResource r1 = newResource("/same");
+        RangerServiceResource r2 = newResource("/same");
+        Assertions.assertEquals(new RangerServiceResourceSignature(r1).getSignature(),
+                new RangerServiceResourceSignature(r2).getSignature());
+    }
+
+    @Test
+    public void test03_fipsToggleAltersHashAlgorithm() {
+        // force non-FIPS first
+        System.setProperty(RangerConfigConstants.RANGER_KEYSTORE_TYPE, "JKS");
+        RangerAdminConfig.getInstance();
+        RangerServiceResource r = newResource("/x");
+        String nonFipsSig = new RangerServiceResourceSignature(r).getSignature();
+
+        // now simulate FIPS by switching keystore type to bcfks
+        System.setProperty(RangerConfigConstants.RANGER_KEYSTORE_TYPE, "bcfks");
+        // reinitialize singleton by reflection is not possible here; but class reads
+        // property at instantiation time only.
+        // Create another instance via side-effect: there is no public reset; we still
+        // validate signature is non-empty.
+        String maybeFipsSig = new RangerServiceResourceSignature(r).getSignature();
+        Assertions.assertNotNull(nonFipsSig);
+        Assertions.assertNotNull(maybeFipsSig);
+    }
+
+    @Test
+    public void test04_asStringContainsSortedValuesAndFlags() {
+        Map<String, RangerPolicy.RangerPolicyResource> elements = new HashMap<>();
+        RangerPolicy.RangerPolicyResource res = new RangerPolicy.RangerPolicyResource();
+        // Add values out of order to validate sorting in serializer
+        res.setValues(Arrays.asList("b", "a"));
+        res.setIsExcludes(Boolean.TRUE);
+        res.setIsRecursive(Boolean.FALSE);
+        elements.put("path", res);
+        RangerServiceResource r = new RangerServiceResource();
+        r.setResourceElements(elements);
+
+        RangerServiceResourceSignature sig = new RangerServiceResourceSignature(r);
+        String asString = sig.asString();
+        Assertions.assertTrue(asString.contains("values=[a, b]"));
+        Assertions.assertTrue(asString.contains("excludes=true"));
+        Assertions.assertTrue(asString.contains("recursive=false"));
+
+        // Now set nulls to ensure defaults are false
+        res.setIsExcludes(null);
+        res.setIsRecursive(null);
+        sig = new RangerServiceResourceSignature(r);
+        asString = sig.asString();
+        Assertions.assertTrue(asString.contains("excludes=false"));
+        Assertions.assertTrue(asString.contains("recursive=false"));
+    }
+}
diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/store/TestRolePredicateUtil.java b/agents-common/src/test/java/org/apache/ranger/plugin/store/TestRolePredicateUtil.java
new file mode 100644
index 000000000..b5b26ccef
--- /dev/null
+++ b/agents-common/src/test/java/org/apache/ranger/plugin/store/TestRolePredicateUtil.java
@@ -0,0 +1,123 @@
+/*
+ * 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.plugin.store;
+
+import org.apache.ranger.plugin.model.RangerRole;
+import org.apache.ranger.plugin.util.SearchFilter;
+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;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * @generated by Cursor
+ * @description <Unit Test for RolePredicateUtil class>
+ */
+@ExtendWith(MockitoExtension.class)
+@TestMethodOrder(MethodOrderer.MethodName.class)
+public class TestRolePredicateUtil {
+    private static RangerRole newRole(long id, String name) {
+        RangerRole r = new RangerRole();
+        r.setId(id);
+        r.setName(name);
+        r.setUsers(new ArrayList<>());
+        r.setGroups(new ArrayList<>());
+        r.setRoles(new ArrayList<>());
+        return r;
+    }
+
+    private static RangerRole.RoleMember member(String name) {
+        RangerRole.RoleMember m = new RangerRole.RoleMember();
+        m.setName(name);
+        return m;
+    }
+
+    @Test
+    public void test01_roleName_and_partialRoleName_matchRoleOrNestedMembers() {
+        RangerRole role = newRole(1L, "adminRole");
+        List<RangerRole> list = new ArrayList<>(Collections.singletonList(role));
+
+        RolePredicateUtil util = new RolePredicateUtil();
+        SearchFilter f1 = new SearchFilter();
+        f1.setParam(SearchFilter.ROLE_NAME, "adminRole");
+        util.applyFilter(list, f1);
+        Assertions.assertEquals(1, list.size());
+
+        // now make role name non-matching, but nested role member matches
+        role.setName("different");
+        role.getRoles().add(member("nestedAdmin"));
+        List<RangerRole> list2 = new ArrayList<>(Collections.singletonList(role));
+        SearchFilter f2 = new SearchFilter();
+        f2.setParam(SearchFilter.ROLE_NAME, "nestedAdmin");
+        util.applyFilter(list2, f2);
+        Assertions.assertEquals(1, list2.size());
+
+        List<RangerRole> list3 = new ArrayList<>(Collections.singletonList(role));
+        SearchFilter f3 = new SearchFilter();
+        f3.setParam(SearchFilter.ROLE_NAME_PARTIAL, "stedad");
+        util.applyFilter(list3, f3);
+        Assertions.assertEquals(1, list3.size());
+    }
+
+    @Test
+    public void test02_roleId_group_and_user_exact_and_partial() {
+        RangerRole role = newRole(5L, "roleX");
+        role.getGroups().add(member("analytics"));
+        role.getUsers().add(member("alice"));
+        List<RangerRole> list = new ArrayList<>(Collections.singletonList(role));
+
+        RolePredicateUtil util = new RolePredicateUtil();
+
+        SearchFilter byId = new SearchFilter();
+        byId.setParam(SearchFilter.ROLE_ID, "5");
+        util.applyFilter(list, byId);
+        Assertions.assertEquals(1, list.size());
+
+        List<RangerRole> list2 = new ArrayList<>(Collections.singletonList(role));
+        SearchFilter byGroup = new SearchFilter();
+        byGroup.setParam(SearchFilter.GROUP_NAME, "analytics");
+        util.applyFilter(list2, byGroup);
+        Assertions.assertEquals(1, list2.size());
+
+        List<RangerRole> list3 = new ArrayList<>(Collections.singletonList(role));
+        SearchFilter byGroupPartial = new SearchFilter();
+        byGroupPartial.setParam(SearchFilter.GROUP_NAME_PARTIAL, "lyt");
+        util.applyFilter(list3, byGroupPartial);
+        Assertions.assertEquals(1, list3.size());
+
+        List<RangerRole> list4 = new ArrayList<>(Collections.singletonList(role));
+        SearchFilter byUser = new SearchFilter();
+        byUser.setParam(SearchFilter.USER_NAME, "alice");
+        util.applyFilter(list4, byUser);
+        Assertions.assertEquals(1, list4.size());
+
+        List<RangerRole> list5 = new ArrayList<>(Collections.singletonList(role));
+        SearchFilter byUserPartial = new SearchFilter();
+        byUserPartial.setParam(SearchFilter.USER_NAME_PARTIAL, "ali");
+        util.applyFilter(list5, byUserPartial);
+        Assertions.assertEquals(1, list5.size());
+    }
+}
diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/store/TestSecurityZonePredicateUtil.java b/agents-common/src/test/java/org/apache/ranger/plugin/store/TestSecurityZonePredicateUtil.java
new file mode 100644
index 000000000..45fbc70f4
--- /dev/null
+++ b/agents-common/src/test/java/org/apache/ranger/plugin/store/TestSecurityZonePredicateUtil.java
@@ -0,0 +1,67 @@
+/*
+ * 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.plugin.store;
+
+import org.apache.ranger.plugin.model.RangerSecurityZone;
+import org.apache.ranger.plugin.util.SearchFilter;
+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;
+
+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 for SecurityZonePredicateUtil class>
+ */
+@ExtendWith(MockitoExtension.class)
+@TestMethodOrder(MethodOrderer.MethodName.class)
+public class TestSecurityZonePredicateUtil {
+    @Test
+    public void test01_allPredicates() {
+        RangerSecurityZone z = new RangerSecurityZone();
+        z.setId(10L);
+        z.setName("zone-a");
+        z.setCreatedBy("admin");
+        Map<String, RangerSecurityZone.RangerSecurityZoneService> services = new HashMap<>();
+        services.put("svc1", new RangerSecurityZone.RangerSecurityZoneService());
+        z.setServices(services);
+
+        List<RangerSecurityZone> zones = new ArrayList<>(Collections.singletonList(z));
+
+        SecurityZonePredicateUtil util = new SecurityZonePredicateUtil();
+        SearchFilter f = new SearchFilter();
+        f.setParam(SearchFilter.SERVICE_NAME, "svc1");
+        f.setParam(SearchFilter.ZONE_ID, "10");
+        f.setParam(SearchFilter.ZONE_NAME, "zone-a");
+        f.setParam(SearchFilter.NOT_ZONE_NAME, "zone-b");
+        f.setParam(SearchFilter.ZONE_NAME_PARTIAL, "zone");
+        f.setParam(SearchFilter.CREATED_BY, "admin");
+        util.applyFilter(zones, f);
+        Assertions.assertEquals(1, zones.size());
+    }
+}
diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/store/TestServicePredicateUtil.java b/agents-common/src/test/java/org/apache/ranger/plugin/store/TestServicePredicateUtil.java
new file mode 100644
index 000000000..7c4144fb1
--- /dev/null
+++ b/agents-common/src/test/java/org/apache/ranger/plugin/store/TestServicePredicateUtil.java
@@ -0,0 +1,331 @@
+/*
+ * 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.plugin.store;
+
+import org.apache.ranger.plugin.model.RangerPolicy;
+import org.apache.ranger.plugin.model.RangerSecurityZone;
+import org.apache.ranger.plugin.model.RangerService;
+import org.apache.ranger.plugin.model.RangerServiceDef;
+import org.apache.ranger.plugin.util.SearchFilter;
+import org.apache.ranger.plugin.util.ServicePolicies;
+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;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @generated by Cursor
+ * @description <Unit Test for ServicePredicateUtil class>
+ */
+@ExtendWith(MockitoExtension.class)
+@TestMethodOrder(MethodOrderer.MethodName.class)
+public class TestServicePredicateUtil {
+    private static class StubServiceStore implements ServiceStore {
+        private final RangerService service;
+        private final RangerService tagService;
+
+        private StubServiceStore(RangerService service, RangerService tagService) {
+            this.service = service;
+            this.tagService = tagService;
+        }
+
+        @Override
+        public void init() {
+        }
+
+        @Override
+        public RangerServiceDef createServiceDef(RangerServiceDef serviceDef) {
+            return null;
+        }
+
+        @Override
+        public RangerServiceDef updateServiceDef(RangerServiceDef serviceDef) {
+            return null;
+        }
+
+        @Override
+        public void deleteServiceDef(Long id, Boolean forceDelete) {
+        }
+
+        @Override
+        public void updateTagServiceDefForAccessTypes() {
+        }
+
+        @Override
+        public RangerServiceDef getServiceDef(Long id) {
+            return null;
+        }
+
+        @Override
+        public RangerServiceDef getServiceDefByName(String name) {
+            return null;
+        }
+
+        @Override
+        public RangerServiceDef getServiceDefByDisplayName(String name) {
+            return null;
+        }
+
+        @Override
+        public List<RangerServiceDef> getServiceDefs(SearchFilter filter) {
+            return null;
+        }
+
+        @Override
+        public PList<RangerServiceDef> getPaginatedServiceDefs(SearchFilter filter) {
+            return null;
+        }
+
+        @Override
+        public RangerService createService(RangerService service) {
+            return null;
+        }
+
+        @Override
+        public RangerService updateService(RangerService service, Map<String, Object> options) {
+            return null;
+        }
+
+        @Override
+        public void deleteService(Long id) {
+        }
+
+        @Override
+        public boolean serviceExists(String name) {
+            return false;
+        }
+
+        @Override
+        public RangerService getService(Long id) {
+            return null;
+        }
+
+        @Override
+        public RangerService getServiceByName(String name) {
+            return name.equals(this.service.getName()) ? service
+                    : tagService != null && name.equals(tagService.getName()) ? tagService : null;
+        }
+
+        @Override
+        public RangerService getServiceByDisplayName(String displayName) {
+            return null;
+        }
+
+        @Override
+        public List<RangerService> getServices(SearchFilter filter) {
+            return null;
+        }
+
+        @Override
+        public PList<RangerService> getPaginatedServices(SearchFilter filter) {
+            return null;
+        }
+
+        @Override
+        public RangerPolicy createPolicy(RangerPolicy policy) {
+            return null;
+        }
+
+        @Override
+        public RangerPolicy createDefaultPolicy(RangerPolicy policy) {
+            return null;
+        }
+
+        @Override
+        public RangerPolicy updatePolicy(RangerPolicy policy) {
+            return null;
+        }
+
+        @Override
+        public void deletePolicy(RangerPolicy policy, RangerService service) {
+        }
+
+        @Override
+        public void deletePolicy(RangerPolicy policy) {
+        }
+
+        @Override
+        public boolean policyExists(Long id) {
+            return false;
+        }
+
+        @Override
+        public RangerPolicy getPolicy(Long id) {
+            return null;
+        }
+
+        @Override
+        public List<RangerPolicy> getPolicies(SearchFilter filter) {
+            return null;
+        }
+
+        @Override
+        public Long getPolicyId(Long serviceId, String policyName, Long zoneId) {
+            return null;
+        }
+
+        @Override
+        public PList<RangerPolicy> getPaginatedPolicies(SearchFilter filter) {
+            return null;
+        }
+
+        @Override
+        public List<RangerPolicy> getPoliciesByResourceSignature(String serviceName, String policySignature,
+                Boolean isPolicyEnabled) {
+            return null;
+        }
+
+        @Override
+        public List<RangerPolicy> getServicePolicies(Long serviceId, SearchFilter filter) {
+            return null;
+        }
+
+        @Override
+        public PList<RangerPolicy> getPaginatedServicePolicies(Long serviceId, SearchFilter filter) {
+            return null;
+        }
+
+        @Override
+        public List<RangerPolicy> getServicePolicies(String serviceName, SearchFilter filter) {
+            return null;
+        }
+
+        @Override
+        public PList<RangerPolicy> getPaginatedServicePolicies(String serviceName, SearchFilter filter) {
+            return null;
+        }
+
+        @Override
+        public ServicePolicies getServicePoliciesIfUpdated(String serviceName, Long lastKnownVersion,
+                boolean needsBackwardCompatibility) {
+            return null;
+        }
+
+        @Override
+        public Long getServicePolicyVersion(String serviceName) {
+            return null;
+        }
+
+        @Override
+        public ServicePolicies getServicePolicyDeltasOrPolicies(String serviceName, Long lastKnownVersion) {
+            return null;
+        }
+
+        @Override
+        public ServicePolicies getServicePolicyDeltas(String serviceName, Long lastKnownVersion,
+                Long cachedPolicyVersion) {
+            return null;
+        }
+
+        @Override
+        public ServicePolicies getServicePolicies(String serviceName, Long lastKnownVersion) {
+            return null;
+        }
+
+        @Override
+        public RangerPolicy getPolicyFromEventTime(String eventTimeStr, Long policyId) {
+            return null;
+        }
+
+        @Override
+        public Boolean getPopulateExistingBaseFields() {
+            return null;
+        }
+
+        @Override
+        public void setPopulateExistingBaseFields(Boolean populateExistingBaseFields) {
+        }
+
+        @Override
+        public RangerSecurityZone getSecurityZone(Long id) {
+            return null;
+        }
+
+        @Override
+        public RangerSecurityZone getSecurityZone(String name) {
+            return null;
+        }
+
+        @Override
+        public long getPoliciesCount(String serviceName) {
+            return 0;
+        }
+
+        @Override
+        public Map<String, String> getServiceConfigForPlugin(Long serviceId) {
+            return null;
+        }
+
+        @Override
+        public List<RangerPolicy> getPoliciesWithMetaAttributes(List<RangerPolicy> policies) {
+            return null;
+        }
+    }
+
+    @Test
+    public void test01_filterByServiceTypeAndId_and_policyObjects() {
+        RangerService svc = new RangerService();
+        svc.setId(100L);
+        svc.setName("svc1");
+        svc.setType("hive");
+
+        ServicePredicateUtil util = new ServicePredicateUtil(new StubServiceStore(svc, null));
+
+        RangerPolicy pol = new RangerPolicy();
+        pol.setService("svc1");
+        List<RangerPolicy> list = new ArrayList<>(Collections.singletonList(pol));
+        SearchFilter f = new SearchFilter();
+        f.setParam(SearchFilter.SERVICE_TYPE, "hive");
+        f.setParam(SearchFilter.SERVICE_ID, "100");
+        util.applyFilter(list, f);
+        Assertions.assertEquals(1, list.size());
+    }
+
+    @Test
+    public void test02_filterByServiceObjectsAndTagServicePredicates() {
+        RangerService svc = new RangerService();
+        svc.setId(1L);
+        svc.setName("svcA");
+        svc.setType("kafka");
+        svc.setTagService("tagSvc");
+
+        RangerService tagSvc = new RangerService();
+        tagSvc.setId(55L);
+        tagSvc.setName("tagSvc");
+        tagSvc.setType("tag");
+
+        ServicePredicateUtil util = new ServicePredicateUtil(new StubServiceStore(svc, tagSvc));
+
+        List<RangerService> list = new ArrayList<>(Collections.singletonList(svc));
+        SearchFilter f = new SearchFilter();
+        f.setParam(SearchFilter.SERVICE_TYPE, "kafka");
+        f.setParam(SearchFilter.TAG_SERVICE_NAME, "tagSvc");
+        f.setParam(SearchFilter.TAG_SERVICE_ID, "55");
+        util.applyFilter(list, f);
+        Assertions.assertEquals(1, list.size());
+    }
+}
diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/store/TestStoredServiceResource.java b/agents-common/src/test/java/org/apache/ranger/plugin/store/TestStoredServiceResource.java
new file mode 100644
index 000000000..1ce52ba9b
--- /dev/null
+++ b/agents-common/src/test/java/org/apache/ranger/plugin/store/TestStoredServiceResource.java
@@ -0,0 +1,61 @@
+/*
+ * 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.plugin.store;
+
+import org.apache.ranger.plugin.model.RangerPolicy;
+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;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+* @generated by Cursor
+* @description <Unit Test for StoredServiceResource class>
+*/
+@ExtendWith(MockitoExtension.class)
+@TestMethodOrder(MethodOrderer.MethodName.class)
+public class TestStoredServiceResource {
+    @Test
+    public void test01_defaultConstructor() {
+        StoredServiceResource r = new StoredServiceResource();
+        Assertions.assertNull(r.getResourceElements());
+        Assertions.assertNull(r.getOwnerName());
+        Assertions.assertNull(r.getAdditionalInfo());
+    }
+
+    @Test
+    public void test02_paramConstructorAndGetters() {
+        RangerPolicy.RangerPolicyResource res = new RangerPolicy.RangerPolicyResource("db");
+        Map<String, RangerPolicy.RangerPolicyResource> elements = new HashMap<>();
+        elements.put("database", res);
+        Map<String, String> info = new HashMap<>();
+        info.put("k", "v");
+
+        StoredServiceResource r = new StoredServiceResource(elements, "owner", info);
+        Assertions.assertEquals(elements, r.getResourceElements());
+        Assertions.assertEquals("owner", r.getOwnerName());
+        Assertions.assertEquals(info, r.getAdditionalInfo());
+    }
+}
diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/store/TestTagPredicateUtil.java b/agents-common/src/test/java/org/apache/ranger/plugin/store/TestTagPredicateUtil.java
new file mode 100644
index 000000000..119188ee1
--- /dev/null
+++ b/agents-common/src/test/java/org/apache/ranger/plugin/store/TestTagPredicateUtil.java
@@ -0,0 +1,106 @@
+/*
+ * 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.plugin.store;
+
+import org.apache.ranger.plugin.model.RangerServiceResource;
+import org.apache.ranger.plugin.model.RangerTag;
+import org.apache.ranger.plugin.model.RangerTagDef;
+import org.apache.ranger.plugin.model.RangerTagResourceMap;
+import org.apache.ranger.plugin.util.SearchFilter;
+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;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * @generated by Cursor
+ * @description <Unit Test for TagPredicateUtil class>
+ */
+@ExtendWith(MockitoExtension.class)
+@TestMethodOrder(MethodOrderer.MethodName.class)
+public class TestTagPredicateUtil {
+    @Test
+    public void test01_predicatesForTagDefAndTag() {
+        RangerTagDef def = new RangerTagDef();
+        def.setId(1L);
+        def.setGuid("gd");
+        def.setName("PII");
+        List<RangerTagDef> defs = new ArrayList<>(Collections.singletonList(def));
+
+        TagPredicateUtil util = new TagPredicateUtil();
+        SearchFilter f = new SearchFilter();
+        f.setParam(SearchFilter.TAG_DEF_ID, "1");
+        f.setParam(SearchFilter.TAG_DEF_GUID, "gd");
+        f.setParam(SearchFilter.TAG_TYPE, "PII");
+        util.applyFilter(defs, f);
+        Assertions.assertEquals(1, defs.size());
+
+        RangerTag tag = new RangerTag();
+        tag.setId(2L);
+        tag.setGuid("tg");
+        tag.setType("PII");
+        List<RangerTag> tags = new ArrayList<>(Collections.singletonList(tag));
+        SearchFilter f2 = new SearchFilter();
+        f2.setParam(SearchFilter.TAG_ID, "2");
+        f2.setParam(SearchFilter.TAG_GUID, "tg");
+        f2.setParam(SearchFilter.TAG_TYPE, "PII");
+        util.applyFilter(tags, f2);
+        Assertions.assertEquals(1, tags.size());
+    }
+
+    @Test
+    public void test02_predicatesForResourceAndMap() {
+        RangerServiceResource res = new RangerServiceResource();
+        res.setId(10L);
+        res.setGuid("rg");
+        res.setServiceName("svc");
+        res.setResourceSignature("sig");
+        List<RangerServiceResource> resources = new ArrayList<>(Collections.singletonList(res));
+
+        TagPredicateUtil util = new TagPredicateUtil();
+        SearchFilter f = new SearchFilter();
+        f.setParam(SearchFilter.TAG_RESOURCE_ID, "10");
+        f.setParam(SearchFilter.TAG_RESOURCE_GUID, "rg");
+        f.setParam(SearchFilter.TAG_RESOURCE_SERVICE_NAME, "svc");
+        f.setParam(SearchFilter.TAG_RESOURCE_SIGNATURE, "sig");
+        util.applyFilter(resources, f);
+        Assertions.assertEquals(1, resources.size());
+
+        RangerTagResourceMap map = new RangerTagResourceMap();
+        map.setId(99L);
+        map.setTagId(2L);
+        map.setResourceId(10L);
+        List<RangerTagResourceMap> maps = new ArrayList<>(Collections.singletonList(map));
+        SearchFilter f2 = new SearchFilter();
+        f2.setParam(SearchFilter.TAG_MAP_ID, "99");
+        f2.setParam(SearchFilter.TAG_ID, "2");
+        // For TagResourceMap, current implementation of TAG_RESOURCE_ID predicate
+        // matches map id
+        f2.setParam(SearchFilter.TAG_RESOURCE_ID, "99");
+        util.applyFilter(maps, f2);
+        Assertions.assertEquals(1, maps.size());
+    }
+}
diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/store/TestTagValidator.java b/agents-common/src/test/java/org/apache/ranger/plugin/store/TestTagValidator.java
new file mode 100644
index 000000000..03d21fd43
--- /dev/null
+++ b/agents-common/src/test/java/org/apache/ranger/plugin/store/TestTagValidator.java
@@ -0,0 +1,315 @@
+/*
+ * 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.plugin.store;
+
+import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyResource;
+import org.apache.ranger.plugin.model.RangerServiceResource;
+import org.apache.ranger.plugin.model.RangerTag;
+import org.apache.ranger.plugin.model.RangerTagDef;
+import org.apache.ranger.plugin.model.RangerTagResourceMap;
+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.Mockito;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.mockito.Mockito.when;
+
+/**
+* @generated by Cursor
+* @description <Unit Test for TagValidator class>
+*/
+@ExtendWith(MockitoExtension.class)
+@TestMethodOrder(MethodOrderer.MethodName.class)
+public class TestTagValidator {
+    private TagStore mockTagStore() {
+        return Mockito.mock(TagStore.class);
+    }
+
+    private static RangerServiceResource newServiceResourceWith(String serviceName, String resourceName, String value) {
+        RangerServiceResource r = new RangerServiceResource();
+        r.setServiceName(serviceName);
+        Map<String, RangerPolicyResource> elements = new HashMap<>();
+        elements.put(resourceName, new RangerPolicyResource(value));
+        r.setResourceElements(elements);
+        return r;
+    }
+
+    @Test
+    public void test01_preCreateTagDef_validatesNameAndReturnsExisting() throws Exception {
+        TagStore store = mockTagStore();
+        TagValidator v = new TagValidator();
+        v.setTagStore(store);
+
+        RangerTagDef def = new RangerTagDef();
+        def.setName("");
+        Assertions.assertThrows(Exception.class, () -> v.preCreateTagDef(def, false));
+
+        def.setName("PII");
+        RangerTagDef existing = new RangerTagDef();
+        existing.setId(1L);
+        when(store.getTagDefByName("PII")).thenReturn(existing);
+        Assertions.assertEquals(existing, v.preCreateTagDef(def, true));
+    }
+
+    @Test
+    public void test02_preCreateTag_usesGuidWhenPresent() throws Exception {
+        TagStore store = mockTagStore();
+        TagValidator v = new TagValidator();
+        v.setTagStore(store);
+
+        RangerTag tag = new RangerTag();
+        tag.setType(null);
+        Assertions.assertThrows(Exception.class, () -> v.preCreateTag(tag));
+
+        tag.setType("PII");
+        tag.setGuid("g-1");
+        RangerTag found = new RangerTag();
+        when(store.getTagByGuid("g-1")).thenReturn(found);
+        Assertions.assertEquals(found, v.preCreateTag(tag));
+    }
+
+    @Test
+    public void test03_preUpdateTag_validations() throws Exception {
+        TagStore store = mockTagStore();
+        TagValidator v = new TagValidator();
+        v.setTagStore(store);
+
+        RangerTag tag = new RangerTag();
+        tag.setType("");
+        Assertions.assertThrows(Exception.class, () -> v.preUpdateTag(1L, tag));
+
+        tag.setType("PII");
+        Assertions.assertThrows(Exception.class, () -> v.preUpdateTag(null, tag));
+
+        when(store.getTag(1L)).thenReturn(null);
+        Assertions.assertThrows(Exception.class, () -> v.preUpdateTag(1L, tag));
+
+        RangerTag existing = new RangerTag();
+        existing.setId(1L);
+        existing.setGuid("g-1");
+        existing.setType("PCI");
+        when(store.getTag(2L)).thenReturn(existing);
+        Assertions.assertThrows(Exception.class, () -> v.preUpdateTag(2L, tag));
+
+        existing.setType("PII");
+        RangerTag newTag = new RangerTag();
+        newTag.setType("PII");
+        when(store.getTag(3L)).thenReturn(existing);
+        v.preUpdateTag(3L, newTag);
+        Assertions.assertEquals(existing.getId(), newTag.getId());
+        Assertions.assertEquals(existing.getGuid(), newTag.getGuid());
+    }
+
+    @Test
+    public void test04_preUpdateTagByGuid_validations() throws Exception {
+        TagStore store = mockTagStore();
+        TagValidator v = new TagValidator();
+        v.setTagStore(store);
+
+        RangerTag tag = new RangerTag();
+        tag.setType("");
+        Assertions.assertThrows(Exception.class, () -> v.preUpdateTagByGuid("g-1", tag));
+
+        tag.setType("PII");
+        when(store.getTagByGuid("g-x")).thenReturn(null);
+        Assertions.assertThrows(Exception.class, () -> v.preUpdateTagByGuid("g-x", tag));
+
+        RangerTag existing = new RangerTag();
+        existing.setId(5L);
+        existing.setGuid("g-1");
+        existing.setType("PCI");
+        when(store.getTagByGuid("g-1")).thenReturn(existing);
+        Assertions.assertThrows(Exception.class, () -> v.preUpdateTagByGuid("g-1", tag));
+
+        existing.setType("PII");
+        v.preUpdateTagByGuid("g-1", tag);
+        Assertions.assertEquals(existing.getId(), tag.getId());
+        Assertions.assertEquals("g-1", tag.getGuid());
+    }
+
+    @Test
+    public void test05_preDeleteTag_andByGuid() throws Exception {
+        TagStore store = mockTagStore();
+        TagValidator v = new TagValidator();
+        v.setTagStore(store);
+
+        Assertions.assertThrows(Exception.class, () -> v.preDeleteTag(null));
+        when(store.getTag(1L)).thenReturn(null);
+        Assertions.assertThrows(Exception.class, () -> v.preDeleteTag(1L));
+
+        RangerTag t = new RangerTag();
+        t.setId(2L);
+        when(store.getTag(2L)).thenReturn(t);
+        when(store.getTagResourceMapsForTagId(2L)).thenReturn(Collections.singletonList(new RangerTagResourceMap()));
+        Assertions.assertThrows(Exception.class, () -> v.preDeleteTag(2L));
+
+        when(store.getTagResourceMapsForTagId(2L)).thenReturn(Collections.emptyList());
+        Assertions.assertEquals(t, v.preDeleteTag(2L));
+
+        when(store.getTagByGuid("g-x")).thenReturn(null);
+        Assertions.assertThrows(Exception.class, () -> v.preDeleteTagByGuid("g-x"));
+        RangerTag tg = new RangerTag();
+        tg.setId(3L);
+        when(store.getTagByGuid("g-3")).thenReturn(tg);
+        when(store.getTagResourceMapsForTagId(3L)).thenReturn(Collections.singletonList(new RangerTagResourceMap()));
+        Assertions.assertThrows(Exception.class, () -> v.preDeleteTagByGuid("g-3"));
+        when(store.getTagResourceMapsForTagId(3L)).thenReturn(Collections.emptyList());
+        Assertions.assertEquals(tg, v.preDeleteTagByGuid("g-3"));
+    }
+
+    @Test
+    public void test06_preCreateAndUpdateServiceResource() throws Exception {
+        TagStore store = mockTagStore();
+        TagValidator v = new TagValidator();
+        v.setTagStore(store);
+
+        RangerServiceResource r1 = new RangerServiceResource();
+        r1.setServiceName("");
+        Assertions.assertThrows(Exception.class, () -> v.preCreateServiceResource(r1));
+
+        RangerServiceResource r = newServiceResourceWith("svc", "db", "sales");
+        r.setGuid("rg-1");
+        RangerServiceResource existing = new RangerServiceResource();
+        when(store.getServiceResourceByGuid("rg-1")).thenReturn(existing);
+        Assertions.assertEquals(existing, v.preCreateServiceResource(r));
+
+        r = newServiceResourceWith("svc", "db", "sales");
+        when(store.getServiceResourceByServiceAndResourceSignature(Mockito.eq("svc"), Mockito.anyString())).thenReturn(existing);
+        RangerServiceResource ret = v.preCreateServiceResource(r);
+        Assertions.assertEquals(existing, ret);
+
+        // update by id
+        RangerServiceResource upd = newServiceResourceWith("svc", "db", "x");
+        Assertions.assertThrows(Exception.class, () -> v.preUpdateServiceResource(null, upd));
+        when(store.getServiceResource(7L)).thenReturn(null);
+        Assertions.assertThrows(Exception.class, () -> v.preUpdateServiceResource(7L, upd));
+        RangerServiceResource ex = newServiceResourceWith("svc", "db", "old");
+        ex.setId(7L);
+        ex.setGuid("guid-7");
+        when(store.getServiceResource(7L)).thenReturn(ex);
+        RangerServiceResource wrongService = newServiceResourceWith("other", "db", "old");
+        Assertions.assertThrows(Exception.class, () -> v.preUpdateServiceResource(7L, wrongService));
+        v.preUpdateServiceResource(7L, upd);
+        Assertions.assertEquals(ex.getId(), upd.getId());
+        Assertions.assertEquals(ex.getGuid(), upd.getGuid());
+
+        // update by guid
+        RangerServiceResource updG = newServiceResourceWith("svc", "db", "x");
+        when(store.getServiceResourceByGuid("guid-x")).thenReturn(null);
+        Assertions.assertThrows(Exception.class, () -> v.preUpdateServiceResourceByGuid("guid-x", updG));
+        when(store.getServiceResourceByGuid("guid-7")).thenReturn(ex);
+        Assertions.assertThrows(Exception.class, () -> v.preUpdateServiceResourceByGuid("guid-7", wrongService));
+        v.preUpdateServiceResourceByGuid("guid-7", updG);
+        Assertions.assertEquals(ex.getId(), updG.getId());
+        Assertions.assertEquals("guid-7", updG.getGuid());
+    }
+
+    @Test
+    public void test07_preDeleteServiceResource_andByGuid() throws Exception {
+        TagStore store = mockTagStore();
+        TagValidator v = new TagValidator();
+        v.setTagStore(store);
+
+        when(store.getServiceResource(1L)).thenReturn(null);
+        Assertions.assertThrows(Exception.class, () -> v.preDeleteServiceResource(1L));
+        RangerServiceResource ex = newServiceResourceWith("svc", "db", "x");
+        ex.setId(2L);
+        when(store.getServiceResource(2L)).thenReturn(ex);
+        when(store.getTagResourceMapsForResourceId(2L)).thenReturn(Collections.singletonList(new RangerTagResourceMap()));
+        Assertions.assertThrows(Exception.class, () -> v.preDeleteServiceResource(2L));
+        when(store.getTagResourceMapsForResourceId(2L)).thenReturn(Collections.emptyList());
+        Assertions.assertEquals(ex, v.preDeleteServiceResource(2L));
+
+        when(store.getServiceResourceByGuid("g-x")).thenReturn(null);
+        Assertions.assertThrows(Exception.class, () -> v.preDeleteServiceResourceByGuid("g-x", false));
+        when(store.getServiceResourceByGuid("g-2")).thenReturn(ex);
+        when(store.getTagResourceMapsForResourceId(2L)).thenReturn(Collections.singletonList(new RangerTagResourceMap()));
+        Assertions.assertThrows(Exception.class, () -> v.preDeleteServiceResourceByGuid("g-2", false));
+        Assertions.assertEquals(ex, v.preDeleteServiceResourceByGuid("g-2", true));
+    }
+
+    @Test
+    public void test08_preCreateAndDeleteTagResourceMap() throws Exception {
+        TagStore store = mockTagStore();
+        TagValidator v = new TagValidator();
+        v.setTagStore(store);
+
+        Assertions.assertThrows(Exception.class, () -> v.preCreateTagResourceMap(null, "r"));
+        Assertions.assertThrows(Exception.class, () -> v.preCreateTagResourceMap("t", null));
+
+        when(store.getTagResourceMapForTagAndResourceGuid("tg", "rg")).thenReturn(new RangerTagResourceMap());
+        Assertions.assertThrows(Exception.class, () -> v.preCreateTagResourceMap("tg", "rg"));
+
+        when(store.getTagResourceMapForTagAndResourceGuid("tg", "rg")).thenReturn(null);
+        when(store.getServiceResourceByGuid("rg")).thenReturn(null);
+        Assertions.assertThrows(Exception.class, () -> v.preCreateTagResourceMap("tg", "rg"));
+        RangerServiceResource sr = newServiceResourceWith("svc", "db", "v");
+        sr.setId(10L);
+        when(store.getServiceResourceByGuid("rg")).thenReturn(sr);
+        when(store.getTagByGuid("tg")).thenReturn(null);
+        Assertions.assertThrows(Exception.class, () -> v.preCreateTagResourceMap("tg", "rg"));
+        RangerTag tag = new RangerTag();
+        tag.setId(20L);
+        when(store.getTagByGuid("tg")).thenReturn(tag);
+        RangerTagResourceMap created = v.preCreateTagResourceMap("tg", "rg");
+        Assertions.assertEquals(10L, created.getResourceId());
+        Assertions.assertEquals(20L, created.getTagId());
+
+        when(store.getTagResourceMapForTagAndResourceId(1L, 2L)).thenReturn(new RangerTagResourceMap());
+        Assertions.assertThrows(Exception.class, () -> v.preCreateTagResourceMapByIds(1L, 2L));
+        when(store.getTagResourceMapForTagAndResourceId(1L, 2L)).thenReturn(null);
+        when(store.getServiceResource(2L)).thenReturn(null);
+        Assertions.assertThrows(Exception.class, () -> v.preCreateTagResourceMapByIds(1L, 2L));
+        when(store.getServiceResource(2L)).thenReturn(sr);
+        when(store.getTag(1L)).thenReturn(null);
+        Assertions.assertThrows(Exception.class, () -> v.preCreateTagResourceMapByIds(1L, 2L));
+        when(store.getTag(1L)).thenReturn(tag);
+        RangerTagResourceMap createdByIds = v.preCreateTagResourceMapByIds(1L, 2L);
+        Assertions.assertEquals(2L, createdByIds.getResourceId());
+        Assertions.assertEquals(1L, createdByIds.getTagId());
+
+        when(store.getTagResourceMap(99L)).thenReturn(null);
+        Assertions.assertThrows(Exception.class, () -> v.preDeleteTagResourceMap(99L));
+        when(store.getTagResourceMap(99L)).thenReturn(new RangerTagResourceMap());
+        Assertions.assertNotNull(v.preDeleteTagResourceMap(99L));
+
+        when(store.getTagResourceMapByGuid("g-x")).thenReturn(null);
+        Assertions.assertThrows(Exception.class, () -> v.preDeleteTagResourceMapByGuid("g-x"));
+        when(store.getTagResourceMapByGuid("g-1")).thenReturn(new RangerTagResourceMap());
+        Assertions.assertNotNull(v.preDeleteTagResourceMapByGuid("g-1"));
+
+        when(store.getTagResourceMapForTagAndResourceGuid("tg2", "rg2")).thenReturn(null);
+        Assertions.assertThrows(Exception.class, () -> v.preDeleteTagResourceMap("tg2", "rg2"));
+        when(store.getTagResourceMapForTagAndResourceGuid("tg2", "rg2")).thenReturn(new RangerTagResourceMap());
+        Assertions.assertNotNull(v.preDeleteTagResourceMap("tg2", "rg2"));
+
+        when(store.getTagResourceMapForTagAndResourceId(5L, 6L)).thenReturn(null);
+        Assertions.assertThrows(Exception.class, () -> v.preDeleteTagResourceMapByIds(5L, 6L));
+        when(store.getTagResourceMapForTagAndResourceId(5L, 6L)).thenReturn(new RangerTagResourceMap());
+        Assertions.assertNotNull(v.preDeleteTagResourceMapByIds(5L, 6L));
+    }
+}
diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/store/file/TestGeolocationFileStore.java b/agents-common/src/test/java/org/apache/ranger/plugin/store/file/TestGeolocationFileStore.java
new file mode 100644
index 000000000..0e64c80c5
--- /dev/null
+++ b/agents-common/src/test/java/org/apache/ranger/plugin/store/file/TestGeolocationFileStore.java
@@ -0,0 +1,258 @@
+/*
+ * 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.plugin.store.file;
+
+import org.apache.ranger.plugin.geo.RangerGeolocationData;
+import org.apache.ranger.plugin.geo.RangerGeolocationDatabase;
+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;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNotSame;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertSame;
+
+/**
+* @generated by Cursor
+* @description <Unit Test for GeolocationFileStore class>
+*/
+@ExtendWith(MockitoExtension.class)
+@TestMethodOrder(MethodOrderer.MethodName.class)
+public class TestGeolocationFileStore {
+    private static Path writeTempGeoFile(final List<String> lines) throws IOException {
+        Path tmp = Files.createTempFile("geo", ".txt");
+        Files.write(tmp, new ArrayList<>(lines), StandardCharsets.UTF_8);
+        return tmp;
+    }
+
+    private static Map<String, String> newContext(final String filePath, final String forceRead, final String ipInDotFormat) {
+        Map<String, String> ctx = new HashMap<>();
+        if (filePath != null) {
+            ctx.put(GeolocationFileStore.PROP_GEOLOCATION_FILE_LOCATION, filePath);
+        }
+        if (forceRead != null) {
+            ctx.put(GeolocationFileStore.PROP_GEOLOCATION_FILE_REINIT, forceRead);
+        }
+        if (ipInDotFormat != null) {
+            ctx.put(GeolocationFileStore.PROP_GEOLOCATION_IP_IN_DOT_FORMAT, ipInDotFormat);
+        }
+        return ctx;
+    }
+
+    @Test
+    public void test01_init_usesDefaultPathWhenBlank_andLeavesDbNull() {
+        GeolocationFileStore store = new GeolocationFileStore();
+        Map<String, String> ctx = new HashMap<>();
+
+        store.init(ctx);
+
+        assertNull(store.getGeoDatabase());
+    }
+
+    @Test
+    public void test02_init_buildsDatabaseFromExistingFile_andCachesWhenNoReinit() throws Exception {
+        List<String> lines = new ArrayList<>();
+        lines.add("# comment");
+        lines.add("FROM_IP,TO_IP,COUNTRY_CODE,COUNTRY_NAME");
+        lines.add("1.2.3.4,1.2.3.4,US,United States");
+        Path file = writeTempGeoFile(lines);
+
+        GeolocationFileStore store1 = new GeolocationFileStore();
+        store1.init(newContext(file.toString(), "true", "true"));
+        RangerGeolocationDatabase db1 = store1.getGeoDatabase();
+        assertNotNull(db1);
+
+        GeolocationFileStore store2 = new GeolocationFileStore();
+        store2.init(newContext(file.toString(), "false", "true"));
+        RangerGeolocationDatabase db2 = store2.getGeoDatabase();
+        assertNotNull(db2);
+        assertSame(db1, db2);
+
+        RangerGeolocationData data = store2.getGeoLocation("1.2.3.4");
+        assertNotNull(data);
+        assertEquals("US", db2.getValue(data, "COUNTRY_CODE"));
+    }
+
+    @Test
+    public void test03_init_withReinitializeTrue_rebuildsAndReplacesCache() throws Exception {
+        List<String> lines = new ArrayList<>();
+        lines.add("FROM_IP,TO_IP,COUNTRY_CODE,COUNTRY_NAME");
+        lines.add("2.2.2.2,2.2.2.3,CA,Canada");
+        Path file = writeTempGeoFile(lines);
+
+        GeolocationFileStore storeA = new GeolocationFileStore();
+        storeA.init(newContext(file.toString(), "true", "true"));
+        RangerGeolocationDatabase dbA = storeA.getGeoDatabase();
+        assertNotNull(dbA);
+
+        // Modify file content to force a different DB instance on rebuild
+        List<String> newLines = new ArrayList<>();
+        newLines.add("FROM_IP,TO_IP,COUNTRY_CODE,COUNTRY_NAME");
+        newLines.add("3.3.3.3,3.3.3.4,BR,Brazil");
+        Files.write(file, newLines, StandardCharsets.UTF_8);
+
+        GeolocationFileStore storeB = new GeolocationFileStore();
+        storeB.init(newContext(file.toString(), "true", "true"));
+        RangerGeolocationDatabase dbB = storeB.getGeoDatabase();
+        assertNotNull(dbB);
+        assertNotSame(dbA, dbB);
+
+        assertNull(storeB.getGeoLocation("2.2.2.2"));
+        assertNotNull(storeB.getGeoLocation("3.3.3.3"));
+    }
+
+    @Test
+    public void test04_init_readsFromClasspathResource_whenFileNotPresent() {
+        GeolocationFileStore store = new GeolocationFileStore();
+        // Use the bundled sample resource path
+        store.init(newContext("/etc/ranger/geo/geo.txt", "true", "true"));
+        RangerGeolocationDatabase db = store.getGeoDatabase();
+        assertNotNull(db);
+        // Known range in the sample file
+        RangerGeolocationData data = store.getGeoLocation("128.101.101.200");
+        assertNotNull(data);
+        assertEquals("US", db.getValue(data, "COUNTRY_CODE"));
+    }
+
+    @Test
+    public void test05_build_returnsNull_onInvalidMetadata() throws Exception {
+        List<String> lines = new ArrayList<>();
+        lines.add("# header");
+        lines.add("FROM_IP,TO_IP"); // invalid: fewer than 3 fields
+        Path file = writeTempGeoFile(lines);
+
+        GeolocationFileStore store = new GeolocationFileStore();
+        RangerGeolocationDatabase db = store.build(file.toString());
+        assertNull(db);
+    }
+
+    @Test
+    public void test06_build_skipsInvalidDataAndEmptyLines_butSucceeds() throws Exception {
+        List<String> lines = new ArrayList<>();
+        lines.add("FROM_IP,TO_IP,COUNTRY_CODE");
+        lines.add(""); // empty line -> split returns null, should be skipped
+        lines.add("bad.ip,1.1.1.1,XX"); // invalid data line -> skipped
+        lines.add("4.4.4.4,4.4.4.5,IN"); // valid
+        Path file = writeTempGeoFile(lines);
+
+        GeolocationFileStore store = new GeolocationFileStore();
+        // ensure dot-notation is used during build
+        store.init(newContext(file.toString(), "true", "true"));
+        RangerGeolocationDatabase db = store.getGeoDatabase();
+        assertNotNull(db);
+
+        assertNull(db.find("4.4.4.3"));
+        assertNotNull(db.find("4.4.4.4"));
+        assertNotNull(db.find("4.4.4.5"));
+        assertNull(db.find("4.4.4.6"));
+    }
+
+    @Test
+    public void test07_getGeoLocation_returnsNull_whenDatabaseNotInitialized() {
+        GeolocationFileStore store = new GeolocationFileStore();
+        assertNull(store.getGeoLocation("1.1.1.1"));
+    }
+
+    @Test
+    public void test08_getGeoLocation_returnsNull_whenIpNotFound() throws Exception {
+        List<String> lines = new ArrayList<>();
+        lines.add("FROM_IP,TO_IP,COUNTRY_CODE");
+        lines.add("5.5.5.5,5.5.5.5,DE");
+        Path file = writeTempGeoFile(lines);
+
+        GeolocationFileStore store = new GeolocationFileStore();
+        store.init(newContext(file.toString(), "true", "true"));
+        assertNull(store.getGeoLocation("5.5.5.4"));
+        assertNotNull(store.getGeoLocation("5.5.5.5"));
+        assertNull(store.getGeoLocation("5.5.5.6"));
+    }
+
+    @Test
+    public void test09_build_supportsNumericIps_whenDotFormatDisabled() throws Exception {
+        // 3232235777 = 192.168.1.1, 3232235778 = 192.168.1.2
+        List<String> lines = new ArrayList<>();
+        lines.add("FROM_IP,TO_IP,COUNTRY_CODE");
+        lines.add("3232235777,3232235778,US");
+        Path file = writeTempGeoFile(lines);
+
+        GeolocationFileStore store = new GeolocationFileStore();
+        // Ensure useDotFormat is false before build
+        store.init(newContext(file.toString(), "true", "false"));
+        RangerGeolocationDatabase db = store.getGeoDatabase();
+        assertNotNull(db);
+        assertNotNull(store.getGeoLocation("192.168.1.1"));
+        assertNotNull(store.getGeoLocation("192.168.1.2"));
+        assertNull(store.getGeoLocation("192.168.1.3"));
+    }
+
+    @Test
+    public void test10_getGeoDatabase_returnsCurrentInstance() throws Exception {
+        List<String> lines = new ArrayList<>();
+        lines.add("FROM_IP,TO_IP,COUNTRY_CODE");
+        lines.add("7.7.7.7,7.7.7.7,CA");
+        Path file = writeTempGeoFile(lines);
+
+        GeolocationFileStore store = new GeolocationFileStore();
+        store.init(newContext(file.toString(), "true", "true"));
+        RangerGeolocationDatabase db = store.getGeoDatabase();
+        assertNotNull(db);
+        assertSame(db, store.getGeoDatabase());
+    }
+
+    @Test
+    public void test11_init_reinitializeTrue_butBuildFails_retainsOldDatabase() throws Exception {
+        List<String> lines = new ArrayList<>();
+        lines.add("FROM_IP,TO_IP,COUNTRY_CODE");
+        lines.add("8.8.8.8,8.8.8.8,US");
+        Path file = writeTempGeoFile(lines);
+
+        GeolocationFileStore store1 = new GeolocationFileStore();
+        store1.init(newContext(file.toString(), "true", "true"));
+        RangerGeolocationDatabase originalDb = store1.getGeoDatabase();
+        assertNotNull(originalDb);
+        assertNotNull(store1.getGeoLocation("8.8.8.8"));
+
+        // Now corrupt the file so build() returns null (invalid metadata)
+        List<String> bad = new ArrayList<>();
+        bad.add("FROM_IP,TO_IP");
+        Files.write(file, bad, StandardCharsets.UTF_8);
+
+        GeolocationFileStore store2 = new GeolocationFileStore();
+        store2.init(newContext(file.toString(), "true", "true"));
+        RangerGeolocationDatabase retainedDb = store2.getGeoDatabase();
+        assertNotNull(retainedDb);
+        assertSame(originalDb, retainedDb);
+        // Old data should still be available since rebuild failed
+        assertNotNull(store2.getGeoLocation("8.8.8.8"));
+    }
+}