This is an automated email from the ASF dual-hosted git repository.
zhangliang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/shardingsphere.git
The following commit(s) were added to refs/heads/master by this push:
new c4fb46f5a8c Improve coverage for transaction and unicast handlers
(#37564)
c4fb46f5a8c is described below
commit c4fb46f5a8ccafb5492b6835a8044624cdc70ebd
Author: Liang Zhang <[email protected]>
AuthorDate: Sun Dec 28 23:26:55 2025 +0800
Improve coverage for transaction and unicast handlers (#37564)
* Improve coverage for transaction and unicast handlers
* Improve coverage for transaction and unicast handlers
* Improve coverage for transaction and unicast handlers
---
AGENTS.md | 8 +++
.../jdbc/datasource/JDBCBackendDataSourceTest.java | 46 +++++++++++-
.../UnicastDatabaseProxyBackendHandlerTest.java | 83 ++++++++++++++++++++++
.../backend/session/ConnectionSessionTest.java | 10 +++
4 files changed, 144 insertions(+), 3 deletions(-)
diff --git a/AGENTS.md b/AGENTS.md
index 4d570f81438..2188cfe9b70 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -80,6 +80,14 @@ This guide is written **for AI coding agents only**. Follow
it literally; improv
- Validate: run the narrowest meaningful checks (see Verification & Commands)
and prefer scoped runs; note any sandbox or limit blocks and alternatives.
- Report & self-check: share intent, edits, verification results, and next
steps; ensure all required instructions, coverage, and mocking rules are
satisfied, with remaining risks called out.
+## Compliance Guardrails & Checklists
+- **Pre-task checklist (do before planning/coding):** re-read AGENTS.md and
`CODE_OF_CONDUCT.md`; restate user goal, constraints, forbidden tools/APIs,
coverage expectations, sandbox/network/approval limits; prefer
`rg`/`./mvnw`/`apply_patch`; avoid destructive commands (`git reset --hard`,
`git checkout --`, bulk deletes) and generated paths like `target/`.
+- **Risk gate:** if any action fits the Dangerous Operation Checklist, pause
and use the confirmation template before proceeding.
+- **Planning rules:** use Sequential Thinking with 3-10 actionable steps (no
single-step plans) via the plan tool for non-trivial tasks; convert all hard
requirements (SPI usage, mocking rules, coverage/test naming, forbidden APIs)
into a checklist inside the plan and do not code until each item is addressed
or explicitly waived.
+- **Execution discipline:** inspect existing code before edits; keep changes
minimal; default to mocks and SPI loaders; keep variable declarations near
first use and mark retained values `final`; delete dead code and avoid
placeholders/TODOs.
+- **Post-task self-check (before replying):** confirm all instructions were
honored; verify no placeholders/unused code; ensure Checkstyle/Spotless gates
for touched modules are satisfied or explain why not run and what to run; list
commands with exit codes; call out risks and follow-ups.
+- **Final response template:** include intent/why, changed files with paths,
rationale per file/section, commands run (with exit codes), verification
status, and remaining risks/next actions (if tests skipped, state reason and
the exact command to run).
+
## Coverage & Branch Checklist
- When coverage targets are declared (including 100%), list every branch/path
with its planned test before coding.
- Map each branch to exactly one test; add cases until all declared branches
are covered or explicitly waived.
diff --git
a/proxy/backend/core/src/test/java/org/apache/shardingsphere/proxy/backend/connector/jdbc/datasource/JDBCBackendDataSourceTest.java
b/proxy/backend/core/src/test/java/org/apache/shardingsphere/proxy/backend/connector/jdbc/datasource/JDBCBackendDataSourceTest.java
index 13826d8f425..0112f1105dc 100644
---
a/proxy/backend/core/src/test/java/org/apache/shardingsphere/proxy/backend/connector/jdbc/datasource/JDBCBackendDataSourceTest.java
+++
b/proxy/backend/core/src/test/java/org/apache/shardingsphere/proxy/backend/connector/jdbc/datasource/JDBCBackendDataSourceTest.java
@@ -18,6 +18,7 @@
package org.apache.shardingsphere.proxy.backend.connector.jdbc.datasource;
import lombok.RequiredArgsConstructor;
+import
org.apache.shardingsphere.database.connector.core.GlobalDataSourceRegistry;
import org.apache.shardingsphere.database.connector.core.type.DatabaseType;
import org.apache.shardingsphere.infra.config.props.ConfigurationProperties;
import
org.apache.shardingsphere.infra.exception.kernel.connection.OverallConnectionNotEnoughException;
@@ -25,6 +26,7 @@ import
org.apache.shardingsphere.infra.executor.sql.execute.engine.ConnectionMod
import org.apache.shardingsphere.infra.metadata.ShardingSphereMetaData;
import
org.apache.shardingsphere.infra.metadata.database.ShardingSphereDatabase;
import
org.apache.shardingsphere.infra.metadata.database.resource.ResourceMetaData;
+import
org.apache.shardingsphere.infra.metadata.database.resource.unit.StorageUnit;
import org.apache.shardingsphere.infra.metadata.database.rule.RuleMetaData;
import
org.apache.shardingsphere.infra.metadata.statistics.ShardingSphereStatistics;
import
org.apache.shardingsphere.infra.metadata.statistics.builder.ShardingSphereStatisticsFactory;
@@ -35,13 +37,14 @@ import
org.apache.shardingsphere.proxy.backend.connector.jdbc.datasource.fixture
import org.apache.shardingsphere.proxy.backend.context.ProxyContext;
import
org.apache.shardingsphere.test.infra.framework.extension.mock.AutoMockExtension;
import
org.apache.shardingsphere.test.infra.framework.extension.mock.StaticMockSettings;
+import
org.apache.shardingsphere.transaction.ShardingSphereTransactionManagerEngine;
+import org.apache.shardingsphere.transaction.api.TransactionType;
import org.apache.shardingsphere.transaction.rule.TransactionRule;
+import
org.apache.shardingsphere.transaction.spi.ShardingSphereDistributedTransactionManager;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.MockedStatic;
-import org.mockito.junit.jupiter.MockitoSettings;
-import org.mockito.quality.Strictness;
import javax.sql.DataSource;
import java.sql.Connection;
@@ -67,11 +70,12 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.mockStatic;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@ExtendWith(AutoMockExtension.class)
@StaticMockSettings(ProxyContext.class)
-@MockitoSettings(strictness = Strictness.LENIENT)
class JDBCBackendDataSourceTest {
private static final String DATA_SOURCE_PATTERN = "ds_%s";
@@ -118,6 +122,42 @@ class JDBCBackendDataSourceTest {
assertThrows(OverallConnectionNotEnoughException.class, () -> new
JDBCBackendDataSource().getConnections("schema",
String.format(DATA_SOURCE_PATTERN, 1), 6, ConnectionMode.MEMORY_STRICTLY));
}
+ @Test
+ void assertGetConnectionsWithConnectionStrictlyMode() throws SQLException {
+ List<Connection> actual = new
JDBCBackendDataSource().getConnections("schema",
String.format(DATA_SOURCE_PATTERN, 0), 2, ConnectionMode.CONNECTION_STRICTLY);
+ assertThat(actual.size(), is(2));
+ }
+
+ @Test
+ void assertGetConnectionsFromDistributedTransactionManagerAndSetCatalog()
throws SQLException {
+ ContextManager contextManager = mock(ContextManager.class,
RETURNS_DEEP_STUBS);
+ StorageUnit storageUnit = mock(StorageUnit.class, RETURNS_DEEP_STUBS);
+ DataSource dataSource = mock(DataSource.class);
+ when(storageUnit.getDataSource()).thenReturn(dataSource);
+
when(contextManager.getMetaDataContexts().getMetaData().getDatabase("schema").getResourceMetaData().getStorageUnits().get("cached.ds1")).thenReturn(storageUnit);
+ ShardingSphereDistributedTransactionManager
distributedTransactionManager =
mock(ShardingSphereDistributedTransactionManager.class);
+ when(distributedTransactionManager.isInTransaction()).thenReturn(true);
+ Connection connection = mock(Connection.class);
+ when(distributedTransactionManager.getConnection("schema",
"cached.ds1")).thenReturn(connection);
+ ShardingSphereTransactionManagerEngine engine =
mock(ShardingSphereTransactionManagerEngine.class);
+
when(engine.getTransactionManager(TransactionType.XA)).thenReturn(distributedTransactionManager);
+ TransactionRule transactionRule = mock(TransactionRule.class);
+ when(transactionRule.getDefaultType()).thenReturn(TransactionType.XA);
+ when(transactionRule.getResource()).thenReturn(engine);
+ RuleMetaData ruleMetaData = new
RuleMetaData(Collections.singleton(transactionRule));
+
when(contextManager.getMetaDataContexts().getMetaData().getGlobalRuleMetaData()).thenReturn(ruleMetaData);
+
GlobalDataSourceRegistry.getInstance().getCachedDataSources().put("cached",
dataSource);
+ try {
+
when(ProxyContext.getInstance().getContextManager()).thenReturn(contextManager);
+ List<Connection> actual = new
JDBCBackendDataSource().getConnections("schema", "cached.ds1", 1,
ConnectionMode.CONNECTION_STRICTLY);
+ assertThat(actual.size(), is(1));
+ verify(connection).setCatalog("ds1");
+ verify(dataSource, never()).getConnection();
+ } finally {
+
GlobalDataSourceRegistry.getInstance().getCachedDataSources().clear();
+ }
+ }
+
@Test
void assertGetConnectionsByMultiThreads() throws InterruptedException {
JDBCBackendDataSource jdbcBackendDataSource = new
JDBCBackendDataSource();
diff --git
a/proxy/backend/core/src/test/java/org/apache/shardingsphere/proxy/backend/handler/data/type/UnicastDatabaseProxyBackendHandlerTest.java
b/proxy/backend/core/src/test/java/org/apache/shardingsphere/proxy/backend/handler/data/type/UnicastDatabaseProxyBackendHandlerTest.java
index 29401d05bbf..d5ac667cd95 100644
---
a/proxy/backend/core/src/test/java/org/apache/shardingsphere/proxy/backend/handler/data/type/UnicastDatabaseProxyBackendHandlerTest.java
+++
b/proxy/backend/core/src/test/java/org/apache/shardingsphere/proxy/backend/handler/data/type/UnicastDatabaseProxyBackendHandlerTest.java
@@ -17,12 +17,15 @@
package org.apache.shardingsphere.proxy.backend.handler.data.type;
+import org.apache.shardingsphere.authority.model.ShardingSpherePrivileges;
+import org.apache.shardingsphere.authority.rule.AuthorityRule;
import org.apache.shardingsphere.database.connector.core.type.DatabaseType;
import
org.apache.shardingsphere.infra.binder.context.statement.SQLStatementContext;
import org.apache.shardingsphere.infra.hint.HintValueContext;
import org.apache.shardingsphere.infra.metadata.ShardingSphereMetaData;
import
org.apache.shardingsphere.infra.metadata.database.ShardingSphereDatabase;
import
org.apache.shardingsphere.infra.metadata.database.resource.ResourceMetaData;
+import org.apache.shardingsphere.infra.metadata.database.rule.RuleMetaData;
import
org.apache.shardingsphere.infra.metadata.statistics.ShardingSphereStatistics;
import
org.apache.shardingsphere.infra.metadata.statistics.builder.ShardingSphereStatisticsFactory;
import org.apache.shardingsphere.infra.session.connection.ConnectionContext;
@@ -35,6 +38,7 @@ import
org.apache.shardingsphere.proxy.backend.connector.DatabaseProxyConnectorF
import
org.apache.shardingsphere.proxy.backend.connector.ProxyDatabaseConnectionManager;
import org.apache.shardingsphere.proxy.backend.response.header.ResponseHeader;
import
org.apache.shardingsphere.proxy.backend.response.header.update.UpdateResponseHeader;
+import org.apache.shardingsphere.proxy.backend.response.data.QueryResponseRow;
import org.apache.shardingsphere.proxy.backend.session.ConnectionSession;
import
org.apache.shardingsphere.test.infra.framework.extension.mock.AutoMockExtension;
import
org.apache.shardingsphere.test.infra.framework.extension.mock.StaticMockSettings;
@@ -43,9 +47,13 @@ import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Answers;
import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoSettings;
+import org.mockito.quality.Strictness;
import java.sql.SQLException;
+import java.util.Arrays;
import java.util.Collections;
+import java.util.List;
import java.util.Optional;
import static org.hamcrest.CoreMatchers.is;
@@ -55,10 +63,13 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@ExtendWith(AutoMockExtension.class)
@StaticMockSettings(DatabaseProxyConnectorFactory.class)
+@MockitoSettings(strictness = Strictness.LENIENT)
class UnicastDatabaseProxyBackendHandlerTest {
private static final String EXECUTE_SQL = "SELECT 1 FROM user WHERE id =
1";
@@ -114,4 +125,76 @@ class UnicastDatabaseProxyBackendHandlerTest {
assertThat(unicastDatabaseProxyBackendHandler.getRowData().getData().size(),
is(1));
}
}
+
+ @Test
+ void assertExecuteWithNullCurrentDatabaseChoosesFirstAvailable() throws
SQLException {
+ ConnectionSession connectionSession = mock(ConnectionSession.class,
RETURNS_DEEP_STUBS);
+
when(connectionSession.getConnectionContext().getCurrentDatabaseName()).thenReturn(Optional.empty());
+ AuthorityRule authorityRule = mock(AuthorityRule.class,
RETURNS_DEEP_STUBS);
+ ShardingSpherePrivileges privileges =
mock(ShardingSpherePrivileges.class);
+ when(privileges.hasPrivileges("bar_db")).thenReturn(true);
+
when(authorityRule.findPrivileges(any())).thenReturn(Optional.of(privileges));
+ ContextManager contextManager =
mockContextManagerWithAuthority(authorityRule, Arrays.asList("foo_db",
"bar_db"), Collections.singletonList("bar_db"));
+
when(DatabaseProxyConnectorFactory.newInstance(any(QueryContext.class),
any(ProxyDatabaseConnectionManager.class), eq(false)))
+ .thenReturn(mock(DatabaseProxyConnector.class,
RETURNS_DEEP_STUBS));
+ QueryContext queryContext = new
QueryContext(mock(SQLStatementContext.class, RETURNS_DEEP_STUBS), EXECUTE_SQL,
Collections.emptyList(), new HintValueContext(),
+ connectionSession.getConnectionContext(),
contextManager.getMetaDataContexts().getMetaData());
+ new UnicastDatabaseProxyBackendHandler(queryContext, contextManager,
connectionSession).execute();
+ verify(connectionSession).setCurrentDatabaseName("bar_db");
+ }
+
+ @Test
+ void assertGetRowDataAndCloseAfterExecute() throws SQLException {
+ AuthorityRule authorityRule = mock(AuthorityRule.class,
RETURNS_DEEP_STUBS);
+ ShardingSpherePrivileges privileges =
mock(ShardingSpherePrivileges.class);
+ when(privileges.hasPrivileges("foo_db")).thenReturn(true);
+
when(authorityRule.findPrivileges(any())).thenReturn(Optional.of(privileges));
+ ContextManager contextManager =
mockContextManagerWithAuthority(authorityRule,
Collections.singletonList("foo_db"), Collections.singletonList("foo_db"));
+ ConnectionSession connectionSession = mock(ConnectionSession.class,
RETURNS_DEEP_STUBS);
+ when(connectionSession.getCurrentDatabaseName()).thenReturn("foo_db");
+
when(connectionSession.getConnectionContext().getCurrentDatabaseName()).thenReturn(Optional.of("foo_db"));
+ DatabaseProxyConnector connector = mock(DatabaseProxyConnector.class);
+ QueryResponseRow row = mock(QueryResponseRow.class);
+ when(connector.execute()).thenReturn(new UpdateResponseHeader(mock()));
+ when(connector.next()).thenReturn(true, false);
+ when(connector.getRowData()).thenReturn(row);
+
when(DatabaseProxyConnectorFactory.newInstance(any(QueryContext.class),
any(ProxyDatabaseConnectionManager.class), eq(false))).thenReturn(connector);
+ QueryContext queryContext = new
QueryContext(mock(SQLStatementContext.class, RETURNS_DEEP_STUBS), EXECUTE_SQL,
Collections.emptyList(), new HintValueContext(),
+ connectionSession.getConnectionContext(),
contextManager.getMetaDataContexts().getMetaData());
+ UnicastDatabaseProxyBackendHandler handler = new
UnicastDatabaseProxyBackendHandler(queryContext, contextManager,
connectionSession);
+ handler.execute();
+ handler.next();
+ assertThat(handler.getRowData(), is(row));
+ handler.close();
+ verify(connector).close();
+ }
+
+ @Test
+ void assertCloseWithoutExecute() throws SQLException {
+ AuthorityRule authorityRule = mock(AuthorityRule.class,
RETURNS_DEEP_STUBS);
+ ContextManager contextManager =
mockContextManagerWithAuthority(authorityRule,
Collections.singletonList("foo_db"), Collections.singletonList("foo_db"));
+ ConnectionSession connectionSession = mock(ConnectionSession.class,
RETURNS_DEEP_STUBS);
+ QueryContext queryContext = new
QueryContext(mock(SQLStatementContext.class, RETURNS_DEEP_STUBS), EXECUTE_SQL,
Collections.emptyList(), new HintValueContext(),
+ connectionSession.getConnectionContext(),
contextManager.getMetaDataContexts().getMetaData());
+ DatabaseProxyConnector connector = mock(DatabaseProxyConnector.class);
+
when(DatabaseProxyConnectorFactory.newInstance(any(QueryContext.class),
any(ProxyDatabaseConnectionManager.class), eq(false))).thenReturn(connector);
+ new UnicastDatabaseProxyBackendHandler(queryContext, contextManager,
connectionSession).close();
+ verify(connector, never()).close();
+ }
+
+ private ContextManager mockContextManagerWithAuthority(final AuthorityRule
authorityRule, final List<String> databaseNames, final List<String>
databasesWithStorageUnit) {
+ ContextManager result = mock(ContextManager.class, RETURNS_DEEP_STUBS);
+ when(result.getAllDatabaseNames()).thenReturn(databaseNames);
+ databaseNames.forEach(each -> {
+ ShardingSphereDatabase database =
mock(ShardingSphereDatabase.class);
+
when(database.containsDataSource()).thenReturn(databasesWithStorageUnit.contains(each));
+ when(result.getDatabase(each)).thenReturn(database);
+ });
+ ShardingSphereMetaData metaData = mock(ShardingSphereMetaData.class,
RETURNS_DEEP_STUBS);
+ RuleMetaData ruleMetaData = new
RuleMetaData(Collections.singleton(authorityRule));
+ when(metaData.getGlobalRuleMetaData()).thenReturn(ruleMetaData);
+ when(result.getMetaDataContexts().getMetaData()).thenReturn(metaData);
+
when(result.getMetaDataContexts().getMetaData().getGlobalRuleMetaData()).thenReturn(ruleMetaData);
+ return result;
+ }
}
diff --git
a/proxy/backend/core/src/test/java/org/apache/shardingsphere/proxy/backend/session/ConnectionSessionTest.java
b/proxy/backend/core/src/test/java/org/apache/shardingsphere/proxy/backend/session/ConnectionSessionTest.java
index cf97fd94c62..81178e5d818 100644
---
a/proxy/backend/core/src/test/java/org/apache/shardingsphere/proxy/backend/session/ConnectionSessionTest.java
+++
b/proxy/backend/core/src/test/java/org/apache/shardingsphere/proxy/backend/session/ConnectionSessionTest.java
@@ -50,6 +50,7 @@ import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.when;
@ExtendWith(AutoMockExtension.class)
@@ -82,6 +83,15 @@ class ConnectionSessionTest {
verify(contextMock, times(1)).setCurrentDatabaseName("db");
}
+ @Test
+ void assertSetCurrentSchemaWithSameNameDoNothing() throws
ReflectiveOperationException {
+ connectionSession.setCurrentDatabaseName("db");
+ ConnectionContext contextMock = mock(ConnectionContext.class);
+ getConnectionContextReference().set(contextMock);
+ connectionSession.setCurrentDatabaseName("db");
+ verify(contextMock, never()).setCurrentDatabaseName("db");
+ }
+
@Test
void assertSetCurrentSchemaWithNull() throws ReflectiveOperationException {
connectionSession.setCurrentDatabaseName("db");