This is an automated email from the ASF dual-hosted git repository.
funky-eyes 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 9c235df8f1 test : add integration tx api mock tests (#8123)
9c235df8f1 is described below
commit 9c235df8f17fdb6bd379fa0a0002d1e59b5caa9a
Author: legendpei <[email protected]>
AuthorDate: Tue Jun 2 09:24:13 2026 +0800
test : add integration tx api mock tests (#8123)
---
changes/en-us/2.x.md | 1 +
changes/zh-cn/2.x.md | 1 +
integration-tx-api/pom.xml | 5 +
.../store/db/CommonFenceStoreDataBaseDAOTest.java | 198 +++++++++++++++++++++
.../tx/api/interceptor/ActionContextUtilTest.java | 173 ++++++++++++++++++
.../rm/tcc/api/BusinessActionContextTest.java | 102 +++++++++++
.../rm/tcc/api/BusinessActionContextUtilTest.java | 145 +++++++++++++++
7 files changed, 625 insertions(+)
diff --git a/changes/en-us/2.x.md b/changes/en-us/2.x.md
index 6173486a13..9cdb1687f5 100644
--- a/changes/en-us/2.x.md
+++ b/changes/en-us/2.x.md
@@ -97,6 +97,7 @@ Add changes here for all PR submitted to the 2.x branch.
- [[#8003](https://github.com/apache/incubator-seata/pull/8003)] enhance Nacos
registry service test coverage
- [[#7915](https://github.com/apache/incubator-seata/pull/7915)] add unit
tests for saga-engine module
- [[#8051](https://github.com/apache/incubator-seata/pull/8051)] isolate
compatibility tests from transport protocol interference
+- [[#8123](https://github.com/apache/incubator-seata/pull/8123)] add
integration tx api mock tests
### refactor:
diff --git a/changes/zh-cn/2.x.md b/changes/zh-cn/2.x.md
index 7afc5bbfcb..285ccfc0e0 100644
--- a/changes/zh-cn/2.x.md
+++ b/changes/zh-cn/2.x.md
@@ -101,6 +101,7 @@
- [[#8003](https://github.com/apache/incubator-seata/pull/8003)] 增强 Nacos
注册服务测试覆盖率
- [[#7915](https://github.com/apache/incubator-seata/pull/7915)] 为 saga-engine
模块添加单元测试
- [[#8051](https://github.com/apache/incubator-seata/pull/8051)]
隔离兼容性测试与传输协议的干扰
+- [[#8123](https://github.com/apache/incubator-seata/pull/8123)]
为integration-tx-api模块添加测试
### refactor:
diff --git a/integration-tx-api/pom.xml b/integration-tx-api/pom.xml
index f5e67b49e5..4620018300 100644
--- a/integration-tx-api/pom.xml
+++ b/integration-tx-api/pom.xml
@@ -66,6 +66,11 @@
<groupId>net.bytebuddy</groupId>
<artifactId>byte-buddy</artifactId>
</dependency>
+ <dependency>
+ <groupId>com.fasterxml.jackson.core</groupId>
+ <artifactId>jackson-databind</artifactId>
+ <scope>test</scope>
+ </dependency>
</dependencies>
</project>
diff --git
a/integration-tx-api/src/test/java/org/apache/seata/integration/tx/api/fence/store/db/CommonFenceStoreDataBaseDAOTest.java
b/integration-tx-api/src/test/java/org/apache/seata/integration/tx/api/fence/store/db/CommonFenceStoreDataBaseDAOTest.java
new file mode 100644
index 0000000000..b1451e7964
--- /dev/null
+++
b/integration-tx-api/src/test/java/org/apache/seata/integration/tx/api/fence/store/db/CommonFenceStoreDataBaseDAOTest.java
@@ -0,0 +1,198 @@
+/*
+ * 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.integration.tx.api.fence.store.db;
+
+import org.apache.seata.common.DefaultValues;
+import org.apache.seata.common.exception.DataAccessException;
+import org.apache.seata.common.exception.StoreException;
+import
org.apache.seata.integration.tx.api.fence.exception.CommonFenceException;
+import org.apache.seata.integration.tx.api.fence.store.CommonFenceDO;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.sql.Connection;
+import java.sql.DatabaseMetaData;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.SQLIntegrityConstraintViolationException;
+import java.sql.Timestamp;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.Set;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class CommonFenceStoreDataBaseDAOTest {
+
+ private CommonFenceStoreDataBaseDAO dao;
+
+ @BeforeEach
+ public void setUp() {
+ dao = (CommonFenceStoreDataBaseDAO)
CommonFenceStoreDataBaseDAO.getInstance();
+ dao.setLogTableName(DefaultValues.DEFAULT_COMMON_FENCE_LOG_TABLE_NAME);
+ }
+
+ @Test
+ public void testQueryCommonFenceDO() throws SQLException {
+ Connection connection = mock(Connection.class);
+ PreparedStatement statement = mock(PreparedStatement.class);
+ ResultSet resultSet = mock(ResultSet.class);
+ when(connection.prepareStatement(anyString())).thenReturn(statement);
+ when(statement.executeQuery()).thenReturn(resultSet);
+ when(resultSet.next()).thenReturn(true);
+ when(resultSet.getString("xid")).thenReturn("xid");
+ when(resultSet.getLong("branch_id")).thenReturn(1L);
+ when(resultSet.getInt("status")).thenReturn(2);
+
+ CommonFenceDO fenceDO = dao.queryCommonFenceDO(connection, "xid", 1L);
+
+ assertEquals("xid", fenceDO.getXid());
+ assertEquals(1L, fenceDO.getBranchId());
+ assertEquals(2, fenceDO.getStatus());
+ verify(statement).setString(1, "xid");
+ verify(statement).setLong(2, 1L);
+ }
+
+ @Test
+ public void testQueryCommonFenceDOReturnsNullWhenNoRow() throws
SQLException {
+ Connection connection = mock(Connection.class);
+ PreparedStatement statement = mock(PreparedStatement.class);
+ ResultSet resultSet = mock(ResultSet.class);
+ when(connection.prepareStatement(anyString())).thenReturn(statement);
+ when(statement.executeQuery()).thenReturn(resultSet);
+ when(resultSet.next()).thenReturn(false);
+
+ assertNull(dao.queryCommonFenceDO(connection, "xid", 1L));
+ }
+
+ @Test
+ public void testQueryCommonFenceDOWrapsSqlException() throws SQLException {
+ Connection connection = mock(Connection.class);
+ when(connection.prepareStatement(anyString())).thenThrow(new
SQLException("failed"));
+
+ assertThrows(DataAccessException.class, () ->
dao.queryCommonFenceDO(connection, "xid", 1L));
+ }
+
+ @Test
+ public void testQueryEndStatusXidsByDate() throws SQLException {
+ Connection connection = mock(Connection.class);
+ DatabaseMetaData metaData = mock(DatabaseMetaData.class);
+ PreparedStatement statement = mock(PreparedStatement.class);
+ ResultSet resultSet = mock(ResultSet.class);
+ when(connection.getMetaData()).thenReturn(metaData);
+
when(metaData.getURL()).thenReturn("jdbc:oracle:thin:@localhost:1521:xe");
+ when(connection.prepareStatement(anyString())).thenReturn(statement);
+ when(statement.executeQuery()).thenReturn(resultSet);
+ when(resultSet.next()).thenReturn(true, true, false);
+ when(resultSet.getString("xid")).thenReturn("xid1", "xid2");
+
+ Set<String> xids = dao.queryEndStatusXidsByDate(connection, new
Date(1000L), 10);
+
+ assertEquals(2, xids.size());
+ assertTrue(xids.contains("xid1"));
+ assertTrue(xids.contains("xid2"));
+ verify(statement).setTimestamp(1, new Timestamp(1000L));
+ verify(statement).setInt(2, 10);
+ }
+
+ @Test
+ public void testInsertCommonFenceDO() throws SQLException {
+ Connection connection = mock(Connection.class);
+ PreparedStatement statement = mock(PreparedStatement.class);
+ when(connection.prepareStatement(anyString())).thenReturn(statement);
+ when(statement.executeUpdate()).thenReturn(1, 0);
+
+ assertTrue(dao.insertCommonFenceDO(connection, newFenceDO()));
+ assertFalse(dao.insertCommonFenceDO(connection, newFenceDO()));
+ verify(statement, times(2)).setString(1, "xid");
+ verify(statement, times(2)).setLong(2, 1L);
+ verify(statement, times(2)).setString(3, "prepare");
+ verify(statement, times(2)).setInt(4, 1);
+ }
+
+ @Test
+ public void testInsertCommonFenceDOWrapsDuplicateKey() throws SQLException
{
+ Connection connection = mock(Connection.class);
+ PreparedStatement statement = mock(PreparedStatement.class);
+ when(connection.prepareStatement(anyString())).thenReturn(statement);
+ when(statement.executeUpdate()).thenThrow(new
SQLIntegrityConstraintViolationException("duplicate"));
+
+ assertThrows(CommonFenceException.class, () ->
dao.insertCommonFenceDO(connection, newFenceDO()));
+ }
+
+ @Test
+ public void testUpdateAndDeleteCommonFenceDO() throws SQLException {
+ Connection connection = mock(Connection.class);
+ PreparedStatement statement = mock(PreparedStatement.class);
+ when(connection.prepareStatement(anyString())).thenReturn(statement);
+ when(statement.executeUpdate()).thenReturn(1, 0, 1);
+
+ assertTrue(dao.updateCommonFenceDO(connection, "xid", 1L, 2, 1));
+ assertFalse(dao.updateCommonFenceDO(connection, "xid", 1L, 2, 1));
+ assertTrue(dao.deleteCommonFenceDO(connection, "xid", 1L));
+
+ verify(statement, times(2)).setInt(1, 2);
+ verify(statement, times(2)).setString(3, "xid");
+ verify(statement, times(2)).setLong(4, 1L);
+ verify(statement, times(2)).setInt(5, 1);
+ }
+
+ @Test
+ public void testDeleteTCCFenceDO() throws SQLException {
+ Connection connection = mock(Connection.class);
+ PreparedStatement statement = mock(PreparedStatement.class);
+ when(connection.prepareStatement(anyString())).thenReturn(statement);
+ when(statement.executeUpdate()).thenReturn(2);
+
+ assertEquals(2, dao.deleteTCCFenceDO(connection, Arrays.asList("xid1",
"xid2")));
+ verify(statement).setString(1, "xid1");
+ verify(statement).setString(2, "xid2");
+ }
+
+ @Test
+ public void testStoreMethodsWrapSqlException() throws SQLException {
+ Connection connection = mock(Connection.class);
+ PreparedStatement statement = mock(PreparedStatement.class);
+ when(connection.prepareStatement(anyString())).thenReturn(statement);
+ doThrow(new SQLException("failed")).when(statement).executeUpdate();
+
+ assertThrows(StoreException.class, () ->
dao.insertCommonFenceDO(connection, newFenceDO()));
+ assertThrows(StoreException.class, () ->
dao.updateCommonFenceDO(connection, "xid", 1L, 2, 1));
+ assertThrows(StoreException.class, () ->
dao.deleteCommonFenceDO(connection, "xid", 1L));
+ assertThrows(StoreException.class, () ->
dao.deleteTCCFenceDO(connection, Arrays.asList("xid1", "xid2")));
+ }
+
+ private static CommonFenceDO newFenceDO() {
+ CommonFenceDO fenceDO = new CommonFenceDO();
+ fenceDO.setXid("xid");
+ fenceDO.setBranchId(1L);
+ fenceDO.setActionName("prepare");
+ fenceDO.setStatus(1);
+ return fenceDO;
+ }
+}
diff --git
a/integration-tx-api/src/test/java/org/apache/seata/integration/tx/api/interceptor/ActionContextUtilTest.java
b/integration-tx-api/src/test/java/org/apache/seata/integration/tx/api/interceptor/ActionContextUtilTest.java
new file mode 100644
index 0000000000..cac02e5c20
--- /dev/null
+++
b/integration-tx-api/src/test/java/org/apache/seata/integration/tx/api/interceptor/ActionContextUtilTest.java
@@ -0,0 +1,173 @@
+/*
+ * 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.integration.tx.api.interceptor;
+
+import org.apache.seata.common.exception.FrameworkException;
+import org.apache.seata.rm.tcc.api.BusinessActionContext;
+import org.apache.seata.rm.tcc.api.BusinessActionContextParameter;
+import org.apache.seata.rm.tcc.api.ParamType;
+import org.junit.jupiter.api.Test;
+
+import java.lang.reflect.Method;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class ActionContextUtilTest {
+
+ @Test
+ public void testGetByIndex() {
+ assertEquals(
+ "first", ActionContextUtil.getByIndex(ParamType.PARAM,
"names", Collections.singletonList("first"), 0));
+ assertNull(ActionContextUtil.getByIndex(ParamType.PARAM, "names",
Collections.emptyList(), 0));
+ assertNull(ActionContextUtil.getByIndex(ParamType.PARAM, "names",
Collections.singletonList("first"), 1));
+ assertEquals("plain", ActionContextUtil.getByIndex(ParamType.FIELD,
"name", "plain", 0));
+ }
+
+ @Test
+ public void testPutActionContextHandlesValuesAndDetectsChanges() {
+ Map<String, Object> context = new HashMap<>();
+
+ assertFalse(ActionContextUtil.putActionContext(context, "nullValue",
null));
+ assertTrue(ActionContextUtil.putActionContext(context, "name",
"seata"));
+ assertFalse(ActionContextUtil.putActionContext(context, "name",
"seata"));
+
+ assertTrue(ActionContextUtil.putActionContext(context, "payload", new
Payload("alice", 7)));
+ String payload = (String) context.get("payload");
+ assertTrue(payload.contains("\"name\":\"alice\""));
+ assertTrue(payload.contains("\"count\":7"));
+ }
+
+ @Test
+ public void testPutActionContextWithoutHandle() {
+ Map<String, Object> context = new HashMap<>();
+ Payload payload = new Payload("bob", 9);
+
+ assertTrue(ActionContextUtil.putActionContextWithoutHandle(context,
"payload", payload));
+ assertEquals(payload, context.get("payload"));
+ assertFalse(ActionContextUtil.putActionContextWithoutHandle(context,
"payload", payload));
+ assertFalse(ActionContextUtil.putActionContextWithoutHandle(context,
"payload", null));
+ }
+
+ @Test
+ public void testConvertActionContext() {
+ Payload payload = new Payload("carol", 11);
+
+ assertThrows(
+ IllegalArgumentException.class, () ->
ActionContextUtil.convertActionContext("count", 1, int.class));
+ assertNull(ActionContextUtil.convertActionContext("payload", null,
Payload.class));
+ assertEquals(payload,
ActionContextUtil.convertActionContext("payload", payload, Payload.class));
+ assertEquals("12", ActionContextUtil.convertActionContext("count", 12,
String.class));
+
+ Payload fromJson =
+ ActionContextUtil.convertActionContext("payload",
"{\"name\":\"dave\",\"count\":13}", Payload.class);
+ assertEquals("dave", fromJson.getName());
+ assertEquals(13, fromJson.getCount());
+
+ assertThrows(
+ FrameworkException.class,
+ () -> ActionContextUtil.convertActionContext("payload",
"not-json", Payload.class));
+ }
+
+ @Test
+ public void testLoadParamByAnnotationAndPutToContext() throws
NoSuchMethodException {
+ BusinessActionContextParameter propertyAnnotation =
getMethod("withPropertyParam", PropertyParam.class)
+ .getParameters()[0]
+ .getAnnotation(BusinessActionContextParameter.class);
+ Map<String, Object> context = new HashMap<>();
+
+ ActionContextUtil.loadParamByAnnotationAndPutToContext(
+ ParamType.PARAM, "param", new PropertyParam(),
propertyAnnotation, context);
+
+ assertEquals("fieldValue", context.get("fieldName"));
+
+ BusinessActionContextParameter aliasAnnotation =
getMethod("withAliasParam", String.class)
+ .getParameters()[0]
+ .getAnnotation(BusinessActionContextParameter.class);
+ ActionContextUtil.loadParamByAnnotationAndPutToContext(
+ ParamType.PARAM, "origin", "value", aliasAnnotation, context);
+
+ assertEquals("value", context.get("aliasName"));
+ }
+
+ @Test
+ public void testGetTwoPhaseArgsRequiresAnnotationForNonContextParameter()
throws NoSuchMethodException {
+ Method validMethod = getMethod("twoPhase",
BusinessActionContext.class, String.class);
+ String[] keys = ActionContextUtil.getTwoPhaseArgs(validMethod,
validMethod.getParameterTypes());
+
+ assertNull(keys[0]);
+ assertEquals("code", keys[1]);
+
+ Method invalidMethod = getMethod("missingAnnotation",
BusinessActionContext.class, String.class);
+ assertThrows(
+ IllegalArgumentException.class,
+ () -> ActionContextUtil.getTwoPhaseArgs(invalidMethod,
invalidMethod.getParameterTypes()));
+ }
+
+ private static Method getMethod(String name, Class<?>... parameterTypes)
throws NoSuchMethodException {
+ return SampleAction.class.getDeclaredMethod(name, parameterTypes);
+ }
+
+ public static class Payload {
+ private String name;
+ private int count;
+
+ public Payload() {}
+
+ public Payload(String name, int count) {
+ this.name = name;
+ this.count = count;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public int getCount() {
+ return count;
+ }
+
+ public void setCount(int count) {
+ this.count = count;
+ }
+ }
+
+ public static class PropertyParam {
+ @BusinessActionContextParameter(paramName = "fieldName")
+ private final String field = "fieldValue";
+ }
+
+ public static class SampleAction {
+ public void
withPropertyParam(@BusinessActionContextParameter(isParamInProperty = true)
PropertyParam param) {}
+
+ public void withAliasParam(@BusinessActionContextParameter(paramName =
"aliasName") String param) {}
+
+ public void twoPhase(BusinessActionContext context,
@BusinessActionContextParameter("code") String code) {}
+
+ public void missingAnnotation(BusinessActionContext context, String
code) {}
+ }
+}
diff --git
a/integration-tx-api/src/test/java/org/apache/seata/rm/tcc/api/BusinessActionContextTest.java
b/integration-tx-api/src/test/java/org/apache/seata/rm/tcc/api/BusinessActionContextTest.java
new file mode 100644
index 0000000000..7242540557
--- /dev/null
+++
b/integration-tx-api/src/test/java/org/apache/seata/rm/tcc/api/BusinessActionContextTest.java
@@ -0,0 +1,102 @@
+/*
+ * 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.tcc.api;
+
+import org.apache.seata.core.model.BranchType;
+import org.junit.jupiter.api.Test;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class BusinessActionContextTest {
+
+ @Test
+ public void testBranchIdAccessors() {
+ BusinessActionContext context = new BusinessActionContext();
+
+ assertEquals(-1, context.getBranchId());
+
+ context.setBranchId(10L);
+ assertEquals(10L, context.getBranchId());
+
+ context.setBranchId("11");
+ assertEquals(11L, context.getBranchId());
+ }
+
+ @Test
+ @SuppressWarnings("deprecation")
+ public void testAddActionContextUpdatesFlagOnlyWhenChanged() {
+ Map<String, Object> actionContext = new HashMap<>();
+ BusinessActionContext context = new BusinessActionContext("xid", "1",
actionContext);
+
+ assertFalse(context.addActionContext("name", null));
+ assertNull(context.getUpdated());
+
+ assertTrue(context.addActionContext("name", "seata"));
+ assertTrue(context.getUpdated());
+ context.setUpdated(null);
+
+ assertFalse(context.addActionContext("name", "seata"));
+ assertNull(context.getUpdated());
+ }
+
+ @Test
+ public void testGetActionContextWithTargetClass() {
+ Map<String, Object> actionContext = new HashMap<>();
+ actionContext.put("count", 3);
+ actionContext.put("payload", "{\"name\":\"payload-name\"}");
+ BusinessActionContext context = new BusinessActionContext("xid", "2",
actionContext);
+
+ assertEquals(3, context.getActionContext("count", Integer.class));
+ assertEquals(
+ "payload-name",
+ context.getActionContext("payload", Payload.class).getName());
+ }
+
+ @Test
+ public void testToStringIncludesCoreFields() {
+ BusinessActionContext context = new BusinessActionContext("xid", "3",
new HashMap<>());
+ context.setActionName("prepare");
+ context.setDelayReport(true);
+ context.setUpdated(false);
+ context.setBranchType(BranchType.TCC);
+
+ String value = context.toString();
+
+ assertTrue(value.contains("xid:xid"));
+ assertTrue(value.contains("branch_Id:3"));
+ assertTrue(value.contains("action_name:prepare"));
+ assertTrue(value.contains("branch_type:TCC"));
+ }
+
+ public static class Payload {
+ private String name;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+ }
+}
diff --git
a/integration-tx-api/src/test/java/org/apache/seata/rm/tcc/api/BusinessActionContextUtilTest.java
b/integration-tx-api/src/test/java/org/apache/seata/rm/tcc/api/BusinessActionContextUtilTest.java
new file mode 100644
index 0000000000..ccf1be85c4
--- /dev/null
+++
b/integration-tx-api/src/test/java/org/apache/seata/rm/tcc/api/BusinessActionContextUtilTest.java
@@ -0,0 +1,145 @@
+/*
+ * 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.tcc.api;
+
+import org.apache.seata.common.Constants;
+import org.apache.seata.common.exception.FrameworkException;
+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.rm.DefaultResourceManager;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.MockedStatic;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertSame;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.mockStatic;
+import static org.mockito.Mockito.verify;
+
+public class BusinessActionContextUtilTest {
+
+ @AfterEach
+ public void tearDown() {
+ BusinessActionContextUtil.clear();
+ }
+
+ @Test
+ public void testContextHolder() {
+ BusinessActionContext context = new BusinessActionContext();
+
+ BusinessActionContextUtil.setContext(context);
+ assertSame(context, BusinessActionContextUtil.getContext());
+
+ BusinessActionContextUtil.clear();
+ assertNull(BusinessActionContextUtil.getContext());
+ }
+
+ @Test
+ public void testAddContextReturnsFalseForEmptyOrDelayedContext() {
+
assertFalse(BusinessActionContextUtil.addContext(Collections.emptyMap()));
+ assertFalse(BusinessActionContextUtil.addContext("key", null));
+
+ BusinessActionContext context = newActionContext();
+ context.setDelayReport(true);
+ BusinessActionContextUtil.setContext(context);
+
+ assertFalse(BusinessActionContextUtil.addContext("name", "seata"));
+ assertTrue(context.getUpdated());
+ assertEquals("seata", context.getActionContext().get("name"));
+ }
+
+ @Test
+ public void testAddContextReportsImmediatelyWhenChanged() throws
TransactionException {
+ BusinessActionContext context = newActionContext();
+ BusinessActionContextUtil.setContext(context);
+ DefaultResourceManager resourceManager =
mock(DefaultResourceManager.class);
+
+ try (MockedStatic<DefaultResourceManager> mocked =
mockStatic(DefaultResourceManager.class)) {
+
mocked.when(DefaultResourceManager::get).thenReturn(resourceManager);
+
+ assertTrue(BusinessActionContextUtil.addContext("name", "seata"));
+ }
+
+ assertNull(context.getUpdated());
+ verify(resourceManager)
+ .branchReport(eq(BranchType.TCC), eq("xid"), eq(1L),
eq(BranchStatus.Registered), anyString());
+ }
+
+ @Test
+ public void testReportContextSkipsWhenNotUpdated() throws
TransactionException {
+ BusinessActionContext context = newActionContext();
+ DefaultResourceManager resourceManager =
mock(DefaultResourceManager.class);
+
+ try (MockedStatic<DefaultResourceManager> mocked =
mockStatic(DefaultResourceManager.class)) {
+
mocked.when(DefaultResourceManager::get).thenReturn(resourceManager);
+
+ assertFalse(BusinessActionContextUtil.reportContext(context));
+ }
+ }
+
+ @Test
+ public void testReportContextWrapsTransactionException() throws
TransactionException {
+ BusinessActionContext context = newActionContext();
+ context.setUpdated(true);
+ DefaultResourceManager resourceManager =
mock(DefaultResourceManager.class);
+
+ try (MockedStatic<DefaultResourceManager> mocked =
mockStatic(DefaultResourceManager.class)) {
+
mocked.when(DefaultResourceManager::get).thenReturn(resourceManager);
+ org.mockito.Mockito.doThrow(new TransactionException("failed"))
+ .when(resourceManager)
+ .branchReport(eq(BranchType.TCC), eq("xid"), eq(1L),
eq(BranchStatus.Registered), anyString());
+
+ assertThrows(FrameworkException.class, () ->
BusinessActionContextUtil.reportContext(context));
+ }
+ }
+
+ @Test
+ public void testGetBusinessActionContext() {
+ String applicationData = "{\"" + Constants.TX_ACTION_CONTEXT +
"\":{\"name\":\"seata\"}}";
+
+ BusinessActionContext context =
+ BusinessActionContextUtil.getBusinessActionContext("xid", 2L,
"prepare", applicationData);
+
+ assertEquals("xid", context.getXid());
+ assertEquals(2L, context.getBranchId());
+ assertEquals("prepare", context.getActionName());
+ assertEquals("seata", context.getActionContext("name"));
+
+ BusinessActionContext emptyContext =
+ BusinessActionContextUtil.getBusinessActionContext("xid", 3L,
"prepare", "");
+ assertTrue(emptyContext.getActionContext().isEmpty());
+ }
+
+ private static BusinessActionContext newActionContext() {
+ Map<String, Object> actionContext = new HashMap<>();
+ BusinessActionContext context = new BusinessActionContext("xid", "1",
actionContext);
+ context.setBranchType(BranchType.TCC);
+ return context;
+ }
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]