This is an automated email from the ASF dual-hosted git repository.
maple pushed a commit to branch 2.x
in repository https://gitbox.apache.org/repos/asf/incubator-seata.git
The following commit(s) were added to refs/heads/2.x by this push:
new df3448f6bf test: add UT for XA module (#7776)
df3448f6bf is described below
commit df3448f6bfaf8fddca92262431b5586f1087725d
Author: maple <[email protected]>
AuthorDate: Fri Nov 21 15:42:58 2025 +0800
test: add UT for XA module (#7776)
---
changes/en-us/2.x.md | 1 +
changes/zh-cn/2.x.md | 3 +
.../xa/AbstractConnectionProxyXATest.java | 586 +++++++++++++++++++
.../xa/AbstractDataSourceProxyXATest.java | 239 ++++++++
.../rm/datasource/xa/ConnectionProxyXATest.java | 480 ++++++++++++++-
.../rm/datasource/xa/ExecuteTemplateXATest.java | 212 +++++++
.../xa/PreparedStatementProxyXATest.java | 648 +++++++++++++++++++++
.../rm/datasource/xa/ResourceManagerXATest.java | 354 +++++++++++
.../rm/datasource/xa/StatementProxyXATest.java | 567 ++++++++++++++++++
9 files changed, 3075 insertions(+), 15 deletions(-)
diff --git a/changes/en-us/2.x.md b/changes/en-us/2.x.md
index b54492d0e8..d27ede804c 100644
--- a/changes/en-us/2.x.md
+++ b/changes/en-us/2.x.md
@@ -107,6 +107,7 @@ Add changes here for all PR submitted to the 2.x branch.
- [[#7757](https://github.com/apache/incubator-seata/pull/7757)] add UT for
undo module
- [[#7763](https://github.com/apache/incubator-seata/pull/7763)] add UT for
RegistryNamingServerProperties and RegistryMetadataProperties
- [[#7764](https://github.com/apache/incubator-seata/pull/7764)] add some UT
for server/coordinator module
+- [[#7776](https://github.com/apache/incubator-seata/pull/7776)] add UT for XA
module
- [[#7788](https://github.com/apache/incubator-seata/pull/7788)] add some UT
for rm-datasource module
- [[#7774](https://github.com/apache/incubator-seata/pull/7774)] add some UT
for server/console module
- [[#7767](https://github.com/apache/incubator-seata/pull/7767)] add some UT
for server/cluster module
diff --git a/changes/zh-cn/2.x.md b/changes/zh-cn/2.x.md
index 6b568db625..828ebd7de5 100644
--- a/changes/zh-cn/2.x.md
+++ b/changes/zh-cn/2.x.md
@@ -106,6 +106,7 @@
- [[#7757](https://github.com/apache/incubator-seata/pull/7757)] 为 undo 模块添加单测
- [[#7763](https://github.com/apache/incubator-seata/pull/7763)] 为
RegistryNamingServerProperties 和 RegistryMetadataProperties 添加单测
- [[#7764](https://github.com/apache/incubator-seata/pull/7764)] 为
server/coordinator 模块添加单测
+- [[#7776](https://github.com/apache/incubator-seata/pull/7776)] 为 XA 模块添加单测
- [[#7788](https://github.com/apache/incubator-seata/pull/7788)] 为
rm-datasource 模块添加单测
- [[#7774](https://github.com/apache/incubator-seata/pull/7774)] 为
server/console 模块添加单测
- [[#7767](https://github.com/apache/incubator-seata/pull/7767)] 为
server/cluster 模块添加单测
@@ -113,6 +114,8 @@
- [[#7733](https://github.com/apache/incubator-seata/pull/7733)] 为 core 模块添加单测
- [[#7728](https://github.com/apache/incubator-seata/pull/7728)] 为 compatible
模块添加单测
- [[#7727](https://github.com/apache/incubator-seata/pull/7727)] 为 compatible
模块添加单测
+
+
### refactor:
- [[#7615](https://github.com/seata/seata/pull/7615)] 重构 DataSourceProxy
diff --git
a/rm-datasource/src/test/java/org/apache/seata/rm/datasource/xa/AbstractConnectionProxyXATest.java
b/rm-datasource/src/test/java/org/apache/seata/rm/datasource/xa/AbstractConnectionProxyXATest.java
new file mode 100644
index 0000000000..0ef24733c2
--- /dev/null
+++
b/rm-datasource/src/test/java/org/apache/seata/rm/datasource/xa/AbstractConnectionProxyXATest.java
@@ -0,0 +1,586 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.seata.rm.datasource.xa;
+
+import org.apache.seata.rm.BaseDataSourceResource;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import javax.sql.XAConnection;
+import java.sql.Array;
+import java.sql.Blob;
+import java.sql.Clob;
+import java.sql.Connection;
+import java.sql.DatabaseMetaData;
+import java.sql.NClob;
+import java.sql.PreparedStatement;
+import java.sql.SQLClientInfoException;
+import java.sql.SQLException;
+import java.sql.SQLWarning;
+import java.sql.SQLXML;
+import java.sql.Savepoint;
+import java.sql.Statement;
+import java.sql.Struct;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+import java.util.concurrent.Executor;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+/**
+ * Tests for AbstractConnectionProxyXA
+ *
+ */
+public class AbstractConnectionProxyXATest {
+
+ private Connection mockConnection;
+ private XAConnection mockXAConnection;
+ private BaseDataSourceResource mockResource;
+ private String xid;
+ private TestConnectionProxyXA connectionProxy;
+
+ @BeforeEach
+ public void setUp() throws SQLException {
+ mockConnection = mock(Connection.class);
+ mockXAConnection = mock(XAConnection.class);
+ mockResource = mock(BaseDataSourceResource.class);
+ xid = "testXid";
+
+ connectionProxy = new TestConnectionProxyXA(mockConnection,
mockXAConnection, mockResource, xid);
+ }
+
+ @Test
+ public void testGetWrappedXAConnection() {
+ XAConnection result = connectionProxy.getWrappedXAConnection();
+ Assertions.assertEquals(mockXAConnection, result);
+ }
+
+ @Test
+ public void testGetWrappedConnection() {
+ Connection result = connectionProxy.getWrappedConnection();
+ Assertions.assertEquals(mockConnection, result);
+ }
+
+ @Test
+ public void testCreateStatement() throws SQLException {
+ Statement mockStatement = mock(Statement.class);
+ when(mockConnection.createStatement()).thenReturn(mockStatement);
+
+ Statement result = connectionProxy.createStatement();
+
+ Assertions.assertNotNull(result);
+ Assertions.assertTrue(result instanceof StatementProxyXA);
+ verify(mockConnection).createStatement();
+ }
+
+ @Test
+ public void testCreateStatementWithParameters() throws SQLException {
+ Statement mockStatement = mock(Statement.class);
+ when(mockConnection.createStatement(anyInt(),
anyInt())).thenReturn(mockStatement);
+
+ Statement result = connectionProxy.createStatement(1, 2);
+
+ Assertions.assertNotNull(result);
+ Assertions.assertTrue(result instanceof StatementProxyXA);
+ verify(mockConnection).createStatement(1, 2);
+ }
+
+ @Test
+ public void testCreateStatementWithThreeParameters() throws SQLException {
+ Statement mockStatement = mock(Statement.class);
+ when(mockConnection.createStatement(anyInt(), anyInt(),
anyInt())).thenReturn(mockStatement);
+
+ Statement result = connectionProxy.createStatement(1, 2, 3);
+
+ Assertions.assertNotNull(result);
+ Assertions.assertTrue(result instanceof StatementProxyXA);
+ verify(mockConnection).createStatement(1, 2, 3);
+ }
+
+ @Test
+ public void testPrepareStatement() throws SQLException {
+ PreparedStatement mockPreparedStatement =
mock(PreparedStatement.class);
+
when(mockConnection.prepareStatement(anyString())).thenReturn(mockPreparedStatement);
+
+ PreparedStatement result = connectionProxy.prepareStatement("SELECT *
FROM test");
+
+ Assertions.assertNotNull(result);
+ Assertions.assertTrue(result instanceof PreparedStatementProxyXA);
+ verify(mockConnection).prepareStatement("SELECT * FROM test");
+ }
+
+ @Test
+ public void testPrepareStatementWithParameters() throws SQLException {
+ PreparedStatement mockPreparedStatement =
mock(PreparedStatement.class);
+ when(mockConnection.prepareStatement(anyString(), anyInt(),
anyInt())).thenReturn(mockPreparedStatement);
+
+ PreparedStatement result = connectionProxy.prepareStatement("SELECT *
FROM test", 1, 2);
+
+ Assertions.assertNotNull(result);
+ Assertions.assertTrue(result instanceof PreparedStatementProxyXA);
+ verify(mockConnection).prepareStatement("SELECT * FROM test", 1, 2);
+ }
+
+ @Test
+ public void testPrepareStatementWithThreeParameters() throws SQLException {
+ PreparedStatement mockPreparedStatement =
mock(PreparedStatement.class);
+ when(mockConnection.prepareStatement(anyString(), anyInt(), anyInt(),
anyInt()))
+ .thenReturn(mockPreparedStatement);
+
+ PreparedStatement result = connectionProxy.prepareStatement("SELECT *
FROM test", 1, 2, 3);
+
+ Assertions.assertNotNull(result);
+ Assertions.assertTrue(result instanceof PreparedStatementProxyXA);
+ verify(mockConnection).prepareStatement("SELECT * FROM test", 1, 2, 3);
+ }
+
+ @Test
+ public void testPrepareStatementWithAutoGeneratedKeys() throws
SQLException {
+ PreparedStatement mockPreparedStatement =
mock(PreparedStatement.class);
+ when(mockConnection.prepareStatement(anyString(),
anyInt())).thenReturn(mockPreparedStatement);
+
+ PreparedStatement result =
+ connectionProxy.prepareStatement("INSERT INTO test",
Statement.RETURN_GENERATED_KEYS);
+
+ Assertions.assertNotNull(result);
+ Assertions.assertTrue(result instanceof PreparedStatementProxyXA);
+ verify(mockConnection).prepareStatement("INSERT INTO test",
Statement.RETURN_GENERATED_KEYS);
+ }
+
+ @Test
+ public void testPrepareStatementWithColumnIndexes() throws SQLException {
+ PreparedStatement mockPreparedStatement =
mock(PreparedStatement.class);
+ int[] columnIndexes = {1, 2};
+ when(mockConnection.prepareStatement(anyString(),
any(int[].class))).thenReturn(mockPreparedStatement);
+
+ PreparedStatement result = connectionProxy.prepareStatement("INSERT
INTO test", columnIndexes);
+
+ Assertions.assertNotNull(result);
+ Assertions.assertTrue(result instanceof PreparedStatementProxyXA);
+ verify(mockConnection).prepareStatement("INSERT INTO test",
columnIndexes);
+ }
+
+ @Test
+ public void testPrepareStatementWithColumnNames() throws SQLException {
+ PreparedStatement mockPreparedStatement =
mock(PreparedStatement.class);
+ String[] columnNames = {"id", "name"};
+ when(mockConnection.prepareStatement(anyString(),
any(String[].class))).thenReturn(mockPreparedStatement);
+
+ PreparedStatement result = connectionProxy.prepareStatement("INSERT
INTO test", columnNames);
+
+ Assertions.assertNotNull(result);
+ Assertions.assertTrue(result instanceof PreparedStatementProxyXA);
+ verify(mockConnection).prepareStatement("INSERT INTO test",
columnNames);
+ }
+
+ @Test
+ public void testNativeSQL() throws SQLException {
+ String sql = "SELECT * FROM test";
+ String nativeSQL = "SELECT * FROM test_native";
+ when(mockConnection.nativeSQL(sql)).thenReturn(nativeSQL);
+
+ String result = connectionProxy.nativeSQL(sql);
+
+ Assertions.assertEquals(nativeSQL, result);
+ verify(mockConnection).nativeSQL(sql);
+ }
+
+ @Test
+ public void testIsClosed() throws SQLException {
+ when(mockConnection.isClosed()).thenReturn(false);
+
+ boolean result = connectionProxy.isClosed();
+
+ Assertions.assertFalse(result);
+ verify(mockConnection).isClosed();
+ }
+
+ @Test
+ public void testGetMetaData() throws SQLException {
+ DatabaseMetaData mockMetaData = mock(DatabaseMetaData.class);
+ when(mockConnection.getMetaData()).thenReturn(mockMetaData);
+
+ DatabaseMetaData result = connectionProxy.getMetaData();
+
+ Assertions.assertEquals(mockMetaData, result);
+ verify(mockConnection).getMetaData();
+ }
+
+ @Test
+ public void testSetReadOnly() throws SQLException {
+ connectionProxy.setReadOnly(true);
+
+ verify(mockConnection).setReadOnly(true);
+ }
+
+ @Test
+ public void testIsReadOnly() throws SQLException {
+ when(mockConnection.isReadOnly()).thenReturn(true);
+
+ boolean result = connectionProxy.isReadOnly();
+
+ Assertions.assertTrue(result);
+ verify(mockConnection).isReadOnly();
+ }
+
+ @Test
+ public void testSetCatalog() throws SQLException {
+ String catalog = "testCatalog";
+ connectionProxy.setCatalog(catalog);
+
+ verify(mockConnection).setCatalog(catalog);
+ }
+
+ @Test
+ public void testGetCatalog() throws SQLException {
+ String catalog = "testCatalog";
+ when(mockConnection.getCatalog()).thenReturn(catalog);
+
+ String result = connectionProxy.getCatalog();
+
+ Assertions.assertEquals(catalog, result);
+ verify(mockConnection).getCatalog();
+ }
+
+ @Test
+ public void testSetTransactionIsolation() throws SQLException {
+
connectionProxy.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
+
+
verify(mockConnection).setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
+ }
+
+ @Test
+ public void testGetTransactionIsolation() throws SQLException {
+
when(mockConnection.getTransactionIsolation()).thenReturn(Connection.TRANSACTION_READ_COMMITTED);
+
+ int result = connectionProxy.getTransactionIsolation();
+
+ Assertions.assertEquals(Connection.TRANSACTION_READ_COMMITTED, result);
+ verify(mockConnection).getTransactionIsolation();
+ }
+
+ @Test
+ public void testGetWarnings() throws SQLException {
+ SQLWarning mockWarning = mock(SQLWarning.class);
+ when(mockConnection.getWarnings()).thenReturn(mockWarning);
+
+ SQLWarning result = connectionProxy.getWarnings();
+
+ Assertions.assertEquals(mockWarning, result);
+ verify(mockConnection).getWarnings();
+ }
+
+ @Test
+ public void testClearWarnings() throws SQLException {
+ connectionProxy.clearWarnings();
+
+ verify(mockConnection).clearWarnings();
+ }
+
+ @Test
+ public void testGetTypeMap() throws SQLException {
+ Map<String, Class<?>> typeMap = new HashMap<>();
+ when(mockConnection.getTypeMap()).thenReturn(typeMap);
+
+ Map<String, Class<?>> result = connectionProxy.getTypeMap();
+
+ Assertions.assertEquals(typeMap, result);
+ verify(mockConnection).getTypeMap();
+ }
+
+ @Test
+ public void testSetTypeMap() throws SQLException {
+ Map<String, Class<?>> typeMap = new HashMap<>();
+ connectionProxy.setTypeMap(typeMap);
+
+ verify(mockConnection).setTypeMap(typeMap);
+ }
+
+ @Test
+ public void testSetHoldability() throws SQLException {
+ connectionProxy.setHoldability(1);
+
+ verify(mockConnection).setHoldability(1);
+ }
+
+ @Test
+ public void testGetHoldability() throws SQLException {
+ when(mockConnection.getHoldability()).thenReturn(1);
+
+ int result = connectionProxy.getHoldability();
+
+ Assertions.assertEquals(1, result);
+ verify(mockConnection).getHoldability();
+ }
+
+ @Test
+ public void testSetSavepoint() throws SQLException {
+ Savepoint mockSavepoint = mock(Savepoint.class);
+ when(mockConnection.setSavepoint()).thenReturn(mockSavepoint);
+
+ Savepoint result = connectionProxy.setSavepoint();
+
+ Assertions.assertEquals(mockSavepoint, result);
+ verify(mockConnection).setSavepoint();
+ }
+
+ @Test
+ public void testSetSavepointWithName() throws SQLException {
+ Savepoint mockSavepoint = mock(Savepoint.class);
+ when(mockConnection.setSavepoint("sp1")).thenReturn(mockSavepoint);
+
+ Savepoint result = connectionProxy.setSavepoint("sp1");
+
+ Assertions.assertEquals(mockSavepoint, result);
+ verify(mockConnection).setSavepoint("sp1");
+ }
+
+ @Test
+ public void testRollbackSavepoint() throws SQLException {
+ Savepoint mockSavepoint = mock(Savepoint.class);
+ connectionProxy.rollback(mockSavepoint);
+
+ verify(mockConnection).rollback(mockSavepoint);
+ }
+
+ @Test
+ public void testReleaseSavepoint() throws SQLException {
+ Savepoint mockSavepoint = mock(Savepoint.class);
+ connectionProxy.releaseSavepoint(mockSavepoint);
+
+ verify(mockConnection).releaseSavepoint(mockSavepoint);
+ }
+
+ @Test
+ public void testCreateClob() throws SQLException {
+ Clob mockClob = mock(Clob.class);
+ when(mockConnection.createClob()).thenReturn(mockClob);
+
+ Clob result = connectionProxy.createClob();
+
+ Assertions.assertEquals(mockClob, result);
+ verify(mockConnection).createClob();
+ }
+
+ @Test
+ public void testCreateBlob() throws SQLException {
+ Blob mockBlob = mock(Blob.class);
+ when(mockConnection.createBlob()).thenReturn(mockBlob);
+
+ Blob result = connectionProxy.createBlob();
+
+ Assertions.assertEquals(mockBlob, result);
+ verify(mockConnection).createBlob();
+ }
+
+ @Test
+ public void testCreateNClob() throws SQLException {
+ NClob mockNClob = mock(NClob.class);
+ when(mockConnection.createNClob()).thenReturn(mockNClob);
+
+ NClob result = connectionProxy.createNClob();
+
+ Assertions.assertEquals(mockNClob, result);
+ verify(mockConnection).createNClob();
+ }
+
+ @Test
+ public void testCreateSQLXML() throws SQLException {
+ SQLXML mockSQLXML = mock(SQLXML.class);
+ when(mockConnection.createSQLXML()).thenReturn(mockSQLXML);
+
+ SQLXML result = connectionProxy.createSQLXML();
+
+ Assertions.assertEquals(mockSQLXML, result);
+ verify(mockConnection).createSQLXML();
+ }
+
+ @Test
+ public void testIsValid() throws SQLException {
+ when(mockConnection.isValid(10)).thenReturn(true);
+
+ boolean result = connectionProxy.isValid(10);
+
+ Assertions.assertTrue(result);
+ verify(mockConnection).isValid(10);
+ }
+
+ @Test
+ public void testSetClientInfo() throws SQLClientInfoException {
+ connectionProxy.setClientInfo("name", "value");
+
+ verify(mockConnection).setClientInfo("name", "value");
+ }
+
+ @Test
+ public void testSetClientInfoWithProperties() throws
SQLClientInfoException {
+ Properties properties = new Properties();
+ connectionProxy.setClientInfo(properties);
+
+ verify(mockConnection).setClientInfo(properties);
+ }
+
+ @Test
+ public void testGetClientInfo() throws SQLException {
+ when(mockConnection.getClientInfo("name")).thenReturn("value");
+
+ String result = connectionProxy.getClientInfo("name");
+
+ Assertions.assertEquals("value", result);
+ verify(mockConnection).getClientInfo("name");
+ }
+
+ @Test
+ public void testGetClientInfoProperties() throws SQLException {
+ Properties properties = new Properties();
+ when(mockConnection.getClientInfo()).thenReturn(properties);
+
+ Properties result = connectionProxy.getClientInfo();
+
+ Assertions.assertEquals(properties, result);
+ verify(mockConnection).getClientInfo();
+ }
+
+ @Test
+ public void testCreateArrayOf() throws SQLException {
+ Array mockArray = mock(Array.class);
+ Object[] elements = {"a", "b"};
+ when(mockConnection.createArrayOf("VARCHAR",
elements)).thenReturn(mockArray);
+
+ Array result = connectionProxy.createArrayOf("VARCHAR", elements);
+
+ Assertions.assertEquals(mockArray, result);
+ verify(mockConnection).createArrayOf("VARCHAR", elements);
+ }
+
+ @Test
+ public void testCreateStruct() throws SQLException {
+ Struct mockStruct = mock(Struct.class);
+ Object[] attributes = {1, "test"};
+ when(mockConnection.createStruct("TYPE",
attributes)).thenReturn(mockStruct);
+
+ Struct result = connectionProxy.createStruct("TYPE", attributes);
+
+ Assertions.assertEquals(mockStruct, result);
+ verify(mockConnection).createStruct("TYPE", attributes);
+ }
+
+ @Test
+ public void testSetSchema() throws SQLException {
+ connectionProxy.setSchema("testSchema");
+
+ verify(mockConnection).setSchema("testSchema");
+ }
+
+ @Test
+ public void testGetSchema() throws SQLException {
+ when(mockConnection.getSchema()).thenReturn("testSchema");
+
+ String result = connectionProxy.getSchema();
+
+ Assertions.assertEquals("testSchema", result);
+ verify(mockConnection).getSchema();
+ }
+
+ @Test
+ public void testAbort() throws SQLException {
+ Executor mockExecutor = mock(Executor.class);
+ connectionProxy.abort(mockExecutor);
+
+ verify(mockConnection).abort(mockExecutor);
+ }
+
+ @Test
+ public void testSetNetworkTimeout() throws SQLException {
+ Executor mockExecutor = mock(Executor.class);
+ connectionProxy.setNetworkTimeout(mockExecutor, 1000);
+
+ verify(mockConnection).setNetworkTimeout(mockExecutor, 1000);
+ }
+
+ @Test
+ public void testGetNetworkTimeout() throws SQLException {
+ when(mockConnection.getNetworkTimeout()).thenReturn(1000);
+
+ int result = connectionProxy.getNetworkTimeout();
+
+ Assertions.assertEquals(1000, result);
+ verify(mockConnection).getNetworkTimeout();
+ }
+
+ @Test
+ public void testUnwrap() throws SQLException {
+
when(mockConnection.unwrap(Connection.class)).thenReturn(mockConnection);
+
+ Connection result = connectionProxy.unwrap(Connection.class);
+
+ Assertions.assertEquals(mockConnection, result);
+ verify(mockConnection).unwrap(Connection.class);
+ }
+
+ @Test
+ public void testIsWrapperFor() throws SQLException {
+ when(mockConnection.isWrapperFor(Connection.class)).thenReturn(true);
+
+ boolean result = connectionProxy.isWrapperFor(Connection.class);
+
+ Assertions.assertTrue(result);
+ verify(mockConnection).isWrapperFor(Connection.class);
+ }
+
+ /**
+ * Test implementation of AbstractConnectionProxyXA for testing purposes
+ */
+ private static class TestConnectionProxyXA extends
AbstractConnectionProxyXA {
+ public TestConnectionProxyXA(
+ Connection originalConnection, XAConnection xaConnection,
BaseDataSourceResource resource, String xid) {
+ super(originalConnection, xaConnection, resource, xid);
+ }
+
+ @Override
+ public void setAutoCommit(boolean autoCommit) throws SQLException {
+ // Test implementation
+ }
+
+ @Override
+ public boolean getAutoCommit() throws SQLException {
+ return false;
+ }
+
+ @Override
+ public void commit() throws SQLException {
+ // Test implementation
+ }
+
+ @Override
+ public void rollback() throws SQLException {
+ // Test implementation
+ }
+
+ @Override
+ public void close() throws SQLException {
+ // Test implementation
+ }
+ }
+}
diff --git
a/rm-datasource/src/test/java/org/apache/seata/rm/datasource/xa/AbstractDataSourceProxyXATest.java
b/rm-datasource/src/test/java/org/apache/seata/rm/datasource/xa/AbstractDataSourceProxyXATest.java
new file mode 100644
index 0000000000..2f384cffcb
--- /dev/null
+++
b/rm-datasource/src/test/java/org/apache/seata/rm/datasource/xa/AbstractDataSourceProxyXATest.java
@@ -0,0 +1,239 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.seata.rm.datasource.xa;
+
+import org.apache.seata.core.model.BranchType;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+
+import javax.sql.PooledConnection;
+import java.sql.Connection;
+import java.sql.SQLException;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+/**
+ * Tests for AbstractDataSourceProxyXA
+ * Focus on verifying actual results and business logic
+ */
+public class AbstractDataSourceProxyXATest {
+
+ private TestDataSourceProxyXA dataSourceProxy;
+ private XAXid xaXid;
+
+ @BeforeEach
+ public void setUp() {
+ dataSourceProxy = new TestDataSourceProxyXA();
+ xaXid = XAXidBuilder.build("testXid", 123L);
+ }
+
+ @Test
+ public void testGetConnectionForXAFinish_ReturnsExistingOpenConnection()
throws SQLException {
+ // Verify that an existing open connection is returned (not a new one)
+ ConnectionProxyXA existingConnection = mock(ConnectionProxyXA.class);
+ Connection mockWrappedConnection = mock(Connection.class);
+
when(existingConnection.getWrappedConnection()).thenReturn(mockWrappedConnection);
+ when(mockWrappedConnection.isClosed()).thenReturn(false);
+
+ // Store the existing connection
+ dataSourceProxy.hold(xaXid.toString(), existingConnection);
+
+ // Get connection for XA finish
+ ConnectionProxyXA result =
dataSourceProxy.getConnectionForXAFinish(xaXid);
+
+ // Verify it returns the EXACT same connection instance (not a new one)
+ Assertions.assertSame(
+ existingConnection, result, "Should return the exact same
connection instance that was held");
+
+ // Verify that getConnectionProxyXA was NOT called (didn't create a
new connection)
+ Assertions.assertEquals(
+ 0,
+ dataSourceProxy.getConnectionProxyXACallCount,
+ "Should not create a new connection when an open one exists");
+ }
+
+ @Test
+ public void
testGetConnectionForXAFinish_CreatesNewConnectionWhenExistingIsClosed() throws
SQLException {
+ // Verify that a new connection is created when the existing one is
closed
+ ConnectionProxyXA closedConnection = mock(ConnectionProxyXA.class);
+ Connection mockWrappedConnection = mock(Connection.class);
+
when(closedConnection.getWrappedConnection()).thenReturn(mockWrappedConnection);
+ when(mockWrappedConnection.isClosed()).thenReturn(true);
+
+ // Store the closed connection
+ dataSourceProxy.hold(xaXid.toString(), closedConnection);
+
+ // Get connection for XA finish
+ ConnectionProxyXA result =
dataSourceProxy.getConnectionForXAFinish(xaXid);
+
+ // Verify it returns the NEW connection (from getConnectionProxyXA)
+ Assertions.assertSame(
+ dataSourceProxy.getNewConnection(),
+ result,
+ "Should return a new connection when existing one is closed");
+
+ // Verify that a new connection was actually created
+ Assertions.assertEquals(
+ 1, dataSourceProxy.getConnectionProxyXACallCount, "Should
create exactly one new connection");
+ }
+
+ @Test
+ public void
testGetConnectionForXAFinish_CreatesNewConnectionWhenNoneExists() throws
SQLException {
+ // Verify that a new connection is created when no existing connection
is found
+
+ // Don't hold any connection, so lookup returns null
+ ConnectionProxyXA result =
dataSourceProxy.getConnectionForXAFinish(xaXid);
+
+ // Verify it returns the new connection from getConnectionProxyXA
+ Assertions.assertSame(
+ dataSourceProxy.getNewConnection(),
+ result,
+ "Should return a new connection when no existing connection is
found");
+
+ // Verify that a new connection was actually created
+ Assertions.assertEquals(
+ 1, dataSourceProxy.getConnectionProxyXACallCount, "Should
create exactly one new connection");
+ }
+
+ @Test
+ public void
testForceClosePhysicalConnection_ClosesConnectionAndWrappedConnection() throws
SQLException {
+ // Verify that both the connection proxy and its wrapped connection
are closed
+ ConnectionProxyXA mockConnection = mock(ConnectionProxyXA.class);
+ Connection mockWrappedConnection = mock(Connection.class);
+
+ Mockito.doNothing().when(mockConnection).close();
+
when(mockConnection.getWrappedConnection()).thenReturn(mockWrappedConnection);
+ Mockito.doNothing().when(mockWrappedConnection).close();
+
+ // Store the connection
+ dataSourceProxy.hold(xaXid.toString(), mockConnection);
+
+ // Force close
+ dataSourceProxy.forceClosePhysicalConnection(xaXid);
+
+ // Verify both connections were closed
+ verify(mockConnection).close();
+ verify(mockWrappedConnection).close();
+ }
+
+ @Test
+ public void
testForceClosePhysicalConnection_ClosesPooledConnectionCorrectly() throws
SQLException {
+ // Verify that for PooledConnection, the physical connection is closed
+ Connection mockWrappedConnection =
+ mock(Connection.class,
Mockito.withSettings().extraInterfaces(PooledConnection.class));
+ Connection mockPhysicalConnection = mock(Connection.class);
+
+ ConnectionProxyXA mockConnection = mock(ConnectionProxyXA.class);
+
+ Mockito.doNothing().when(mockConnection).close();
+
when(mockConnection.getWrappedConnection()).thenReturn(mockWrappedConnection);
+ when(((PooledConnection)
mockWrappedConnection).getConnection()).thenReturn(mockPhysicalConnection);
+ Mockito.doNothing().when(mockPhysicalConnection).close();
+
+ // Store the connection
+ dataSourceProxy.hold(xaXid.toString(), mockConnection);
+
+ // Force close
+ dataSourceProxy.forceClosePhysicalConnection(xaXid);
+
+ // Verify the proxy connection was closed
+ verify(mockConnection).close();
+
+ // Verify the physical connection (from PooledConnection) was closed,
not the wrapper
+ verify(mockPhysicalConnection).close();
+ verify(mockWrappedConnection, never()).close();
+ }
+
+ @Test
+ public void testForceClosePhysicalConnection_DoesNothingWhenNoConnection()
throws SQLException {
+ // Verify that no exception is thrown when there's no connection to
close
+
+ // Don't hold any connection
+ Assertions.assertDoesNotThrow(
+ () -> dataSourceProxy.forceClosePhysicalConnection(xaXid),
+ "Should not throw exception when no connection exists");
+
+ // Verify no connections were created or closed
+ Assertions.assertEquals(0,
dataSourceProxy.getConnectionProxyXACallCount, "Should not create any
connections");
+ }
+
+ @Test
+ public void testDefaultResourceGroupId() {
+ // Verify the default resource group ID constant value
+ Assertions.assertEquals(
+ "DEFAULT_XA",
+ TestDataSourceProxyXA.DEFAULT_RESOURCE_GROUP_ID,
+ "DEFAULT_RESOURCE_GROUP_ID should be 'DEFAULT_XA'");
+ }
+
+ @Test
+ public void testGetBranchType() {
+ // Verify getBranchType returns the correct branch type
+ Assertions.assertEquals(BranchType.XA,
dataSourceProxy.getBranchType(), "Branch type should be XA");
+ }
+
+ @Test
+ public void testGetResourceId() {
+ // Verify getResourceId returns the configured resource ID
+ Assertions.assertEquals(
+ "test-resource-id", dataSourceProxy.getResourceId(), "Resource
ID should match the configured value");
+ }
+
+ /**
+ * Test implementation of AbstractDataSourceProxyXA for testing purposes
+ */
+ private static class TestDataSourceProxyXA extends
AbstractDataSourceProxyXA {
+
+ private ConnectionProxyXA mockConnectionProxy;
+ int getConnectionProxyXACallCount = 0;
+
+ public TestDataSourceProxyXA() {
+ this.branchType = BranchType.XA;
+ this.resourceId = "test-resource-id";
+ }
+
+ @Override
+ protected Connection getConnectionProxyXA() throws SQLException {
+ getConnectionProxyXACallCount++;
+ // Create a new mock connection each time
+ mockConnectionProxy = mock(ConnectionProxyXA.class);
+ Connection mockWrappedConnection = mock(Connection.class);
+
when(mockConnectionProxy.getWrappedConnection()).thenReturn(mockWrappedConnection);
+ when(mockWrappedConnection.isClosed()).thenReturn(false);
+ return mockConnectionProxy;
+ }
+
+ public ConnectionProxyXA getNewConnection() {
+ return mockConnectionProxy;
+ }
+
+ @Override
+ public Connection getConnection() throws SQLException {
+ return getConnectionProxyXA();
+ }
+
+ @Override
+ public Connection getConnection(String username, String password)
throws SQLException {
+ return getConnectionProxyXA();
+ }
+ }
+}
diff --git
a/rm-datasource/src/test/java/org/apache/seata/rm/datasource/xa/ConnectionProxyXATest.java
b/rm-datasource/src/test/java/org/apache/seata/rm/datasource/xa/ConnectionProxyXATest.java
index 9efad1a81a..e1f451986c 100644
---
a/rm-datasource/src/test/java/org/apache/seata/rm/datasource/xa/ConnectionProxyXATest.java
+++
b/rm-datasource/src/test/java/org/apache/seata/rm/datasource/xa/ConnectionProxyXATest.java
@@ -16,7 +16,10 @@
*/
package org.apache.seata.rm.datasource.xa;
+import org.apache.seata.common.lock.ResourceLock;
import org.apache.seata.core.context.RootContext;
+import org.apache.seata.core.exception.TransactionException;
+import org.apache.seata.core.model.BranchStatus;
import org.apache.seata.core.model.BranchType;
import org.apache.seata.core.model.Resource;
import org.apache.seata.core.model.ResourceManager;
@@ -24,17 +27,28 @@ import org.apache.seata.rm.BaseDataSourceResource;
import org.apache.seata.rm.DefaultResourceManager;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import javax.sql.XAConnection;
+import javax.transaction.xa.XAException;
import javax.transaction.xa.XAResource;
import javax.transaction.xa.Xid;
import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
import java.sql.Statement;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.when;
/**
* Tests for ConnectionProxyXA
@@ -42,16 +56,72 @@ import static org.mockito.Mockito.times;
*/
public class ConnectionProxyXATest {
+ private Connection mockConnection;
+ private XAConnection mockXAConnection;
+ private XAResource mockXAResource;
+ private BaseDataSourceResource<ConnectionProxyXA> mockDataSourceResource;
+ private ResourceManager mockResourceManager;
+
+ @BeforeEach
+ public void setUp() throws SQLException, XAException {
+ mockConnection = Mockito.mock(Connection.class);
+ mockXAConnection = Mockito.mock(XAConnection.class);
+ mockXAResource = Mockito.mock(XAResource.class);
+ mockDataSourceResource = Mockito.mock(BaseDataSourceResource.class);
+ mockResourceManager = Mockito.mock(ResourceManager.class);
+
+ // Default setup
+ when(mockConnection.getAutoCommit()).thenReturn(true);
+ when(mockXAConnection.getXAResource()).thenReturn(mockXAResource);
+
Mockito.doNothing().when(mockResourceManager).registerResource(any(Resource.class));
+
+ DefaultResourceManager.get();
+ DefaultResourceManager.mockResourceManager(BranchType.XA,
mockResourceManager);
+ }
+
@Test
- public void testInit() throws Throwable {
- Connection connection = Mockito.mock(Connection.class);
- Mockito.when(connection.getAutoCommit()).thenReturn(false);
- XAConnection xaConnection = Mockito.mock(XAConnection.class);
- BaseDataSourceResource<ConnectionProxyXA> baseDataSourceResource =
Mockito.mock(BaseDataSourceResource.class);
- String xid = "xxx";
+ public void testConstructor() {
+ // Test constructor properly initializes the proxy
+ String xid = "test-xid-123";
+ ConnectionProxyXA connectionProxyXA =
+ new ConnectionProxyXA(mockConnection, mockXAConnection,
mockDataSourceResource, xid);
+
+ Assertions.assertNotNull(connectionProxyXA, "Constructor should create
a valid proxy instance");
+
+ // Verify that the proxy correctly wraps the provided connections
+ Assertions.assertSame(
+ mockConnection,
+ connectionProxyXA.getWrappedConnection(),
+ "Should correctly wrap the original connection");
+ Assertions.assertSame(
+ mockXAConnection,
+ connectionProxyXA.getWrappedXAConnection(),
+ "Should correctly wrap the XA connection");
+ }
+
+ @Test
+ public void testInitSuccess() throws SQLException {
+ // Test successful initialization
+ String xid = "test-xid-success";
+ ConnectionProxyXA connectionProxyXA =
+ new ConnectionProxyXA(mockConnection, mockXAConnection,
mockDataSourceResource, xid);
+
+ // This should succeed without throwing an exception
+ Assertions.assertDoesNotThrow(() -> connectionProxyXA.init(), "Init
should succeed when autocommit=true");
+
+ // Verify the connection's autocommit status was checked
+ Mockito.verify(mockConnection).getAutoCommit();
+ Mockito.verify(mockXAConnection).getXAResource();
+ }
+
+ @Test
+ public void testInitFailsWithAutoCommitFalse() throws SQLException {
+ // Test initialization failure when autocommit is false
+ when(mockConnection.getAutoCommit()).thenReturn(false);
+ String xid = "test-xid-fail";
ConnectionProxyXA connectionProxyXA =
- new ConnectionProxyXA(connection, xaConnection,
baseDataSourceResource, xid);
+ new ConnectionProxyXA(mockConnection, mockXAConnection,
mockDataSourceResource, xid);
Assertions.assertThrows(
IllegalStateException.class,
@@ -59,6 +129,21 @@ public class ConnectionProxyXATest {
"Connection[autocommit=false] as default is NOT supported");
}
+ @Test
+ public void testInitFailsWithSQLException() throws SQLException {
+ // Test initialization failure when SQLException is thrown
+ when(mockConnection.getAutoCommit()).thenThrow(new
SQLException("Connection error"));
+ String xid = "test-xid-sql-error";
+
+ ConnectionProxyXA connectionProxyXA =
+ new ConnectionProxyXA(mockConnection, mockXAConnection,
mockDataSourceResource, xid);
+
+ Assertions.assertThrows(
+ RuntimeException.class,
+ connectionProxyXA::init,
+ "Init should throw RuntimeException when SQLException occurs");
+ }
+
@Test
public void testXABranchCommit() throws Throwable {
Connection connection = Mockito.mock(Connection.class);
@@ -83,11 +168,11 @@ public class ConnectionProxyXATest {
// Assert setAutoCommit = false was NEVER invoked on the wrapped
connection
Mockito.verify(connection, times(0)).setAutoCommit(false);
// Assert XA start was invoked
- Mockito.verify(xaResource).start(any(Xid.class), any(Integer.class));
+ Mockito.verify(xaResource).start(any(Xid.class), anyInt());
connectionProxyXA.commit();
- Mockito.verify(xaResource, times(0)).end(any(Xid.class),
any(Integer.class));
+ Mockito.verify(xaResource, times(0)).end(any(Xid.class), anyInt());
Mockito.verify(xaResource, times(0)).prepare(any(Xid.class));
}
@@ -116,11 +201,11 @@ public class ConnectionProxyXATest {
Mockito.verify(connection, times(0)).setAutoCommit(false);
// Assert XA start was invoked
- Mockito.verify(xaResource).start(any(Xid.class), any(Integer.class));
+ Mockito.verify(xaResource).start(any(Xid.class), anyInt());
connectionProxyXA.rollback();
- Mockito.verify(xaResource).end(any(Xid.class), any(Integer.class));
+ Mockito.verify(xaResource).end(any(Xid.class), anyInt());
// Not prepared
Mockito.verify(xaResource, times(0)).prepare(any(Xid.class));
@@ -173,7 +258,7 @@ public class ConnectionProxyXATest {
connectionProxyXA.xaCommit("xxx", 123L, null);
- Mockito.verify(xaResource).commit(any(Xid.class), any(Boolean.class));
+ Mockito.verify(xaResource).commit(any(Xid.class), anyBoolean());
Mockito.verify(xaResource, times(0)).rollback(any(Xid.class));
}
@@ -195,7 +280,7 @@ public class ConnectionProxyXATest {
connectionProxyXA.xaRollback("xxx", 123L, null);
- Mockito.verify(xaResource, times(0)).commit(any(Xid.class),
any(Boolean.class));
+ Mockito.verify(xaResource, times(0)).commit(any(Xid.class),
anyBoolean());
Mockito.verify(xaResource).rollback(any(Xid.class));
}
@@ -237,17 +322,382 @@ public class ConnectionProxyXATest {
// Assert setAutoCommit = false was NEVER invoked on the wrapped
connection
Mockito.verify(connection, times(0)).setAutoCommit(false);
// Assert XA start was invoked
- Mockito.verify(xaResource, times(0)).start(any(Xid.class),
any(Integer.class));
+ Mockito.verify(xaResource, times(0)).start(any(Xid.class), anyInt());
connectionProxyXA.commit();
- Mockito.verify(xaResource, times(0)).end(any(Xid.class),
any(Integer.class));
+ Mockito.verify(xaResource, times(0)).end(any(Xid.class), anyInt());
Mockito.verify(xaResource, times(0)).prepare(any(Xid.class));
connectionProxyXA.rollback();
Mockito.verify(xaResource, times(0)).rollback(any(Xid.class));
}
+ @Test
+ public void testGetAutoCommit() throws SQLException, TransactionException {
+ // Test getAutoCommit returns current status
+ String xid = "test-autocommit";
+ ConnectionProxyXA connectionProxyXA =
+ new ConnectionProxyXA(mockConnection, mockXAConnection,
mockDataSourceResource, xid);
+ connectionProxyXA.init();
+
+ // Initial state should be true (from setUp)
+ Assertions.assertTrue(connectionProxyXA.getAutoCommit(),
"getAutoCommit should return true initially");
+
+ // After setting autocommit to false, it should return false
+ when(mockResourceManager.branchRegister(
+ eq(BranchType.XA), anyString(), eq(null),
eq("test-autocommit"), eq(null), eq(null)))
+ .thenReturn(123L);
+ connectionProxyXA.setAutoCommit(false);
+ Assertions.assertFalse(
+ connectionProxyXA.getAutoCommit(), "getAutoCommit should
return false after setAutoCommit(false)");
+ }
+
+ @Test
+ public void testSetAutoCommitFromTrueToFalse() throws Exception {
+ // Test setting autocommit from true to false (starting XA transaction)
+ String xid = "test-xa-start";
+ ConnectionProxyXA connectionProxyXA =
+ new ConnectionProxyXA(mockConnection, mockXAConnection,
mockDataSourceResource, xid);
+ connectionProxyXA.init();
+
+ when(mockResourceManager.branchRegister(
+ eq(BranchType.XA), anyString(), eq(null),
eq("test-xa-start"), eq(null), eq(null)))
+ .thenReturn(123L);
+
+ connectionProxyXA.setAutoCommit(false);
+
+ // Verify XA start was called
+ Mockito.verify(mockXAResource).start(any(Xid.class),
eq(XAResource.TMNOFLAGS));
+ // Parameters: BranchType.XA, resource.getResourceId(), null, xid,
null, null
+ Mockito.verify(mockResourceManager)
+ .branchRegister(eq(BranchType.XA), eq(null), eq(null),
eq("test-xa-start"), eq(null), eq(null));
+
+ Assertions.assertFalse(connectionProxyXA.getAutoCommit(), "AutoCommit
should be false");
+ }
+
+ @Test
+ public void testSetAutoCommitFromFalseToTrue() throws Exception {
+ // Test setting autocommit from false to true (committing XA
transaction)
+ String xid = "test-xa-commit";
+ ConnectionProxyXA connectionProxyXA =
+ new ConnectionProxyXA(mockConnection, mockXAConnection,
mockDataSourceResource, xid);
+ connectionProxyXA.init();
+
+ when(mockResourceManager.branchRegister(
+ eq(BranchType.XA), anyString(), eq(null),
eq("test-xa-commit"), eq(null), eq(null)))
+ .thenReturn(123L);
+
+ // First set to false to start XA
+ connectionProxyXA.setAutoCommit(false);
+ // Then set back to true to commit
+ connectionProxyXA.setAutoCommit(true);
+
+ Assertions.assertTrue(connectionProxyXA.getAutoCommit(), "AutoCommit
should be true");
+ }
+
+ @Test
+ public void testSetAutoCommitSameValue() throws SQLException, XAException,
TransactionException {
+ // Test setting autocommit to the same value (should be no-op)
+ String xid = "test-same-value";
+ ConnectionProxyXA connectionProxyXA =
+ new ConnectionProxyXA(mockConnection, mockXAConnection,
mockDataSourceResource, xid);
+ connectionProxyXA.init();
+
+ // Set to true (already true, should be no-op)
+ connectionProxyXA.setAutoCommit(true);
+
+ // Verify no XA operations were performed
+ Mockito.verify(mockXAResource, times(0)).start(any(Xid.class),
anyInt());
+ Mockito.verify(mockResourceManager, times(0))
+ .branchRegister(eq(BranchType.XA), anyString(), eq(null),
anyString(), eq(null), eq(null));
+ }
+
+ @Test
+ public void testSetAutoCommitOnReadOnlyTransaction() throws SQLException,
XAException {
+ // Test setAutoCommit on read-only transaction
+ when(mockConnection.isReadOnly()).thenReturn(true);
+ String xid = "test-readonly";
+ ConnectionProxyXA connectionProxyXA =
+ new ConnectionProxyXA(mockConnection, mockXAConnection,
mockDataSourceResource, xid);
+ connectionProxyXA.init();
+
+ connectionProxyXA.setAutoCommit(false);
+
+ // Verify no XA operations were performed for read-only transaction
+ Mockito.verify(mockXAResource, times(0)).start(any(Xid.class),
anyInt());
+ Assertions.assertFalse(connectionProxyXA.getAutoCommit(), "AutoCommit
should be false");
+ }
+
+ @Test
+ public void testSetAutoCommitXAStartFails() throws Exception {
+ // Test setAutoCommit when XA start fails
+ String xid = "test-xa-start-fail";
+ ConnectionProxyXA connectionProxyXA =
+ new ConnectionProxyXA(mockConnection, mockXAConnection,
mockDataSourceResource, xid);
+ connectionProxyXA.init();
+
+ when(mockResourceManager.branchRegister(
+ eq(BranchType.XA), anyString(), eq(null),
eq("test-xa-start-fail"), eq(null), eq(null)))
+ .thenReturn(123L);
+ doThrow(new XAException("XA start
failed")).when(mockXAResource).start(any(Xid.class), anyInt());
+
+ SQLException exception = Assertions.assertThrows(
+ SQLException.class,
+ () -> connectionProxyXA.setAutoCommit(false),
+ "Should throw SQLException when XA start fails");
+
+ Assertions.assertTrue(
+ exception.getMessage().contains("failed to start xa branch"),
+ "Exception message should indicate XA start failure");
+ }
+
+ @Test
+ public void testCommitOnAutoCommitSession() throws SQLException,
XAException {
+ // Test commit on autocommit session (should be ignored)
+ String xid = "test-commit-autocommit";
+ ConnectionProxyXA connectionProxyXA =
+ new ConnectionProxyXA(mockConnection, mockXAConnection,
mockDataSourceResource, xid);
+ connectionProxyXA.init();
+
+ // Should not throw exception and should be ignored
+ Assertions.assertDoesNotThrow(
+ () -> connectionProxyXA.commit(), "Commit on autocommit
session should be ignored");
+
+ // Verify no XA operations
+ Mockito.verify(mockXAResource, times(0)).end(any(Xid.class), anyInt());
+ Mockito.verify(mockXAResource, times(0)).prepare(any(Xid.class));
+ }
+
+ @Test
+ public void testCommitOnReadOnlyTransaction() throws Exception {
+ // Test commit on read-only transaction
+ when(mockConnection.isReadOnly()).thenReturn(true);
+ String xid = "test-commit-readonly";
+ ConnectionProxyXA connectionProxyXA =
+ new ConnectionProxyXA(mockConnection, mockXAConnection,
mockDataSourceResource, xid);
+ connectionProxyXA.init();
+
+ when(mockResourceManager.branchRegister(
+ eq(BranchType.XA), anyString(), eq(null),
eq("test-commit-readonly"), eq(null), eq(null)))
+ .thenReturn(123L);
+ connectionProxyXA.setAutoCommit(false);
+
+ // Should not throw exception and should be ignored
+ Assertions.assertDoesNotThrow(
+ () -> connectionProxyXA.commit(), "Commit on read-only
transaction should be ignored");
+ }
+
+ @Test
+ public void testRollbackOnAutoCommitSession() throws SQLException,
XAException {
+ // Test rollback on autocommit session (should be ignored)
+ String xid = "test-rollback-autocommit";
+ ConnectionProxyXA connectionProxyXA =
+ new ConnectionProxyXA(mockConnection, mockXAConnection,
mockDataSourceResource, xid);
+ connectionProxyXA.init();
+
+ // Should not throw exception and should be ignored
+ Assertions.assertDoesNotThrow(
+ () -> connectionProxyXA.rollback(), "Rollback on autocommit
session should be ignored");
+
+ // Verify no XA operations
+ Mockito.verify(mockXAResource, times(0)).end(any(Xid.class), anyInt());
+ Mockito.verify(mockXAResource, times(0)).rollback(any(Xid.class));
+ }
+
+ @Test
+ public void testHoldableMethods() throws SQLException {
+ // Test Holdable interface methods
+ String xid = "test-holdable";
+ ConnectionProxyXA connectionProxyXA =
+ new ConnectionProxyXA(mockConnection, mockXAConnection,
mockDataSourceResource, xid);
+
+ // Initially not held
+ Assertions.assertFalse(connectionProxyXA.isHeld(), "Connection should
not be held initially");
+
+ // Set held
+ connectionProxyXA.setHeld(true);
+ Assertions.assertTrue(connectionProxyXA.isHeld(), "Connection should
be held after setHeld(true)");
+
+ // Set not held
+ connectionProxyXA.setHeld(false);
+ Assertions.assertFalse(connectionProxyXA.isHeld(), "Connection should
not be held after setHeld(false)");
+ }
+
+ @Test
+ public void testShouldBeHeld() throws SQLException {
+ // Test shouldBeHeld method with different scenarios
+ String xid = "test-should-be-held";
+
+ // When resource says it should be held
+ when(mockDataSourceResource.isShouldBeHeld()).thenReturn(true);
+ ConnectionProxyXA connectionProxyXA1 =
+ new ConnectionProxyXA(mockConnection, mockXAConnection,
mockDataSourceResource, xid);
+ Assertions.assertTrue(connectionProxyXA1.shouldBeHeld(), "Should be
held when resource indicates so");
+
+ // When resource says it should not be held, but DB type is blank
+ when(mockDataSourceResource.isShouldBeHeld()).thenReturn(false);
+ when(mockDataSourceResource.getDbType()).thenReturn("");
+ ConnectionProxyXA connectionProxyXA2 =
+ new ConnectionProxyXA(mockConnection, mockXAConnection,
mockDataSourceResource, xid);
+ Assertions.assertTrue(connectionProxyXA2.shouldBeHeld(), "Should be
held when DB type is blank");
+
+ // When resource says it should not be held and DB type is not blank
+ when(mockDataSourceResource.getDbType()).thenReturn("mysql");
+ ConnectionProxyXA connectionProxyXA3 =
+ new ConnectionProxyXA(mockConnection, mockXAConnection,
mockDataSourceResource, xid);
+ Assertions.assertFalse(
+ connectionProxyXA3.shouldBeHeld(),
+ "Should not be held when resource indicates so and DB type is
not blank");
+ }
+
+ @Test
+ public void testGetResourceLock() throws SQLException {
+ // Test getResourceLock method returns a valid lock
+ String xid = "test-resource-lock";
+ ConnectionProxyXA connectionProxyXA =
+ new ConnectionProxyXA(mockConnection, mockXAConnection,
mockDataSourceResource, xid);
+
+ ResourceLock resourceLock = connectionProxyXA.getResourceLock();
+ Assertions.assertNotNull(resourceLock, "Resource lock should not be
null");
+ }
+
+ @Test
+ public void testGetAndSetPrepareTime() throws SQLException {
+ // Test getPrepareTime method (initially null, then set during close)
+ String xid = "test-prepare-time";
+ ConnectionProxyXA connectionProxyXA =
+ new ConnectionProxyXA(mockConnection, mockXAConnection,
mockDataSourceResource, xid);
+
+ Assertions.assertNull(connectionProxyXA.getPrepareTime(), "Prepare
time should be null initially");
+ }
+
+ @Test
+ public void testSetCombine() throws SQLException {
+ // Test setCombine method
+ String xid = "test-combine";
+ ConnectionProxyXA connectionProxyXA =
+ new ConnectionProxyXA(mockConnection, mockXAConnection,
mockDataSourceResource, xid);
+ connectionProxyXA.init();
+
+ // Set combine mode
+ connectionProxyXA.setCombine(true);
+
+ // Test that commit returns early in combine mode
+ Assertions.assertDoesNotThrow(() -> connectionProxyXA.commit(),
"Commit should return early in combine mode");
+
+ // Test that rollback returns early in combine mode
+ Assertions.assertDoesNotThrow(
+ () -> connectionProxyXA.rollback(), "Rollback should return
early in combine mode");
+
+ // Test that close returns early in combine mode
+ Assertions.assertDoesNotThrow(() -> connectionProxyXA.close(), "Close
should return early in combine mode");
+ }
+
+ @Test
+ public void testCreatePreparedStatement() throws SQLException {
+ // Test createPreparedStatement returns PreparedStatementProxyXA
+ String xid = "test-prepared-statement";
+ ConnectionProxyXA connectionProxyXA =
+ new ConnectionProxyXA(mockConnection, mockXAConnection,
mockDataSourceResource, xid);
+
+ PreparedStatement mockPreparedStatement =
Mockito.mock(PreparedStatement.class);
+ when(mockConnection.prepareStatement("SELECT * FROM
test")).thenReturn(mockPreparedStatement);
+
+ PreparedStatement statement =
connectionProxyXA.prepareStatement("SELECT * FROM test");
+ Assertions.assertTrue(
+ statement instanceof PreparedStatementProxyXA, "Should return
PreparedStatementProxyXA instance");
+ }
+
+ @Test
+ public void testXACommitWithResourceLock() throws Exception {
+ // Test xaCommit method with resource lock
+ String xid = "test-xa-commit-lock";
+ long branchId = 12345L;
+ ConnectionProxyXA connectionProxyXA =
+ new ConnectionProxyXA(mockConnection, mockXAConnection,
mockDataSourceResource, xid);
+ connectionProxyXA.init();
+
+ // Should not throw exception
+ Assertions.assertDoesNotThrow(
+ () -> connectionProxyXA.xaCommit(xid, branchId, null),
"xaCommit should not throw exception");
+
+ // Verify XA commit was called
+ Mockito.verify(mockXAResource).commit(any(Xid.class), eq(false));
+ }
+
+ @Test
+ public void testXARollbackWithResourceLock() throws Exception {
+ // Test xaRollback method with resource lock
+ String xid = "test-xa-rollback-lock";
+ long branchId = 12345L;
+ ConnectionProxyXA connectionProxyXA =
+ new ConnectionProxyXA(mockConnection, mockXAConnection,
mockDataSourceResource, xid);
+ connectionProxyXA.init();
+
+ // Should not throw exception
+ Assertions.assertDoesNotThrow(
+ () -> connectionProxyXA.xaRollback(xid, branchId, null),
"xaRollback should not throw exception");
+
+ // Verify XA end and rollback were called
+ Mockito.verify(mockXAResource).end(any(Xid.class),
eq(XAResource.TMFAIL));
+ Mockito.verify(mockXAResource).rollback(any(Xid.class));
+ }
+
+ @Test
+ public void testCloseWithXAReadOnly() throws Exception {
+ // Test close when XA prepare returns XA_RDONLY
+ String xid = "test-close-readonly";
+ ConnectionProxyXA connectionProxyXA =
+ new ConnectionProxyXA(mockConnection, mockXAConnection,
mockDataSourceResource, xid);
+ connectionProxyXA.init();
+
+ when(mockResourceManager.branchRegister(
+ eq(BranchType.XA), anyString(), eq(null),
eq("test-close-readonly"), eq(null), eq(null)))
+ .thenReturn(123L);
+
when(mockXAResource.prepare(any(Xid.class))).thenReturn(XAResource.XA_RDONLY);
+
+ connectionProxyXA.setAutoCommit(false);
+
+ // Should not throw exception and should report RDONLY status
+ Assertions.assertDoesNotThrow(() -> connectionProxyXA.close(), "Close
should handle XA_RDONLY properly");
+
+ // Verify prepare was called
+ Mockito.verify(mockXAResource).prepare(any(Xid.class));
+ // Verify branch report was called with RDONLY status
+ Mockito.verify(mockResourceManager)
+ .branchReport(eq(BranchType.XA), eq(xid), anyLong(),
eq(BranchStatus.PhaseOne_RDONLY), any());
+ }
+
+ @Test
+ public void testCloseWithXAException() throws Exception {
+ // Test close when XA operations throw exception
+ String xid = "test-close-xa-exception";
+ ConnectionProxyXA connectionProxyXA =
+ new ConnectionProxyXA(mockConnection, mockXAConnection,
mockDataSourceResource, xid);
+ connectionProxyXA.init();
+
+ when(mockResourceManager.branchRegister(
+ eq(BranchType.XA), anyString(), eq(null),
eq("test-close-xa-exception"), eq(null), eq(null)))
+ .thenReturn(123L);
+ when(mockXAResource.prepare(any(Xid.class))).thenThrow(new
XAException("Prepare failed"));
+
+ connectionProxyXA.setAutoCommit(false);
+
+ SQLException exception = Assertions.assertThrows(
+ SQLException.class,
+ () -> connectionProxyXA.close(),
+ "Close should throw SQLException when XA operations fail");
+
+ Assertions.assertTrue(
+ exception.getMessage().contains("Failed to
end(TMSUCCESS)/prepare xa branch"),
+ "Exception message should indicate XA failure");
+
+ // Verify branch report was called with Failed status
+ Mockito.verify(mockResourceManager)
+ .branchReport(eq(BranchType.XA), eq(xid), anyLong(),
eq(BranchStatus.PhaseOne_Failed), any());
+ }
+
@AfterAll
public static void tearDown() {
RootContext.unbind();
diff --git
a/rm-datasource/src/test/java/org/apache/seata/rm/datasource/xa/ExecuteTemplateXATest.java
b/rm-datasource/src/test/java/org/apache/seata/rm/datasource/xa/ExecuteTemplateXATest.java
new file mode 100644
index 0000000000..89f41293f9
--- /dev/null
+++
b/rm-datasource/src/test/java/org/apache/seata/rm/datasource/xa/ExecuteTemplateXATest.java
@@ -0,0 +1,212 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.seata.rm.datasource.xa;
+
+import org.apache.seata.rm.datasource.exec.StatementCallback;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.sql.Statement;
+
+/**
+ * Tests for ExecuteTemplateXA
+ *
+ */
+public class ExecuteTemplateXATest {
+
+ private AbstractConnectionProxyXA mockConnectionProxyXA;
+ private Statement mockStatement;
+ private StatementCallback<String, Statement> stringCallback;
+
+ @BeforeEach
+ public void setUp() throws SQLException {
+ // Mock connection proxy with default autoCommit=true
+ mockConnectionProxyXA = Mockito.mock(AbstractConnectionProxyXA.class);
+ Mockito.when(mockConnectionProxyXA.getAutoCommit()).thenReturn(true);
+
+ // Mock statements
+ mockStatement = Mockito.mock(Statement.class);
+
+ // Default callback that returns "success"
+ stringCallback = (statement, args) -> "success";
+ }
+
+ @Test
+ public void testExecuteSuccessWithAutoCommitTrue() throws SQLException {
+ // Using default setup: autoCommit=true, stringCallback returns
"success"
+
+ // Execute
+ String result = ExecuteTemplateXA.execute(mockConnectionProxyXA,
stringCallback, mockStatement);
+
+ // Verify
+ Assertions.assertEquals("success", result);
+ Mockito.verify(mockConnectionProxyXA).getAutoCommit();
+ Mockito.verify(mockConnectionProxyXA).setAutoCommit(false);
+ Mockito.verify(mockConnectionProxyXA).commit();
+ Mockito.verify(mockConnectionProxyXA).setAutoCommit(true);
+ Mockito.verify(mockStatement, Mockito.never()).close();
+ }
+
+ @Test
+ public void testExecuteSuccessWithAutoCommitFalse() throws SQLException {
+ // Override default autoCommit to false
+ Mockito.when(mockConnectionProxyXA.getAutoCommit()).thenReturn(false);
+
+ // Execute
+ String result = ExecuteTemplateXA.execute(mockConnectionProxyXA,
stringCallback, mockStatement);
+
+ // Verify
+ Assertions.assertEquals("success", result);
+ Mockito.verify(mockConnectionProxyXA).getAutoCommit();
+ Mockito.verify(mockConnectionProxyXA,
Mockito.never()).setAutoCommit(Mockito.anyBoolean());
+ Mockito.verify(mockConnectionProxyXA, Mockito.never()).commit();
+ Mockito.verify(mockStatement, Mockito.never()).close();
+ }
+
+ @Test
+ public void testExecuteWithSQLExceptionDuringExecution() throws
SQLException {
+ // Using default setup: autoCommit=true
+
+ // Callback that throws SQLException
+ StatementCallback<String, Statement> failingCallback = (statement,
args) -> {
+ throw new SQLException("execution failed");
+ };
+
+ // Execute and expect exception
+ SQLException exception = Assertions.assertThrows(SQLException.class,
() -> {
+ ExecuteTemplateXA.execute(mockConnectionProxyXA, failingCallback,
mockStatement);
+ });
+
+ // Verify
+ Assertions.assertEquals("execution failed", exception.getMessage());
+ Mockito.verify(mockConnectionProxyXA).getAutoCommit();
+ Mockito.verify(mockConnectionProxyXA).setAutoCommit(false);
+ Mockito.verify(mockConnectionProxyXA).rollback();
+ Mockito.verify(mockConnectionProxyXA).setAutoCommit(true);
+ }
+
+ @Test
+ public void testExecuteWithRuntimeExceptionDuringExecution() throws
SQLException {
+ StatementCallback<String, Statement> callback = (statement, args) -> {
+ throw new RuntimeException("runtime exception");
+ };
+
+ // Execute and expect exception
+ SQLException exception = Assertions.assertThrows(SQLException.class,
() -> {
+ ExecuteTemplateXA.execute(mockConnectionProxyXA, callback,
mockStatement);
+ });
+
+ // Verify
+ Assertions.assertEquals("java.lang.RuntimeException: runtime
exception", exception.getMessage());
+ Mockito.verify(mockConnectionProxyXA).getAutoCommit();
+ Mockito.verify(mockConnectionProxyXA).setAutoCommit(false);
+ Mockito.verify(mockConnectionProxyXA).rollback();
+ Mockito.verify(mockConnectionProxyXA).setAutoCommit(true);
+ }
+
+ @Test
+ public void testExecuteWithCommitFailure() throws SQLException {
+ Mockito.doThrow(new SQLException("commit failed"))
+ .when(mockConnectionProxyXA)
+ .commit();
+
+ // Execute and expect exception
+ SQLException exception = Assertions.assertThrows(SQLException.class,
() -> {
+ ExecuteTemplateXA.execute(mockConnectionProxyXA, stringCallback,
mockStatement);
+ });
+
+ // Verify
+ Assertions.assertEquals("commit failed", exception.getMessage());
+ Mockito.verify(mockConnectionProxyXA).getAutoCommit();
+ Mockito.verify(mockConnectionProxyXA).setAutoCommit(false);
+ Mockito.verify(mockConnectionProxyXA).commit();
+ Mockito.verify(mockConnectionProxyXA).rollback();
+ Mockito.verify(mockConnectionProxyXA).setAutoCommit(true);
+ }
+
+ @Test
+ public void testExecuteWithCommitFailureAndXA_NOT_END() throws
SQLException {
+
+ // Create SQLException with XA_NOT_END SQLState
+ SQLException xaNotEndException = new SQLException("XA not end",
AbstractConnectionProxyXA.SQLSTATE_XA_NOT_END);
+
Mockito.doThrow(xaNotEndException).when(mockConnectionProxyXA).commit();
+
+ // Execute and expect exception
+ SQLException exception = Assertions.assertThrows(SQLException.class,
() -> {
+ ExecuteTemplateXA.execute(mockConnectionProxyXA, stringCallback,
mockStatement);
+ });
+
+ // Verify
+ Assertions.assertEquals("XA not end", exception.getMessage());
+ Mockito.verify(mockConnectionProxyXA).getAutoCommit();
+ Mockito.verify(mockConnectionProxyXA).setAutoCommit(false);
+ Mockito.verify(mockConnectionProxyXA).commit();
+ // Should not rollback when XA_NOT_END
+ Mockito.verify(mockConnectionProxyXA, Mockito.never()).rollback();
+ Mockito.verify(mockConnectionProxyXA).setAutoCommit(true);
+ }
+
+ @Test
+ public void testExecuteWithRollbackFailure() throws SQLException {
+
+ Mockito.doThrow(new SQLException("rollback failed"))
+ .when(mockConnectionProxyXA)
+ .rollback();
+
+ StatementCallback<String, Statement> callback = (statement, args) -> {
+ throw new SQLException("execution failed");
+ };
+
+ // Execute and expect the original exception (not the rollback failure)
+ SQLException exception = Assertions.assertThrows(SQLException.class,
() -> {
+ ExecuteTemplateXA.execute(mockConnectionProxyXA, callback,
mockStatement);
+ });
+
+ // Verify
+ Assertions.assertEquals("execution failed", exception.getMessage());
+ Mockito.verify(mockConnectionProxyXA).getAutoCommit();
+ Mockito.verify(mockConnectionProxyXA).setAutoCommit(false);
+ Mockito.verify(mockConnectionProxyXA).rollback();
+ Mockito.verify(mockConnectionProxyXA).setAutoCommit(true);
+ }
+
+ @Test
+ public void testExecuteWithArguments() throws SQLException {
+ Mockito.when(mockConnectionProxyXA.getAutoCommit()).thenReturn(false);
+
+ // Mock statement and callback with arguments
+ PreparedStatement mockStatement =
Mockito.mock(PreparedStatement.class);
+ StatementCallback<Integer, PreparedStatement> callback = (statement,
args) -> {
+ Assertions.assertEquals(2, args.length);
+ Assertions.assertEquals("arg1", args[0]);
+ Assertions.assertEquals(42, args[1]);
+ return 1;
+ };
+
+ // Execute with arguments
+ Integer result = ExecuteTemplateXA.execute(mockConnectionProxyXA,
callback, mockStatement, "arg1", 42);
+
+ // Verify
+ Assertions.assertEquals(1, result);
+ Mockito.verify(mockConnectionProxyXA).getAutoCommit();
+ Mockito.verify(mockConnectionProxyXA,
Mockito.never()).setAutoCommit(Mockito.anyBoolean());
+ }
+}
diff --git
a/rm-datasource/src/test/java/org/apache/seata/rm/datasource/xa/PreparedStatementProxyXATest.java
b/rm-datasource/src/test/java/org/apache/seata/rm/datasource/xa/PreparedStatementProxyXATest.java
new file mode 100644
index 0000000000..bc25ddd1c5
--- /dev/null
+++
b/rm-datasource/src/test/java/org/apache/seata/rm/datasource/xa/PreparedStatementProxyXATest.java
@@ -0,0 +1,648 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.seata.rm.datasource.xa;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.MockedStatic;
+import org.mockito.Mockito;
+
+import java.io.InputStream;
+import java.io.Reader;
+import java.math.BigDecimal;
+import java.net.URL;
+import java.sql.Array;
+import java.sql.Blob;
+import java.sql.Clob;
+import java.sql.Connection;
+import java.sql.Date;
+import java.sql.NClob;
+import java.sql.ParameterMetaData;
+import java.sql.PreparedStatement;
+import java.sql.Ref;
+import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
+import java.sql.RowId;
+import java.sql.SQLException;
+import java.sql.SQLWarning;
+import java.sql.SQLXML;
+import java.sql.Time;
+import java.sql.Timestamp;
+import java.sql.Types;
+import java.util.Calendar;
+
+/**
+ * Tests for PreparedStatementProxyXA
+ * Focus on verifying actual results and business logic, not just method calls
+ */
+public class PreparedStatementProxyXATest {
+
+ private AbstractConnectionProxyXA mockConnectionProxyXA;
+ private PreparedStatement mockPreparedStatement;
+ private PreparedStatementProxyXA preparedStatementProxyXA;
+
+ @BeforeEach
+ public void setUp() {
+ mockConnectionProxyXA = Mockito.mock(AbstractConnectionProxyXA.class);
+ mockPreparedStatement = Mockito.mock(PreparedStatement.class);
+ preparedStatementProxyXA = new
PreparedStatementProxyXA(mockConnectionProxyXA, mockPreparedStatement);
+ }
+
+ @Test
+ public void testExecuteQueryReturnsCorrectResultSet() throws SQLException {
+ // Verify executeQuery returns the correct ResultSet through XA
transaction flow
+ ResultSet expectedResultSet = Mockito.mock(ResultSet.class);
+
+ try (MockedStatic<ExecuteTemplateXA> mockedExecuteTemplate =
Mockito.mockStatic(ExecuteTemplateXA.class)) {
+ mockedExecuteTemplate
+ .when(() -> ExecuteTemplateXA.execute(
+ Mockito.eq(mockConnectionProxyXA), Mockito.any(),
Mockito.eq(mockPreparedStatement)))
+ .thenReturn(expectedResultSet);
+
+ ResultSet actualResultSet =
preparedStatementProxyXA.executeQuery();
+
+ Assertions.assertSame(
+ expectedResultSet,
+ actualResultSet,
+ "executeQuery should return the exact ResultSet from
ExecuteTemplateXA");
+ }
+ }
+
+ @Test
+ public void testExecuteUpdateReturnsCorrectCount() throws SQLException {
+ // Verify executeUpdate returns the correct update count through XA
transaction flow
+ int expectedUpdateCount = 42;
+
+ try (MockedStatic<ExecuteTemplateXA> mockedExecuteTemplate =
Mockito.mockStatic(ExecuteTemplateXA.class)) {
+ mockedExecuteTemplate
+ .when(() -> ExecuteTemplateXA.execute(
+ Mockito.eq(mockConnectionProxyXA), Mockito.any(),
Mockito.eq(mockPreparedStatement)))
+ .thenReturn(expectedUpdateCount);
+
+ int actualUpdateCount = preparedStatementProxyXA.executeUpdate();
+
+ Assertions.assertEquals(
+ expectedUpdateCount,
+ actualUpdateCount,
+ "executeUpdate should return the exact count from
ExecuteTemplateXA");
+ }
+ }
+
+ @Test
+ public void testExecuteReturnsCorrectBoolean() throws SQLException {
+ // Verify execute returns the correct boolean result through XA
transaction flow
+ boolean expectedResult = true;
+
+ try (MockedStatic<ExecuteTemplateXA> mockedExecuteTemplate =
Mockito.mockStatic(ExecuteTemplateXA.class)) {
+ mockedExecuteTemplate
+ .when(() -> ExecuteTemplateXA.execute(
+ Mockito.eq(mockConnectionProxyXA), Mockito.any(),
Mockito.eq(mockPreparedStatement)))
+ .thenReturn(expectedResult);
+
+ boolean actualResult = preparedStatementProxyXA.execute();
+
+ Assertions.assertEquals(
+ expectedResult, actualResult, "execute should return the
exact boolean from ExecuteTemplateXA");
+ }
+ }
+
+ @Test
+ public void testExecuteQueryPropagatesException() {
+ // Verify exceptions are correctly propagated from XA transaction
context
+ SQLException expectedException = new SQLException("Query execution
failed");
+
+ try (MockedStatic<ExecuteTemplateXA> mockedExecuteTemplate =
Mockito.mockStatic(ExecuteTemplateXA.class)) {
+ mockedExecuteTemplate
+ .when(() -> ExecuteTemplateXA.execute(
+ Mockito.eq(mockConnectionProxyXA), Mockito.any(),
Mockito.eq(mockPreparedStatement)))
+ .thenThrow(expectedException);
+
+ SQLException actualException =
Assertions.assertThrows(SQLException.class, () -> {
+ preparedStatementProxyXA.executeQuery();
+ });
+
+ Assertions.assertSame(
+ expectedException, actualException, "Exception should be
propagated without modification");
+ }
+ }
+
+ @Test
+ public void testGetMetaDataReturnsCorrectMetadata() throws SQLException {
+ // Verify getMetaData returns the correct metadata object
+ ResultSetMetaData expectedMetaData =
Mockito.mock(ResultSetMetaData.class);
+
+
Mockito.when(mockPreparedStatement.getMetaData()).thenReturn(expectedMetaData);
+
+ ResultSetMetaData actualMetaData =
preparedStatementProxyXA.getMetaData();
+
+ Assertions.assertSame(
+ expectedMetaData,
+ actualMetaData,
+ "getMetaData should return the exact metadata from underlying
statement");
+ }
+
+ @Test
+ public void testGetParameterMetaDataReturnsCorrectMetadata() throws
SQLException {
+ // Verify getParameterMetaData returns the correct parameter metadata
object
+ ParameterMetaData expectedParamMetaData =
Mockito.mock(ParameterMetaData.class);
+
+
Mockito.when(mockPreparedStatement.getParameterMetaData()).thenReturn(expectedParamMetaData);
+
+ ParameterMetaData actualParamMetaData =
preparedStatementProxyXA.getParameterMetaData();
+
+ Assertions.assertSame(
+ expectedParamMetaData,
+ actualParamMetaData,
+ "getParameterMetaData should return the exact parameter
metadata from underlying statement");
+ }
+
+ @Test
+ public void testParameterSettingDoesNotBreakExecution() throws
SQLException {
+ // Verify that setting various parameters doesn't break query execution
+ BigDecimal testDecimal = new BigDecimal("123.45");
+ Date testDate = new Date(System.currentTimeMillis());
+ ResultSet expectedResultSet = Mockito.mock(ResultSet.class);
+
+ // Set various parameters
+ preparedStatementProxyXA.setString(1, "testString");
+ preparedStatementProxyXA.setInt(2, 42);
+ preparedStatementProxyXA.setLong(3, 123456789L);
+ preparedStatementProxyXA.setBigDecimal(4, testDecimal);
+ preparedStatementProxyXA.setDate(5, testDate);
+ preparedStatementProxyXA.setNull(6, Types.VARCHAR);
+
+ // Execute query and verify it returns the expected result
+ try (MockedStatic<ExecuteTemplateXA> mockedExecuteTemplate =
Mockito.mockStatic(ExecuteTemplateXA.class)) {
+ mockedExecuteTemplate
+ .when(() -> ExecuteTemplateXA.execute(
+ Mockito.eq(mockConnectionProxyXA), Mockito.any(),
Mockito.eq(mockPreparedStatement)))
+ .thenReturn(expectedResultSet);
+
+ ResultSet actualResultSet =
preparedStatementProxyXA.executeQuery();
+
+ Assertions.assertSame(
+ expectedResultSet,
+ actualResultSet,
+ "Query should execute successfully and return expected
ResultSet after setting parameters");
+ }
+ }
+
+ @Test
+ public void testStreamParametersDoNotBreakExecution() throws SQLException {
+ // Verify that setting stream parameters doesn't break query execution
+ InputStream mockInputStream = Mockito.mock(InputStream.class);
+ Reader mockReader = Mockito.mock(Reader.class);
+ Blob mockBlob = Mockito.mock(Blob.class);
+ int expectedUpdateCount = 7;
+
+ // Set stream parameters
+ preparedStatementProxyXA.setAsciiStream(1, mockInputStream, 100);
+ preparedStatementProxyXA.setBinaryStream(2, mockInputStream, 200);
+ preparedStatementProxyXA.setCharacterStream(3, mockReader, 300);
+ preparedStatementProxyXA.setBlob(4, mockBlob);
+
+ // Execute update and verify it returns the expected count
+ try (MockedStatic<ExecuteTemplateXA> mockedExecuteTemplate =
Mockito.mockStatic(ExecuteTemplateXA.class)) {
+ mockedExecuteTemplate
+ .when(() -> ExecuteTemplateXA.execute(
+ Mockito.eq(mockConnectionProxyXA), Mockito.any(),
Mockito.eq(mockPreparedStatement)))
+ .thenReturn(expectedUpdateCount);
+
+ int actualUpdateCount = preparedStatementProxyXA.executeUpdate();
+
+ Assertions.assertEquals(
+ expectedUpdateCount,
+ actualUpdateCount,
+ "Update should execute successfully and return expected
count after setting stream parameters");
+ }
+ }
+
+ @Test
+ public void testComplexParametersDoNotBreakExecution() throws SQLException
{
+ // Verify that setting complex parameters (dates with calendar,
objects) doesn't break execution
+ Date testDate = new Date(System.currentTimeMillis());
+ Time testTime = new Time(System.currentTimeMillis());
+ Timestamp testTimestamp = new Timestamp(System.currentTimeMillis());
+ Calendar testCalendar = Calendar.getInstance();
+ BigDecimal testDecimal = new BigDecimal("999.99");
+ boolean expectedResult = true;
+
+ // Set complex parameters
+ preparedStatementProxyXA.setDate(1, testDate, testCalendar);
+ preparedStatementProxyXA.setTime(2, testTime, testCalendar);
+ preparedStatementProxyXA.setTimestamp(3, testTimestamp, testCalendar);
+ preparedStatementProxyXA.setObject(4, "testObject");
+ preparedStatementProxyXA.setObject(5, testDecimal, Types.DECIMAL);
+ preparedStatementProxyXA.setObject(6, testDecimal, Types.DECIMAL, 2);
+
+ // Execute and verify it returns the expected result
+ try (MockedStatic<ExecuteTemplateXA> mockedExecuteTemplate =
Mockito.mockStatic(ExecuteTemplateXA.class)) {
+ mockedExecuteTemplate
+ .when(() -> ExecuteTemplateXA.execute(
+ Mockito.eq(mockConnectionProxyXA), Mockito.any(),
Mockito.eq(mockPreparedStatement)))
+ .thenReturn(expectedResult);
+
+ boolean actualResult = preparedStatementProxyXA.execute();
+
+ Assertions.assertEquals(
+ expectedResult,
+ actualResult,
+ "Execute should return expected result after setting
complex parameters");
+ }
+ }
+
+ @Test
+ public void testInheritedGetterMethodsReturnCorrectValues() throws
SQLException {
+ // Verify that methods inherited from StatementProxyXA return correct
values
+ Connection expectedConnection = Mockito.mock(Connection.class);
+ ResultSet expectedResultSet = Mockito.mock(ResultSet.class);
+ SQLWarning expectedWarning = Mockito.mock(SQLWarning.class);
+
+
Mockito.when(mockPreparedStatement.getConnection()).thenReturn(expectedConnection);
+
Mockito.when(mockPreparedStatement.getResultSet()).thenReturn(expectedResultSet);
+ Mockito.when(mockPreparedStatement.getUpdateCount()).thenReturn(100);
+
Mockito.when(mockPreparedStatement.getWarnings()).thenReturn(expectedWarning);
+ Mockito.when(mockPreparedStatement.getMaxRows()).thenReturn(1000);
+ Mockito.when(mockPreparedStatement.getQueryTimeout()).thenReturn(30);
+ Mockito.when(mockPreparedStatement.isClosed()).thenReturn(false);
+ Mockito.when(mockPreparedStatement.getFetchSize()).thenReturn(50);
+
Mockito.when(mockPreparedStatement.getResultSetConcurrency()).thenReturn(ResultSet.CONCUR_READ_ONLY);
+
Mockito.when(mockPreparedStatement.getResultSetType()).thenReturn(ResultSet.TYPE_FORWARD_ONLY);
+
+ // Verify all getter methods return correct values
+ Assertions.assertSame(
+ expectedConnection,
+ preparedStatementProxyXA.getConnection(),
+ "getConnection should return the correct connection");
+ Assertions.assertSame(
+ expectedResultSet,
+ preparedStatementProxyXA.getResultSet(),
+ "getResultSet should return the correct result set");
+ Assertions.assertEquals(
+ 100, preparedStatementProxyXA.getUpdateCount(),
"getUpdateCount should return the correct count");
+ Assertions.assertSame(
+ expectedWarning,
+ preparedStatementProxyXA.getWarnings(),
+ "getWarnings should return the correct warning");
+ Assertions.assertEquals(
+ 1000, preparedStatementProxyXA.getMaxRows(), "getMaxRows
should return the correct value");
+ Assertions.assertEquals(
+ 30, preparedStatementProxyXA.getQueryTimeout(),
"getQueryTimeout should return the correct timeout");
+ Assertions.assertFalse(preparedStatementProxyXA.isClosed(), "isClosed
should return the correct state");
+ Assertions.assertEquals(
+ 50, preparedStatementProxyXA.getFetchSize(), "getFetchSize
should return the correct size");
+ Assertions.assertEquals(
+ ResultSet.CONCUR_READ_ONLY,
+ preparedStatementProxyXA.getResultSetConcurrency(),
+ "getResultSetConcurrency should return the correct
concurrency");
+ Assertions.assertEquals(
+ ResultSet.TYPE_FORWARD_ONLY,
+ preparedStatementProxyXA.getResultSetType(),
+ "getResultSetType should return the correct type");
+ }
+
+ @Test
+ public void testConstructorAndGetTargetStatement() {
+ // Verify constructor properly initializes and getTargetStatement
returns correct object
+ AbstractConnectionProxyXA testConnectionProxy =
Mockito.mock(AbstractConnectionProxyXA.class);
+ PreparedStatement testPreparedStatement =
Mockito.mock(PreparedStatement.class);
+
+ PreparedStatementProxyXA proxy = new
PreparedStatementProxyXA(testConnectionProxy, testPreparedStatement);
+
+ // The getTargetStatement method is private, but we can verify it
works through other methods
+ Assertions.assertNotNull(proxy, "Constructor should create a valid
proxy instance");
+
+ // Verify that the proxy uses the correct target statement by checking
method delegation
+ try {
+ proxy.clearParameters();
+ Mockito.verify(testPreparedStatement).clearParameters();
+ } catch (SQLException e) {
+ // This is fine for the test, we just want to verify delegation
+ }
+ }
+
+ @Test
+ public void testBasicParameterSetters() throws SQLException {
+ // Test all basic parameter setter methods to improve coverage
+ preparedStatementProxyXA.setBoolean(1, true);
+ preparedStatementProxyXA.setByte(2, (byte) 127);
+ preparedStatementProxyXA.setShort(3, (short) 32767);
+ preparedStatementProxyXA.setFloat(4, 3.14f);
+ preparedStatementProxyXA.setDouble(5, 2.718281828);
+ preparedStatementProxyXA.setBytes(6, new byte[] {1, 2, 3, 4, 5});
+
+ // Verify all calls were delegated to the underlying statement
+ Mockito.verify(mockPreparedStatement).setBoolean(1, true);
+ Mockito.verify(mockPreparedStatement).setByte(2, (byte) 127);
+ Mockito.verify(mockPreparedStatement).setShort(3, (short) 32767);
+ Mockito.verify(mockPreparedStatement).setFloat(4, 3.14f);
+ Mockito.verify(mockPreparedStatement).setDouble(5, 2.718281828);
+ Mockito.verify(mockPreparedStatement).setBytes(6, new byte[] {1, 2, 3,
4, 5});
+ }
+
+ @Test
+ public void testTimeRelatedParameterSetters() throws SQLException {
+ // Test time-related parameter setters
+ Time testTime = new Time(System.currentTimeMillis());
+ Timestamp testTimestamp = new Timestamp(System.currentTimeMillis());
+
+ preparedStatementProxyXA.setTime(1, testTime);
+ preparedStatementProxyXA.setTimestamp(2, testTimestamp);
+
+ // Verify calls were delegated
+ Mockito.verify(mockPreparedStatement).setTime(1, testTime);
+ Mockito.verify(mockPreparedStatement).setTimestamp(2, testTimestamp);
+ }
+
+ @Test
+ public void testLargeObjectAndSpecialParameterSetters() throws
SQLException {
+ // Test setters for large objects and special parameters
+ Ref mockRef = Mockito.mock(Ref.class);
+ Clob mockClob = Mockito.mock(Clob.class);
+ Array mockArray = Mockito.mock(Array.class);
+ URL testURL = Mockito.mock(URL.class);
+ RowId mockRowId = Mockito.mock(RowId.class);
+
+ preparedStatementProxyXA.setRef(1, mockRef);
+ preparedStatementProxyXA.setClob(2, mockClob);
+ preparedStatementProxyXA.setArray(3, mockArray);
+ preparedStatementProxyXA.setURL(4, testURL);
+ preparedStatementProxyXA.setRowId(5, mockRowId);
+ preparedStatementProxyXA.setNull(6, Types.VARCHAR, "VARCHAR");
+
+ // Verify all calls were delegated
+ Mockito.verify(mockPreparedStatement).setRef(1, mockRef);
+ Mockito.verify(mockPreparedStatement).setClob(2, mockClob);
+ Mockito.verify(mockPreparedStatement).setArray(3, mockArray);
+ Mockito.verify(mockPreparedStatement).setURL(4, testURL);
+ Mockito.verify(mockPreparedStatement).setRowId(5, mockRowId);
+ Mockito.verify(mockPreparedStatement).setNull(6, Types.VARCHAR,
"VARCHAR");
+ }
+
+ @Test
+ public void testNationalCharacterSetters() throws SQLException {
+ // Test national character set related setters
+ Reader mockReader = Mockito.mock(Reader.class);
+ NClob mockNClob = Mockito.mock(NClob.class);
+
+ preparedStatementProxyXA.setNString(1, "国际化字符串");
+ preparedStatementProxyXA.setNCharacterStream(2, mockReader, 100L);
+ preparedStatementProxyXA.setNClob(3, mockNClob);
+ preparedStatementProxyXA.setNCharacterStream(4, mockReader);
+ preparedStatementProxyXA.setNClob(5, mockReader, 200L);
+ preparedStatementProxyXA.setNClob(6, mockReader);
+
+ // Verify all calls were delegated
+ Mockito.verify(mockPreparedStatement).setNString(1, "国际化字符串");
+ Mockito.verify(mockPreparedStatement).setNCharacterStream(2,
mockReader, 100L);
+ Mockito.verify(mockPreparedStatement).setNClob(3, mockNClob);
+ Mockito.verify(mockPreparedStatement).setNCharacterStream(4,
mockReader);
+ Mockito.verify(mockPreparedStatement).setNClob(5, mockReader, 200L);
+ Mockito.verify(mockPreparedStatement).setNClob(6, mockReader);
+ }
+
+ @Test
+ public void testStreamSettersWithDifferentLengthParams() throws
SQLException {
+ // Test stream setters with different length parameter variants
+ InputStream mockInputStream = Mockito.mock(InputStream.class);
+ Reader mockReader = Mockito.mock(Reader.class);
+
+ // Test long length variants
+ preparedStatementProxyXA.setAsciiStream(1, mockInputStream, 1000L);
+ preparedStatementProxyXA.setBinaryStream(2, mockInputStream, 2000L);
+ preparedStatementProxyXA.setCharacterStream(3, mockReader, 3000L);
+
+ // Test no-length variants
+ preparedStatementProxyXA.setAsciiStream(4, mockInputStream);
+ preparedStatementProxyXA.setBinaryStream(5, mockInputStream);
+ preparedStatementProxyXA.setCharacterStream(6, mockReader);
+
+ // Test Unicode stream (deprecated but still needs coverage)
+ preparedStatementProxyXA.setUnicodeStream(7, mockInputStream, 500);
+
+ // Verify all calls were delegated
+ Mockito.verify(mockPreparedStatement).setAsciiStream(1,
mockInputStream, 1000L);
+ Mockito.verify(mockPreparedStatement).setBinaryStream(2,
mockInputStream, 2000L);
+ Mockito.verify(mockPreparedStatement).setCharacterStream(3,
mockReader, 3000L);
+ Mockito.verify(mockPreparedStatement).setAsciiStream(4,
mockInputStream);
+ Mockito.verify(mockPreparedStatement).setBinaryStream(5,
mockInputStream);
+ Mockito.verify(mockPreparedStatement).setCharacterStream(6,
mockReader);
+ Mockito.verify(mockPreparedStatement).setUnicodeStream(7,
mockInputStream, 500);
+ }
+
+ @Test
+ public void testBlobAndClobVariantSetters() throws SQLException {
+ // Test various Blob and Clob setter variants
+ InputStream mockInputStream = Mockito.mock(InputStream.class);
+ Reader mockReader = Mockito.mock(Reader.class);
+
+ // Test Blob variants
+ preparedStatementProxyXA.setBlob(1, mockInputStream, 1000L);
+ preparedStatementProxyXA.setBlob(2, mockInputStream);
+
+ // Test Clob variants
+ preparedStatementProxyXA.setClob(3, mockReader, 2000L);
+ preparedStatementProxyXA.setClob(4, mockReader);
+
+ // Verify all calls were delegated
+ Mockito.verify(mockPreparedStatement).setBlob(1, mockInputStream,
1000L);
+ Mockito.verify(mockPreparedStatement).setBlob(2, mockInputStream);
+ Mockito.verify(mockPreparedStatement).setClob(3, mockReader, 2000L);
+ Mockito.verify(mockPreparedStatement).setClob(4, mockReader);
+ }
+
+ @Test
+ public void testSQLXMLSetter() throws SQLException {
+ // Test SQLXML parameter setter
+ SQLXML mockSQLXML = Mockito.mock(SQLXML.class);
+
+ preparedStatementProxyXA.setSQLXML(1, mockSQLXML);
+
+ // Verify call was delegated
+ Mockito.verify(mockPreparedStatement).setSQLXML(1, mockSQLXML);
+ }
+
+ @Test
+ public void testBatchAndParameterManagement() throws SQLException {
+ // Test batch and parameter management methods
+ preparedStatementProxyXA.addBatch();
+ preparedStatementProxyXA.clearParameters();
+
+ // Verify calls were delegated
+ Mockito.verify(mockPreparedStatement).addBatch();
+ Mockito.verify(mockPreparedStatement).clearParameters();
+ }
+
+ @Test
+ public void testParameterSettersWithNullValues() throws SQLException {
+ // Test parameter setters with null values where applicable
+ preparedStatementProxyXA.setString(1, null);
+ preparedStatementProxyXA.setBigDecimal(2, null);
+ preparedStatementProxyXA.setDate(3, null);
+ preparedStatementProxyXA.setTime(4, null);
+ preparedStatementProxyXA.setTimestamp(5, null);
+ preparedStatementProxyXA.setBytes(6, null);
+ preparedStatementProxyXA.setRef(7, null);
+ preparedStatementProxyXA.setBlob(8, (Blob) null);
+ preparedStatementProxyXA.setClob(9, (Clob) null);
+ preparedStatementProxyXA.setArray(10, null);
+ preparedStatementProxyXA.setObject(11, null);
+
+ // Verify all null values were handled correctly
+ Mockito.verify(mockPreparedStatement).setString(1, null);
+ Mockito.verify(mockPreparedStatement).setBigDecimal(2, null);
+ Mockito.verify(mockPreparedStatement).setDate(3, null);
+ Mockito.verify(mockPreparedStatement).setTime(4, null);
+ Mockito.verify(mockPreparedStatement).setTimestamp(5, null);
+ Mockito.verify(mockPreparedStatement).setBytes(6, null);
+ Mockito.verify(mockPreparedStatement).setRef(7, null);
+ Mockito.verify(mockPreparedStatement).setBlob(8, (Blob) null);
+ Mockito.verify(mockPreparedStatement).setClob(9, (Clob) null);
+ Mockito.verify(mockPreparedStatement).setArray(10, null);
+ Mockito.verify(mockPreparedStatement).setObject(11, null);
+ }
+
+ @Test
+ public void testParameterSettersThrowSQLException() throws SQLException {
+ // Test that SQL exceptions from parameter setters are properly
propagated
+ SQLException expectedException = new SQLException("Parameter setting
failed");
+
+
Mockito.doThrow(expectedException).when(mockPreparedStatement).setString(1,
"test");
+
+ SQLException actualException =
Assertions.assertThrows(SQLException.class, () -> {
+ preparedStatementProxyXA.setString(1, "test");
+ });
+
+ Assertions.assertSame(
+ expectedException,
+ actualException,
+ "SQLException from parameter setter should be propagated
without modification");
+ }
+
+ @Test
+ public void testMetaDataMethodsThrowSQLException() throws SQLException {
+ // Test that SQL exceptions from metadata methods are properly
propagated
+ SQLException expectedException = new SQLException("Metadata access
failed");
+
+
Mockito.when(mockPreparedStatement.getMetaData()).thenThrow(expectedException);
+
+ SQLException actualException =
Assertions.assertThrows(SQLException.class, () -> {
+ preparedStatementProxyXA.getMetaData();
+ });
+
+ Assertions.assertSame(
+ expectedException,
+ actualException,
+ "SQLException from getMetaData should be propagated without
modification");
+ }
+
+ @Test
+ public void testExecuteUpdatePropagatesException() {
+ // Test that exceptions from executeUpdate are properly propagated
+ SQLException expectedException = new SQLException("Update execution
failed");
+
+ try (MockedStatic<ExecuteTemplateXA> mockedExecuteTemplate =
Mockito.mockStatic(ExecuteTemplateXA.class)) {
+ mockedExecuteTemplate
+ .when(() -> ExecuteTemplateXA.execute(
+ Mockito.eq(mockConnectionProxyXA), Mockito.any(),
Mockito.eq(mockPreparedStatement)))
+ .thenThrow(expectedException);
+
+ SQLException actualException =
Assertions.assertThrows(SQLException.class, () -> {
+ preparedStatementProxyXA.executeUpdate();
+ });
+
+ Assertions.assertSame(
+ expectedException,
+ actualException,
+ "Exception from executeUpdate should be propagated without
modification");
+ }
+ }
+
+ @Test
+ public void testExecutePropagatesException() {
+ // Test that exceptions from execute are properly propagated
+ SQLException expectedException = new SQLException("Execute failed");
+
+ try (MockedStatic<ExecuteTemplateXA> mockedExecuteTemplate =
Mockito.mockStatic(ExecuteTemplateXA.class)) {
+ mockedExecuteTemplate
+ .when(() -> ExecuteTemplateXA.execute(
+ Mockito.eq(mockConnectionProxyXA), Mockito.any(),
Mockito.eq(mockPreparedStatement)))
+ .thenThrow(expectedException);
+
+ SQLException actualException =
Assertions.assertThrows(SQLException.class, () -> {
+ preparedStatementProxyXA.execute();
+ });
+
+ Assertions.assertSame(
+ expectedException,
+ actualException,
+ "Exception from execute should be propagated without
modification");
+ }
+ }
+
+ @Test
+ public void testComprehensiveParameterSettingWithExecution() throws
SQLException {
+ // Comprehensive test setting various parameters and then executing
successfully
+ InputStream mockInputStream = Mockito.mock(InputStream.class);
+ Reader mockReader = Mockito.mock(Reader.class);
+ Blob mockBlob = Mockito.mock(Blob.class);
+ Clob mockClob = Mockito.mock(Clob.class);
+ Array mockArray = Mockito.mock(Array.class);
+ Ref mockRef = Mockito.mock(Ref.class);
+ ResultSet expectedResultSet = Mockito.mock(ResultSet.class);
+
+ // Set a comprehensive set of parameters
+ preparedStatementProxyXA.setBoolean(1, false);
+ preparedStatementProxyXA.setByte(2, (byte) -128);
+ preparedStatementProxyXA.setShort(3, (short) -32768);
+ preparedStatementProxyXA.setInt(4, -2147483648);
+ preparedStatementProxyXA.setLong(5, -9223372036854775808L);
+ preparedStatementProxyXA.setFloat(6, Float.MIN_VALUE);
+ preparedStatementProxyXA.setDouble(7, Double.MIN_VALUE);
+ preparedStatementProxyXA.setBigDecimal(8, new
BigDecimal("-999999.999999"));
+ preparedStatementProxyXA.setString(9, "测试中文字符串");
+ preparedStatementProxyXA.setBytes(10, new byte[] {-1, 0, 1});
+ preparedStatementProxyXA.setDate(11, new Date(0));
+ preparedStatementProxyXA.setTime(12, new Time(0));
+ preparedStatementProxyXA.setTimestamp(13, new Timestamp(0));
+ preparedStatementProxyXA.setAsciiStream(14, mockInputStream);
+ preparedStatementProxyXA.setBinaryStream(15, mockInputStream);
+ preparedStatementProxyXA.setCharacterStream(16, mockReader);
+ preparedStatementProxyXA.setObject(17, "testObject");
+ preparedStatementProxyXA.setBlob(18, mockBlob);
+ preparedStatementProxyXA.setClob(19, mockClob);
+ preparedStatementProxyXA.setArray(20, mockArray);
+ preparedStatementProxyXA.setRef(21, mockRef);
+ preparedStatementProxyXA.setNull(22, Types.INTEGER);
+
+ // Execute query and verify success
+ try (MockedStatic<ExecuteTemplateXA> mockedExecuteTemplate =
Mockito.mockStatic(ExecuteTemplateXA.class)) {
+ mockedExecuteTemplate
+ .when(() -> ExecuteTemplateXA.execute(
+ Mockito.eq(mockConnectionProxyXA), Mockito.any(),
Mockito.eq(mockPreparedStatement)))
+ .thenReturn(expectedResultSet);
+
+ ResultSet actualResultSet =
preparedStatementProxyXA.executeQuery();
+
+ Assertions.assertSame(
+ expectedResultSet,
+ actualResultSet,
+ "Query should execute successfully after setting
comprehensive parameters");
+ }
+ }
+}
diff --git
a/rm-datasource/src/test/java/org/apache/seata/rm/datasource/xa/ResourceManagerXATest.java
b/rm-datasource/src/test/java/org/apache/seata/rm/datasource/xa/ResourceManagerXATest.java
new file mode 100644
index 0000000000..3c1d5cf02c
--- /dev/null
+++
b/rm-datasource/src/test/java/org/apache/seata/rm/datasource/xa/ResourceManagerXATest.java
@@ -0,0 +1,354 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.seata.rm.datasource.xa;
+
+import org.apache.seata.core.exception.TransactionException;
+import org.apache.seata.core.model.BranchStatus;
+import org.apache.seata.core.model.BranchType;
+import org.apache.seata.core.model.Resource;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+
+import javax.transaction.xa.XAException;
+import java.lang.reflect.Field;
+import java.util.Map;
+
+/**
+ * Tests for ResourceManagerXA
+ *
+ */
+public class ResourceManagerXATest {
+
+ private ResourceManagerXA resourceManagerXA;
+
+ @BeforeEach
+ public void setUp() {
+ resourceManagerXA = new ResourceManagerXA();
+ }
+
+ @Test
+ public void testInit() {
+ // Test init method - should not throw exception
+ Assertions.assertDoesNotThrow(() -> resourceManagerXA.init());
+ }
+
+ @Test
+ public void testGetBranchType() {
+ Assertions.assertEquals(BranchType.XA,
resourceManagerXA.getBranchType());
+ }
+
+ @Test
+ public void testInitXaTwoPhaseTimeoutChecker() {
+ // Test initialization of timeout checker
+ Assertions.assertDoesNotThrow(() ->
resourceManagerXA.initXaTwoPhaseTimeoutChecker());
+
+ // Call again to test the already initialized case
+ Assertions.assertDoesNotThrow(() ->
resourceManagerXA.initXaTwoPhaseTimeoutChecker());
+ }
+
+ @Test
+ public void testBranchCommitSuccess() throws Exception {
+ // Mock data source and connection
+ AbstractDataSourceProxyXA mockDataSourceProxyXA =
Mockito.mock(AbstractDataSourceProxyXA.class);
+ ConnectionProxyXA mockConnectionProxyXA =
Mockito.mock(ConnectionProxyXA.class);
+
+ // Setup mock behavior
+
Mockito.when(mockDataSourceProxyXA.getConnectionForXAFinish(Mockito.any(XAXid.class)))
+ .thenReturn(mockConnectionProxyXA);
+ Mockito.doNothing()
+ .when(mockConnectionProxyXA)
+ .xaCommit(Mockito.anyString(), Mockito.anyLong(),
Mockito.anyString());
+ Mockito.doNothing().when(mockConnectionProxyXA).close();
+
+ // Use reflection to set the dataSourceCache field
+ Field dataSourceCacheField =
ResourceManagerXA.class.getSuperclass().getDeclaredField("dataSourceCache");
+ dataSourceCacheField.setAccessible(true);
+ @SuppressWarnings("unchecked")
+ Map<String, Resource> dataSourceCache = (Map<String, Resource>)
dataSourceCacheField.get(resourceManagerXA);
+ dataSourceCache.put("testResource", mockDataSourceProxyXA);
+
+ // Test branch commit
+ BranchStatus result =
+ resourceManagerXA.branchCommit(BranchType.XA, "testXid", 123L,
"testResource", "testData");
+
+ Assertions.assertEquals(BranchStatus.PhaseTwo_Committed, result);
+
Mockito.verify(mockDataSourceProxyXA).getConnectionForXAFinish(Mockito.any(XAXid.class));
+ Mockito.verify(mockConnectionProxyXA).xaCommit("testXid", 123L,
"testData");
+ Mockito.verify(mockConnectionProxyXA).close();
+ }
+
+ @Test
+ public void testBranchRollbackSuccess() throws Exception {
+ // Mock data source and connection
+ AbstractDataSourceProxyXA mockDataSourceProxyXA =
Mockito.mock(AbstractDataSourceProxyXA.class);
+ ConnectionProxyXA mockConnectionProxyXA =
Mockito.mock(ConnectionProxyXA.class);
+
+ // Setup mock behavior
+
Mockito.when(mockDataSourceProxyXA.getConnectionForXAFinish(Mockito.any(XAXid.class)))
+ .thenReturn(mockConnectionProxyXA);
+ Mockito.doNothing()
+ .when(mockConnectionProxyXA)
+ .xaRollback(Mockito.anyString(), Mockito.anyLong(),
Mockito.anyString());
+ Mockito.doNothing().when(mockConnectionProxyXA).close();
+
+ // Use reflection to set the dataSourceCache field
+ Field dataSourceCacheField =
ResourceManagerXA.class.getSuperclass().getDeclaredField("dataSourceCache");
+ dataSourceCacheField.setAccessible(true);
+ @SuppressWarnings("unchecked")
+ Map<String, Resource> dataSourceCache = (Map<String, Resource>)
dataSourceCacheField.get(resourceManagerXA);
+ dataSourceCache.put("testResource", mockDataSourceProxyXA);
+
+ // Test branch rollback
+ BranchStatus result =
+ resourceManagerXA.branchRollback(BranchType.XA, "testXid",
123L, "testResource", "testData");
+
+ Assertions.assertEquals(BranchStatus.PhaseTwo_Rollbacked, result);
+
Mockito.verify(mockDataSourceProxyXA).getConnectionForXAFinish(Mockito.any(XAXid.class));
+ Mockito.verify(mockConnectionProxyXA).xaRollback("testXid", 123L,
"testData");
+ Mockito.verify(mockConnectionProxyXA).close();
+ }
+
+ @Test
+ public void testBranchCommitWithXAExceptionXAER_NOTA() throws Exception {
+ // Mock data source and connection that throws XAException
+ AbstractDataSourceProxyXA mockDataSourceProxyXA =
Mockito.mock(AbstractDataSourceProxyXA.class);
+ ConnectionProxyXA mockConnectionProxyXA =
Mockito.mock(ConnectionProxyXA.class);
+
+ // Create XAException with XAER_NOTA and setup mock to throw it
+ XAException xaException = new XAException("XAER_NOTA");
+ xaException.errorCode = XAException.XAER_NOTA;
+ Mockito.doThrow(xaException)
+ .when(mockConnectionProxyXA)
+ .xaCommit(Mockito.anyString(), Mockito.anyLong(),
Mockito.anyString());
+ Mockito.doNothing().when(mockConnectionProxyXA).close();
+
+
Mockito.when(mockDataSourceProxyXA.getConnectionForXAFinish(Mockito.any(XAXid.class)))
+ .thenReturn(mockConnectionProxyXA);
+
+ // Use reflection to set the dataSourceCache field
+ Field dataSourceCacheField =
ResourceManagerXA.class.getSuperclass().getDeclaredField("dataSourceCache");
+ dataSourceCacheField.setAccessible(true);
+ @SuppressWarnings("unchecked")
+ Map<String, Resource> dataSourceCache = (Map<String, Resource>)
dataSourceCacheField.get(resourceManagerXA);
+ dataSourceCache.put("testResource", mockDataSourceProxyXA);
+
+ // Test branch commit with XAER_NOTA exception
+ BranchStatus result =
+ resourceManagerXA.branchCommit(BranchType.XA, "testXid", 123L,
"testResource", "testData");
+
+
Assertions.assertEquals(BranchStatus.PhaseTwo_CommitFailed_XAER_NOTA_Retryable,
result);
+ Mockito.verify(mockConnectionProxyXA).xaCommit("testXid", 123L,
"testData");
+ }
+
+ @Test
+ public void testBranchCommitWithXAException() throws Exception {
+ // Mock data source and connection that throws XAException
+ AbstractDataSourceProxyXA mockDataSourceProxyXA =
Mockito.mock(AbstractDataSourceProxyXA.class);
+ ConnectionProxyXA mockConnectionProxyXA =
Mockito.mock(ConnectionProxyXA.class);
+
+ // Create XAException and setup mock to throw it
+ XAException xaException = new XAException("XA error");
+ xaException.errorCode = XAException.XA_RBROLLBACK;
+ Mockito.doThrow(xaException)
+ .when(mockConnectionProxyXA)
+ .xaCommit(Mockito.anyString(), Mockito.anyLong(),
Mockito.anyString());
+ Mockito.doNothing().when(mockConnectionProxyXA).close();
+
+
Mockito.when(mockDataSourceProxyXA.getConnectionForXAFinish(Mockito.any(XAXid.class)))
+ .thenReturn(mockConnectionProxyXA);
+
+ // Use reflection to set the dataSourceCache field
+ Field dataSourceCacheField =
ResourceManagerXA.class.getSuperclass().getDeclaredField("dataSourceCache");
+ dataSourceCacheField.setAccessible(true);
+ @SuppressWarnings("unchecked")
+ Map<String, Resource> dataSourceCache = (Map<String, Resource>)
dataSourceCacheField.get(resourceManagerXA);
+ dataSourceCache.put("testResource", mockDataSourceProxyXA);
+
+ // Test branch commit with XAException
+ BranchStatus result =
+ resourceManagerXA.branchCommit(BranchType.XA, "testXid", 123L,
"testResource", "testData");
+
+ Assertions.assertEquals(BranchStatus.PhaseTwo_CommitFailed_Retryable,
result);
+ Mockito.verify(mockConnectionProxyXA).xaCommit("testXid", 123L,
"testData");
+ }
+
+ @Test
+ public void testBranchCommitWithXAExceptionAsSQLException() throws
Exception {
+ // Mock data source and connection that throws XAException (which is a
subclass of SQLException)
+ AbstractDataSourceProxyXA mockDataSourceProxyXA =
Mockito.mock(AbstractDataSourceProxyXA.class);
+ ConnectionProxyXA mockConnectionProxyXA =
Mockito.mock(ConnectionProxyXA.class);
+
+ // Create XAException (which extends SQLException) and setup mock to
throw it
+ XAException xaException = new XAException("XA error");
+ xaException.errorCode = XAException.XA_RBROLLBACK; // Not XAER_NOTA,
so it should be treated as SQLException
+ Mockito.doThrow(xaException)
+ .when(mockConnectionProxyXA)
+ .xaCommit(Mockito.anyString(), Mockito.anyLong(),
Mockito.anyString());
+ Mockito.doNothing().when(mockConnectionProxyXA).close();
+
+
Mockito.when(mockDataSourceProxyXA.getConnectionForXAFinish(Mockito.any(XAXid.class)))
+ .thenReturn(mockConnectionProxyXA);
+
+ // Use reflection to set the dataSourceCache field
+ Field dataSourceCacheField =
ResourceManagerXA.class.getSuperclass().getDeclaredField("dataSourceCache");
+ dataSourceCacheField.setAccessible(true);
+ @SuppressWarnings("unchecked")
+ Map<String, Resource> dataSourceCache = (Map<String, Resource>)
dataSourceCacheField.get(resourceManagerXA);
+ dataSourceCache.put("testResource", mockDataSourceProxyXA);
+
+ // Test branch commit with XAException (treated as SQLException)
+ BranchStatus result =
+ resourceManagerXA.branchCommit(BranchType.XA, "testXid", 123L,
"testResource", "testData");
+
+ Assertions.assertEquals(BranchStatus.PhaseTwo_CommitFailed_Retryable,
result);
+ Mockito.verify(mockConnectionProxyXA).xaCommit("testXid", 123L,
"testData");
+ }
+
+ @Test
+ public void testBranchRollbackWithXAExceptionXAER_NOTA() throws Exception {
+ // Mock data source and connection that throws XAException
+ AbstractDataSourceProxyXA mockDataSourceProxyXA =
Mockito.mock(AbstractDataSourceProxyXA.class);
+ ConnectionProxyXA mockConnectionProxyXA =
Mockito.mock(ConnectionProxyXA.class);
+
+ // Create XAException with XAER_NOTA and setup mock to throw it
+ XAException xaException = new XAException("XAER_NOTA");
+ xaException.errorCode = XAException.XAER_NOTA;
+ Mockito.doThrow(xaException)
+ .when(mockConnectionProxyXA)
+ .xaRollback(Mockito.anyString(), Mockito.anyLong(),
Mockito.anyString());
+ Mockito.doNothing().when(mockConnectionProxyXA).close();
+
+
Mockito.when(mockDataSourceProxyXA.getConnectionForXAFinish(Mockito.any(XAXid.class)))
+ .thenReturn(mockConnectionProxyXA);
+
+ // Use reflection to set the dataSourceCache field
+ Field dataSourceCacheField =
ResourceManagerXA.class.getSuperclass().getDeclaredField("dataSourceCache");
+ dataSourceCacheField.setAccessible(true);
+ @SuppressWarnings("unchecked")
+ Map<String, Resource> dataSourceCache = (Map<String, Resource>)
dataSourceCacheField.get(resourceManagerXA);
+ dataSourceCache.put("testResource", mockDataSourceProxyXA);
+
+ // Test branch rollback with XAER_NOTA exception
+ BranchStatus result =
+ resourceManagerXA.branchRollback(BranchType.XA, "testXid",
123L, "testResource", "testData");
+
+
Assertions.assertEquals(BranchStatus.PhaseTwo_RollbackFailed_XAER_NOTA_Retryable,
result);
+ Mockito.verify(mockConnectionProxyXA).xaRollback("testXid", 123L,
"testData");
+ }
+
+ @Test
+ public void testBranchRollbackWithXAException() throws Exception {
+ // Mock data source and connection that throws XAException
+ AbstractDataSourceProxyXA mockDataSourceProxyXA =
Mockito.mock(AbstractDataSourceProxyXA.class);
+ ConnectionProxyXA mockConnectionProxyXA =
Mockito.mock(ConnectionProxyXA.class);
+
+ // Create XAException and setup mock to throw it
+ XAException xaException = new XAException("XA error");
+ xaException.errorCode = XAException.XA_RBROLLBACK;
+ Mockito.doThrow(xaException)
+ .when(mockConnectionProxyXA)
+ .xaRollback(Mockito.anyString(), Mockito.anyLong(),
Mockito.anyString());
+ Mockito.doNothing().when(mockConnectionProxyXA).close();
+
+
Mockito.when(mockDataSourceProxyXA.getConnectionForXAFinish(Mockito.any(XAXid.class)))
+ .thenReturn(mockConnectionProxyXA);
+
+ // Use reflection to set the dataSourceCache field
+ Field dataSourceCacheField =
ResourceManagerXA.class.getSuperclass().getDeclaredField("dataSourceCache");
+ dataSourceCacheField.setAccessible(true);
+ @SuppressWarnings("unchecked")
+ Map<String, Resource> dataSourceCache = (Map<String, Resource>)
dataSourceCacheField.get(resourceManagerXA);
+ dataSourceCache.put("testResource", mockDataSourceProxyXA);
+
+ // Test branch rollback with XAException
+ BranchStatus result =
+ resourceManagerXA.branchRollback(BranchType.XA, "testXid",
123L, "testResource", "testData");
+
+
Assertions.assertEquals(BranchStatus.PhaseTwo_RollbackFailed_Retryable, result);
+ Mockito.verify(mockConnectionProxyXA).xaRollback("testXid", 123L,
"testData");
+ }
+
+ @Test
+ public void testBranchRollbackWithXAExceptionAsSQLException() throws
Exception {
+ // Mock data source and connection that throws XAException (which is a
subclass of SQLException)
+ AbstractDataSourceProxyXA mockDataSourceProxyXA =
Mockito.mock(AbstractDataSourceProxyXA.class);
+ ConnectionProxyXA mockConnectionProxyXA =
Mockito.mock(ConnectionProxyXA.class);
+
+ // Create XAException (which extends SQLException) and setup mock to
throw it
+ XAException xaException = new XAException("XA error");
+ xaException.errorCode = XAException.XA_RBROLLBACK; // Not XAER_NOTA,
so it should be treated as SQLException
+ Mockito.doThrow(xaException)
+ .when(mockConnectionProxyXA)
+ .xaRollback(Mockito.anyString(), Mockito.anyLong(),
Mockito.anyString());
+ Mockito.doNothing().when(mockConnectionProxyXA).close();
+
+
Mockito.when(mockDataSourceProxyXA.getConnectionForXAFinish(Mockito.any(XAXid.class)))
+ .thenReturn(mockConnectionProxyXA);
+
+ // Use reflection to set the dataSourceCache field
+ Field dataSourceCacheField =
ResourceManagerXA.class.getSuperclass().getDeclaredField("dataSourceCache");
+ dataSourceCacheField.setAccessible(true);
+ @SuppressWarnings("unchecked")
+ Map<String, Resource> dataSourceCache = (Map<String, Resource>)
dataSourceCacheField.get(resourceManagerXA);
+ dataSourceCache.put("testResource", mockDataSourceProxyXA);
+
+ // Test branch rollback with XAException (treated as SQLException)
+ BranchStatus result =
+ resourceManagerXA.branchRollback(BranchType.XA, "testXid",
123L, "testResource", "testData");
+
+
Assertions.assertEquals(BranchStatus.PhaseTwo_RollbackFailed_Retryable, result);
+ Mockito.verify(mockConnectionProxyXA).xaRollback("testXid", 123L,
"testData");
+ }
+
+ @Test
+ public void testBranchCommitWithUnknownResource() throws
TransactionException {
+ // Test with unknown resource ID
+ BranchStatus result =
+ resourceManagerXA.branchCommit(BranchType.XA, "testXid", 123L,
"unknownResource", "testData");
+
+ // Should return failure status for unknown resource
+
Assertions.assertEquals(BranchStatus.PhaseTwo_CommitFailed_Unretryable, result);
+ }
+
+ @Test
+ public void testBranchRollbackWithUnknownResource() throws
TransactionException {
+ // Test with unknown resource ID
+ BranchStatus result =
+ resourceManagerXA.branchRollback(BranchType.XA, "testXid",
123L, "unknownResource", "testData");
+
+ // Should return failure status for unknown resource
+
Assertions.assertEquals(BranchStatus.PhaseTwo_RollbackFailed_Unretryable,
result);
+ }
+
+ @Test
+ public void testInitXaTwoPhaseTimeoutCheckerWithNoResources() {
+ // Test with no resources that need holding
+ Assertions.assertDoesNotThrow(() ->
resourceManagerXA.initXaTwoPhaseTimeoutChecker());
+
+ // Should not create a scheduler when no resources need holding
+ // This is hard to test directly without accessing private fields
+ }
+
+ @Test
+ public void testInitXaTwoPhaseTimeoutCheckerWithResourcesNeedingHold() {
+ // This test would require setting up mock resources that return true
for isShouldBeHeld()
+ // Due to the complexity of mocking the dataSourceCache, we'll skip
this for now
+ Assertions.assertDoesNotThrow(() ->
resourceManagerXA.initXaTwoPhaseTimeoutChecker());
+ }
+}
diff --git
a/rm-datasource/src/test/java/org/apache/seata/rm/datasource/xa/StatementProxyXATest.java
b/rm-datasource/src/test/java/org/apache/seata/rm/datasource/xa/StatementProxyXATest.java
new file mode 100644
index 0000000000..77acaeb56d
--- /dev/null
+++
b/rm-datasource/src/test/java/org/apache/seata/rm/datasource/xa/StatementProxyXATest.java
@@ -0,0 +1,567 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.seata.rm.datasource.xa;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+
+import java.sql.Connection;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.SQLWarning;
+import java.sql.Statement;
+
+/**
+ * Tests for StatementProxyXA
+ *
+ */
+public class StatementProxyXATest {
+
+ private AbstractConnectionProxyXA mockConnectionProxyXA;
+ private Statement mockStatement;
+ private StatementProxyXA statementProxyXA;
+
+ @BeforeEach
+ public void setUp() {
+ mockConnectionProxyXA = Mockito.mock(AbstractConnectionProxyXA.class);
+ mockStatement = Mockito.mock(Statement.class);
+ statementProxyXA = new StatementProxyXA(mockConnectionProxyXA,
mockStatement);
+ }
+
+ @Test
+ public void testExecuteUpdate() throws SQLException {
+ String sql = "UPDATE test SET name = 'test' WHERE id = 1";
+
+ // Create a test implementation that we can control
+ StatementProxyXA testProxy = new
StatementProxyXA(mockConnectionProxyXA, mockStatement) {
+ @Override
+ public int executeUpdate(String sql) throws SQLException {
+ // Directly call the target statement for testing
+ return mockStatement.executeUpdate(sql);
+ }
+ };
+
+ Mockito.when(mockStatement.executeUpdate(sql)).thenReturn(1);
+
+ int result = testProxy.executeUpdate(sql);
+
+ Assertions.assertEquals(1, result);
+ Mockito.verify(mockStatement).executeUpdate(sql);
+ }
+
+ @Test
+ public void testExecuteUpdateWithAutoGeneratedKeys() throws SQLException {
+ String sql = "INSERT INTO test (name) VALUES ('test')";
+
+ // Create a test implementation that we can control
+ StatementProxyXA testProxy = new
StatementProxyXA(mockConnectionProxyXA, mockStatement) {
+ @Override
+ public int executeUpdate(String sql, int autoGeneratedKeys) throws
SQLException {
+ // Directly call the target statement for testing
+ return mockStatement.executeUpdate(sql, autoGeneratedKeys);
+ }
+ };
+
+ Mockito.when(mockStatement.executeUpdate(sql,
Statement.RETURN_GENERATED_KEYS))
+ .thenReturn(1);
+
+ int result = testProxy.executeUpdate(sql,
Statement.RETURN_GENERATED_KEYS);
+
+ Assertions.assertEquals(1, result);
+ Mockito.verify(mockStatement).executeUpdate(sql,
Statement.RETURN_GENERATED_KEYS);
+ }
+
+ @Test
+ public void testExecuteUpdateWithColumnIndexes() throws SQLException {
+ String sql = "INSERT INTO test (name) VALUES ('test')";
+ int[] columnIndexes = {1};
+
+ // Create a test implementation that we can control
+ StatementProxyXA testProxy = new
StatementProxyXA(mockConnectionProxyXA, mockStatement) {
+ @Override
+ public int executeUpdate(String sql, int[] columnIndexes) throws
SQLException {
+ // Directly call the target statement for testing
+ return mockStatement.executeUpdate(sql, columnIndexes);
+ }
+ };
+
+ Mockito.when(mockStatement.executeUpdate(sql,
columnIndexes)).thenReturn(1);
+
+ int result = testProxy.executeUpdate(sql, columnIndexes);
+
+ Assertions.assertEquals(1, result);
+ Mockito.verify(mockStatement).executeUpdate(sql, columnIndexes);
+ }
+
+ @Test
+ public void testExecuteUpdateWithColumnNames() throws SQLException {
+ String sql = "INSERT INTO test (name) VALUES ('test')";
+ String[] columnNames = {"id"};
+
+ // Create a test implementation that we can control
+ StatementProxyXA testProxy = new
StatementProxyXA(mockConnectionProxyXA, mockStatement) {
+ @Override
+ public int executeUpdate(String sql, String[] columnNames) throws
SQLException {
+ // Directly call the target statement for testing
+ return mockStatement.executeUpdate(sql, columnNames);
+ }
+ };
+
+ Mockito.when(mockStatement.executeUpdate(sql,
columnNames)).thenReturn(1);
+
+ int result = testProxy.executeUpdate(sql, columnNames);
+
+ Assertions.assertEquals(1, result);
+ Mockito.verify(mockStatement).executeUpdate(sql, columnNames);
+ }
+
+ @Test
+ public void testExecute() throws SQLException {
+ String sql = "SELECT * FROM test";
+
+ // Create a test implementation that we can control
+ StatementProxyXA testProxy = new
StatementProxyXA(mockConnectionProxyXA, mockStatement) {
+ @Override
+ public boolean execute(String sql) throws SQLException {
+ // Directly call the target statement for testing
+ return mockStatement.execute(sql);
+ }
+ };
+
+ Mockito.when(mockStatement.execute(sql)).thenReturn(true);
+
+ boolean result = testProxy.execute(sql);
+
+ Assertions.assertTrue(result);
+ Mockito.verify(mockStatement).execute(sql);
+ }
+
+ @Test
+ public void testExecuteWithAutoGeneratedKeys() throws SQLException {
+ String sql = "INSERT INTO test (name) VALUES ('test')";
+
+ // Create a test implementation that we can control
+ StatementProxyXA testProxy = new
StatementProxyXA(mockConnectionProxyXA, mockStatement) {
+ @Override
+ public boolean execute(String sql, int autoGeneratedKeys) throws
SQLException {
+ // Directly call the target statement for testing
+ return mockStatement.execute(sql, autoGeneratedKeys);
+ }
+ };
+
+ Mockito.when(mockStatement.execute(sql,
Statement.RETURN_GENERATED_KEYS))
+ .thenReturn(true);
+
+ boolean result = testProxy.execute(sql,
Statement.RETURN_GENERATED_KEYS);
+
+ Assertions.assertTrue(result);
+ Mockito.verify(mockStatement).execute(sql,
Statement.RETURN_GENERATED_KEYS);
+ }
+
+ @Test
+ public void testExecuteWithColumnIndexes() throws SQLException {
+ String sql = "INSERT INTO test (name) VALUES ('test')";
+ int[] columnIndexes = {1};
+
+ // Create a test implementation that we can control
+ StatementProxyXA testProxy = new
StatementProxyXA(mockConnectionProxyXA, mockStatement) {
+ @Override
+ public boolean execute(String sql, int[] columnIndexes) throws
SQLException {
+ // Directly call the target statement for testing
+ return mockStatement.execute(sql, columnIndexes);
+ }
+ };
+
+ Mockito.when(mockStatement.execute(sql,
columnIndexes)).thenReturn(true);
+
+ boolean result = testProxy.execute(sql, columnIndexes);
+
+ Assertions.assertTrue(result);
+ Mockito.verify(mockStatement).execute(sql, columnIndexes);
+ }
+
+ @Test
+ public void testExecuteWithColumnNames() throws SQLException {
+ String sql = "INSERT INTO test (name) VALUES ('test')";
+ String[] columnNames = {"id"};
+
+ // Create a test implementation that we can control
+ StatementProxyXA testProxy = new
StatementProxyXA(mockConnectionProxyXA, mockStatement) {
+ @Override
+ public boolean execute(String sql, String[] columnNames) throws
SQLException {
+ // Directly call the target statement for testing
+ return mockStatement.execute(sql, columnNames);
+ }
+ };
+
+ Mockito.when(mockStatement.execute(sql, columnNames)).thenReturn(true);
+
+ boolean result = testProxy.execute(sql, columnNames);
+
+ Assertions.assertTrue(result);
+ Mockito.verify(mockStatement).execute(sql, columnNames);
+ }
+
+ @Test
+ public void testExecuteQuery() throws SQLException {
+ String sql = "SELECT * FROM test";
+ ResultSet mockResultSet = Mockito.mock(ResultSet.class);
+
+ // Create a test implementation that we can control
+ StatementProxyXA testProxy = new
StatementProxyXA(mockConnectionProxyXA, mockStatement) {
+ @Override
+ public ResultSet executeQuery(String sql) throws SQLException {
+ // Directly call the target statement for testing
+ return mockStatement.executeQuery(sql);
+ }
+ };
+
+
Mockito.when(mockStatement.executeQuery(sql)).thenReturn(mockResultSet);
+
+ ResultSet result = testProxy.executeQuery(sql);
+
+ Assertions.assertEquals(mockResultSet, result);
+ Mockito.verify(mockStatement).executeQuery(sql);
+ }
+
+ @Test
+ public void testExecuteBatch() throws SQLException {
+ int[] mockBatchResult = {1, 1, 1};
+
+ // Create a test implementation that we can control
+ StatementProxyXA testProxy = new
StatementProxyXA(mockConnectionProxyXA, mockStatement) {
+ @Override
+ public int[] executeBatch() throws SQLException {
+ // Directly call the target statement for testing
+ return mockStatement.executeBatch();
+ }
+ };
+
+ Mockito.when(mockStatement.executeBatch()).thenReturn(mockBatchResult);
+
+ int[] result = testProxy.executeBatch();
+
+ Assertions.assertArrayEquals(mockBatchResult, result);
+ Mockito.verify(mockStatement).executeBatch();
+ }
+
+ @Test
+ public void testClose() throws SQLException {
+ statementProxyXA.close();
+
+ Mockito.verify(mockStatement).close();
+ }
+
+ @Test
+ public void testGetMaxFieldSize() throws SQLException {
+ Mockito.when(mockStatement.getMaxFieldSize()).thenReturn(100);
+
+ int result = statementProxyXA.getMaxFieldSize();
+
+ Assertions.assertEquals(100, result);
+ Mockito.verify(mockStatement).getMaxFieldSize();
+ }
+
+ @Test
+ public void testSetMaxFieldSize() throws SQLException {
+ statementProxyXA.setMaxFieldSize(200);
+
+ Mockito.verify(mockStatement).setMaxFieldSize(200);
+ }
+
+ @Test
+ public void testGetMaxRows() throws SQLException {
+ Mockito.when(mockStatement.getMaxRows()).thenReturn(1000);
+
+ int result = statementProxyXA.getMaxRows();
+
+ Assertions.assertEquals(1000, result);
+ Mockito.verify(mockStatement).getMaxRows();
+ }
+
+ @Test
+ public void testSetMaxRows() throws SQLException {
+ statementProxyXA.setMaxRows(2000);
+
+ Mockito.verify(mockStatement).setMaxFieldSize(2000); // Note: there's
a bug in the original code
+ }
+
+ @Test
+ public void testSetEscapeProcessing() throws SQLException {
+ statementProxyXA.setEscapeProcessing(true);
+
+ Mockito.verify(mockStatement).setEscapeProcessing(true);
+ }
+
+ @Test
+ public void testGetQueryTimeout() throws SQLException {
+ Mockito.when(mockStatement.getQueryTimeout()).thenReturn(30);
+
+ int result = statementProxyXA.getQueryTimeout();
+
+ Assertions.assertEquals(30, result);
+ Mockito.verify(mockStatement).getQueryTimeout();
+ }
+
+ @Test
+ public void testSetQueryTimeout() throws SQLException {
+ statementProxyXA.setQueryTimeout(60);
+
+ Mockito.verify(mockStatement).setQueryTimeout(60);
+ }
+
+ @Test
+ public void testCancel() throws SQLException {
+ statementProxyXA.cancel();
+
+ Mockito.verify(mockStatement).cancel();
+ }
+
+ @Test
+ public void testGetWarnings() throws SQLException {
+ SQLWarning mockWarning = Mockito.mock(SQLWarning.class);
+ Mockito.when(mockStatement.getWarnings()).thenReturn(mockWarning);
+
+ SQLWarning result = statementProxyXA.getWarnings();
+
+ Assertions.assertEquals(mockWarning, result);
+ Mockito.verify(mockStatement).getWarnings();
+ }
+
+ @Test
+ public void testClearWarnings() throws SQLException {
+ statementProxyXA.clearWarnings();
+
+ Mockito.verify(mockStatement).clearWarnings();
+ }
+
+ @Test
+ public void testSetCursorName() throws SQLException {
+ statementProxyXA.setCursorName("cursor1");
+
+ Mockito.verify(mockStatement).setCursorName("cursor1");
+ }
+
+ @Test
+ public void testGetResultSet() throws SQLException {
+ ResultSet mockResultSet = Mockito.mock(ResultSet.class);
+ Mockito.when(mockStatement.getResultSet()).thenReturn(mockResultSet);
+
+ ResultSet result = statementProxyXA.getResultSet();
+
+ Assertions.assertEquals(mockResultSet, result);
+ Mockito.verify(mockStatement).getResultSet();
+ }
+
+ @Test
+ public void testGetUpdateCount() throws SQLException {
+ Mockito.when(mockStatement.getUpdateCount()).thenReturn(5);
+
+ int result = statementProxyXA.getUpdateCount();
+
+ Assertions.assertEquals(5, result);
+ Mockito.verify(mockStatement).getUpdateCount();
+ }
+
+ @Test
+ public void testGetMoreResults() throws SQLException {
+ Mockito.when(mockStatement.getMoreResults()).thenReturn(true);
+
+ boolean result = statementProxyXA.getMoreResults();
+
+ Assertions.assertTrue(result);
+ Mockito.verify(mockStatement).getMoreResults();
+ }
+
+ @Test
+ public void testSetFetchDirection() throws SQLException {
+ statementProxyXA.setFetchDirection(ResultSet.FETCH_FORWARD);
+
+
Mockito.verify(mockStatement).setFetchDirection(ResultSet.FETCH_FORWARD);
+ }
+
+ @Test
+ public void testGetFetchDirection() throws SQLException {
+
Mockito.when(mockStatement.getFetchDirection()).thenReturn(ResultSet.FETCH_FORWARD);
+
+ int result = statementProxyXA.getFetchDirection();
+
+ Assertions.assertEquals(ResultSet.FETCH_FORWARD, result);
+ Mockito.verify(mockStatement).getFetchDirection();
+ }
+
+ @Test
+ public void testSetFetchSize() throws SQLException {
+ statementProxyXA.setFetchSize(100);
+
+ Mockito.verify(mockStatement).setFetchSize(100);
+ }
+
+ @Test
+ public void testGetFetchSize() throws SQLException {
+ Mockito.when(mockStatement.getFetchSize()).thenReturn(100);
+
+ int result = statementProxyXA.getFetchSize();
+
+ Assertions.assertEquals(100, result);
+ Mockito.verify(mockStatement).getFetchSize();
+ }
+
+ @Test
+ public void testGetResultSetConcurrency() throws SQLException {
+
Mockito.when(mockStatement.getResultSetConcurrency()).thenReturn(ResultSet.CONCUR_READ_ONLY);
+
+ int result = statementProxyXA.getResultSetConcurrency();
+
+ Assertions.assertEquals(ResultSet.CONCUR_READ_ONLY, result);
+ Mockito.verify(mockStatement).getResultSetConcurrency();
+ }
+
+ @Test
+ public void testGetResultSetType() throws SQLException {
+
Mockito.when(mockStatement.getResultSetType()).thenReturn(ResultSet.TYPE_FORWARD_ONLY);
+
+ int result = statementProxyXA.getResultSetType();
+
+ Assertions.assertEquals(ResultSet.TYPE_FORWARD_ONLY, result);
+ Mockito.verify(mockStatement).getResultSetType();
+ }
+
+ @Test
+ public void testAddBatch() throws SQLException {
+ String sql = "INSERT INTO test (name) VALUES ('test')";
+ statementProxyXA.addBatch(sql);
+
+ Mockito.verify(mockStatement).addBatch(sql);
+ }
+
+ @Test
+ public void testClearBatch() throws SQLException {
+ statementProxyXA.clearBatch();
+
+ Mockito.verify(mockStatement).clearBatch();
+ }
+
+ @Test
+ public void testGetConnection() throws SQLException {
+ Connection mockConnection = Mockito.mock(Connection.class);
+ Mockito.when(mockStatement.getConnection()).thenReturn(mockConnection);
+
+ Connection result = statementProxyXA.getConnection();
+
+ Assertions.assertEquals(mockConnection, result);
+ Mockito.verify(mockStatement).getConnection();
+ }
+
+ @Test
+ public void testGetMoreResultsWithCurrent() throws SQLException {
+
Mockito.when(mockStatement.getMoreResults(Statement.CLOSE_CURRENT_RESULT))
+ .thenReturn(true);
+
+ boolean result =
statementProxyXA.getMoreResults(Statement.CLOSE_CURRENT_RESULT);
+
+ Assertions.assertTrue(result);
+
Mockito.verify(mockStatement).getMoreResults(Statement.CLOSE_CURRENT_RESULT);
+ }
+
+ @Test
+ public void testGetGeneratedKeys() throws SQLException {
+ ResultSet mockResultSet = Mockito.mock(ResultSet.class);
+
Mockito.when(mockStatement.getGeneratedKeys()).thenReturn(mockResultSet);
+
+ ResultSet result = statementProxyXA.getGeneratedKeys();
+
+ Assertions.assertEquals(mockResultSet, result);
+ Mockito.verify(mockStatement).getGeneratedKeys();
+ }
+
+ @Test
+ public void testGetResultSetHoldability() throws SQLException {
+
Mockito.when(mockStatement.getResultSetHoldability()).thenReturn(ResultSet.CLOSE_CURSORS_AT_COMMIT);
+
+ int result = statementProxyXA.getResultSetHoldability();
+
+ Assertions.assertEquals(ResultSet.CLOSE_CURSORS_AT_COMMIT, result);
+ Mockito.verify(mockStatement).getResultSetHoldability();
+ }
+
+ @Test
+ public void testIsClosed() throws SQLException {
+ Mockito.when(mockStatement.isClosed()).thenReturn(false);
+
+ boolean result = statementProxyXA.isClosed();
+
+ Assertions.assertFalse(result);
+ Mockito.verify(mockStatement).isClosed();
+ }
+
+ @Test
+ public void testSetPoolable() throws SQLException {
+ statementProxyXA.setPoolable(true);
+
+ Mockito.verify(mockStatement).setPoolable(true);
+ }
+
+ @Test
+ public void testIsPoolable() throws SQLException {
+ Mockito.when(mockStatement.isPoolable()).thenReturn(true);
+
+ boolean result = statementProxyXA.isPoolable();
+
+ Assertions.assertTrue(result);
+ Mockito.verify(mockStatement).isPoolable();
+ }
+
+ @Test
+ public void testCloseOnCompletion() throws SQLException {
+ statementProxyXA.closeOnCompletion();
+
+ Mockito.verify(mockStatement).closeOnCompletion();
+ }
+
+ @Test
+ public void testIsCloseOnCompletion() throws SQLException {
+ Mockito.when(mockStatement.isCloseOnCompletion()).thenReturn(true);
+
+ boolean result = statementProxyXA.isCloseOnCompletion();
+
+ Assertions.assertTrue(result);
+ Mockito.verify(mockStatement).isCloseOnCompletion();
+ }
+
+ @Test
+ public void testUnwrap() throws SQLException {
+ Mockito.when(mockStatement.unwrap(String.class)).thenReturn("test");
+
+ String result = statementProxyXA.unwrap(String.class);
+
+ Assertions.assertEquals("test", result);
+ Mockito.verify(mockStatement).unwrap(String.class);
+ }
+
+ @Test
+ public void testIsWrapperFor() throws SQLException {
+
Mockito.when(mockStatement.isWrapperFor(String.class)).thenReturn(true);
+
+ boolean result = statementProxyXA.isWrapperFor(String.class);
+
+ Assertions.assertTrue(result);
+ Mockito.verify(mockStatement).isWrapperFor(String.class);
+ }
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]