From b40250a2695e77dd94f6f1ee006c6b544dd41370 Mon Sep 17 00:00:00 2001 From: Bhavesh Amre Date: Tue, 26 May 2026 16:19:41 +0530 Subject: [PATCH] RANGER-5584:Add unit test cases for plugin-trino Module --- .../trino/TestRangerServiceTrino.java | 293 ++++ .../trino/client/TestTrinoClient.java | 1442 ++++++++++++++++- .../client/TestTrinoConnectionManager.java | 205 +++ .../client/TestTrinoResourceManager.java | 286 ++++ 4 files changed, 2211 insertions(+), 15 deletions(-) create mode 100644 plugin-trino/src/test/java/org/apache/ranger/services/trino/TestRangerServiceTrino.java create mode 100644 plugin-trino/src/test/java/org/apache/ranger/services/trino/client/TestTrinoConnectionManager.java create mode 100644 plugin-trino/src/test/java/org/apache/ranger/services/trino/client/TestTrinoResourceManager.java diff --git a/plugin-trino/src/test/java/org/apache/ranger/services/trino/TestRangerServiceTrino.java b/plugin-trino/src/test/java/org/apache/ranger/services/trino/TestRangerServiceTrino.java new file mode 100644 index 0000000000..75635383fe --- /dev/null +++ b/plugin-trino/src/test/java/org/apache/ranger/services/trino/TestRangerServiceTrino.java @@ -0,0 +1,293 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.ranger.services.trino; + +import org.apache.ranger.plugin.client.HadoopConfigHolder; +import org.apache.ranger.plugin.client.HadoopException; +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.service.RangerBaseService; +import org.apache.ranger.plugin.service.ResourceLookupContext; +import org.apache.ranger.plugin.store.EmbeddedServiceDefsUtil; +import org.apache.ranger.services.trino.client.TrinoResourceManager; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.MockedStatic; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.lang.reflect.Field; +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.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyMap; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.same; +import static org.mockito.Mockito.mockStatic; +import static org.mockito.Mockito.never; + +/** + * @generated by Cursor + * @description : Unit test cases for RangerServiceTrino. + */ +@ExtendWith(MockitoExtension.class) +@TestMethodOrder(MethodOrderer.MethodName.class) +class TestRangerServiceTrino { + private static final String CREATE_DEFAULT_POLICY_PER_HIERARCHY = "create.default.policy.per.hierarchy"; + + @Test + void test01_validateConfig_whenConfigsAbsent_returnsEmptyResponse() throws Exception { + RangerServiceTrino svc = initTrino(sampleConfigs()); + svc.setConfigs(null); + + Map out = svc.validateConfig(); + + assertNotNull(out); + assertTrue(out.isEmpty()); + } + + @Test + void test02_validateConfig_whenConfigsAvailable_delegatesToTrinoResourceManager() throws Exception { + RangerServiceTrino svc = initTrino(sampleConfigs()); + Map expected = Collections.singletonMap("connectivityStatus", Boolean.TRUE); + + try (MockedStatic mocked = mockStatic(TrinoResourceManager.class)) { + mocked.when(() -> TrinoResourceManager.connectionTest(eq("trino-svc"), anyMap())).thenReturn(expected); + + Map actual = svc.validateConfig(); + + assertSame(expected, actual); + mocked.verify(() -> TrinoResourceManager.connectionTest(eq("trino-svc"), anyMap())); + } + } + + @Test + void test03_validateConfig_whenConnectionTestFails_propagatesHadoopException() throws Exception { + RangerServiceTrino svc = initTrino(sampleConfigs()); + HadoopException boom = new HadoopException("failed", new RuntimeException("root")); + + try (MockedStatic mocked = mockStatic(TrinoResourceManager.class)) { + mocked.when(() -> TrinoResourceManager.connectionTest(eq("trino-svc"), anyMap())).thenThrow(boom); + + assertThrows(HadoopException.class, svc::validateConfig); + } + } + + @Test + void test04_lookupResource_whenContextIsNull_returnsEmptyList() throws Exception { + RangerServiceTrino svc = initTrinoForLookup(sampleConfigs()); + + try (MockedStatic mocked = mockStatic(TrinoResourceManager.class)) { + List result = svc.lookupResource(null); + + assertEquals(Collections.emptyList(), result); + + mocked.verify(() -> TrinoResourceManager.getTrinoResources(anyString(), anyString(), anyMap(), any()), + never()); + } + } + + @Test + void test05_lookupResource_whenContextPresent_delegatesToTrinoResourceManager() throws Exception { + RangerServiceTrino svc = initTrinoForLookup(sampleConfigs()); + ResourceLookupContext ctx = sampleLookupContext(); + List expected = List.of("/a", "/b"); + + try (MockedStatic mocked = mockStatic(TrinoResourceManager.class)) { + mocked.when(() -> TrinoResourceManager.getTrinoResources(eq("svc-lookup"), eq("trino"), anyMap(), same(ctx))) + .thenReturn(expected); + + List result = svc.lookupResource(ctx); + + assertSame(expected, result); + } + } + + @Test + void test06_lookupResource_whenDelegateThrows_propagatesException() throws Exception { + RangerServiceTrino svc = initTrinoForLookup(sampleConfigs()); + ResourceLookupContext ctx = sampleLookupContext(); + + try (MockedStatic mocked = mockStatic(TrinoResourceManager.class)) { + mocked.when(() -> TrinoResourceManager.getTrinoResources(eq("svc-lookup"), eq("trino"), anyMap(), same(ctx))) + .thenThrow(new IllegalStateException("lookup failed")); + + assertThrows(IllegalStateException.class, () -> svc.lookupResource(ctx)); + } + } + + @Test + void test07_getDefaultRangerPolicies_addsOwnerStylePoliciesFromEmbeddedDef() throws Exception { + RangerServiceTrino svc = new RangerServiceTrino(); + RangerServiceDef def = EmbeddedServiceDefsUtil.instance().getEmbeddedServiceDef(EmbeddedServiceDefsUtil.EMBEDDED_SERVICEDEF_TRINO_NAME); + + assertNotNull(def); + + svc.init(def, buildService("def-svc", "trino", defaultPolicyConfigs())); + + List policies = svc.getDefaultRangerPolicies(); + + assertFalse(policies.isEmpty()); + + boolean foundAllHierarchyPolicy = policies.stream().anyMatch(p -> + p.getName() != null && p.getName().startsWith("all")); + + assertTrue(foundAllHierarchyPolicy); + } + + @Test + void test08_getDefaultRangerPolicies_whenLookupUserPresent_addsSelectGrantOnPoliciesContainingAll() throws Exception { + RangerServiceTrino svc = new RangerServiceTrino(); + + Field lookUpField = RangerBaseService.class.getDeclaredField("lookUpUser"); + lookUpField.setAccessible(true); + lookUpField.set(svc, "resolver-user"); + + RangerServiceDef def = EmbeddedServiceDefsUtil.instance().getEmbeddedServiceDef(EmbeddedServiceDefsUtil.EMBEDDED_SERVICEDEF_TRINO_NAME); + + assertNotNull(def); + + svc.init(def, buildService("def-svc-2", "trino", defaultPolicyConfigs())); + + List policies = svc.getDefaultRangerPolicies(); + + Set matchingPolicyNames = new HashSet<>(); + for (RangerPolicy p : policies) { + if (p.getName() != null && p.getName().contains("all")) { + boolean hasLookup = p.getPolicyItems().stream().anyMatch(pi -> + pi.getUsers() != null && pi.getUsers().contains("resolver-user")); + boolean hasSelectAccess = p.getPolicyItems().stream().anyMatch(pi -> + pi.getAccesses() != null && pi.getAccesses().stream() + .anyMatch(a -> RangerServiceTrino.ACCESS_TYPE_SELECT.equals(a.getType()))); + assertTrue(hasLookup, "expected lookup user on policy " + p.getName()); + assertTrue(hasSelectAccess, "expected select access on policy " + p.getName()); + matchingPolicyNames.add(p.getName()); + } + } + + assertFalse(matchingPolicyNames.isEmpty()); + } + + @Test + void test09_validateConfig_whenRangerLoginPasswordKeyMissing_putsNullPasswordEntry() throws Exception { + RangerServiceTrino svc = initTrino(configsWithoutRangerLoginPassword()); + + try (MockedStatic mocked = mockStatic(TrinoResourceManager.class)) { + mocked.when(() -> TrinoResourceManager.connectionTest(eq("trino-svc"), anyMap())) + .thenReturn(Collections.singletonMap("connectivityStatus", Boolean.TRUE)); + + svc.validateConfig(); + + mocked.verify(() -> TrinoResourceManager.connectionTest(eq("trino-svc"), argThat(m -> + m.containsKey(HadoopConfigHolder.RANGER_LOGIN_PASSWORD) + && m.get(HadoopConfigHolder.RANGER_LOGIN_PASSWORD) == null))); + } + } + + @Test + void test10_lookupResource_whenRangerLoginPasswordKeyMissing_putsNullPasswordEntry() + throws Exception { + RangerServiceTrino svc = initTrinoForLookup(configsWithoutRangerLoginPassword()); + ResourceLookupContext ctx = sampleLookupContext(); + + try (MockedStatic mocked = mockStatic(TrinoResourceManager.class)) { + mocked.when(() -> TrinoResourceManager.getTrinoResources( + eq("svc-lookup"), eq("trino"), anyMap(), same(ctx))) + .thenReturn(Collections.emptyList()); + + svc.lookupResource(ctx); + + mocked.verify(() -> TrinoResourceManager.getTrinoResources( + eq("svc-lookup"), eq("trino"), argThat(m -> + m.containsKey(HadoopConfigHolder.RANGER_LOGIN_PASSWORD) + && m.get(HadoopConfigHolder.RANGER_LOGIN_PASSWORD) == null), + same(ctx))); + } + } + + private RangerServiceTrino initTrino(Map cfg) throws Exception { + RangerServiceTrino svc = new RangerServiceTrino(); + + RangerServiceDef def = EmbeddedServiceDefsUtil.instance().getEmbeddedServiceDef(EmbeddedServiceDefsUtil.EMBEDDED_SERVICEDEF_TRINO_NAME); + assertNotNull(def); + + svc.init(def, buildService("trino-svc", "trino", cfg)); + return svc; + } + + private RangerServiceTrino initTrinoForLookup(Map cfg) throws Exception { + RangerServiceTrino svc = new RangerServiceTrino(); + + RangerServiceDef def = EmbeddedServiceDefsUtil.instance().getEmbeddedServiceDef(EmbeddedServiceDefsUtil.EMBEDDED_SERVICEDEF_TRINO_NAME); + assertNotNull(def); + + svc.init(def, buildService("svc-lookup", "trino", cfg)); + return svc; + } + + private RangerService buildService(String name, String type, Map cfg) { + RangerService service = new RangerService(); + service.setName(name); + service.setType(type); + service.setConfigs(cfg); + return service; + } + + private Map sampleConfigs() { + Map cfg = new HashMap<>(); + cfg.put("username", "u"); + cfg.put("password", "p"); + cfg.put("jdbc.driverClassName", "io.trino.jdbc.TrinoDriver"); + cfg.put("jdbc.url", "jdbc:trino://localhost:8080"); + return cfg; + } + + /** Config map without {@link HadoopConfigHolder#RANGER_LOGIN_PASSWORD}. */ + private Map configsWithoutRangerLoginPassword() { + Map cfg = new HashMap<>(); + cfg.put("username", "u"); + cfg.put("jdbc.driverClassName", "io.trino.jdbc.TrinoDriver"); + cfg.put("jdbc.url", "jdbc:trino://localhost:8080"); + return cfg; + } + + private ResourceLookupContext sampleLookupContext() { + ResourceLookupContext ctx = new ResourceLookupContext(); + ctx.setUserInput("needle"); + ctx.setResourceName("catalog"); + return ctx; + } + + private Map defaultPolicyConfigs() { + Map cfg = sampleConfigs(); + cfg.put("setup.additional.default.policies", "false"); + cfg.put(CREATE_DEFAULT_POLICY_PER_HIERARCHY, Boolean.TRUE.toString()); + return cfg; + } +} diff --git a/plugin-trino/src/test/java/org/apache/ranger/services/trino/client/TestTrinoClient.java b/plugin-trino/src/test/java/org/apache/ranger/services/trino/client/TestTrinoClient.java index 6d1276d106..dbecc3ccaa 100644 --- a/plugin-trino/src/test/java/org/apache/ranger/services/trino/client/TestTrinoClient.java +++ b/plugin-trino/src/test/java/org/apache/ranger/services/trino/client/TestTrinoClient.java @@ -13,14 +13,20 @@ */ package org.apache.ranger.services.trino.client; +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.Logger; +import org.apache.ranger.plugin.client.HadoopConfigHolder; import org.apache.ranger.plugin.client.HadoopException; +import org.apache.ranger.plugin.util.PasswordUtils; import org.junit.jupiter.api.MethodOrderer; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestMethodOrder; import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.MockedConstruction; import org.mockito.MockedStatic; import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoExtension; +import org.slf4j.LoggerFactory; import javax.security.auth.Subject; @@ -28,36 +34,113 @@ import java.security.PrivilegedAction; import java.sql.Connection; import java.sql.DatabaseMetaData; +import java.sql.Driver; import java.sql.DriverManager; +import java.sql.DriverPropertyInfo; import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.SQLFeatureNotSupportedException; import java.sql.SQLTimeoutException; +import java.sql.Statement; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Properties; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.mockConstruction; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +/** + * @generated by Cursor + * @description : Unit test cases for TrinoClient. + */ @ExtendWith(MockitoExtension.class) @TestMethodOrder(MethodOrderer.MethodName.class) -public class TestTrinoClient { - private TrinoClient createMockedClient() throws Exception { - return createMockedClient(new HashMap<>()); +class TestTrinoClient { + private abstract static class MinimalDriverStub implements Driver { + @Override + public Connection connect(String url, Properties info) throws SQLException { + return null; + } + + @Override + public boolean acceptsURL(String url) throws SQLException { + return false; + } + + @Override + public DriverPropertyInfo[] getPropertyInfo(String url, Properties info) throws SQLException { + return new DriverPropertyInfo[0]; + } + + @Override + public int getMajorVersion() { + return 1; + } + + @Override + public int getMinorVersion() { + return 0; + } + + @Override + public boolean jdbcCompliant() { + return false; + } + + @Override + public java.util.logging.Logger getParentLogger() throws SQLFeatureNotSupportedException { + throw new SQLFeatureNotSupportedException(); + } } - private TrinoClient createMockedClient(Map props) throws Exception { + private static final class DriverWithPrivateCtor extends MinimalDriverStub { + private DriverWithPrivateCtor() { + } + } + + private abstract static class AbstractJdbcDriver extends MinimalDriverStub { + } + + /** + * Driver whose static init fails under {@link Class#forName(String)}; must only be referenced by + * binary name string in tests, not {@code .class}, so test class loading is not affected. + */ + private static final class ClinitFailingJdbcDriver extends MinimalDriverStub { + static { + if (Boolean.TRUE.booleanValue()) { + throw new ExceptionInInitializerError(new IllegalStateException("static init fail")); + } + } + } + + private Map baseConnProps() { Map config = new HashMap<>(); config.put("username", "test"); config.put("password", "test"); config.put("jdbc.driverClassName", "io.trino.jdbc.TrinoDriver"); config.put("jdbc.url", "jdbc:trino://localhost:8080"); + return config; + } + + private TrinoClient createMockedClient() throws Exception { + return createMockedClient(new HashMap<>()); + } + + private TrinoClient createMockedClient(Map props) throws Exception { + Map config = new HashMap<>(baseConnProps()); config.putAll(props); NoConnectionTrinoClient client = new NoConnectionTrinoClient("svc", config); @@ -65,7 +148,7 @@ private TrinoClient createMockedClient(Map props) throws Excepti } @Test - public void test01_getCatalogList_normalOperation() throws Exception { + void test01_getCatalogList_normalOperation() throws Exception { try (MockedStatic dmStatic = Mockito.mockStatic(DriverManager.class); MockedStatic subjectStatic = Mockito.mockStatic(Subject.class)) { Connection mockCon = mock(Connection.class); @@ -89,7 +172,7 @@ public void test01_getCatalogList_normalOperation() throws Exception { } @Test - public void test02_getCatalogList_withExcludeList() throws Exception { + void test02_getCatalogList_withExcludeList() throws Exception { try (MockedStatic dmStatic = Mockito.mockStatic(DriverManager.class); MockedStatic subjectStatic = Mockito.mockStatic(Subject.class)) { Connection mockCon = mock(Connection.class); @@ -113,7 +196,7 @@ public void test02_getCatalogList_withExcludeList() throws Exception { } @Test - public void test03_getCatalogList_timeoutThrowsHadoopException() throws Exception { + void test03_getCatalogList_timeoutThrowsHadoopException() throws Exception { try (MockedStatic dmStatic = Mockito.mockStatic(DriverManager.class); MockedStatic subjectStatic = Mockito.mockStatic(Subject.class)) { Connection mockCon = mock(Connection.class); @@ -133,7 +216,7 @@ public void test03_getCatalogList_timeoutThrowsHadoopException() throws Exceptio } @Test - public void test04_validateSqlIdentifier_rejectsSqlInjection() throws Exception { + void test04_validateSqlIdentifier_rejectsSqlInjection() throws Exception { try (MockedStatic dmStatic = Mockito.mockStatic(DriverManager.class); MockedStatic subjectStatic = Mockito.mockStatic(Subject.class)) { Connection mockCon = mock(Connection.class); @@ -159,7 +242,7 @@ public void test04_validateSqlIdentifier_rejectsSqlInjection() throws Exception } @Test - public void test05_validateSqlIdentifier_rejectsSpecialCharacters() throws Exception { + void test05_validateSqlIdentifier_rejectsSpecialCharacters() throws Exception { try (MockedStatic dmStatic = Mockito.mockStatic(DriverManager.class); MockedStatic subjectStatic = Mockito.mockStatic(Subject.class)) { Connection mockCon = mock(Connection.class); @@ -185,7 +268,7 @@ public void test05_validateSqlIdentifier_rejectsSpecialCharacters() throws Excep } @Test - public void test06_schemaValidation_rejectsSqlInjection() throws Exception { + void test06_schemaValidation_rejectsSqlInjection() throws Exception { try (MockedStatic dmStatic = Mockito.mockStatic(DriverManager.class); MockedStatic subjectStatic = Mockito.mockStatic(Subject.class)) { Connection mockCon = mock(Connection.class); @@ -208,7 +291,7 @@ public void test06_schemaValidation_rejectsSqlInjection() throws Exception { } @Test - public void test07_tableValidation_rejectsSqlInjection() throws Exception { + void test07_tableValidation_rejectsSqlInjection() throws Exception { try (MockedStatic dmStatic = Mockito.mockStatic(DriverManager.class); MockedStatic subjectStatic = Mockito.mockStatic(Subject.class)) { Connection mockCon = mock(Connection.class); @@ -228,7 +311,7 @@ public void test07_tableValidation_rejectsSqlInjection() throws Exception { } @Test - public void test08_columnValidation_rejectsSqlInjection() throws Exception { + void test08_columnValidation_rejectsSqlInjection() throws Exception { try (MockedStatic dmStatic = Mockito.mockStatic(DriverManager.class); MockedStatic subjectStatic = Mockito.mockStatic(Subject.class)) { Connection mockCon = mock(Connection.class); @@ -248,7 +331,7 @@ public void test08_columnValidation_rejectsSqlInjection() throws Exception { } @Test - public void test09_catalogName_validateInList() throws Exception { + void test09_catalogName_validateInList() throws Exception { try (MockedStatic dmStatic = Mockito.mockStatic(DriverManager.class); MockedStatic subjectStatic = Mockito.mockStatic(Subject.class)) { Connection mockCon = mock(Connection.class); @@ -276,7 +359,7 @@ public void test09_catalogName_validateInList() throws Exception { } @Test - public void test10_schemaName_validateInList() throws Exception { + void test10_schemaName_validateInList() throws Exception { try (MockedStatic dmStatic = Mockito.mockStatic(DriverManager.class); MockedStatic subjectStatic = Mockito.mockStatic(Subject.class)) { Connection mockCon = mock(Connection.class); @@ -303,8 +386,1337 @@ public void test10_schemaName_validateInList() throws Exception { } } + @Test + void test11_connectionTest_success_setsConnectivityFlag() { + Map cfg = baseConnProps(); + try (MockedConstruction ignored = mockConstruction(TrinoClient.class, + (mock, ctx) -> when(mock.getCatalogList(eq("*"), any())).thenReturn(Collections.singletonList("c1")))) { + Map resp = TrinoClient.connectionTest("svc", cfg); + assertEquals(Boolean.TRUE, resp.get("connectivityStatus")); + } + } + + @Test + void test12_connectionTest_emptyCatalogList_returnsEmptyResponseMap() { + Map cfg = baseConnProps(); + try (MockedConstruction ignored = mockConstruction(TrinoClient.class, + (mock, ctx) -> when(mock.getCatalogList(eq("*"), any())).thenReturn(Collections.emptyList()))) { + Map resp = TrinoClient.connectionTest("svc", cfg); + assertFalse(resp.containsKey("connectivityStatus")); + } + } + + @Test + void test13_getCatalogList_whenConnectionNull_returnsEmptyList() throws Exception { + try (MockedStatic dmStatic = Mockito.mockStatic(DriverManager.class); + MockedStatic subjectStatic = Mockito.mockStatic(Subject.class)) { + Connection mockCon = mock(Connection.class); + dmStatic.when(() -> DriverManager.getConnection(anyString(), any())).thenReturn(mockCon); + subjectStatic.when(() -> Subject.doAs(any(), any(PrivilegedAction.class))) + .thenAnswer(inv -> { + PrivilegedAction action = inv.getArgument(1); + return action.run(); + }); + + TrinoClient client = createMockedClient(); + Field fCon = TrinoClient.class.getDeclaredField("con"); + fCon.setAccessible(true); + fCon.set(client, null); + + assertEquals(Collections.emptyList(), client.getCatalogList("*", null)); + } + } + + @Test + void test14_getCatalogList_sqlexception_wrappedAsHadoopException() throws Exception { + try (MockedStatic dmStatic = Mockito.mockStatic(DriverManager.class); + MockedStatic subjectStatic = Mockito.mockStatic(Subject.class)) { + Connection mockCon = mock(Connection.class); + DatabaseMetaData metadata = mock(DatabaseMetaData.class); + dmStatic.when(() -> DriverManager.getConnection(anyString(), any())).thenReturn(mockCon); + when(mockCon.getMetaData()).thenReturn(metadata); + doThrow(new SQLException("catalog boom")).when(metadata).getCatalogs(); + subjectStatic.when(() -> Subject.doAs(any(), any(PrivilegedAction.class))) + .thenAnswer(inv -> { + PrivilegedAction action = inv.getArgument(1); + return action.run(); + }); + + TrinoClient client = createMockedClient(); + assertThrows(HadoopException.class, () -> client.getCatalogList("*", null)); + } + } + + @Test + void test15_getCatalogList_patternFiltersCatalogNames() throws Exception { + try (MockedStatic dmStatic = Mockito.mockStatic(DriverManager.class); + MockedStatic subjectStatic = Mockito.mockStatic(Subject.class)) { + Connection mockCon = mock(Connection.class); + DatabaseMetaData metadata = mock(DatabaseMetaData.class); + ResultSet rs = mock(ResultSet.class); + dmStatic.when(() -> DriverManager.getConnection(anyString(), any())).thenReturn(mockCon); + when(mockCon.getMetaData()).thenReturn(metadata); + when(metadata.getCatalogs()).thenReturn(rs); + when(rs.next()).thenReturn(true, true, true, false); + when(rs.getString("TABLE_CAT")).thenReturn("alpha_db", "beta_db", "gamma_other"); + subjectStatic.when(() -> Subject.doAs(any(), any(PrivilegedAction.class))) + .thenAnswer(inv -> { + PrivilegedAction action = inv.getArgument(1); + return action.run(); + }); + + TrinoClient client = createMockedClient(); + List out = client.getCatalogList("be*", null); + assertEquals(Collections.singletonList("beta_db"), out); + } + } + + @Test + void test16_getSchemaList_returnsSchemasAcrossCatalogs() throws Exception { + try (MockedStatic dmStatic = Mockito.mockStatic(DriverManager.class); + MockedStatic subjectStatic = Mockito.mockStatic(Subject.class)) { + Connection mockCon = mock(Connection.class); + DatabaseMetaData metadata = mock(DatabaseMetaData.class); + ResultSet rsCatA = mock(ResultSet.class); + ResultSet rsCatB = mock(ResultSet.class); + dmStatic.when(() -> DriverManager.getConnection(anyString(), any())).thenReturn(mockCon); + when(mockCon.getMetaData()).thenReturn(metadata); + when(metadata.getSchemas(eq("cat-a"), anyString())).thenReturn(rsCatA); + when(metadata.getSchemas(eq("cat-b"), anyString())).thenReturn(rsCatB); + when(rsCatA.next()).thenReturn(true, false); + when(rsCatA.getString("TABLE_SCHEM")).thenReturn("sch-one"); + when(rsCatB.next()).thenReturn(true, false); + when(rsCatB.getString("TABLE_SCHEM")).thenReturn("sch-two"); + subjectStatic.when(() -> Subject.doAs(any(), any(PrivilegedAction.class))) + .thenAnswer(inv -> { + PrivilegedAction action = inv.getArgument(1); + return action.run(); + }); + + TrinoClient client = createMockedClient(); + Field fCon = TrinoClient.class.getDeclaredField("con"); + fCon.setAccessible(true); + fCon.set(client, mockCon); + + List out = client.getSchemaList("*", Arrays.asList("cat-a", "cat-b"), null); + assertEquals(Arrays.asList("sch-one", "sch-two"), out); + } + } + + @Test + void test17_getSchemaList_skipsSchemasInExcludeList() throws Exception { + try (MockedStatic dmStatic = Mockito.mockStatic(DriverManager.class); + MockedStatic subjectStatic = Mockito.mockStatic(Subject.class)) { + Connection mockCon = mock(Connection.class); + DatabaseMetaData metadata = mock(DatabaseMetaData.class); + ResultSet rs = mock(ResultSet.class); + dmStatic.when(() -> DriverManager.getConnection(anyString(), any())).thenReturn(mockCon); + when(mockCon.getMetaData()).thenReturn(metadata); + when(metadata.getSchemas(eq("cat-a"), anyString())).thenReturn(rs); + when(rs.next()).thenReturn(true, true, false); + when(rs.getString("TABLE_SCHEM")).thenReturn("keep_me", "skip_me"); + subjectStatic.when(() -> Subject.doAs(any(), any(PrivilegedAction.class))) + .thenAnswer(inv -> { + PrivilegedAction action = inv.getArgument(1); + return action.run(); + }); + + TrinoClient client = createMockedClient(); + Field fCon = TrinoClient.class.getDeclaredField("con"); + fCon.setAccessible(true); + fCon.set(client, mockCon); + + List out = client.getSchemaList("*", Collections.singletonList("cat-a"), Collections.singletonList("skip_me")); + assertEquals(Collections.singletonList("keep_me"), out); + } + } + + @Test + void test18_getSchemaList_sqlTimeout_wrappedAsHadoopException() throws Exception { + try (MockedStatic dmStatic = Mockito.mockStatic(DriverManager.class); + MockedStatic subjectStatic = Mockito.mockStatic(Subject.class)) { + Connection mockCon = mock(Connection.class); + DatabaseMetaData metadata = mock(DatabaseMetaData.class); + dmStatic.when(() -> DriverManager.getConnection(anyString(), any())).thenReturn(mockCon); + when(mockCon.getMetaData()).thenReturn(metadata); + doThrow(new SQLTimeoutException("timeout")).when(metadata).getSchemas(anyString(), anyString()); + subjectStatic.when(() -> Subject.doAs(any(), any(PrivilegedAction.class))) + .thenAnswer(inv -> { + PrivilegedAction action = inv.getArgument(1); + return action.run(); + }); + + TrinoClient client = createMockedClient(); + Field fCon = TrinoClient.class.getDeclaredField("con"); + fCon.setAccessible(true); + fCon.set(client, mockCon); + + assertThrows(HadoopException.class, + () -> client.getSchemaList("sch", Collections.singletonList("cat-a"), null)); + } + } + + @Test + void test19_getSchemaList_sqlexception_wrappedAsHadoopException() throws Exception { + try (MockedStatic dmStatic = Mockito.mockStatic(DriverManager.class); + MockedStatic subjectStatic = Mockito.mockStatic(Subject.class)) { + Connection mockCon = mock(Connection.class); + DatabaseMetaData metadata = mock(DatabaseMetaData.class); + dmStatic.when(() -> DriverManager.getConnection(anyString(), any())).thenReturn(mockCon); + when(mockCon.getMetaData()).thenReturn(metadata); + doThrow(new SQLException("schema boom")).when(metadata).getSchemas(anyString(), anyString()); + subjectStatic.when(() -> Subject.doAs(any(), any(PrivilegedAction.class))) + .thenAnswer(inv -> { + PrivilegedAction action = inv.getArgument(1); + return action.run(); + }); + + TrinoClient client = createMockedClient(); + Field fCon = TrinoClient.class.getDeclaredField("con"); + fCon.setAccessible(true); + fCon.set(client, mockCon); + + assertThrows(HadoopException.class, + () -> client.getSchemaList("sch", Collections.singletonList("cat-a"), null)); + } + } + + @Test + void test20_getTableList_returnsTablesMatchingPattern() throws Exception { + try (MockedStatic dmStatic = Mockito.mockStatic(DriverManager.class); + MockedStatic subjectStatic = Mockito.mockStatic(Subject.class)) { + Connection mockCon = mock(Connection.class); + DatabaseMetaData metadata = mock(DatabaseMetaData.class); + ResultSet rs = mock(ResultSet.class); + dmStatic.when(() -> DriverManager.getConnection(anyString(), any())).thenReturn(mockCon); + when(mockCon.getMetaData()).thenReturn(metadata); + when(metadata.getTables(eq("cat-a"), eq("sch-a"), anyString(), any())).thenReturn(rs); + when(rs.next()).thenReturn(true, false); + when(rs.getString("TABLE_NAME")).thenReturn("fact_orders"); + subjectStatic.when(() -> Subject.doAs(any(), any(PrivilegedAction.class))) + .thenAnswer(inv -> { + PrivilegedAction action = inv.getArgument(1); + return action.run(); + }); + + TrinoClient client = createMockedClient(); + Field fCon = TrinoClient.class.getDeclaredField("con"); + fCon.setAccessible(true); + fCon.set(client, mockCon); + + List out = client.getTableList("fact*", + Collections.singletonList("cat-a"), + Collections.singletonList("sch-a"), + null); + assertEquals(Collections.singletonList("fact_orders"), out); + } + } + + @Test + void test21_getTableList_skipsTablesInExcludeList() throws Exception { + try (MockedStatic dmStatic = Mockito.mockStatic(DriverManager.class); + MockedStatic subjectStatic = Mockito.mockStatic(Subject.class)) { + Connection mockCon = mock(Connection.class); + DatabaseMetaData metadata = mock(DatabaseMetaData.class); + ResultSet rs = mock(ResultSet.class); + dmStatic.when(() -> DriverManager.getConnection(anyString(), any())).thenReturn(mockCon); + when(mockCon.getMetaData()).thenReturn(metadata); + when(metadata.getTables(eq("c"), eq("s"), anyString(), any())).thenReturn(rs); + when(rs.next()).thenReturn(true, true, false); + when(rs.getString("TABLE_NAME")).thenReturn("t_keep", "t_drop"); + subjectStatic.when(() -> Subject.doAs(any(), any(PrivilegedAction.class))) + .thenAnswer(inv -> { + PrivilegedAction action = inv.getArgument(1); + return action.run(); + }); + + TrinoClient client = createMockedClient(); + Field fCon = TrinoClient.class.getDeclaredField("con"); + fCon.setAccessible(true); + fCon.set(client, mockCon); + + List out = client.getTableList("*", + Collections.singletonList("c"), + Collections.singletonList("s"), + Collections.singletonList("t_drop")); + assertEquals(Collections.singletonList("t_keep"), out); + } + } + + @Test + void test22_getTableList_sqlexception_wrappedAsHadoopException() throws Exception { + try (MockedStatic dmStatic = Mockito.mockStatic(DriverManager.class); + MockedStatic subjectStatic = Mockito.mockStatic(Subject.class)) { + Connection mockCon = mock(Connection.class); + DatabaseMetaData metadata = mock(DatabaseMetaData.class); + dmStatic.when(() -> DriverManager.getConnection(anyString(), any())).thenReturn(mockCon); + when(mockCon.getMetaData()).thenReturn(metadata); + doThrow(new SQLException("tbl boom")).when(metadata).getTables(anyString(), anyString(), anyString(), any()); + subjectStatic.when(() -> Subject.doAs(any(), any(PrivilegedAction.class))) + .thenAnswer(inv -> { + PrivilegedAction action = inv.getArgument(1); + return action.run(); + }); + + TrinoClient client = createMockedClient(); + Field fCon = TrinoClient.class.getDeclaredField("con"); + fCon.setAccessible(true); + fCon.set(client, mockCon); + + assertThrows(HadoopException.class, + () -> client.getTableList("t", + Collections.singletonList("c"), + Collections.singletonList("s"), + null)); + } + } + + @Test + void test23_getColumnList_appliesWildcardMatcherOnNames() throws Exception { + try (MockedStatic dmStatic = Mockito.mockStatic(DriverManager.class); + MockedStatic subjectStatic = Mockito.mockStatic(Subject.class)) { + Connection mockCon = mock(Connection.class); + DatabaseMetaData metadata = mock(DatabaseMetaData.class); + ResultSet rs = mock(ResultSet.class); + dmStatic.when(() -> DriverManager.getConnection(anyString(), any())).thenReturn(mockCon); + when(mockCon.getMetaData()).thenReturn(metadata); + when(metadata.getColumns(eq("c"), eq("s"), eq("t"), anyString())).thenReturn(rs); + when(rs.next()).thenReturn(true, true, false); + when(rs.getString("COLUMN_NAME")).thenReturn("id_ok", "skip_other"); + subjectStatic.when(() -> Subject.doAs(any(), any(PrivilegedAction.class))) + .thenAnswer(inv -> { + PrivilegedAction action = inv.getArgument(1); + return action.run(); + }); + + TrinoClient client = createMockedClient(); + Field fCon = TrinoClient.class.getDeclaredField("con"); + fCon.setAccessible(true); + fCon.set(client, mockCon); + + List out = client.getColumnList("id*", + Collections.singletonList("c"), + Collections.singletonList("s"), + Collections.singletonList("t"), + null); + assertEquals(Collections.singletonList("id_ok"), out); + } + } + + @Test + void test24_getColumnList_skipsColumnsInExcludeList() throws Exception { + try (MockedStatic dmStatic = Mockito.mockStatic(DriverManager.class); + MockedStatic subjectStatic = Mockito.mockStatic(Subject.class)) { + Connection mockCon = mock(Connection.class); + DatabaseMetaData metadata = mock(DatabaseMetaData.class); + ResultSet rs = mock(ResultSet.class); + dmStatic.when(() -> DriverManager.getConnection(anyString(), any())).thenReturn(mockCon); + when(mockCon.getMetaData()).thenReturn(metadata); + when(metadata.getColumns(eq("c"), eq("s"), eq("tbl"), anyString())).thenReturn(rs); + when(rs.next()).thenReturn(true, true, false); + when(rs.getString("COLUMN_NAME")).thenReturn("col_a", "col_b"); + subjectStatic.when(() -> Subject.doAs(any(), any(PrivilegedAction.class))) + .thenAnswer(inv -> { + PrivilegedAction action = inv.getArgument(1); + return action.run(); + }); + + TrinoClient client = createMockedClient(); + Field fCon = TrinoClient.class.getDeclaredField("con"); + fCon.setAccessible(true); + fCon.set(client, mockCon); + + List out = client.getColumnList("*", + Collections.singletonList("c"), + Collections.singletonList("s"), + Collections.singletonList("tbl"), + Collections.singletonList("col_b")); + assertEquals(Collections.singletonList("col_a"), out); + } + } + + @Test + void test25_getColumnList_sqlexception_wrappedAsHadoopException() throws Exception { + try (MockedStatic dmStatic = Mockito.mockStatic(DriverManager.class); + MockedStatic subjectStatic = Mockito.mockStatic(Subject.class)) { + Connection mockCon = mock(Connection.class); + DatabaseMetaData metadata = mock(DatabaseMetaData.class); + dmStatic.when(() -> DriverManager.getConnection(anyString(), any())).thenReturn(mockCon); + when(mockCon.getMetaData()).thenReturn(metadata); + doThrow(new SQLException("col boom")).when(metadata).getColumns(anyString(), anyString(), anyString(), anyString()); + subjectStatic.when(() -> Subject.doAs(any(), any(PrivilegedAction.class))) + .thenAnswer(inv -> { + PrivilegedAction action = inv.getArgument(1); + return action.run(); + }); + + TrinoClient client = createMockedClient(); + Field fCon = TrinoClient.class.getDeclaredField("con"); + fCon.setAccessible(true); + fCon.set(client, mockCon); + + assertThrows(HadoopException.class, + () -> client.getColumnList("c", + Collections.singletonList("cat"), + Collections.singletonList("sch"), + Collections.singletonList("tbl"), + null)); + } + } + + @Test + void test26_getColumnList_sqlTimeout_wrappedAsHadoopException() throws Exception { + try (MockedStatic dmStatic = Mockito.mockStatic(DriverManager.class); + MockedStatic subjectStatic = Mockito.mockStatic(Subject.class)) { + Connection mockCon = mock(Connection.class); + DatabaseMetaData metadata = mock(DatabaseMetaData.class); + dmStatic.when(() -> DriverManager.getConnection(anyString(), any())).thenReturn(mockCon); + when(mockCon.getMetaData()).thenReturn(metadata); + doThrow(new SQLTimeoutException("col timeout")).when(metadata).getColumns(anyString(), anyString(), anyString(), anyString()); + subjectStatic.when(() -> Subject.doAs(any(), any(PrivilegedAction.class))) + .thenAnswer(inv -> { + PrivilegedAction action = inv.getArgument(1); + return action.run(); + }); + + TrinoClient client = createMockedClient(); + Field fCon = TrinoClient.class.getDeclaredField("con"); + fCon.setAccessible(true); + fCon.set(client, mockCon); + + assertThrows(HadoopException.class, + () -> client.getColumnList("c", + Collections.singletonList("cat"), + Collections.singletonList("sch"), + Collections.singletonList("tbl"), + null)); + } + } + + @Test + void test27_close_invokesJdbcConnectionClose() throws Exception { + try (MockedStatic dmStatic = Mockito.mockStatic(DriverManager.class); + MockedStatic subjectStatic = Mockito.mockStatic(Subject.class)) { + Connection mockCon = mock(Connection.class); + dmStatic.when(() -> DriverManager.getConnection(anyString(), any())).thenReturn(mockCon); + subjectStatic.when(() -> Subject.doAs(any(), any(PrivilegedAction.class))) + .thenAnswer(inv -> { + PrivilegedAction action = inv.getArgument(1); + return action.run(); + }); + + TrinoClient client = createMockedClient(); + Field fCon = TrinoClient.class.getDeclaredField("con"); + fCon.setAccessible(true); + fCon.set(client, mockCon); + + client.close(); + verify(mockCon).close(); + } + } + + @Test + void test28_closeStatement_whenCloseFails_logsAndContinues() throws Exception { + try (MockedStatic dmStatic = Mockito.mockStatic(DriverManager.class); + MockedStatic subjectStatic = Mockito.mockStatic(Subject.class)) { + Connection mockCon = mock(Connection.class); + dmStatic.when(() -> DriverManager.getConnection(anyString(), any())).thenReturn(mockCon); + subjectStatic.when(() -> Subject.doAs(any(), any(PrivilegedAction.class))) + .thenAnswer(inv -> { + PrivilegedAction action = inv.getArgument(1); + return action.run(); + }); + + TrinoClient client = createMockedClient(); + Statement st = mock(Statement.class); + doThrow(new SQLException("stmt fail")).when(st).close(); + + client.close(st); + + verify(st).close(); + } + } + + @Test + void test29_closeResultSet_whenCloseFails_logsAndContinues() throws Exception { + try (MockedStatic dmStatic = Mockito.mockStatic(DriverManager.class); + MockedStatic subjectStatic = Mockito.mockStatic(Subject.class)) { + Connection mockCon = mock(Connection.class); + dmStatic.when(() -> DriverManager.getConnection(anyString(), any())).thenReturn(mockCon); + subjectStatic.when(() -> Subject.doAs(any(), any(PrivilegedAction.class))) + .thenAnswer(inv -> { + PrivilegedAction action = inv.getArgument(1); + return action.run(); + }); + + TrinoClient client = createMockedClient(); + ResultSet rs = mock(ResultSet.class); + doThrow(new SQLException("rs fail")).when(rs).close(); + + client.close(rs); + + verify(rs).close(); + } + } + + @Test + void test30_initConnection_withoutDriverClass_skipsRegistrationStillConnects() throws Exception { + Map props = new HashMap<>(); + props.put("jdbc.url", "jdbc:trino://localhost:8080"); + props.put("username", "test"); + props.put("password", "test"); + + try (MockedStatic dmStatic = Mockito.mockStatic(DriverManager.class); + MockedStatic subjectStatic = Mockito.mockStatic(Subject.class)) { + Connection mockCon = mock(Connection.class); + dmStatic.when(() -> DriverManager.getConnection(anyString(), any())).thenReturn(mockCon); + subjectStatic.when(() -> Subject.doAs(any(), any(PrivilegedAction.class))) + .thenAnswer(inv -> { + PrivilegedAction action = inv.getArgument(1); + return action.run(); + }); + + TrinoClient client = createMockedClient(props); + Field fCon = TrinoClient.class.getDeclaredField("con"); + fCon.setAccessible(true); + assertEquals(mockCon, fCon.get(client)); + } + } + + @Test + void test31_passwordDecryptFailure_fallsBackToConfiguredPassword() throws Exception { + try (MockedStatic pwdStatic = Mockito.mockStatic(PasswordUtils.class); + MockedStatic dmStatic = Mockito.mockStatic(DriverManager.class); + MockedStatic subjectStatic = Mockito.mockStatic(Subject.class)) { + pwdStatic.when(() -> PasswordUtils.decryptPassword(anyString())).thenThrow(new IllegalStateException("bad cipher")); + Connection mockCon = mock(Connection.class); + dmStatic.when(() -> DriverManager.getConnection(anyString(), any())).thenReturn(mockCon); + subjectStatic.when(() -> Subject.doAs(any(), any(PrivilegedAction.class))) + .thenAnswer(inv -> { + PrivilegedAction action = inv.getArgument(1); + return action.run(); + }); + + TrinoClient client = createMockedClient(); + Field fCon = TrinoClient.class.getDeclaredField("con"); + fCon.setAccessible(true); + assertEquals(mockCon, fCon.get(client)); + } + } + + @Test + void test32_tableName_validateInGetColumns_rejectsInvalidTableToken() throws Exception { + try (MockedStatic dmStatic = Mockito.mockStatic(DriverManager.class); + MockedStatic subjectStatic = Mockito.mockStatic(Subject.class)) { + Connection mockCon = mock(Connection.class); + dmStatic.when(() -> DriverManager.getConnection(anyString(), any())).thenReturn(mockCon); + subjectStatic.when(() -> Subject.doAs(any(), any(PrivilegedAction.class))) + .thenAnswer(inv -> { + PrivilegedAction action = inv.getArgument(1); + return action.run(); + }); + + TrinoClient client = createMockedClient(); + Field fCon = TrinoClient.class.getDeclaredField("con"); + fCon.setAccessible(true); + fCon.set(client, mockCon); + + HadoopException ex = assertThrows(HadoopException.class, + () -> client.getColumnList("col", + Collections.singletonList("c"), + Collections.singletonList("s"), + Arrays.asList("bad;inject", "ok_tbl"), + null)); + assertTrue(ex.getMessage().contains("Invalid")); + } + } + + @Test + void test33_close_whenConnectionNull_completes() throws Exception { + try (MockedStatic dmStatic = Mockito.mockStatic(DriverManager.class); + MockedStatic subjectStatic = Mockito.mockStatic(Subject.class)) { + Connection mockCon = mock(Connection.class); + dmStatic.when(() -> DriverManager.getConnection(anyString(), any())).thenReturn(mockCon); + subjectStatic.when(() -> Subject.doAs(any(), any(PrivilegedAction.class))) + .thenAnswer(inv -> { + PrivilegedAction action = inv.getArgument(1); + return action.run(); + }); + + TrinoClient client = createMockedClient(); + Field fCon = TrinoClient.class.getDeclaredField("con"); + fCon.setAccessible(true); + fCon.set(client, null); + + client.close(); + } + } + + @Test + void test34_closeStatement_nullArgument_noOp() throws Exception { + try (MockedStatic dmStatic = Mockito.mockStatic(DriverManager.class); + MockedStatic subjectStatic = Mockito.mockStatic(Subject.class)) { + Connection mockCon = mock(Connection.class); + dmStatic.when(() -> DriverManager.getConnection(anyString(), any())).thenReturn(mockCon); + subjectStatic.when(() -> Subject.doAs(any(), any(PrivilegedAction.class))) + .thenAnswer(inv -> { + PrivilegedAction action = inv.getArgument(1); + return action.run(); + }); + + TrinoClient client = createMockedClient(); + client.close((Statement) null); + } + } + + @Test + void test35_closeResultSet_nullArgument_noOp() throws Exception { + try (MockedStatic dmStatic = Mockito.mockStatic(DriverManager.class); + MockedStatic subjectStatic = Mockito.mockStatic(Subject.class)) { + Connection mockCon = mock(Connection.class); + dmStatic.when(() -> DriverManager.getConnection(anyString(), any())).thenReturn(mockCon); + subjectStatic.when(() -> Subject.doAs(any(), any(PrivilegedAction.class))) + .thenAnswer(inv -> { + PrivilegedAction action = inv.getArgument(1); + return action.run(); + }); + + TrinoClient client = createMockedClient(); + client.close((ResultSet) null); + } + } + + @Test + void test36_close_whenJdbcCloseThrows_completesWithoutPropagating() throws Exception { + try (MockedStatic dmStatic = Mockito.mockStatic(DriverManager.class); + MockedStatic subjectStatic = Mockito.mockStatic(Subject.class)) { + Connection mockCon = mock(Connection.class); + dmStatic.when(() -> DriverManager.getConnection(anyString(), any())).thenReturn(mockCon); + subjectStatic.when(() -> Subject.doAs(any(), any(PrivilegedAction.class))) + .thenAnswer(inv -> { + PrivilegedAction action = inv.getArgument(1); + return action.run(); + }); + doThrow(new SQLException("close fail")).when(mockCon).close(); + + TrinoClient client = createMockedClient(); + Field fCon = TrinoClient.class.getDeclaredField("con"); + fCon.setAccessible(true); + fCon.set(client, mockCon); + + client.close(); + verify(mockCon).close(); + } + } + + @Test + void test37_getSchemaList_whenCatalogsEmpty_returnsEmpty() throws Exception { + try (MockedStatic dmStatic = Mockito.mockStatic(DriverManager.class); + MockedStatic subjectStatic = Mockito.mockStatic(Subject.class)) { + Connection mockCon = mock(Connection.class); + dmStatic.when(() -> DriverManager.getConnection(anyString(), any())).thenReturn(mockCon); + subjectStatic.when(() -> Subject.doAs(any(), any(PrivilegedAction.class))) + .thenAnswer(inv -> { + PrivilegedAction action = inv.getArgument(1); + return action.run(); + }); + + TrinoClient client = createMockedClient(); + Field fCon = TrinoClient.class.getDeclaredField("con"); + fCon.setAccessible(true); + fCon.set(client, mockCon); + + assertEquals(Collections.emptyList(), client.getSchemaList("*", Collections.emptyList(), null)); + } + } + + @Test + void test38_getSchemaList_whenCatalogsNull_returnsEmpty() throws Exception { + try (MockedStatic dmStatic = Mockito.mockStatic(DriverManager.class); + MockedStatic subjectStatic = Mockito.mockStatic(Subject.class)) { + Connection mockCon = mock(Connection.class); + dmStatic.when(() -> DriverManager.getConnection(anyString(), any())).thenReturn(mockCon); + subjectStatic.when(() -> Subject.doAs(any(), any(PrivilegedAction.class))) + .thenAnswer(inv -> { + PrivilegedAction action = inv.getArgument(1); + return action.run(); + }); + + TrinoClient client = createMockedClient(); + Field fCon = TrinoClient.class.getDeclaredField("con"); + fCon.setAccessible(true); + fCon.set(client, mockCon); + + assertEquals(Collections.emptyList(), client.getSchemaList("*", null, null)); + } + } + + @Test + void test39_getTableList_whenCatalogsEmpty_returnsEmpty() throws Exception { + try (MockedStatic dmStatic = Mockito.mockStatic(DriverManager.class); + MockedStatic subjectStatic = Mockito.mockStatic(Subject.class)) { + Connection mockCon = mock(Connection.class); + dmStatic.when(() -> DriverManager.getConnection(anyString(), any())).thenReturn(mockCon); + subjectStatic.when(() -> Subject.doAs(any(), any(PrivilegedAction.class))) + .thenAnswer(inv -> { + PrivilegedAction action = inv.getArgument(1); + return action.run(); + }); + + TrinoClient client = createMockedClient(); + Field fCon = TrinoClient.class.getDeclaredField("con"); + fCon.setAccessible(true); + fCon.set(client, mockCon); + + assertEquals(Collections.emptyList(), client.getTableList("*", Collections.emptyList(), + Collections.singletonList("s"), null)); + } + } + + @Test + void test40_getTableList_whenSchemasEmpty_returnsEmpty() throws Exception { + try (MockedStatic dmStatic = Mockito.mockStatic(DriverManager.class); + MockedStatic subjectStatic = Mockito.mockStatic(Subject.class)) { + Connection mockCon = mock(Connection.class); + dmStatic.when(() -> DriverManager.getConnection(anyString(), any())).thenReturn(mockCon); + subjectStatic.when(() -> Subject.doAs(any(), any(PrivilegedAction.class))) + .thenAnswer(inv -> { + PrivilegedAction action = inv.getArgument(1); + return action.run(); + }); + + TrinoClient client = createMockedClient(); + Field fCon = TrinoClient.class.getDeclaredField("con"); + fCon.setAccessible(true); + fCon.set(client, mockCon); + + assertEquals(Collections.emptyList(), client.getTableList("*", Collections.singletonList("c"), + Collections.emptyList(), null)); + } + } + + @Test + void test41_getTableList_whenCatalogsNull_returnsEmpty() throws Exception { + try (MockedStatic dmStatic = Mockito.mockStatic(DriverManager.class); + MockedStatic subjectStatic = Mockito.mockStatic(Subject.class)) { + Connection mockCon = mock(Connection.class); + dmStatic.when(() -> DriverManager.getConnection(anyString(), any())).thenReturn(mockCon); + subjectStatic.when(() -> Subject.doAs(any(), any(PrivilegedAction.class))) + .thenAnswer(inv -> { + PrivilegedAction action = inv.getArgument(1); + return action.run(); + }); + + TrinoClient client = createMockedClient(); + Field fCon = TrinoClient.class.getDeclaredField("con"); + fCon.setAccessible(true); + fCon.set(client, mockCon); + + assertEquals(Collections.emptyList(), client.getTableList("*", null, + Collections.singletonList("s"), null)); + } + } + + @Test + void test42_getColumnList_whenTablesEmpty_returnsEmpty() throws Exception { + try (MockedStatic dmStatic = Mockito.mockStatic(DriverManager.class); + MockedStatic subjectStatic = Mockito.mockStatic(Subject.class)) { + Connection mockCon = mock(Connection.class); + dmStatic.when(() -> DriverManager.getConnection(anyString(), any())).thenReturn(mockCon); + subjectStatic.when(() -> Subject.doAs(any(), any(PrivilegedAction.class))) + .thenAnswer(inv -> { + PrivilegedAction action = inv.getArgument(1); + return action.run(); + }); + + TrinoClient client = createMockedClient(); + Field fCon = TrinoClient.class.getDeclaredField("con"); + fCon.setAccessible(true); + fCon.set(client, mockCon); + + assertEquals(Collections.emptyList(), client.getColumnList("*", + Collections.singletonList("c"), + Collections.singletonList("s"), + Collections.emptyList(), + null)); + } + } + + @Test + void test43_getColumnList_whenNeedleNull_addsEveryColumnReturnedByJdbc() throws Exception { + try (MockedStatic dmStatic = Mockito.mockStatic(DriverManager.class); + MockedStatic subjectStatic = Mockito.mockStatic(Subject.class)) { + Connection mockCon = mock(Connection.class); + DatabaseMetaData metadata = mock(DatabaseMetaData.class); + ResultSet rs = mock(ResultSet.class); + dmStatic.when(() -> DriverManager.getConnection(anyString(), any())).thenReturn(mockCon); + when(mockCon.getMetaData()).thenReturn(metadata); + when(metadata.getColumns(eq("c"), eq("s"), eq("t"), eq("%"))).thenReturn(rs); + when(rs.next()).thenReturn(true, true, false); + when(rs.getString("COLUMN_NAME")).thenReturn("col_a", "col_b"); + subjectStatic.when(() -> Subject.doAs(any(), any(PrivilegedAction.class))) + .thenAnswer(inv -> { + PrivilegedAction action = inv.getArgument(1); + return action.run(); + }); + + TrinoClient client = createMockedClient(); + Field fCon = TrinoClient.class.getDeclaredField("con"); + fCon.setAccessible(true); + fCon.set(client, mockCon); + + List out = client.getColumnList(null, + Collections.singletonList("c"), + Collections.singletonList("s"), + Collections.singletonList("t"), + null); + assertEquals(Arrays.asList("col_a", "col_b"), out); + } + } + + @Test + void test44_getColumnList_whenNeedleEmpty_addsEveryColumnReturnedByJdbc() throws Exception { + try (MockedStatic dmStatic = Mockito.mockStatic(DriverManager.class); + MockedStatic subjectStatic = Mockito.mockStatic(Subject.class)) { + Connection mockCon = mock(Connection.class); + DatabaseMetaData metadata = mock(DatabaseMetaData.class); + ResultSet rs = mock(ResultSet.class); + dmStatic.when(() -> DriverManager.getConnection(anyString(), any())).thenReturn(mockCon); + when(mockCon.getMetaData()).thenReturn(metadata); + when(metadata.getColumns(eq("c"), eq("s"), eq("t"), eq("%"))).thenReturn(rs); + when(rs.next()).thenReturn(true, false); + when(rs.getString("COLUMN_NAME")).thenReturn("only_col"); + subjectStatic.when(() -> Subject.doAs(any(), any(PrivilegedAction.class))) + .thenAnswer(inv -> { + PrivilegedAction action = inv.getArgument(1); + return action.run(); + }); + + TrinoClient client = createMockedClient(); + Field fCon = TrinoClient.class.getDeclaredField("con"); + fCon.setAccessible(true); + fCon.set(client, mockCon); + + List out = client.getColumnList("", + Collections.singletonList("c"), + Collections.singletonList("s"), + Collections.singletonList("t"), + null); + assertEquals(Collections.singletonList("only_col"), out); + } + } + + @Test + void test45_initConnection_whenDriverClassMissing_throwsHadoopException() { + try (MockedStatic subjectStatic = Mockito.mockStatic(Subject.class)) { + subjectStatic.when(() -> Subject.doAs(any(), any(PrivilegedAction.class))) + .thenAnswer(inv -> { + PrivilegedAction action = inv.getArgument(1); + return action.run(); + }); + Map cfg = new HashMap<>(baseConnProps()); + cfg.put("jdbc.driverClassName", "org.apache.ranger.services.trino.client.NonexistentTrinoDriver"); + + assertThrows(HadoopException.class, () -> new NoConnectionTrinoClient("svc", cfg)); + } + } + + @Test + void test46_initConnection_whenGetConnectionFails_throwsHadoopException() { + try (MockedStatic subjectStatic = Mockito.mockStatic(Subject.class); + MockedStatic dmStatic = Mockito.mockStatic(DriverManager.class)) { + subjectStatic.when(() -> Subject.doAs(any(), any(PrivilegedAction.class))) + .thenAnswer(inv -> { + PrivilegedAction action = inv.getArgument(1); + return action.run(); + }); + dmStatic.when(() -> DriverManager.getConnection(anyString(), any())) + .thenAnswer(inv -> { + throw new SQLException("connection failed"); + }); + + assertThrows(HadoopException.class, () -> new NoConnectionTrinoClient("svc", baseConnProps())); + } + } + + @Test + void test47_initConnection_whenGetConnectionThrowsRuntime_throwsHadoopException() { + try (MockedStatic subjectStatic = Mockito.mockStatic(Subject.class); + MockedStatic dmStatic = Mockito.mockStatic(DriverManager.class)) { + subjectStatic.when(() -> Subject.doAs(any(), any(PrivilegedAction.class))) + .thenAnswer(inv -> { + PrivilegedAction action = inv.getArgument(1); + return action.run(); + }); + dmStatic.when(() -> DriverManager.getConnection(anyString(), any())) + .thenAnswer(inv -> { + throw new RuntimeException("unexpected"); + }); + + assertThrows(HadoopException.class, () -> new NoConnectionTrinoClient("svc", baseConnProps())); + } + } + + @Test + void test48_initConnection_whenRegisterDriverFails_throwsHadoopException() { + try (MockedStatic subjectStatic = Mockito.mockStatic(Subject.class); + MockedStatic dmStatic = Mockito.mockStatic(DriverManager.class)) { + subjectStatic.when(() -> Subject.doAs(any(), any(PrivilegedAction.class))) + .thenAnswer(inv -> { + PrivilegedAction action = inv.getArgument(1); + return action.run(); + }); + dmStatic.when(() -> DriverManager.registerDriver(any(Driver.class))) + .thenAnswer(inv -> { + throw new SQLException("register failed"); + }); + + assertThrows(HadoopException.class, () -> new NoConnectionTrinoClient("svc", baseConnProps())); + } + } + + @Test + void test49_initConnection_whenDriverNotInstantiable_throwsHadoopException() { + try (MockedStatic subjectStatic = Mockito.mockStatic(Subject.class)) { + subjectStatic.when(() -> Subject.doAs(any(), any(PrivilegedAction.class))) + .thenAnswer(inv -> { + PrivilegedAction action = inv.getArgument(1); + return action.run(); + }); + Map cfg = new HashMap<>(baseConnProps()); + cfg.put("jdbc.driverClassName", "java.sql.Driver"); + + assertThrows(HadoopException.class, () -> new NoConnectionTrinoClient("svc", cfg)); + } + } + + @Test + void test50_getTableList_sqlTimeout_wrappedAsHadoopException() throws Exception { + try (MockedStatic dmStatic = Mockito.mockStatic(DriverManager.class); + MockedStatic subjectStatic = Mockito.mockStatic(Subject.class)) { + Connection mockCon = mock(Connection.class); + DatabaseMetaData metadata = mock(DatabaseMetaData.class); + dmStatic.when(() -> DriverManager.getConnection(anyString(), any())).thenReturn(mockCon); + when(mockCon.getMetaData()).thenReturn(metadata); + doThrow(new SQLTimeoutException("tbl timeout")).when(metadata).getTables(anyString(), anyString(), anyString(), any()); + subjectStatic.when(() -> Subject.doAs(any(), any(PrivilegedAction.class))) + .thenAnswer(inv -> { + PrivilegedAction action = inv.getArgument(1); + return action.run(); + }); + + TrinoClient client = createMockedClient(); + Field fCon = TrinoClient.class.getDeclaredField("con"); + fCon.setAccessible(true); + fCon.set(client, mockCon); + + assertThrows(HadoopException.class, + () -> client.getTableList("t", + Collections.singletonList("c"), + Collections.singletonList("s"), + null)); + } + } + + @Test + void test51_getColumnList_whenSchemasNull_returnsEmpty() throws Exception { + try (MockedStatic dmStatic = Mockito.mockStatic(DriverManager.class); + MockedStatic subjectStatic = Mockito.mockStatic(Subject.class)) { + Connection mockCon = mock(Connection.class); + dmStatic.when(() -> DriverManager.getConnection(anyString(), any())).thenReturn(mockCon); + subjectStatic.when(() -> Subject.doAs(any(), any(PrivilegedAction.class))) + .thenAnswer(inv -> { + PrivilegedAction action = inv.getArgument(1); + return action.run(); + }); + + TrinoClient client = createMockedClient(); + Field fCon = TrinoClient.class.getDeclaredField("con"); + fCon.setAccessible(true); + fCon.set(client, mockCon); + + assertEquals(Collections.emptyList(), client.getColumnList("*", + Collections.singletonList("c"), + null, + Collections.singletonList("t"), + null)); + } + } + + @Test + void test52_closeStatement_whenCloseSucceeds_invokesJdbcClose() throws Exception { + try (MockedStatic dmStatic = Mockito.mockStatic(DriverManager.class); + MockedStatic subjectStatic = Mockito.mockStatic(Subject.class)) { + Connection mockCon = mock(Connection.class); + dmStatic.when(() -> DriverManager.getConnection(anyString(), any())).thenReturn(mockCon); + subjectStatic.when(() -> Subject.doAs(any(), any(PrivilegedAction.class))) + .thenAnswer(inv -> { + PrivilegedAction action = inv.getArgument(1); + return action.run(); + }); + + TrinoClient client = createMockedClient(); + Statement st = mock(Statement.class); + client.close(st); + verify(st).close(); + } + } + + @Test + void test53_initConnection_whenDecryptSucceeds_usesDecryptedPassword() throws Exception { + try (MockedStatic pwdStatic = Mockito.mockStatic(PasswordUtils.class); + MockedStatic dmStatic = Mockito.mockStatic(DriverManager.class); + MockedStatic subjectStatic = Mockito.mockStatic(Subject.class)) { + pwdStatic.when(() -> PasswordUtils.decryptPassword(anyString())).thenReturn("decrypted-secret"); + Connection mockCon = mock(Connection.class); + dmStatic.when(() -> DriverManager.getConnection(anyString(), any())).thenReturn(mockCon); + subjectStatic.when(() -> Subject.doAs(any(), any(PrivilegedAction.class))) + .thenAnswer(inv -> { + PrivilegedAction action = inv.getArgument(1); + return action.run(); + }); + + TrinoClient client = createMockedClient(); + Field fCon = TrinoClient.class.getDeclaredField("con"); + fCon.setAccessible(true); + assertEquals(mockCon, fCon.get(client)); + } + } + + @Test + void test54_singleArgConstructor_loadsConnectionUsingHadoopConfigHolder() throws Exception { + String ds = "testTrinoSingleArgDs"; + Map cfg = new HashMap<>(baseConnProps()); + HadoopConfigHolder.getInstance(ds, cfg, null); + + try (MockedStatic pwdStatic = Mockito.mockStatic(PasswordUtils.class); + MockedStatic dmStatic = Mockito.mockStatic(DriverManager.class); + MockedStatic subjectStatic = Mockito.mockStatic(Subject.class)) { + pwdStatic.when(() -> PasswordUtils.decryptPassword(anyString())).thenReturn("dec-pw"); + Connection mockCon = mock(Connection.class); + dmStatic.when(() -> DriverManager.getConnection(anyString(), any())).thenReturn(mockCon); + subjectStatic.when(() -> Subject.doAs(any(), any(PrivilegedAction.class))) + .thenAnswer(inv -> { + PrivilegedAction action = inv.getArgument(1); + return action.run(); + }); + + SingleArgNoLoginTrinoClient client = new SingleArgNoLoginTrinoClient(ds); + Field fCon = TrinoClient.class.getDeclaredField("con"); + fCon.setAccessible(true); + assertEquals(mockCon, fCon.get(client)); + } + } + + @Test + void test55_initConnection_whenDriverNewInstanceIllegalAccess_throwsHadoopException() { + try (MockedStatic subjectStatic = Mockito.mockStatic(Subject.class)) { + subjectStatic.when(() -> Subject.doAs(any(), any(PrivilegedAction.class))) + .thenAnswer(inv -> { + PrivilegedAction action = inv.getArgument(1); + return action.run(); + }); + Map cfg = new HashMap<>(baseConnProps()); + cfg.put("jdbc.driverClassName", DriverWithPrivateCtor.class.getName()); + + assertThrows(HadoopException.class, () -> new NoConnectionTrinoClient("svc", cfg)); + } + } + + @Test + void test56_initConnection_whenDriverAbstract_throwsHadoopException() { + try (MockedStatic subjectStatic = Mockito.mockStatic(Subject.class)) { + subjectStatic.when(() -> Subject.doAs(any(), any(PrivilegedAction.class))) + .thenAnswer(inv -> { + PrivilegedAction action = inv.getArgument(1); + return action.run(); + }); + Map cfg = new HashMap<>(baseConnProps()); + cfg.put("jdbc.driverClassName", AbstractJdbcDriver.class.getName()); + + assertThrows(HadoopException.class, () -> new NoConnectionTrinoClient("svc", cfg)); + } + } + + @Test + void test57_initConnection_whenDriverClinitFails_throwsHadoopException() { + try (MockedStatic subjectStatic = Mockito.mockStatic(Subject.class)) { + subjectStatic.when(() -> Subject.doAs(any(), any(PrivilegedAction.class))) + .thenAnswer(inv -> { + PrivilegedAction action = inv.getArgument(1); + return action.run(); + }); + Map cfg = new HashMap<>(baseConnProps()); + cfg.put("jdbc.driverClassName", + "org.apache.ranger.services.trino.client.TestTrinoClient$ClinitFailingJdbcDriver"); + + assertThrows(HadoopException.class, () -> new NoConnectionTrinoClient("svc", cfg)); + } + } + + @Test + void test58_getSchemaList_whenDebugEnabled_logsSqlExceptionAtDebug() throws Exception { + Logger trinoLog = (Logger) LoggerFactory.getLogger(TrinoClient.class); + Level previous = trinoLog.getLevel(); + trinoLog.setLevel(Level.DEBUG); + try { + try (MockedStatic dmStatic = Mockito.mockStatic(DriverManager.class); + MockedStatic subjectStatic = Mockito.mockStatic(Subject.class)) { + Connection mockCon = mock(Connection.class); + DatabaseMetaData metadata = mock(DatabaseMetaData.class); + dmStatic.when(() -> DriverManager.getConnection(anyString(), any())).thenReturn(mockCon); + when(mockCon.getMetaData()).thenReturn(metadata); + doThrow(new SQLException("schema boom debug")).when(metadata).getSchemas(anyString(), anyString()); + subjectStatic.when(() -> Subject.doAs(any(), any(PrivilegedAction.class))) + .thenAnswer(inv -> { + PrivilegedAction action = inv.getArgument(1); + return action.run(); + }); + + TrinoClient client = createMockedClient(); + Field fCon = TrinoClient.class.getDeclaredField("con"); + fCon.setAccessible(true); + fCon.set(client, mockCon); + + assertThrows(HadoopException.class, + () -> client.getSchemaList("sch", Collections.singletonList("cat-a"), null)); + } + } finally { + trinoLog.setLevel(previous); + } + } + + @Test + void test59_getTableList_whenDebugEnabled_logsSqlExceptionAtDebug() throws Exception { + Logger trinoLog = (Logger) LoggerFactory.getLogger(TrinoClient.class); + Level previous = trinoLog.getLevel(); + trinoLog.setLevel(Level.DEBUG); + try { + try (MockedStatic dmStatic = Mockito.mockStatic(DriverManager.class); + MockedStatic subjectStatic = Mockito.mockStatic(Subject.class)) { + Connection mockCon = mock(Connection.class); + DatabaseMetaData metadata = mock(DatabaseMetaData.class); + dmStatic.when(() -> DriverManager.getConnection(anyString(), any())).thenReturn(mockCon); + when(mockCon.getMetaData()).thenReturn(metadata); + doThrow(new SQLException("tbl boom debug")).when(metadata).getTables(anyString(), anyString(), anyString(), any()); + subjectStatic.when(() -> Subject.doAs(any(), any(PrivilegedAction.class))) + .thenAnswer(inv -> { + PrivilegedAction action = inv.getArgument(1); + return action.run(); + }); + + TrinoClient client = createMockedClient(); + Field fCon = TrinoClient.class.getDeclaredField("con"); + fCon.setAccessible(true); + fCon.set(client, mockCon); + + assertThrows(HadoopException.class, + () -> client.getTableList("t", + Collections.singletonList("c"), + Collections.singletonList("s"), + null)); + } + } finally { + trinoLog.setLevel(previous); + } + } + + @Test + void test60_getColumnList_whenDebugEnabled_logsSqlExceptionAtDebug() throws Exception { + Logger trinoLog = (Logger) LoggerFactory.getLogger(TrinoClient.class); + Level previous = trinoLog.getLevel(); + trinoLog.setLevel(Level.DEBUG); + try { + try (MockedStatic dmStatic = Mockito.mockStatic(DriverManager.class); + MockedStatic subjectStatic = Mockito.mockStatic(Subject.class)) { + Connection mockCon = mock(Connection.class); + DatabaseMetaData metadata = mock(DatabaseMetaData.class); + dmStatic.when(() -> DriverManager.getConnection(anyString(), any())).thenReturn(mockCon); + when(mockCon.getMetaData()).thenReturn(metadata); + doThrow(new SQLException("col boom debug")).when(metadata) + .getColumns(anyString(), anyString(), anyString(), anyString()); + subjectStatic.when(() -> Subject.doAs(any(), any(PrivilegedAction.class))) + .thenAnswer(inv -> { + PrivilegedAction action = inv.getArgument(1); + return action.run(); + }); + + TrinoClient client = createMockedClient(); + Field fCon = TrinoClient.class.getDeclaredField("con"); + fCon.setAccessible(true); + fCon.set(client, mockCon); + + assertThrows(HadoopException.class, + () -> client.getColumnList("c", + Collections.singletonList("cat"), + Collections.singletonList("sch"), + Collections.singletonList("tbl"), + null)); + } + } finally { + trinoLog.setLevel(previous); + } + } + + @Test + void test61_getSchemaList_whenDebugEnabled_logsTimeoutAtDebug() throws Exception { + Logger trinoLog = (Logger) LoggerFactory.getLogger(TrinoClient.class); + Level previous = trinoLog.getLevel(); + trinoLog.setLevel(Level.DEBUG); + try { + try (MockedStatic dmStatic = Mockito.mockStatic(DriverManager.class); + MockedStatic subjectStatic = Mockito.mockStatic(Subject.class)) { + Connection mockCon = mock(Connection.class); + DatabaseMetaData metadata = mock(DatabaseMetaData.class); + dmStatic.when(() -> DriverManager.getConnection(anyString(), any())).thenReturn(mockCon); + when(mockCon.getMetaData()).thenReturn(metadata); + doThrow(new SQLTimeoutException("sch timeout debug")).when(metadata) + .getSchemas(anyString(), anyString()); + subjectStatic.when(() -> Subject.doAs(any(), any(PrivilegedAction.class))) + .thenAnswer(inv -> { + PrivilegedAction action = inv.getArgument(1); + return action.run(); + }); + + TrinoClient client = createMockedClient(); + Field fCon = TrinoClient.class.getDeclaredField("con"); + fCon.setAccessible(true); + fCon.set(client, mockCon); + + assertThrows(HadoopException.class, + () -> client.getSchemaList("sch", Collections.singletonList("cat-a"), null)); + } + } finally { + trinoLog.setLevel(previous); + } + } + + @Test + void test62_getTableList_whenDebugEnabled_logsTimeoutAtDebug() throws Exception { + Logger trinoLog = (Logger) LoggerFactory.getLogger(TrinoClient.class); + Level previous = trinoLog.getLevel(); + trinoLog.setLevel(Level.DEBUG); + try { + try (MockedStatic dmStatic = Mockito.mockStatic(DriverManager.class); + MockedStatic subjectStatic = Mockito.mockStatic(Subject.class)) { + Connection mockCon = mock(Connection.class); + DatabaseMetaData metadata = mock(DatabaseMetaData.class); + dmStatic.when(() -> DriverManager.getConnection(anyString(), any())).thenReturn(mockCon); + when(mockCon.getMetaData()).thenReturn(metadata); + doThrow(new SQLTimeoutException("tbl to debug")).when(metadata) + .getTables(anyString(), anyString(), anyString(), any()); + subjectStatic.when(() -> Subject.doAs(any(), any(PrivilegedAction.class))) + .thenAnswer(inv -> { + PrivilegedAction action = inv.getArgument(1); + return action.run(); + }); + + TrinoClient client = createMockedClient(); + Field fCon = TrinoClient.class.getDeclaredField("con"); + fCon.setAccessible(true); + fCon.set(client, mockCon); + + assertThrows(HadoopException.class, + () -> client.getTableList("t", + Collections.singletonList("c"), + Collections.singletonList("s"), + null)); + } + } finally { + trinoLog.setLevel(previous); + } + } + + @Test + void test63_getColumnList_whenDebugEnabled_logsTimeoutAtDebug() throws Exception { + Logger trinoLog = (Logger) LoggerFactory.getLogger(TrinoClient.class); + Level previous = trinoLog.getLevel(); + trinoLog.setLevel(Level.DEBUG); + try { + try (MockedStatic dmStatic = Mockito.mockStatic(DriverManager.class); + MockedStatic subjectStatic = Mockito.mockStatic(Subject.class)) { + Connection mockCon = mock(Connection.class); + DatabaseMetaData metadata = mock(DatabaseMetaData.class); + dmStatic.when(() -> DriverManager.getConnection(anyString(), any())).thenReturn(mockCon); + when(mockCon.getMetaData()).thenReturn(metadata); + doThrow(new SQLTimeoutException("col to debug")).when(metadata) + .getColumns(anyString(), anyString(), anyString(), anyString()); + subjectStatic.when(() -> Subject.doAs(any(), any(PrivilegedAction.class))) + .thenAnswer(inv -> { + PrivilegedAction action = inv.getArgument(1); + return action.run(); + }); + + TrinoClient client = createMockedClient(); + Field fCon = TrinoClient.class.getDeclaredField("con"); + fCon.setAccessible(true); + fCon.set(client, mockCon); + + assertThrows(HadoopException.class, + () -> client.getColumnList("c", + Collections.singletonList("cat"), + Collections.singletonList("sch"), + Collections.singletonList("tbl"), + null)); + } + } finally { + trinoLog.setLevel(previous); + } + } + + @Test + void test64_initConnection_securityManagerBlocksDriver_throwsHadoopException() { + final SecurityManager previous = System.getSecurityManager(); + try { + System.setSecurityManager(new SecurityManager() { + @Override + public void checkPermission(java.security.Permission perm) { + if (previous != null) { + previous.checkPermission(perm); + } + } + + @Override + public void checkPackageAccess(String pkg) { + if (pkg != null && pkg.startsWith("io.trino")) { + throw new SecurityException("blocked trino driver package"); + } + if (previous != null) { + previous.checkPackageAccess(pkg); + } + } + }); + try (MockedStatic subjectStatic = Mockito.mockStatic(Subject.class)) { + subjectStatic.when(() -> Subject.doAs(any(), any(PrivilegedAction.class))) + .thenAnswer(inv -> { + PrivilegedAction action = inv.getArgument(1); + return action.run(); + }); + assertThrows(HadoopException.class, + () -> new NoConnectionTrinoClient("svc", baseConnProps())); + } + } finally { + System.setSecurityManager(previous); + } + } + + private static class SingleArgNoLoginTrinoClient extends TrinoClient { + SingleArgNoLoginTrinoClient(String serviceName) { + super(serviceName); + } + + @Override + protected Subject getLoginSubject() { + return new Subject(); + } + + @Override + protected void login() { + } + } + private static class NoConnectionTrinoClient extends TrinoClient { - public NoConnectionTrinoClient(String serviceName, Map connectionProperties) { + NoConnectionTrinoClient(String serviceName, Map connectionProperties) { super(serviceName, connectionProperties); } diff --git a/plugin-trino/src/test/java/org/apache/ranger/services/trino/client/TestTrinoConnectionManager.java b/plugin-trino/src/test/java/org/apache/ranger/services/trino/client/TestTrinoConnectionManager.java new file mode 100644 index 0000000000..ed5921549d --- /dev/null +++ b/plugin-trino/src/test/java/org/apache/ranger/services/trino/client/TestTrinoConnectionManager.java @@ -0,0 +1,205 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.ranger.services.trino.client; + +import org.apache.ranger.plugin.client.HadoopException; +import org.apache.ranger.plugin.util.TimedEventUtil; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.MockedConstruction; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.Callable; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; + +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; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.mockConstruction; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +/** + * @generated by Cursor + * @description : Unit test cases for TrinoConnectionManager. + */ +@ExtendWith(MockitoExtension.class) +@TestMethodOrder(MethodOrderer.MethodName.class) +class TestTrinoConnectionManager { + private Map sampleConfigs() { + Map cfg = new HashMap<>(); + cfg.put("username", "u"); + cfg.put("password", "p"); + cfg.put("jdbc.driverClassName", "io.trino.jdbc.TrinoDriver"); + cfg.put("jdbc.url", "jdbc:trino://localhost:8080"); + return cfg; + } + + @Test + void test01_getTrinoConnection_whenServiceTypeNull_returnsNull() { + TrinoConnectionManager mgr = new TrinoConnectionManager(); + + TrinoClient out = mgr.getTrinoConnection("svc", null, sampleConfigs()); + + assertNull(out); + } + + @Test + void test02_getTrinoConnection_whenConfigsNull_returnsNull() { + TrinoConnectionManager mgr = new TrinoConnectionManager(); + + try (MockedConstruction unused = mockConstruction(TrinoClient.class)) { + TrinoClient out = mgr.getTrinoConnection("svc", "trino", null); + + assertNull(out); + } + } + + @Test + void test03_getTrinoConnection_whenTimedTaskFails_returnsNull() { + TrinoConnectionManager mgr = new TrinoConnectionManager(); + + try (MockedStatic timed = Mockito.mockStatic(TimedEventUtil.class)) { + timed.when(() -> TimedEventUtil.timedTask(any(Callable.class), eq(5L), eq(TimeUnit.SECONDS))) + .thenThrow(new RuntimeException("connect failed")); + + TrinoClient out = mgr.getTrinoConnection("svc", "trino", sampleConfigs()); + + assertNull(out); + } + } + + @Test + void test04_getTrinoConnection_createsClientAndCachesInstance() { + Map cfg = sampleConfigs(); + + try (MockedConstruction cons = mockConstruction(TrinoClient.class, + (mock, ctx) -> when(mock.getCatalogList(eq("*"), any())).thenReturn(Collections.singletonList("ok")))) { + TrinoConnectionManager mgr = new TrinoConnectionManager(); + + TrinoClient first = mgr.getTrinoConnection("svc", "trino", cfg); + TrinoClient second = mgr.getTrinoConnection("svc", "trino", cfg); + + assertNotNull(first); + assertSame(first, second); + verify(first, times(1)).getCatalogList("*", null); + } + } + + @Test + void test05_getTrinoConnection_whenCachedClientFailsValidation_reconnectsWithNewClient() { + Map cfg = sampleConfigs(); + AtomicInteger validationCalls = new AtomicInteger(); + + try (MockedConstruction cons = mockConstruction(TrinoClient.class, + (mock, ctx) -> when(mock.getCatalogList(eq("*"), any())).thenAnswer(inv -> { + int n = validationCalls.incrementAndGet(); + if (n == 1) { + throw new HadoopException("stale", new RuntimeException()); + } + return List.of("fresh"); + }))) { + TrinoConnectionManager mgr = new TrinoConnectionManager(); + + TrinoClient first = mgr.getTrinoConnection("svc", "trino", cfg); + assertNotNull(first); + + TrinoClient second = mgr.getTrinoConnection("svc", "trino", cfg); + + assertNotNull(second); + assertNotSame(first, second); + assertEquals(2, cons.constructed().size()); + } + } + + @Test + void test06_getTrinoConnection_whenCachedClientFailsValidation_closesStaleClient() { + Map cfg = sampleConfigs(); + AtomicInteger validationCalls = new AtomicInteger(); + + try (MockedConstruction cons = mockConstruction(TrinoClient.class, + (mock, ctx) -> when(mock.getCatalogList(eq("*"), any())).thenAnswer(inv -> { + int n = validationCalls.incrementAndGet(); + if (n == 1) { + throw new HadoopException("stale", new RuntimeException()); + } + return List.of("fresh"); + }))) { + TrinoConnectionManager mgr = new TrinoConnectionManager(); + + mgr.getTrinoConnection("svc", "trino", cfg); + mgr.getTrinoConnection("svc", "trino", cfg); + + List constructed = cons.constructed(); + assertEquals(2, constructed.size()); + verify(constructed.get(0)).close(); + } + } + + @Test + void test07_getTrinoConnection_whenPutIfAbsentSeesExisting_closesDuplicateClient() { + Map cfg = sampleConfigs(); + TrinoClient winner = mock(TrinoClient.class); + + ConcurrentHashMap inner = new ConcurrentHashMap<>(); + inner.put("svc", winner); + + ConcurrentHashMap cache = new ConcurrentHashMap() { + private final AtomicBoolean maskFirstGet = new AtomicBoolean(true); + + @Override + public TrinoClient get(Object key) { + if (maskFirstGet.compareAndSet(true, false)) { + return null; + } + return inner.get(key); + } + + @Override + public TrinoClient putIfAbsent(String key, TrinoClient value) { + return inner.putIfAbsent(key, value); + } + }; + + try (MockedConstruction cons = mockConstruction(TrinoClient.class, + (mock, ctx) -> when(mock.getCatalogList(eq("*"), any())) + .thenReturn(Collections.singletonList("ok")))) { + TrinoConnectionManager mgr = new TrinoConnectionManager(); + mgr.trinoConnectionCache = cache; + + TrinoClient out = mgr.getTrinoConnection("svc", "trino", cfg); + + assertSame(winner, out); + assertEquals(1, cons.constructed().size()); + verify(cons.constructed().get(0)).close(); + } + } +} diff --git a/plugin-trino/src/test/java/org/apache/ranger/services/trino/client/TestTrinoResourceManager.java b/plugin-trino/src/test/java/org/apache/ranger/services/trino/client/TestTrinoResourceManager.java new file mode 100644 index 0000000000..9d8fa6c323 --- /dev/null +++ b/plugin-trino/src/test/java/org/apache/ranger/services/trino/client/TestTrinoResourceManager.java @@ -0,0 +1,286 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.ranger.services.trino.client; + +import org.apache.ranger.plugin.service.ResourceLookupContext; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.MockedConstruction; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyMap; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mockConstruction; +import static org.mockito.Mockito.mockStatic; +import static org.mockito.Mockito.verify; + +/** + * @generated by Cursor + * @description : Unit test cases for TrinoResourceManager. + */ +@ExtendWith(MockitoExtension.class) +@TestMethodOrder(MethodOrderer.MethodName.class) +class TestTrinoResourceManager { + private Map sampleConfigs() { + Map cfg = new HashMap<>(); + cfg.put("username", "u"); + cfg.put("password", "p"); + cfg.put("jdbc.driverClassName", "io.trino.jdbc.TrinoDriver"); + cfg.put("jdbc.url", "jdbc:trino://localhost:8080"); + return cfg; + } + + @Test + void test01_connectionTest_delegatesToTrinoClient() { + Map cfg = sampleConfigs(); + Map expected = new HashMap<>(); + expected.put("connectivityStatus", Boolean.TRUE); + + try (MockedStatic mocked = mockStatic(TrinoClient.class)) { + mocked.when(() -> TrinoClient.connectionTest(eq("svc"), eq(cfg))).thenReturn(expected); + + Map ret = TrinoResourceManager.connectionTest("svc", cfg); + + assertSame(expected, ret); + mocked.verify(() -> TrinoClient.connectionTest(eq("svc"), eq(cfg))); + } + } + + @Test + void test02_connectionTest_whenDelegateThrows_propagatesException() { + Map cfg = sampleConfigs(); + RuntimeException boom = new RuntimeException("boom"); + + try (MockedStatic mocked = mockStatic(TrinoClient.class)) { + mocked.when(() -> TrinoClient.connectionTest(eq("svc"), eq(cfg))).thenThrow(boom); + + assertThrows(RuntimeException.class, () -> TrinoResourceManager.connectionTest("svc", cfg)); + } + } + + @Test + void test03_getTrinoResources_catalogLookup_returnsCatalogNames() throws Exception { + Map cfg = sampleConfigs(); + ResourceLookupContext ctx = new ResourceLookupContext(); + ctx.setUserInput("needle"); + ctx.setResourceName("catalog"); + + List expected = Arrays.asList("c1", "c2"); + + try (MockedConstruction mgrCons = mockConstruction(TrinoConnectionManager.class, + (mockMgr, context) -> { + TrinoClient client = Mockito.mock(TrinoClient.class); + Mockito.when(mockMgr.getTrinoConnection(eq("svc"), eq("trino"), eq(cfg))).thenReturn(client); + Mockito.when(client.getCatalogList(eq("needle"), any())).thenReturn(expected); + })) { + List out = TrinoResourceManager.getTrinoResources("svc", "trino", cfg, ctx); + + assertEquals(expected, out); + } + } + + @Test + void test04_getTrinoResources_schemaLookup_returnsSchemas() throws Exception { + Map cfg = sampleConfigs(); + ResourceLookupContext ctx = new ResourceLookupContext(); + ctx.setUserInput("sch"); + ctx.setResourceName("schema"); + Map> resourceMap = new HashMap<>(); + resourceMap.put("catalog", Collections.singletonList("cat-a")); + ctx.setResources(resourceMap); + + List expected = Collections.singletonList("sch-one"); + + try (MockedConstruction mgrCons = mockConstruction(TrinoConnectionManager.class, + (mockMgr, context) -> { + TrinoClient client = Mockito.mock(TrinoClient.class); + Mockito.when(mockMgr.getTrinoConnection(eq("svc"), eq("trino"), eq(cfg))).thenReturn(client); + Mockito.when(client.getSchemaList(eq("sch"), eq(Collections.singletonList("cat-a")), eq(null))) + .thenReturn(expected); + })) { + List out = TrinoResourceManager.getTrinoResources("svc", "trino", cfg, ctx); + + assertEquals(expected, out); + } + } + + @Test + void test05_getTrinoResources_tableLookup_returnsTables() throws Exception { + Map cfg = sampleConfigs(); + ResourceLookupContext ctx = new ResourceLookupContext(); + ctx.setUserInput("tbl"); + ctx.setResourceName("table"); + Map> resourceMap = new HashMap<>(); + resourceMap.put("catalog", Collections.singletonList("cat-a")); + resourceMap.put("schema", Collections.singletonList("sch-a")); + ctx.setResources(resourceMap); + + List expected = Collections.singletonList("tbl-one"); + + try (MockedConstruction mgrCons = mockConstruction(TrinoConnectionManager.class, + (mockMgr, context) -> { + TrinoClient client = Mockito.mock(TrinoClient.class); + Mockito.when(mockMgr.getTrinoConnection(eq("svc"), eq("trino"), eq(cfg))).thenReturn(client); + Mockito.when(client.getTableList(eq("tbl"), + eq(Collections.singletonList("cat-a")), + eq(Collections.singletonList("sch-a")), + eq(null))).thenReturn(expected); + })) { + List out = TrinoResourceManager.getTrinoResources("svc", "trino", cfg, ctx); + + assertEquals(expected, out); + } + } + + @Test + void test06_getTrinoResources_columnLookup_appendsWildcardAndQueriesColumns() throws Exception { + Map cfg = sampleConfigs(); + ResourceLookupContext ctx = new ResourceLookupContext(); + ctx.setUserInput("col"); + ctx.setResourceName("column"); + Map> resourceMap = new HashMap<>(); + resourceMap.put("catalog", Collections.singletonList("cat-a")); + resourceMap.put("schema", Collections.singletonList("sch-a")); + resourceMap.put("table", Collections.singletonList("tbl-a")); + ctx.setResources(resourceMap); + + List expected = Collections.singletonList("col-one"); + + try (MockedConstruction mgrCons = mockConstruction(TrinoConnectionManager.class, + (mockMgr, context) -> { + TrinoClient client = Mockito.mock(TrinoClient.class); + Mockito.when(mockMgr.getTrinoConnection(eq("svc"), eq("trino"), eq(cfg))).thenReturn(client); + Mockito.when(client.getColumnList(eq("col*"), + eq(Collections.singletonList("cat-a")), + eq(Collections.singletonList("sch-a")), + eq(Collections.singletonList("tbl-a")), + eq(null))).thenReturn(expected); + })) { + List out = TrinoResourceManager.getTrinoResources("svc", "trino", cfg, ctx); + + assertEquals(expected, out); + } + } + + @Test + void test07_getTrinoResources_whenServiceNameNull_returnsNull() throws Exception { + ResourceLookupContext ctx = new ResourceLookupContext(); + ctx.setUserInput("x"); + ctx.setResourceName("catalog"); + + List out = TrinoResourceManager.getTrinoResources(null, "trino", sampleConfigs(), ctx); + + assertNull(out); + } + + @Test + void test08_getTrinoResources_whenUserInputNull_returnsNull() throws Exception { + ResourceLookupContext ctx = new ResourceLookupContext(); + ctx.setUserInput(null); + ctx.setResourceName("catalog"); + + List out = TrinoResourceManager.getTrinoResources("svc", "trino", sampleConfigs(), ctx); + + assertNull(out); + } + + @Test + void test09_getTrinoResources_unknownResourceName_returnsNull() throws Exception { + ResourceLookupContext ctx = new ResourceLookupContext(); + ctx.setUserInput("needle"); + ctx.setResourceName("unknown-layer"); + + try (MockedConstruction mgrCons = mockConstruction(TrinoConnectionManager.class, + (mockMgr, context) -> { + TrinoClient client = Mockito.mock(TrinoClient.class); + Mockito.when(mockMgr.getTrinoConnection(anyString(), anyString(), anyMap())).thenReturn(client); + })) { + List out = TrinoResourceManager.getTrinoResources("svc", "trino", sampleConfigs(), ctx); + + assertNull(out); + List constructed = mgrCons.constructed(); + assertEquals(1, constructed.size()); + verify(constructed.get(0)).getTrinoConnection(eq("svc"), eq("trino"), anyMap()); + } + } + + @Test + void test10_getTrinoResources_resourceNameTrimmedCaseInsensitive() throws Exception { + Map cfg = sampleConfigs(); + ResourceLookupContext ctx = new ResourceLookupContext(); + ctx.setUserInput("needle"); + ctx.setResourceName(" CATALOG "); + + List expected = Collections.singletonList("c1"); + + try (MockedConstruction mgrCons = mockConstruction(TrinoConnectionManager.class, + (mockMgr, context) -> { + TrinoClient client = Mockito.mock(TrinoClient.class); + Mockito.when(mockMgr.getTrinoConnection(eq("svc"), eq("trino"), eq(cfg))).thenReturn(client); + Mockito.when(client.getCatalogList(eq("needle"), any())).thenReturn(expected); + })) { + List out = TrinoResourceManager.getTrinoResources("svc", "trino", cfg, ctx); + + assertEquals(expected, out); + } + } + + @Test + void test11_getTrinoResources_whenTrinoClientNull_returnsNull() throws Exception { + ResourceLookupContext ctx = new ResourceLookupContext(); + ctx.setUserInput("needle"); + ctx.setResourceName("catalog"); + + try (MockedConstruction mgrCons = mockConstruction(TrinoConnectionManager.class, + (mockMgr, context) -> Mockito.when(mockMgr.getTrinoConnection(anyString(), anyString(), anyMap())) + .thenReturn(null))) { + List out = TrinoResourceManager.getTrinoResources("svc", "trino", sampleConfigs(), ctx); + + assertNull(out); + } + } + + @Test + void test12_getTrinoResources_whenTimedLookupFails_propagatesException() throws Exception { + ResourceLookupContext ctx = new ResourceLookupContext(); + ctx.setUserInput("needle"); + ctx.setResourceName("catalog"); + + try (MockedConstruction mgrCons = mockConstruction(TrinoConnectionManager.class, + (mockMgr, context) -> { + TrinoClient client = Mockito.mock(TrinoClient.class); + Mockito.when(mockMgr.getTrinoConnection(anyString(), anyString(), anyMap())).thenReturn(client); + Mockito.when(client.getCatalogList(anyString(), any())).thenThrow(new IllegalStateException("bad")); + })) { + assertThrows(IllegalStateException.class, + () -> TrinoResourceManager.getTrinoResources("svc", "trino", sampleConfigs(), ctx)); + } + } +}