This is an automated email from the ASF dual-hosted git repository.
zhangyv 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 57d170db06 test: add UT for dm module (#7709)
57d170db06 is described below
commit 57d170db06c4c6e572f3aca3de58eaa4eef4d5c6
Author: maple <[email protected]>
AuthorDate: Sat Oct 18 19:22:35 2025 +0800
test: add UT for dm module (#7709)
---
changes/en-us/2.x.md | 2 +
changes/zh-cn/2.x.md | 2 +
.../datasource/undo/dm/DmUndoDeleteExecutor.java | 13 +-
.../undo/dm/DmUndoDeleteExecutorTest.java | 158 +++++++++++
.../undo/dm/DmUndoExecutorHolderTest.java | 107 ++++++++
.../undo/dm/DmUndoInsertExecutorTest.java | 185 +++++++++++++
.../datasource/undo/dm/DmUndoLogManagerTest.java | 289 +++++++++++++++++++++
.../undo/dm/DmUndoUpdateExecutorTest.java | 173 ++++++++++++
8 files changed, 922 insertions(+), 7 deletions(-)
diff --git a/changes/en-us/2.x.md b/changes/en-us/2.x.md
index ecbabfb7b7..4670599c2d 100644
--- a/changes/en-us/2.x.md
+++ b/changes/en-us/2.x.md
@@ -46,6 +46,7 @@ Add changes here for all PR submitted to the 2.x branch.
- [[#7644](https://github.com/apache/incubator-seata/pull/7644)] fix the
compatibility issue of spotless when java 25
- [[#7662](https://github.com/apache/incubator-seata/pull/7662)] ensure
visibility of rm and The methods in MockTest are executed in order
- [[#7683](https://github.com/apache/incubator-seata/pull/7683)] Override
XABranchXid equals() and hashCode() to fix memory leak in mysql driver
+- [[#7643](https://github.com/apache/incubator-seata/pull/7643)] fix DM
transaction rollback not using database auto-increment primary keys
### optimize:
@@ -82,6 +83,7 @@ Add changes here for all PR submitted to the 2.x branch.
- [[#7672](https://github.com/apache/incubator-seata/pull/7672)] Add unit
tests for the `seata-common` module
- [[#7679](https://github.com/apache/incubator-seata/pull/7679)] fix old
version connect timeout
- [[#7638](https://github.com/apache/incubator-seata/pull/7638)]Add unit tests
for the `seata-common` module and remove a todo
+- [[#7709](https://github.com/apache/incubator-seata/pull/7709)] add UT for dm
module
### refactor:
diff --git a/changes/zh-cn/2.x.md b/changes/zh-cn/2.x.md
index dc6c1a8459..8fb0e909a8 100644
--- a/changes/zh-cn/2.x.md
+++ b/changes/zh-cn/2.x.md
@@ -46,6 +46,7 @@
- [[#7644](https://github.com/apache/incubator-seata/pull/7644)]
修复JAVA25时spotless的兼容性问题
- [[#7662](https://github.com/apache/incubator-seata/pull/7662)] 确保 rm 的可见性,并且
MockTest 中的方法按顺序执行
- [[#7683](https://github.com/apache/incubator-seata/pull/7683)] 重写
XABranchXid的equals和hashCode,解决mysql driver内存泄漏问题
+- [[#7643](https://github.com/apache/incubator-seata/pull/7643)] 修复 DM
事务回滚不使用数据库自动增量主键
### optimize:
@@ -81,6 +82,7 @@
- [[#7672](https://github.com/apache/incubator-seata/pull/7672)] 增加
`seata-common` 模块的测试用例
- [[#7679](https://github.com/apache/incubator-seata/pull/7679)] 修复旧版本协议测试超时问题
- [[#7638](https://github.com/apache/incubator-seata/pull/7638)] 增加了
`seata-common` 模块的测试用例,删去了一个todo
+- [[#7709](https://github.com/apache/incubator-seata/pull/7709)] 为dm模块增加单测
### refactor:
diff --git
a/rm-datasource/src/main/java/org/apache/seata/rm/datasource/undo/dm/DmUndoDeleteExecutor.java
b/rm-datasource/src/main/java/org/apache/seata/rm/datasource/undo/dm/DmUndoDeleteExecutor.java
index 61e3347eb3..c10d728b55 100644
---
a/rm-datasource/src/main/java/org/apache/seata/rm/datasource/undo/dm/DmUndoDeleteExecutor.java
+++
b/rm-datasource/src/main/java/org/apache/seata/rm/datasource/undo/dm/DmUndoDeleteExecutor.java
@@ -95,13 +95,12 @@ public class DmUndoDeleteExecutor extends
AbstractUndoExecutor {
+ sqlUndoLog.getTableName()
+ " OFF;";
} else {
- return " INSERT INTO " +
- sqlUndoLog.getTableName() +
- " (" +
- insertColumns +
- ") VALUES (" +
- insertValues +
- ");";
+ return " INSERT INTO " + sqlUndoLog.getTableName()
+ + " ("
+ + insertColumns
+ + ") VALUES ("
+ + insertValues
+ + ");";
}
}
diff --git
a/rm-datasource/src/test/java/org/apache/seata/rm/datasource/undo/dm/DmUndoDeleteExecutorTest.java
b/rm-datasource/src/test/java/org/apache/seata/rm/datasource/undo/dm/DmUndoDeleteExecutorTest.java
new file mode 100644
index 0000000000..4f3934825a
--- /dev/null
+++
b/rm-datasource/src/test/java/org/apache/seata/rm/datasource/undo/dm/DmUndoDeleteExecutorTest.java
@@ -0,0 +1,158 @@
+/*
+ * 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.undo.dm;
+
+import org.apache.seata.rm.datasource.sql.struct.Row;
+import org.apache.seata.rm.datasource.sql.struct.TableRecords;
+import org.apache.seata.rm.datasource.undo.BaseExecutorTest;
+import org.apache.seata.rm.datasource.undo.SQLUndoLog;
+import org.apache.seata.sqlparser.SQLType;
+import org.apache.seata.sqlparser.struct.ColumnMeta;
+import org.apache.seata.sqlparser.struct.TableMeta;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * The type DmUndoDeleteExecutor test.
+ */
+public class DmUndoDeleteExecutorTest extends BaseExecutorTest {
+
+ @Test
+ public void buildUndoSQL() {
+ DmUndoDeleteExecutor executor = createExecutor();
+
+ String sql = executor.buildUndoSQL();
+ Assertions.assertNotNull(sql);
+ Assertions.assertTrue(sql.contains("INSERT INTO"));
+ Assertions.assertTrue(sql.contains("\"id\""));
+ Assertions.assertTrue(sql.contains("\"age\""));
+ Assertions.assertTrue(sql.contains("table_name"));
+ }
+
+ @Test
+ public void buildUndoSQLWithAutoIncrement() {
+ DmUndoDeleteExecutor executor = createExecutorWithAutoIncrement();
+
+ String sql = executor.buildUndoSQL();
+ Assertions.assertNotNull(sql);
+ Assertions.assertTrue(sql.contains("SET IDENTITY_INSERT"));
+ Assertions.assertTrue(sql.contains("INSERT INTO"));
+ Assertions.assertTrue(sql.contains("\"id\""));
+ Assertions.assertTrue(sql.contains("\"age\""));
+ Assertions.assertTrue(sql.contains("table_name"));
+ }
+
+ @Test
+ public void getUndoRows() {
+ DmUndoDeleteExecutor executor = createExecutor();
+ Assertions.assertEquals(executor.getUndoRows(),
executor.getSqlUndoLog().getBeforeImage());
+ }
+
+ @Test
+ public void testInvalidUndoLog() {
+ TableMeta tableMeta = Mockito.mock(TableMeta.class);
+
Mockito.when(tableMeta.getPrimaryKeyOnlyName()).thenReturn(Arrays.asList("id"));
+ Mockito.when(tableMeta.getTableName()).thenReturn("table_name");
+
+ TableRecords beforeImage = new TableRecords();
+ beforeImage.setTableName("table_name");
+ beforeImage.setTableMeta(tableMeta);
+ beforeImage.setRows(new ArrayList<>());
+
+ SQLUndoLog sqlUndoLog = new SQLUndoLog();
+ sqlUndoLog.setSqlType(SQLType.DELETE);
+ sqlUndoLog.setTableMeta(tableMeta);
+ sqlUndoLog.setTableName("table_name");
+ sqlUndoLog.setBeforeImage(beforeImage);
+ sqlUndoLog.setAfterImage(new TableRecords());
+
+ DmUndoDeleteExecutor executor = new DmUndoDeleteExecutor(sqlUndoLog);
+
+ Assertions.assertThrows(Exception.class, executor::buildUndoSQL);
+ }
+
+ private DmUndoDeleteExecutor createExecutor() {
+ TableMeta tableMeta = Mockito.mock(TableMeta.class);
+
Mockito.when(tableMeta.getPrimaryKeyOnlyName()).thenReturn(Arrays.asList("id"));
+ Mockito.when(tableMeta.getTableName()).thenReturn("table_name");
+
+ TableRecords beforeImage = new TableRecords();
+ beforeImage.setTableName("table_name");
+ beforeImage.setTableMeta(tableMeta);
+ List<Row> beforeRows = new ArrayList<>();
+ Row row0 = new Row();
+ addField(row0, "id", 1, "12345");
+ addField(row0, "age", 1, "1");
+ beforeRows.add(row0);
+ Row row1 = new Row();
+ addField(row1, "id", 1, "12346");
+ addField(row1, "age", 1, "1");
+ beforeRows.add(row1);
+ beforeImage.setRows(beforeRows);
+
+ TableRecords afterImage = new TableRecords();
+ afterImage.setTableName("table_name");
+ afterImage.setTableMeta(tableMeta);
+
+ SQLUndoLog sqlUndoLog = new SQLUndoLog();
+ sqlUndoLog.setSqlType(SQLType.DELETE);
+ sqlUndoLog.setTableMeta(tableMeta);
+ sqlUndoLog.setTableName("table_name");
+ sqlUndoLog.setBeforeImage(beforeImage);
+ sqlUndoLog.setAfterImage(afterImage);
+
+ return new DmUndoDeleteExecutor(sqlUndoLog);
+ }
+
+ private DmUndoDeleteExecutor createExecutorWithAutoIncrement() {
+ TableMeta tableMeta = Mockito.mock(TableMeta.class);
+
Mockito.when(tableMeta.getPrimaryKeyOnlyName()).thenReturn(Arrays.asList("id"));
+ Mockito.when(tableMeta.getTableName()).thenReturn("table_name");
+
+ ColumnMeta idColumnMeta = Mockito.mock(ColumnMeta.class);
+ Mockito.when(idColumnMeta.isAutoincrement()).thenReturn(true);
+ Mockito.when(tableMeta.getColumnMeta("id")).thenReturn(idColumnMeta);
+
+ TableRecords beforeImage = new TableRecords();
+ beforeImage.setTableName("table_name");
+ beforeImage.setTableMeta(tableMeta);
+ List<Row> beforeRows = new ArrayList<>();
+ Row row0 = new Row();
+ addField(row0, "id", 1, "12345");
+ addField(row0, "age", 1, "1");
+ beforeRows.add(row0);
+ beforeImage.setRows(beforeRows);
+
+ TableRecords afterImage = new TableRecords();
+ afterImage.setTableName("table_name");
+ afterImage.setTableMeta(tableMeta);
+
+ SQLUndoLog sqlUndoLog = new SQLUndoLog();
+ sqlUndoLog.setSqlType(SQLType.DELETE);
+ sqlUndoLog.setTableMeta(tableMeta);
+ sqlUndoLog.setTableName("table_name");
+ sqlUndoLog.setBeforeImage(beforeImage);
+ sqlUndoLog.setAfterImage(afterImage);
+
+ return new DmUndoDeleteExecutor(sqlUndoLog);
+ }
+}
diff --git
a/rm-datasource/src/test/java/org/apache/seata/rm/datasource/undo/dm/DmUndoExecutorHolderTest.java
b/rm-datasource/src/test/java/org/apache/seata/rm/datasource/undo/dm/DmUndoExecutorHolderTest.java
new file mode 100644
index 0000000000..4fe7ab5b83
--- /dev/null
+++
b/rm-datasource/src/test/java/org/apache/seata/rm/datasource/undo/dm/DmUndoExecutorHolderTest.java
@@ -0,0 +1,107 @@
+/*
+ * 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.undo.dm;
+
+import org.apache.seata.rm.datasource.undo.AbstractUndoExecutor;
+import org.apache.seata.rm.datasource.undo.SQLUndoLog;
+import org.apache.seata.sqlparser.SQLType;
+import org.apache.seata.sqlparser.struct.TableMeta;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+
+/**
+ * The type DmUndoExecutorHolder test.
+ */
+public class DmUndoExecutorHolderTest {
+
+ private DmUndoExecutorHolder holder;
+ private SQLUndoLog sqlUndoLog;
+
+ @BeforeEach
+ public void setup() {
+ holder = new DmUndoExecutorHolder();
+
+ sqlUndoLog = new SQLUndoLog();
+ TableMeta tableMeta = Mockito.mock(TableMeta.class);
+ Mockito.when(tableMeta.getTableName()).thenReturn("test_table");
+
+ sqlUndoLog.setTableMeta(tableMeta);
+ sqlUndoLog.setTableName("test_table");
+ }
+
+ @Test
+ public void testGetInsertExecutor() {
+ sqlUndoLog.setSqlType(SQLType.INSERT);
+
+ AbstractUndoExecutor executor = holder.getInsertExecutor(sqlUndoLog);
+
+ Assertions.assertNotNull(executor);
+ Assertions.assertTrue(executor instanceof DmUndoInsertExecutor);
+ Assertions.assertEquals(sqlUndoLog, executor.getSqlUndoLog());
+ }
+
+ @Test
+ public void testGetUpdateExecutor() {
+ sqlUndoLog.setSqlType(SQLType.UPDATE);
+
+ AbstractUndoExecutor executor = holder.getUpdateExecutor(sqlUndoLog);
+
+ Assertions.assertNotNull(executor);
+ Assertions.assertTrue(executor instanceof DmUndoUpdateExecutor);
+ Assertions.assertEquals(sqlUndoLog, executor.getSqlUndoLog());
+ }
+
+ @Test
+ public void testGetDeleteExecutor() {
+ sqlUndoLog.setSqlType(SQLType.DELETE);
+
+ AbstractUndoExecutor executor = holder.getDeleteExecutor(sqlUndoLog);
+
+ Assertions.assertNotNull(executor);
+ Assertions.assertTrue(executor instanceof DmUndoDeleteExecutor);
+ Assertions.assertEquals(sqlUndoLog, executor.getSqlUndoLog());
+ }
+
+ @Test
+ public void testAllExecutorsWithNullUndoLog() {
+ // These methods don't throw NullPointerException, they just pass null
to constructors
+ AbstractUndoExecutor insertExecutor = holder.getInsertExecutor(null);
+ AbstractUndoExecutor updateExecutor = holder.getUpdateExecutor(null);
+ AbstractUndoExecutor deleteExecutor = holder.getDeleteExecutor(null);
+
+ Assertions.assertNotNull(insertExecutor);
+ Assertions.assertNotNull(updateExecutor);
+ Assertions.assertNotNull(deleteExecutor);
+ Assertions.assertTrue(insertExecutor instanceof DmUndoInsertExecutor);
+ Assertions.assertTrue(updateExecutor instanceof DmUndoUpdateExecutor);
+ Assertions.assertTrue(deleteExecutor instanceof DmUndoDeleteExecutor);
+ }
+
+ @Test
+ public void testExecutorReturnsDifferentInstances() {
+ sqlUndoLog.setSqlType(SQLType.INSERT);
+
+ AbstractUndoExecutor executor1 = holder.getInsertExecutor(sqlUndoLog);
+ AbstractUndoExecutor executor2 = holder.getInsertExecutor(sqlUndoLog);
+
+ Assertions.assertNotSame(executor1, executor2);
+ Assertions.assertTrue(executor1 instanceof DmUndoInsertExecutor);
+ Assertions.assertTrue(executor2 instanceof DmUndoInsertExecutor);
+ }
+}
diff --git
a/rm-datasource/src/test/java/org/apache/seata/rm/datasource/undo/dm/DmUndoInsertExecutorTest.java
b/rm-datasource/src/test/java/org/apache/seata/rm/datasource/undo/dm/DmUndoInsertExecutorTest.java
new file mode 100644
index 0000000000..05e63cd8c7
--- /dev/null
+++
b/rm-datasource/src/test/java/org/apache/seata/rm/datasource/undo/dm/DmUndoInsertExecutorTest.java
@@ -0,0 +1,185 @@
+/*
+ * 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.undo.dm;
+
+import org.apache.seata.rm.datasource.sql.struct.Field;
+import org.apache.seata.rm.datasource.sql.struct.KeyType;
+import org.apache.seata.rm.datasource.sql.struct.Row;
+import org.apache.seata.rm.datasource.sql.struct.TableRecords;
+import org.apache.seata.rm.datasource.undo.BaseExecutorTest;
+import org.apache.seata.rm.datasource.undo.SQLUndoLog;
+import org.apache.seata.sqlparser.SQLType;
+import org.apache.seata.sqlparser.struct.TableMeta;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * The type DmUndoInsertExecutor test.
+ */
+public class DmUndoInsertExecutorTest extends BaseExecutorTest {
+
+ @Test
+ public void buildUndoSQL() {
+ DmUndoInsertExecutor executor = createExecutor();
+
+ String sql = executor.buildUndoSQL();
+ Assertions.assertNotNull(sql);
+ Assertions.assertTrue(sql.contains("DELETE FROM"));
+ Assertions.assertTrue(sql.contains("WHERE"));
+ Assertions.assertTrue(sql.contains("\"id\" = ?"));
+ Assertions.assertTrue(sql.contains("table_name"));
+ }
+
+ @Test
+ public void getUndoRows() {
+ DmUndoInsertExecutor executor = createExecutor();
+ Assertions.assertEquals(executor.getUndoRows(),
executor.getSqlUndoLog().getAfterImage());
+ }
+
+ @Test
+ public void testInvalidUndoLog() {
+ TableMeta tableMeta = Mockito.mock(TableMeta.class);
+
Mockito.when(tableMeta.getPrimaryKeyOnlyName()).thenReturn(Arrays.asList("id"));
+ Mockito.when(tableMeta.getTableName()).thenReturn("table_name");
+
+ TableRecords afterImage = new TableRecords();
+ afterImage.setTableName("table_name");
+ afterImage.setTableMeta(tableMeta);
+ afterImage.setRows(new ArrayList<>());
+
+ SQLUndoLog sqlUndoLog = new SQLUndoLog();
+ sqlUndoLog.setSqlType(SQLType.INSERT);
+ sqlUndoLog.setTableMeta(tableMeta);
+ sqlUndoLog.setTableName("table_name");
+ sqlUndoLog.setBeforeImage(new TableRecords());
+ sqlUndoLog.setAfterImage(afterImage);
+
+ DmUndoInsertExecutor executor = new DmUndoInsertExecutor(sqlUndoLog);
+
+ Assertions.assertThrows(Exception.class, executor::buildUndoSQL);
+ }
+
+ @Test
+ public void testCompositePrimaryKey() {
+ DmUndoInsertExecutor executor =
createExecutorWithCompositePrimaryKey();
+
+ String sql = executor.buildUndoSQL();
+ Assertions.assertNotNull(sql);
+ Assertions.assertTrue(sql.contains("DELETE FROM"));
+ Assertions.assertTrue(sql.contains("WHERE"));
+
+ // Check that both primary key columns appear in the WHERE clause
+ Assertions.assertTrue(sql.contains("id1"));
+ Assertions.assertTrue(sql.contains("id2"));
+ Assertions.assertTrue(sql.contains(" = ?"));
+ Assertions.assertTrue(sql.contains(" and "));
+ }
+
+ @Test
+ public void testUndoPrepare() throws SQLException {
+ DmUndoInsertExecutor executor = createExecutor();
+ PreparedStatement mockStatement =
Mockito.mock(PreparedStatement.class);
+
+ ArrayList<Field> undoValues = new ArrayList<>();
+ List<Field> pkValueList = new ArrayList<>();
+ Field pkField = new Field("id", 1, "12345");
+ pkValueList.add(pkField);
+
+ Assertions.assertDoesNotThrow(() ->
executor.undoPrepare(mockStatement, undoValues, pkValueList));
+
+ // Verify that setObject was called
+ Mockito.verify(mockStatement, Mockito.times(1)).setObject(1, "12345",
1);
+ }
+
+ private DmUndoInsertExecutor createExecutor() {
+ TableMeta tableMeta = Mockito.mock(TableMeta.class);
+
Mockito.when(tableMeta.getPrimaryKeyOnlyName()).thenReturn(Arrays.asList("id"));
+ Mockito.when(tableMeta.getTableName()).thenReturn("table_name");
+
+ TableRecords beforeImage = new TableRecords();
+ beforeImage.setTableName("table_name");
+ beforeImage.setTableMeta(tableMeta);
+
+ TableRecords afterImage = new TableRecords();
+ afterImage.setTableName("table_name");
+ afterImage.setTableMeta(tableMeta);
+ List<Row> afterRows = new ArrayList<>();
+ Row row0 = new Row();
+ addField(row0, "id", 1, "12345");
+ addField(row0, "age", 1, "1");
+ afterRows.add(row0);
+ Row row1 = new Row();
+ addField(row1, "id", 1, "12346");
+ addField(row1, "age", 1, "1");
+ afterRows.add(row1);
+ afterImage.setRows(afterRows);
+
+ SQLUndoLog sqlUndoLog = new SQLUndoLog();
+ sqlUndoLog.setSqlType(SQLType.INSERT);
+ sqlUndoLog.setTableMeta(tableMeta);
+ sqlUndoLog.setTableName("table_name");
+ sqlUndoLog.setBeforeImage(beforeImage);
+ sqlUndoLog.setAfterImage(afterImage);
+
+ return new DmUndoInsertExecutor(sqlUndoLog);
+ }
+
+ private DmUndoInsertExecutor createExecutorWithCompositePrimaryKey() {
+ TableMeta tableMeta = Mockito.mock(TableMeta.class);
+
Mockito.when(tableMeta.getPrimaryKeyOnlyName()).thenReturn(Arrays.asList("id1",
"id2"));
+ Mockito.when(tableMeta.getTableName()).thenReturn("table_name");
+
+ TableRecords beforeImage = new TableRecords();
+ beforeImage.setTableName("table_name");
+ beforeImage.setTableMeta(tableMeta);
+
+ TableRecords afterImage = new TableRecords();
+ afterImage.setTableName("table_name");
+ afterImage.setTableMeta(tableMeta);
+ List<Row> afterRows = new ArrayList<>();
+ Row row0 = new Row();
+
+ // Manually create primary key fields since addField only sets "id" as
primary key
+ Field id1Field = new Field("id1", 1, "123");
+ id1Field.setKeyType(KeyType.PRIMARY_KEY);
+ row0.add(id1Field);
+
+ Field id2Field = new Field("id2", 1, "456");
+ id2Field.setKeyType(KeyType.PRIMARY_KEY);
+ row0.add(id2Field);
+
+ addField(row0, "age", 1, "25");
+ afterRows.add(row0);
+ afterImage.setRows(afterRows);
+
+ SQLUndoLog sqlUndoLog = new SQLUndoLog();
+ sqlUndoLog.setSqlType(SQLType.INSERT);
+ sqlUndoLog.setTableMeta(tableMeta);
+ sqlUndoLog.setTableName("table_name");
+ sqlUndoLog.setBeforeImage(beforeImage);
+ sqlUndoLog.setAfterImage(afterImage);
+
+ return new DmUndoInsertExecutor(sqlUndoLog);
+ }
+}
diff --git
a/rm-datasource/src/test/java/org/apache/seata/rm/datasource/undo/dm/DmUndoLogManagerTest.java
b/rm-datasource/src/test/java/org/apache/seata/rm/datasource/undo/dm/DmUndoLogManagerTest.java
new file mode 100644
index 0000000000..90af788097
--- /dev/null
+++
b/rm-datasource/src/test/java/org/apache/seata/rm/datasource/undo/dm/DmUndoLogManagerTest.java
@@ -0,0 +1,289 @@
+/*
+ * 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.undo.dm;
+
+import com.alibaba.druid.pool.DruidDataSource;
+import com.alibaba.druid.pool.DruidStatementConnection;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+import org.apache.seata.common.loader.EnhancedServiceLoader;
+import org.apache.seata.rm.datasource.ConnectionContext;
+import org.apache.seata.rm.datasource.ConnectionProxy;
+import org.apache.seata.rm.datasource.DataSourceProxy;
+import org.apache.seata.rm.datasource.DataSourceProxyTest;
+import org.apache.seata.rm.datasource.mock.MockDriver;
+import org.apache.seata.rm.datasource.sql.struct.Row;
+import org.apache.seata.rm.datasource.sql.struct.TableRecords;
+import org.apache.seata.rm.datasource.undo.*;
+import org.apache.seata.rm.datasource.undo.parser.JacksonUndoLogParser;
+import org.apache.seata.sqlparser.SQLRecognizerFactory;
+import org.apache.seata.sqlparser.SQLType;
+import org.apache.seata.sqlparser.SqlParserType;
+import org.apache.seata.sqlparser.druid.DruidDelegatingSQLRecognizerFactory;
+import org.apache.seata.sqlparser.druid.SQLOperateRecognizerHolder;
+import org.apache.seata.sqlparser.druid.SQLOperateRecognizerHolderFactory;
+import org.apache.seata.sqlparser.struct.TableMeta;
+import org.apache.seata.sqlparser.util.JdbcConstants;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.sql.Types;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * The type DmUndoLogManager test.
+ */
+public class DmUndoLogManagerTest {
+
+ List<String> returnValueColumnLabels = Lists.newArrayList("log_status");
+ Object[][] returnValue = new Object[][] {
+ new Object[] {1}, new Object[] {2},
+ };
+ Object[][] columnMetas = new Object[][] {
+ new Object[] {
+ "",
+ "",
+ "table_plain_executor_test",
+ "id",
+ Types.INTEGER,
+ "INTEGER",
+ 64,
+ 0,
+ 10,
+ 1,
+ "",
+ "",
+ 0,
+ 0,
+ 64,
+ 1,
+ "NO",
+ "YES"
+ },
+ new Object[] {
+ "",
+ "",
+ "table_plain_executor_test",
+ "name",
+ Types.VARCHAR,
+ "VARCHAR",
+ 64,
+ 0,
+ 10,
+ 0,
+ "",
+ "",
+ 0,
+ 0,
+ 64,
+ 2,
+ "YES",
+ "NO"
+ },
+ };
+ Object[][] indexMetas = new Object[][] {
+ new Object[] {"PRIMARY", "id", false, "", 3, 1, "A", 34},
+ };
+
+ private DruidDataSource dataSource;
+ private DataSourceProxy dataSourceProxy;
+ private ConnectionProxy connectionProxy;
+ private DmUndoLogManager undoLogManager;
+ private TableMeta tableMeta;
+
+ @BeforeAll
+ public static void setup() {
+ EnhancedServiceLoader.load(
+ SQLOperateRecognizerHolder.class,
+ JdbcConstants.DM,
+ SQLOperateRecognizerHolderFactory.class.getClassLoader());
+ DruidDelegatingSQLRecognizerFactory recognizerFactory =
(DruidDelegatingSQLRecognizerFactory)
+ EnhancedServiceLoader.load(SQLRecognizerFactory.class,
SqlParserType.SQL_PARSER_TYPE_DRUID);
+ }
+
+ @BeforeEach
+ public void init() throws SQLException {
+ MockDriver mockDriver = new MockDriver(returnValueColumnLabels,
returnValue, columnMetas, indexMetas);
+ dataSource = new DruidDataSource();
+ dataSource.setUrl("jdbc:mock:xxx");
+ dataSource.setDriver(mockDriver);
+
+ dataSourceProxy = DataSourceProxyTest.getDataSourceProxy(dataSource);
+
+ connectionProxy = new ConnectionProxy(dataSourceProxy,
getPhysicsConnection(dataSource));
+ undoLogManager = new DmUndoLogManager();
+ tableMeta = new TableMeta();
+ tableMeta.setTableName("table_plain_executor_test");
+ }
+
+ private Connection getPhysicsConnection(DruidDataSource dataSource) throws
SQLException {
+ Connection connection = dataSource.getConnection().getConnection();
+ if (connection instanceof DruidStatementConnection) {
+ return ((DruidStatementConnection) connection).getConnection();
+ }
+ return connection;
+ }
+
+ @Test
+ public void testDeleteUndoLogByLogCreated() throws SQLException {
+ Assertions.assertEquals(
+ 0, undoLogManager.deleteUndoLogByLogCreated(new Date(), 3000,
dataSource.getConnection()));
+ Assertions.assertDoesNotThrow(
+ () -> undoLogManager.deleteUndoLogByLogCreated(new Date(),
3000, connectionProxy));
+ }
+
+ @Test
+ public void testInsertUndoLog() throws SQLException {
+ Assertions.assertDoesNotThrow(() ->
undoLogManager.insertUndoLogWithGlobalFinished(
+ "xid", 1L, new JacksonUndoLogParser(),
dataSource.getConnection()));
+
+ Assertions.assertDoesNotThrow(
+ () -> undoLogManager.insertUndoLogWithNormal("xid", 1L, "",
new byte[] {}, dataSource.getConnection()));
+
+ Assertions.assertDoesNotThrow(
+ () -> undoLogManager.deleteUndoLogByLogCreated(new Date(),
3000, connectionProxy));
+ }
+
+ @Test
+ public void testDeleteUndoLog() {
+ Assertions.assertDoesNotThrow(() ->
undoLogManager.deleteUndoLog("xid", 1L, dataSource.getConnection()));
+ Assertions.assertDoesNotThrow(() ->
undoLogManager.deleteUndoLog("xid", 1L, connectionProxy));
+ }
+
+ @Test
+ public void testBatchDeleteUndoLog() {
+ Assertions.assertDoesNotThrow(() -> undoLogManager.batchDeleteUndoLog(
+ Sets.newHashSet("xid"), Sets.newHashSet(1L),
dataSource.getConnection()));
+
+ Assertions.assertDoesNotThrow(
+ () ->
undoLogManager.batchDeleteUndoLog(Sets.newHashSet("xid"), Sets.newHashSet(1L),
connectionProxy));
+ }
+
+ @Test
+ public void testBatchDeleteUndoLogWithEmptyParams() {
+ Assertions.assertDoesNotThrow(() ->
+ undoLogManager.batchDeleteUndoLog(Sets.newHashSet(),
Sets.newHashSet(1L), dataSource.getConnection()));
+
+ Assertions.assertDoesNotThrow(() -> undoLogManager.batchDeleteUndoLog(
+ Sets.newHashSet("xid"), Sets.newHashSet(),
dataSource.getConnection()));
+
+ Assertions.assertDoesNotThrow(
+ () -> undoLogManager.batchDeleteUndoLog(null,
Sets.newHashSet(1L), dataSource.getConnection()));
+
+ Assertions.assertDoesNotThrow(
+ () ->
undoLogManager.batchDeleteUndoLog(Sets.newHashSet("xid"), null,
dataSource.getConnection()));
+ }
+
+ @Test
+ public void testFlushUndoLogs()
+ throws NoSuchMethodException, InvocationTargetException,
IllegalAccessException, NoSuchFieldException {
+ connectionProxy.bind("xid");
+ ConnectionContext context = connectionProxy.getContext();
+ Method method = context.getClass().getDeclaredMethod("setBranchId",
Long.class);
+ method.setAccessible(true);
+ method.invoke(context, 1L);
+
+ SQLUndoLog undoLogItem = getUndoLogItem(1);
+ undoLogItem.setTableName("test");
+ Method appendUndoItemMethod =
context.getClass().getDeclaredMethod("appendUndoItem", SQLUndoLog.class);
+ appendUndoItemMethod.setAccessible(true);
+ appendUndoItemMethod.invoke(context, undoLogItem);
+
+ Assertions.assertDoesNotThrow(() ->
undoLogManager.flushUndoLogs(connectionProxy));
+ }
+
+ @Test
+ public void testNeedCompress()
+ throws NoSuchFieldException, IllegalAccessException,
NoSuchMethodException, InvocationTargetException {
+ SQLUndoLog smallUndoItem = getUndoLogItem(1);
+ BranchUndoLog smallBranchUndoLog = new BranchUndoLog();
+ smallBranchUndoLog.setBranchId(1L);
+ smallBranchUndoLog.setXid("test_xid");
+
smallBranchUndoLog.setSqlUndoLogs(Collections.singletonList(smallUndoItem));
+ UndoLogParser parser = UndoLogParserFactory.getInstance();
+ byte[] smallUndoLogContent = parser.encode(smallBranchUndoLog);
+
+ Method method =
AbstractUndoLogManager.class.getDeclaredMethod("needCompress", byte[].class);
+ method.setAccessible(true);
+ Assertions.assertFalse((Boolean) method.invoke(undoLogManager,
smallUndoLogContent));
+
+ SQLUndoLog hugeUndoItem = getUndoLogItem(10000);
+ BranchUndoLog hugeBranchUndoLog = new BranchUndoLog();
+ hugeBranchUndoLog.setBranchId(2L);
+ hugeBranchUndoLog.setXid("test_xid1");
+
hugeBranchUndoLog.setSqlUndoLogs(Collections.singletonList(hugeUndoItem));
+ byte[] hugeUndoLogContent = parser.encode(hugeBranchUndoLog);
+ Assertions.assertTrue((Boolean) method.invoke(undoLogManager,
hugeUndoLogContent));
+ }
+
+ @Test
+ public void testUndo() throws SQLException {
+ Assertions.assertDoesNotThrow(() ->
undoLogManager.undo(dataSourceProxy, "xid", 1L));
+ }
+
+ @Test
+ public void testToBatchDeleteSubUndoLogSql() {
+ String sql = DmUndoLogManager.toBatchDeleteSubUndoLogSql(2, 3);
+ Assertions.assertNotNull(sql);
+ Assertions.assertTrue(sql.contains("DELETE FROM"));
+ Assertions.assertTrue(sql.contains("\"CONTEXT\" IN"));
+ Assertions.assertTrue(sql.contains("xid IN"));
+ Assertions.assertTrue(sql.contains("(?,?,?)"));
+ Assertions.assertTrue(sql.contains("(?,?)"));
+ }
+
+ @Test
+ public void testBatchDeleteWithMultipleXidsAndBranchIds() {
+ Assertions.assertDoesNotThrow(() -> undoLogManager.batchDeleteUndoLog(
+ Sets.newHashSet("xid1", "xid2", "xid3"), Sets.newHashSet(1L,
2L, 3L, 4L), dataSource.getConnection()));
+ }
+
+ private SQLUndoLog getUndoLogItem(int size) throws NoSuchFieldException,
IllegalAccessException {
+ SQLUndoLog sqlUndoLog = new SQLUndoLog();
+ sqlUndoLog.setTableName("table_plain_executor_test");
+ sqlUndoLog.setSqlType(SQLType.INSERT);
+ sqlUndoLog.setTableMeta(tableMeta);
+
+ Field rowsField = TableRecords.class.getDeclaredField("rows");
+ rowsField.setAccessible(true);
+
+ List<Row> rows = new ArrayList<>(size);
+ for (int i = 0; i < size; i++) {
+ Row row = new Row();
+ row.add(new org.apache.seata.rm.datasource.sql.struct.Field("id",
1, "value_id_" + i));
+ row.add(new
org.apache.seata.rm.datasource.sql.struct.Field("name", 1, "value_name_" + i));
+ rows.add(row);
+ }
+
+ sqlUndoLog.setAfterImage(TableRecords.empty(tableMeta));
+ TableRecords afterImage = new TableRecords(tableMeta);
+ rowsField.set(afterImage, rows);
+ sqlUndoLog.setAfterImage(afterImage);
+
+ return sqlUndoLog;
+ }
+}
diff --git
a/rm-datasource/src/test/java/org/apache/seata/rm/datasource/undo/dm/DmUndoUpdateExecutorTest.java
b/rm-datasource/src/test/java/org/apache/seata/rm/datasource/undo/dm/DmUndoUpdateExecutorTest.java
new file mode 100644
index 0000000000..8c37c9309e
--- /dev/null
+++
b/rm-datasource/src/test/java/org/apache/seata/rm/datasource/undo/dm/DmUndoUpdateExecutorTest.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.rm.datasource.undo.dm;
+
+import org.apache.seata.rm.datasource.sql.struct.Row;
+import org.apache.seata.rm.datasource.sql.struct.TableRecords;
+import org.apache.seata.rm.datasource.undo.BaseExecutorTest;
+import org.apache.seata.rm.datasource.undo.SQLUndoLog;
+import org.apache.seata.sqlparser.SQLType;
+import org.apache.seata.sqlparser.struct.TableMeta;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * The type DmUndoUpdateExecutor test.
+ */
+public class DmUndoUpdateExecutorTest extends BaseExecutorTest {
+
+ @Test
+ public void buildUndoSQL() {
+ DmUndoUpdateExecutor executor = createExecutor();
+
+ String sql = executor.buildUndoSQL();
+ Assertions.assertNotNull(sql);
+ Assertions.assertTrue(sql.contains("UPDATE"));
+ Assertions.assertTrue(sql.contains("SET"));
+ Assertions.assertTrue(sql.contains("\"age\" = ?"));
+ Assertions.assertTrue(sql.contains("WHERE"));
+ Assertions.assertTrue(sql.contains("\"id\" = ?"));
+ Assertions.assertTrue(sql.contains("table_name"));
+ }
+
+ @Test
+ public void getUndoRows() {
+ DmUndoUpdateExecutor executor = createExecutor();
+ Assertions.assertEquals(executor.getUndoRows(),
executor.getSqlUndoLog().getBeforeImage());
+ }
+
+ @Test
+ public void testInvalidUndoLog() {
+ TableMeta tableMeta = Mockito.mock(TableMeta.class);
+
Mockito.when(tableMeta.getPrimaryKeyOnlyName()).thenReturn(Arrays.asList("id"));
+ Mockito.when(tableMeta.getTableName()).thenReturn("table_name");
+
+ TableRecords beforeImage = new TableRecords();
+ beforeImage.setTableName("table_name");
+ beforeImage.setTableMeta(tableMeta);
+ beforeImage.setRows(new ArrayList<>());
+
+ SQLUndoLog sqlUndoLog = new SQLUndoLog();
+ sqlUndoLog.setSqlType(SQLType.UPDATE);
+ sqlUndoLog.setTableMeta(tableMeta);
+ sqlUndoLog.setTableName("table_name");
+ sqlUndoLog.setBeforeImage(beforeImage);
+ sqlUndoLog.setAfterImage(new TableRecords());
+
+ DmUndoUpdateExecutor executor = new DmUndoUpdateExecutor(sqlUndoLog);
+
+ Assertions.assertThrows(Exception.class, executor::buildUndoSQL);
+ }
+
+ @Test
+ public void testMultipleColumns() {
+ DmUndoUpdateExecutor executor = createExecutorWithMultipleColumns();
+
+ String sql = executor.buildUndoSQL();
+ Assertions.assertNotNull(sql);
+ Assertions.assertTrue(sql.contains("UPDATE"));
+ Assertions.assertTrue(sql.contains("\"age\" = ?"));
+ Assertions.assertTrue(sql.contains("\"name\" = ?"));
+ Assertions.assertTrue(sql.contains("WHERE"));
+ Assertions.assertTrue(sql.contains("\"id\" = ?"));
+ }
+
+ private DmUndoUpdateExecutor createExecutor() {
+ TableMeta tableMeta = Mockito.mock(TableMeta.class);
+
Mockito.when(tableMeta.getPrimaryKeyOnlyName()).thenReturn(Arrays.asList("id"));
+ Mockito.when(tableMeta.getTableName()).thenReturn("table_name");
+
+ TableRecords beforeImage = new TableRecords();
+ beforeImage.setTableName("table_name");
+ beforeImage.setTableMeta(tableMeta);
+ List<Row> beforeRows = new ArrayList<>();
+ Row row0 = new Row();
+ addField(row0, "id", 1, "12345");
+ addField(row0, "age", 1, "1");
+ beforeRows.add(row0);
+ Row row1 = new Row();
+ addField(row1, "id", 1, "12346");
+ addField(row1, "age", 1, "1");
+ beforeRows.add(row1);
+ beforeImage.setRows(beforeRows);
+
+ TableRecords afterImage = new TableRecords();
+ afterImage.setTableName("table_name");
+ afterImage.setTableMeta(tableMeta);
+ List<Row> afterRows = new ArrayList<>();
+ Row row2 = new Row();
+ addField(row2, "id", 1, "12345");
+ addField(row2, "age", 1, "2");
+ afterRows.add(row2);
+ Row row3 = new Row();
+ addField(row3, "id", 1, "12346");
+ addField(row3, "age", 1, "2");
+ afterRows.add(row3);
+ afterImage.setRows(afterRows);
+
+ SQLUndoLog sqlUndoLog = new SQLUndoLog();
+ sqlUndoLog.setSqlType(SQLType.UPDATE);
+ sqlUndoLog.setTableMeta(tableMeta);
+ sqlUndoLog.setTableName("table_name");
+ sqlUndoLog.setBeforeImage(beforeImage);
+ sqlUndoLog.setAfterImage(afterImage);
+
+ return new DmUndoUpdateExecutor(sqlUndoLog);
+ }
+
+ private DmUndoUpdateExecutor createExecutorWithMultipleColumns() {
+ TableMeta tableMeta = Mockito.mock(TableMeta.class);
+
Mockito.when(tableMeta.getPrimaryKeyOnlyName()).thenReturn(Arrays.asList("id"));
+ Mockito.when(tableMeta.getTableName()).thenReturn("table_name");
+
+ TableRecords beforeImage = new TableRecords();
+ beforeImage.setTableName("table_name");
+ beforeImage.setTableMeta(tableMeta);
+ List<Row> beforeRows = new ArrayList<>();
+ Row row0 = new Row();
+ addField(row0, "id", 1, "12345");
+ addField(row0, "age", 1, "25");
+ addField(row0, "name", 12, "John");
+ beforeRows.add(row0);
+ beforeImage.setRows(beforeRows);
+
+ TableRecords afterImage = new TableRecords();
+ afterImage.setTableName("table_name");
+ afterImage.setTableMeta(tableMeta);
+ List<Row> afterRows = new ArrayList<>();
+ Row row1 = new Row();
+ addField(row1, "id", 1, "12345");
+ addField(row1, "age", 1, "26");
+ addField(row1, "name", 12, "Jane");
+ afterRows.add(row1);
+ afterImage.setRows(afterRows);
+
+ SQLUndoLog sqlUndoLog = new SQLUndoLog();
+ sqlUndoLog.setSqlType(SQLType.UPDATE);
+ sqlUndoLog.setTableMeta(tableMeta);
+ sqlUndoLog.setTableName("table_name");
+ sqlUndoLog.setBeforeImage(beforeImage);
+ sqlUndoLog.setAfterImage(afterImage);
+
+ return new DmUndoUpdateExecutor(sqlUndoLog);
+ }
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]