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 ad8ffb30f73 Add more test cases on ProxyDatabaseConnectionManagerTest
(#37980)
ad8ffb30f73 is described below
commit ad8ffb30f7362dcb4204ed4d91650015eeec549b
Author: Liang Zhang <[email protected]>
AuthorDate: Sun Feb 8 00:23:09 2026 +0800
Add more test cases on ProxyDatabaseConnectionManagerTest (#37980)
* Add more test cases on ProxyDatabaseConnectionManagerTest
* Add more test cases on ProxyDatabaseConnectionManagerTest
* Add more test cases on ProxyDatabaseConnectionManagerTest
---
.codex/skills/analyze-issue/SKILL.md | 2 -
.codex/skills/gen-ut/SKILL.md | 42 ++++-
.codex/skills/review-pr/SKILL.md | 6 +-
AGENTS.md | 11 +-
.../ProxyDatabaseConnectionManagerTest.java | 199 ++++++++++++++++++---
5 files changed, 218 insertions(+), 42 deletions(-)
diff --git a/.codex/skills/analyze-issue/SKILL.md
b/.codex/skills/analyze-issue/SKILL.md
index 912daa64ff4..2da33936b64 100644
--- a/.codex/skills/analyze-issue/SKILL.md
+++ b/.codex/skills/analyze-issue/SKILL.md
@@ -187,8 +187,6 @@ Type-specific rules:
## Mandatory Output Structure
-Respond in the same language as the user.
-
Four-section structure (Question, Misunderstanding / Invalid Usage):
1. Problem Understanding
2. Root Cause
diff --git a/.codex/skills/gen-ut/SKILL.md b/.codex/skills/gen-ut/SKILL.md
index 8b847f90071..83805e26257 100644
--- a/.codex/skills/gen-ut/SKILL.md
+++ b/.codex/skills/gen-ut/SKILL.md
@@ -43,6 +43,20 @@ Test class placeholder convention:
- `R10`: If a related test class already exists for a target class, extend
that class to add only missing-coverage tests; create a new test class only
when no related test class exists.
- `R11`: Do not claim completion if target tests were not actually executed
due compile/runtime blockers. First remove blockers with minimal test-scope
fixes and rerun verification;
only when blockers are outside scope and cannot be resolved safely in-turn,
report exact blocker files/lines/commands and request user decision.
+- `R12`: Boolean assertion policy in tests:
+ - Must use `assertTrue` / `assertFalse` for boolean checks.
+ - Forbidden assertion patterns:
+ - `assertThat(<boolean expression>, is(true))`
+ - `assertThat(<boolean expression>, is(false))`
+ - `assertEquals(true, ...)`
+ - `assertEquals(false, ...)`
+- `R13`: Hard gate for `R12`:
+ - Use the unified scan regex:
+ -
`assertThat\\s*\\(.*is\\s*\\(\\s*(true|false)\\s*\\)\\s*\\)|assertEquals\\s*\\(\\s*(true|false)\\s*,`
+ - Run hard-gate scan twice:
+ - after test implementation (early fail-fast gate);
+ - before final delivery (final release gate).
+ - If any match is found, task state is "not complete" until all violations
are fixed and scan is rerun clean.
## Execution Boundary
@@ -58,9 +72,11 @@ Test class placeholder convention:
3. Output branch-path inventory according to `R4`.
4. Output branch-to-test mapping according to `R5`.
5. Perform dead-code analysis according to `R8` and record findings.
-6. Implement or extend tests according to `R2/R3/R6/R7/R10`.
-7. Run verification commands and iterate.
-8. Deliver results using the output structure.
+6. Implement or extend tests according to `R2/R3/R6/R7/R10/R12/R13`.
+7. Run the first `R13` hard-gate scan (early fail-fast) and fix all hits.
+8. Run verification commands and iterate.
+9. Run the second `R13` hard-gate scan (final release gate) and ensure clean.
+10. Deliver results using the output structure.
## Verification and Commands
@@ -81,6 +97,11 @@ With module input:
./mvnw -pl <module> -am -Pcheck checkstyle:check -DskipTests
```
+4. `R13` hard-gate scan (must be clean, run in step 7 and step 9):
+```bash
+bash -lc 'if rg -n
"assertThat\\s*\\(.*is\\s*\\(\\s*(true|false)\\s*\\)\\s*\\)|assertEquals\\s*\\(\\s*(true|false)\\s*,"
<module>/src/test/java; then echo "[R13] forbidden boolean assertion found";
exit 1; fi'
+```
+
Without module input:
1. Targeted unit tests:
@@ -98,6 +119,11 @@ Without module input:
./mvnw -Pcheck checkstyle:check -DskipTests
```
+4. `R13` hard-gate scan (must be clean, run in step 7 and step 9):
+```bash
+bash -lc 'if rg -n
"assertThat\\s*\\(.*is\\s*\\(\\s*(true|false)\\s*\\)\\s*\\)|assertEquals\\s*\\(\\s*(true|false)\\s*,"
. --glob "**/src/test/java/**"; then echo "[R13] forbidden boolean assertion
found"; exit 1; fi'
+```
+
Command execution rules:
- Record every command and exit code.
- If a command fails, record the failure reason and execute at least one
remediation attempt; if still blocked, continue clearing blockers within test
scope before escalating.
@@ -105,18 +131,20 @@ Command execution rules:
## Output Structure
-Respond in the same language as the user and follow this order:
+Follow this order:
-1. Goal and constraints (mapped to `R1-R11`)
+1. Goal and constraints (mapped to `R1-R13`)
2. Plan and implementation (including branch mapping result)
3. Dead-code and coverage results (according to `R8/R9`)
4. Verification commands and exit codes
-5. Risks and next actions
+5. `R13` hard-gate evidence (both scan commands and exit codes)
+6. Risks and next actions
## Quality Self-Check
- Rule definitions must exist only in "Mandatory Constraints"; other sections
should reference rule IDs only.
-- Final state must satisfy `R9`, and all applicable rules (including `R10` and
`R11`) must be met, with complete command and exit-code records.
+- Final state must satisfy `R9`, and all applicable rules (including `R10`,
`R11`, `R12`, and `R13`) must be met, with complete command and exit-code
records.
+- `R13` command is mandatory evidence; missing `R13` command record or
non-clean scan means not complete.
## Maintenance Rules
diff --git a/.codex/skills/review-pr/SKILL.md b/.codex/skills/review-pr/SKILL.md
index 8a6395a5392..0edf8989ed1 100644
--- a/.codex/skills/review-pr/SKILL.md
+++ b/.codex/skills/review-pr/SKILL.md
@@ -34,10 +34,8 @@ description: >-
and list the minimum additional information required.
5. Change request replies must be gentle in tone and contain no emojis.
6. If unrelated changes exist, you must explicitly ask for rollback; if none
exist, do not output that section.
-7. Use the same language as the user;
- if the user explicitly specifies a language, prioritize that language.
-8. Any "fallback-only without root-cause repair" or "unresolved risk" must not
receive `Merge Verdict: Mergeable`.
-9. Review only the PR's latest code version; do not reuse conclusions from
older versions.
+7. Any "fallback-only without root-cause repair" or "unresolved risk" must not
receive `Merge Verdict: Mergeable`.
+8. Review only the PR's latest code version; do not reuse conclusions from
older versions.
## Execution Boundary
diff --git a/AGENTS.md b/AGENTS.md
index c9e3f481742..e37047451fa 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -62,7 +62,7 @@ This guide is written **for AI coding agents only**. Follow
it literally; improv
- `topic`: technical topic or concept (e.g., "adaptive servo control").
- `depth`: 1-3 to control semantic layers.
-## ⚠️ Dangerous Operation Confirmation Mechanism
+## Dangerous Operation Confirmation Mechanism
### High-Risk Operation Checklist—obtain explicit confirmation **before**
doing any of the following:
- **File System**: deleting files/directories, bulk edits, or moving system
files.
@@ -74,7 +74,7 @@ This guide is written **for AI coding agents only**. Follow
it literally; improv
### Confirmation Template
-⚠️ Dangerous operation detected! Operation type: [specific action] Scope of
impact: [affected area] Risk assessment: [potential consequence] Please confirm
whether to continue. [Requires explicit “yes”, “confirm”, or “proceed”]
+Dangerous operation detected! Operation type: [specific action] Scope of
impact: [affected area] Risk assessment: [potential consequence] Please confirm
whether to continue. [Requires explicit “yes”, “confirm”, or “proceed”]
## Workflow
- Use Sequential Thinking when tasks need decomposition: 6-10 steps (fallback
3-5), one sentence each, actionable.
@@ -101,8 +101,9 @@ This guide is written **for AI coding agents only**. Follow
it literally; improv
## Response Style
### Language and Tone
+- **Language Consistency**: respond in the same language as the user; if the
user explicitly specifies a language, prioritize that language.
- **Friendly and Natural**: interact like a professional peer; avoid stiff
formal language.
-- **Use Light Accents**: prepend headings or bullets with emojis such as ✨⚠️
to highlight key points.
+- **No Emojis or Symbols**: do not use emojis or decorative graphic symbols in
any reply.
- **Hit the Point Fast**: start with a sentence that captures the core idea,
especially for complex problems.
### Content Organization
@@ -110,7 +111,7 @@ This guide is written **for AI coding agents only**. Follow
it literally; improv
- **Focused Bullets**: break long paragraphs into short sentences or bullets,
each covering a single idea.
- **Logical Flow**: use ordered lists for multi-step work (1. 2. 3.) and
unordered lists for peers (- or *).
- **Proper Spacing**: keep blank lines or `---` between blocks to boost
readability.
-> ❌ Avoid complex tables in the terminal (especially for long, code-heavy, or
narrative content).
+> Avoid complex tables in the terminal (especially for long, code-heavy, or
narrative content).
### Visual & Layout Optimization
- **Keep It Simple**: limit each line length to ≤200 characters.
@@ -135,7 +136,7 @@ This guide is written **for AI coding agents only**. Follow
it literally; improv
- **Visible Status**: surface progress for important actions (e.g.,
“Processing...”).
- **Friendly Errors**: clearly explain failures and suggest actionable fixes.
-### ✅ Ending Suggestions
+### Ending Suggestions
- Append a **short summary** after complex content to reiterate the core
points.
- **Guide the Next Step**: close with actionable advice, instructions, or an
invitation for follow-up questions.
diff --git
a/proxy/backend/core/src/test/java/org/apache/shardingsphere/proxy/backend/connector/ProxyDatabaseConnectionManagerTest.java
b/proxy/backend/core/src/test/java/org/apache/shardingsphere/proxy/backend/connector/ProxyDatabaseConnectionManagerTest.java
index f4c280d90a6..6803abf5d23 100644
---
a/proxy/backend/core/src/test/java/org/apache/shardingsphere/proxy/backend/connector/ProxyDatabaseConnectionManagerTest.java
+++
b/proxy/backend/core/src/test/java/org/apache/shardingsphere/proxy/backend/connector/ProxyDatabaseConnectionManagerTest.java
@@ -30,6 +30,7 @@ import
org.apache.shardingsphere.infra.metadata.database.resource.unit.StorageUn
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.rule.ShardingSphereRule;
import
org.apache.shardingsphere.infra.session.connection.transaction.TransactionConnectionContext;
import org.apache.shardingsphere.infra.spi.type.typed.TypedSPILoader;
import org.apache.shardingsphere.mode.manager.ContextManager;
@@ -44,14 +45,19 @@ import
org.apache.shardingsphere.proxy.backend.handler.ProxyBackendHandler;
import org.apache.shardingsphere.proxy.backend.session.ConnectionSession;
import
org.apache.shardingsphere.proxy.backend.session.RequiredSessionVariableRecorder;
import
org.apache.shardingsphere.proxy.backend.session.transaction.TransactionStatus;
+import
org.apache.shardingsphere.sql.parser.statement.core.enums.TransactionIsolationLevel;
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.api.TransactionType;
import org.apache.shardingsphere.transaction.rule.TransactionRule;
+import org.apache.shardingsphere.transaction.spi.TransactionHook;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
import org.mockito.Answers;
import org.mockito.Mock;
import org.mockito.MockedConstruction;
@@ -69,21 +75,26 @@ import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.stream.Stream;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+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.anyInt;
+import static org.mockito.ArgumentMatchers.anyList;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.mockConstruction;
-import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
@@ -157,6 +168,12 @@ class ProxyDatabaseConnectionManagerTest {
assertTrue(connectionSession.getTransactionStatus().isInTransaction());
}
+ @Test
+ void assertGetConnectionsWithNullDatabaseName() {
+ NullPointerException actualException =
assertThrows(NullPointerException.class, () ->
databaseConnectionManager.getConnections(null, "ds1", 0, 1,
ConnectionMode.MEMORY_STRICTLY));
+ assertThat(actualException.getMessage(), is("Current database name is
null."));
+ }
+
@Test
void assertGetConnectionSizeLessThanCache() throws SQLException {
connectionSession.getTransactionStatus().setInTransaction(true);
@@ -190,6 +207,35 @@ class ProxyDatabaseConnectionManagerTest {
assertTrue(connectionSession.getTransactionStatus().isInTransaction());
}
+ @Test
+ void assertGetConnectionWithTransactionHook() throws SQLException {
+ connectionSession.getTransactionStatus().setInTransaction(true);
+ ShardingSphereRule rule = mock(ShardingSphereRule.class);
+ TransactionHook transactionHook = mock(TransactionHook.class);
+ setTransactionHooks(Collections.singletonMap(rule, transactionHook));
+ when(backendDataSource.getConnections(anyString(), anyString(), eq(1),
any())).thenReturn(MockConnectionUtils.mockNewConnections(1));
+ databaseConnectionManager.getConnections("foo_db", "ds1", 0, 1,
ConnectionMode.MEMORY_STRICTLY);
+ verify(transactionHook).afterCreateConnections(eq(rule), any(),
anyList(), any());
+ }
+
+ @Test
+ void assertGetConnectionWithReplayTransactionOption() throws SQLException {
+ when(connectionSession.isReadOnly()).thenReturn(true);
+
when(connectionSession.getIsolationLevel()).thenReturn(Optional.of(TransactionIsolationLevel.READ_UNCOMMITTED));
+ Connection connection = mock(Connection.class);
+ when(backendDataSource.getConnections(anyString(), anyString(), eq(1),
any())).thenReturn(Collections.singletonList(connection));
+ databaseConnectionManager.getConnections("foo_db", "ds1", 0, 1,
ConnectionMode.MEMORY_STRICTLY);
+ verify(connection).setReadOnly(true);
+ verify(connection).setTransactionIsolation(anyInt());
+ }
+
+ @Test
+ void assertGetConnectionWithNullConnection() throws SQLException {
+ when(backendDataSource.getConnections(anyString(), anyString(), eq(1),
any())).thenReturn(Collections.singletonList(null));
+ List<Connection> actualConnections =
databaseConnectionManager.getConnections("foo_db", "ds1", 0, 1,
ConnectionMode.MEMORY_STRICTLY);
+ assertThat(actualConnections, is(Collections.singletonList(null)));
+ }
+
@SneakyThrows(ReflectiveOperationException.class)
private void setConnectionPostProcessors() {
ConnectionPostProcessor connectionPostProcessor =
mock(ConnectionPostProcessor.class);
@@ -198,6 +244,11 @@ class ProxyDatabaseConnectionManagerTest {
Plugins.getMemberAccessor().set(ProxyDatabaseConnectionManager.class.getDeclaredField("connectionPostProcessors"),
databaseConnectionManager, connectionPostProcessors);
}
+ @SneakyThrows(ReflectiveOperationException.class)
+ private void setTransactionHooks(final Map<ShardingSphereRule,
TransactionHook> transactionHooks) {
+
Plugins.getMemberAccessor().set(ProxyDatabaseConnectionManager.class.getDeclaredField("transactionHooks"),
databaseConnectionManager, transactionHooks);
+ }
+
@SuppressWarnings("unchecked")
@Test
void assertCloseConnectionsCorrectlyWhenNotForceRollback() throws
ReflectiveOperationException, SQLException {
@@ -219,28 +270,36 @@ class ProxyDatabaseConnectionManagerTest {
assertTrue(connectionPostProcessors.isEmpty());
}
- @Test
- void assertCloseConnectionsCorrectlyWhenForceRollbackAndNotInTransaction()
throws SQLException {
- connectionSession.getTransactionStatus().setInTransaction(false);
+ @ParameterizedTest(name = "{0}")
+ @MethodSource("closeConnectionsWithForceRollbackArguments")
+ void assertCloseConnectionsWithForceRollback(final String scenario, final
boolean inTransaction, final boolean rollbackFailed, final int
expectedRollbackCount) throws SQLException {
+
connectionSession.getTransactionStatus().setInTransaction(inTransaction);
Connection connection = prepareCachedConnections();
- databaseConnectionManager.closeConnections(true);
- verify(connection, never()).rollback();
- }
-
- @Test
- void assertCloseConnectionsCorrectlyWhenForceRollbackAndInTransaction()
throws SQLException {
- connectionSession.getTransactionStatus().setInTransaction(true);
- Connection connection = prepareCachedConnections();
- databaseConnectionManager.closeConnections(true);
- verify(connection).rollback();
+ if (rollbackFailed) {
+ doThrow(new SQLException("")).when(connection).rollback();
+ }
+ Collection<SQLException> actualExceptions =
databaseConnectionManager.closeConnections(true);
+ assertTrue(actualExceptions.isEmpty());
+ verify(connection, times(expectedRollbackCount)).rollback();
+ verify(connection).close();
}
- @Test
- void assertCloseConnectionsCorrectlyWhenSQLExceptionThrown() throws
SQLException {
+ @ParameterizedTest(name = "{0}")
+ @MethodSource("closeConnectionsWithCloseExceptionArguments")
+ void assertCloseConnectionsWithCloseException(final String scenario, final
boolean connectionClosed, final boolean checkClosedFailed, final int
expectedExceptionCount) throws SQLException {
Connection connection = prepareCachedConnections();
- SQLException sqlException = new SQLException("");
- doThrow(sqlException).when(connection).close();
-
assertTrue(databaseConnectionManager.closeConnections(false).contains(sqlException));
+ SQLException expectedException = new SQLException("");
+ doThrow(expectedException).when(connection).close();
+ if (checkClosedFailed) {
+ when(connection.isClosed()).thenThrow(new SQLException(""));
+ } else {
+ when(connection.isClosed()).thenReturn(connectionClosed);
+ }
+ Collection<SQLException> actualExceptions =
databaseConnectionManager.closeConnections(false);
+ assertThat(actualExceptions.size(), is(expectedExceptionCount));
+ if (1 == expectedExceptionCount) {
+ assertThat(actualExceptions.iterator().next(),
is(expectedException));
+ }
}
@SuppressWarnings("JDBCResourceOpenedButNotSafelyClosed")
@@ -267,6 +326,13 @@ class ProxyDatabaseConnectionManagerTest {
verify(actualConnection.createStatement()).execute("SET key=value");
}
+ @Test
+ void assertGetConnectionsWithEmptyConnectionAndSessionVariables() throws
SQLException {
+
connectionSession.getRequiredSessionVariableRecorder().setVariable("key",
"value");
+ when(backendDataSource.getConnections(anyString(), anyString(), eq(1),
any())).thenReturn(Collections.emptyList());
+ assertThrows(IndexOutOfBoundsException.class, () ->
databaseConnectionManager.getConnections("foo_db", "ds1", 0, 1,
ConnectionMode.CONNECTION_STRICTLY));
+ }
+
@SuppressWarnings("JDBCResourceOpenedButNotSafelyClosed")
@Test
void assertGetConnectionsAndFailedToReplaySessionVariables() throws
SQLException {
@@ -285,6 +351,25 @@ class ProxyDatabaseConnectionManagerTest {
}
}
+ @SuppressWarnings("JDBCResourceOpenedButNotSafelyClosed")
+ @Test
+ void assertGetConnectionsAndFailedToReleaseConnection() throws
SQLException {
+
connectionSession.getRequiredSessionVariableRecorder().setVariable("key",
"value");
+ SQLException expectedException = new SQLException("");
+ SQLException expectedNextException = new SQLException("");
+ Connection firstConnection = mock(Connection.class,
RETURNS_DEEP_STUBS);
+ Connection secondConnection = mock(Connection.class,
RETURNS_DEEP_STUBS);
+
when(firstConnection.getMetaData().getDatabaseProductName()).thenReturn("PostgreSQL");
+ when(firstConnection.createStatement().execute("SET
key=value")).thenThrow(expectedException);
+
when(secondConnection.getMetaData().getDatabaseProductName()).thenReturn("PostgreSQL");
+ doThrow(expectedNextException).when(secondConnection).close();
+
when(ProxyContext.getInstance().getBackendDataSource().getConnections(anyString(),
anyString(), anyInt(), any(ConnectionMode.class)))
+ .thenReturn(Arrays.asList(firstConnection, secondConnection));
+ SQLException actualException = assertThrows(SQLException.class, () ->
databaseConnectionManager.getConnections("foo_db", "", 0, 2,
ConnectionMode.CONNECTION_STRICTLY));
+ assertThat(actualException, is(expectedException));
+ assertThat(actualException.getNextException(),
is(expectedNextException));
+ }
+
@Test
void assertGetConnectionsWithoutTransactions() throws SQLException {
connectionSession.getTransactionStatus().setInTransaction(false);
@@ -318,13 +403,17 @@ class ProxyDatabaseConnectionManagerTest {
not(databaseConnectionManager.getConnections("foo_db", "ds1",
1, 1, ConnectionMode.MEMORY_STRICTLY)));
}
- @Test
- void assertHandleAutoCommit() {
- when(connectionSession.isAutoCommit()).thenReturn(false);
- connectionSession.getTransactionStatus().setInTransaction(false);
+ @ParameterizedTest(name = "{0}")
+ @MethodSource("handleAutoCommitArguments")
+ void assertHandleAutoCommit(final String scenario, final boolean
autoCommit, final boolean inTransaction, final int
expectedTransactionManagerCount) {
+ when(connectionSession.isAutoCommit()).thenReturn(autoCommit);
+
connectionSession.getTransactionStatus().setInTransaction(inTransaction);
try (MockedConstruction<ProxyBackendTransactionManager>
mockedConstruction = mockConstruction(ProxyBackendTransactionManager.class)) {
databaseConnectionManager.handleAutoCommit();
- verify(mockedConstruction.constructed().get(0)).begin();
+ assertThat(mockedConstruction.constructed().size(),
is(expectedTransactionManagerCount));
+ if (1 == expectedTransactionManagerCount) {
+ verify(mockedConstruction.constructed().get(0)).begin();
+ }
}
}
@@ -408,6 +497,32 @@ class ProxyDatabaseConnectionManagerTest {
assertThat(getInUseProxyBackendHandlers(),
is(Collections.singleton(inUseHandler)));
}
+ @Test
+ void assertCloseExecutionResourcesInTransactionWhenClosed() throws
BackendConnectionException, SQLException {
+ connectionSession.getTransactionStatus().setInTransaction(true);
+ ProxyBackendHandler inUseHandler = mock(ProxyBackendHandler.class);
+ getProxyBackendHandlers().add(inUseHandler);
+ getInUseProxyBackendHandlers().add(inUseHandler);
+ Connection cachedConnection = mock(Connection.class);
+
databaseConnectionManager.getCachedConnections().put("ignoredDataSourceName",
cachedConnection);
+ databaseConnectionManager.getClosed().set(true);
+ databaseConnectionManager.closeExecutionResources();
+ verify(inUseHandler).close();
+ verify(cachedConnection).rollback();
+ verify(cachedConnection).close();
+ }
+
+ @Test
+ void assertCloseExecutionResourcesWithException() throws SQLException {
+ ProxyBackendHandler handler = mock(ProxyBackendHandler.class);
+ SQLException expectedException = new SQLException("");
+ doThrow(expectedException).when(handler).close();
+ getProxyBackendHandlers().add(handler);
+ BackendConnectionException actualException =
assertThrows(BackendConnectionException.class, () ->
databaseConnectionManager.closeExecutionResources());
+ assertThat(actualException.getExceptions().size(), is(1));
+ assertThat(actualException.getExceptions().iterator().next(),
is(expectedException));
+ }
+
@SuppressWarnings("unchecked")
@SneakyThrows(ReflectiveOperationException.class)
private Collection<ProxyBackendHandler> getProxyBackendHandlers() {
@@ -473,6 +588,13 @@ class ProxyDatabaseConnectionManagerTest {
assertThat(actualExceptions,
is(Collections.singletonList(expectedException)));
}
+ @Test
+ void assertCloseConnectionsWithoutCachedConnectionsAndVariables() {
+
connectionSession.getRequiredSessionVariableRecorder().setVariable("key",
"default");
+ databaseConnectionManager.closeConnections(false);
+
assertFalse(connectionSession.getRequiredSessionVariableRecorder().isEmpty());
+ }
+
@Test
void assertGetDataSourceNamesOfCachedConnections() {
databaseConnectionManager.getCachedConnections().put(connectionSession.getUsedDatabaseName()
+ ".ds_0", null);
@@ -482,4 +604,33 @@ class ProxyDatabaseConnectionManagerTest {
Collections.sort(actual);
assertThat(actual, is(Arrays.asList("ds_0", "ds_1", "ds_2")));
}
+
+ @Test
+ void assertGetDataSourceNamesWithoutCurrentDatabaseName() {
+
databaseConnectionManager.getCachedConnections().put(connectionSession.getUsedDatabaseName()
+ ".ds_0", mock(Connection.class));
+ databaseConnectionManager.getCachedConnections().put("schema_1.ds_1",
mock(Connection.class));
+ List<String> actual = new
ArrayList<>(databaseConnectionManager.getUsedDataSourceNames());
+ assertThat(actual, is(Collections.singletonList("ds_0")));
+ }
+
+ private static Stream<Arguments>
closeConnectionsWithForceRollbackArguments() {
+ return Stream.of(
+
Arguments.of("closeConnections_forceRollback_notInTransaction", false, false,
0),
+ Arguments.of("closeConnections_forceRollback_inTransaction",
true, false, 1),
+ Arguments.of("closeConnections_forceRollback_rollbackFailed",
true, true, 1));
+ }
+
+ private static Stream<Arguments>
closeConnectionsWithCloseExceptionArguments() {
+ return Stream.of(
+ Arguments.of("closeConnections_closeException_notClosed",
false, false, 1),
+
Arguments.of("closeConnections_closeException_connectionClosed", true, false,
0),
+
Arguments.of("closeConnections_closeException_checkClosedFailed", false, true,
1));
+ }
+
+ private static Stream<Arguments> handleAutoCommitArguments() {
+ return Stream.of(
+ Arguments.of("handleAutoCommit_beginTransaction", false,
false, 1),
+ Arguments.of("handleAutoCommit_autoCommitEnabled", true,
false, 0),
+ Arguments.of("handleAutoCommit_inTransaction", false, true,
0));
+ }
}