This is an automated email from the ASF dual-hosted git repository.
yowuwuuuw 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 0ce2f9b452 test: add UT for seata-saga-statelang module (#7777)
0ce2f9b452 is described below
commit 0ce2f9b452e0fda03a95a6619cf8066423083651
Author: lokidundun <[email protected]>
AuthorDate: Sun Nov 23 15:33:01 2025 +0800
test: add UT for seata-saga-statelang module (#7777)
---
changes/en-us/2.x.md | 1 +
changes/zh-cn/2.x.md | 1 +
.../saga/statelang/domain/ExecutionStatusTest.java | 38 +++++
.../saga/statelang/domain/RecoverStrategyTest.java | 31 ++++
.../seata/saga/statelang/domain/StateTypeTest.java | 52 ++++++
.../domain/impl/AbstractTaskStateTest.java | 152 +++++++++++++++++
.../saga/statelang/domain/impl/BaseStateTest.java | 190 +++++++++++++++++++++
.../statelang/domain/impl/ChoiceStateImplTest.java | 111 ++++++++++++
.../CompensateSubStateMachineStateImplTest.java | 34 ++++
.../impl/CompensationTriggerStateImplTest.java | 34 ++++
.../domain/impl/FailEndStateImplTest.java | 44 +++++
.../domain/impl/LoopStartStateImplTest.java | 32 ++++
.../domain/impl/ScriptTaskStateImplTest.java | 46 +++++
.../domain/impl/ServiceTaskStateImplTest.java | 59 +++++++
.../domain/impl/StateInstanceImplTest.java | 105 ++++++++++++
.../domain/impl/StateMachineImplTest.java | 53 ++++++
.../domain/impl/StateMachineInstanceImplTest.java | 58 +++++++
.../domain/impl/SubStateMachineImplTest.java | 42 +++++
.../domain/impl/SucceedEndStateImplTest.java | 32 ++++
.../statelang/parser/StateParserFactoryTest.java | 76 +++++++++
.../parser/utils/DesignerJsonTransformerTest.java | 73 ++++++++
.../saga/statelang/parser/utils/IOUtilsTest.java | 68 ++++++++
.../saga/statelang/validator/RuleFactoryTest.java | 44 +++++
.../validator/ValidationExceptionTest.java | 78 +++++++++
.../validator/impl/FiniteTerminationRuleTest.java | 78 +++++++++
.../impl/NoRecursiveSubStateMachineRuleTest.java | 49 ++++++
.../validator/impl/StateNameExistsRuleTest.java | 50 ++++++
27 files changed, 1631 insertions(+)
diff --git a/changes/en-us/2.x.md b/changes/en-us/2.x.md
index af11f8b06e..d8c4431a83 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
+- [[#7777](https://github.com/apache/incubator-seata/pull/7777)] add UT for
seata-saga-statelang 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
diff --git a/changes/zh-cn/2.x.md b/changes/zh-cn/2.x.md
index 5dbdd006ea..f8acfd59d0 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 模块添加单测
+- [[#7777](https://github.com/apache/incubator-seata/pull/7777)] 为
seata-saga-statelang 模块添加单测
- [[#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 模块添加单测
diff --git
a/saga/seata-saga-statelang/src/test/java/org/apache/seata/saga/statelang/domain/ExecutionStatusTest.java
b/saga/seata-saga-statelang/src/test/java/org/apache/seata/saga/statelang/domain/ExecutionStatusTest.java
new file mode 100644
index 0000000000..5ac629e053
--- /dev/null
+++
b/saga/seata-saga-statelang/src/test/java/org/apache/seata/saga/statelang/domain/ExecutionStatusTest.java
@@ -0,0 +1,38 @@
+/*
+ * 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.saga.statelang.domain;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class ExecutionStatusTest {
+
+ @Test
+ public void testExecutionStatusGetStatusString() {
+ assertEquals("Running", ExecutionStatus.RU.getStatusString());
+ assertEquals("Succeed", ExecutionStatus.SU.getStatusString());
+ assertEquals("Failed", ExecutionStatus.FA.getStatusString());
+ assertEquals("Unknown", ExecutionStatus.UN.getStatusString());
+ assertEquals("Skipped", ExecutionStatus.SK.getStatusString());
+ }
+
+ @Test
+ public void testExecutionStatusConstants() {
+ assertEquals(5, ExecutionStatus.values().length);
+ }
+}
diff --git
a/saga/seata-saga-statelang/src/test/java/org/apache/seata/saga/statelang/domain/RecoverStrategyTest.java
b/saga/seata-saga-statelang/src/test/java/org/apache/seata/saga/statelang/domain/RecoverStrategyTest.java
new file mode 100644
index 0000000000..a8ffef882d
--- /dev/null
+++
b/saga/seata-saga-statelang/src/test/java/org/apache/seata/saga/statelang/domain/RecoverStrategyTest.java
@@ -0,0 +1,31 @@
+/*
+ * 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.saga.statelang.domain;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+public class RecoverStrategyTest {
+
+ @Test
+ public void testRecoverStrategyConstants() {
+ assertEquals(2, RecoverStrategy.values().length);
+ assertSame(RecoverStrategy.valueOf("Compensate"),
RecoverStrategy.Compensate);
+ assertSame(RecoverStrategy.valueOf("Forward"),
RecoverStrategy.Forward);
+ }
+}
diff --git
a/saga/seata-saga-statelang/src/test/java/org/apache/seata/saga/statelang/domain/StateTypeTest.java
b/saga/seata-saga-statelang/src/test/java/org/apache/seata/saga/statelang/domain/StateTypeTest.java
new file mode 100644
index 0000000000..d23d2d41cf
--- /dev/null
+++
b/saga/seata-saga-statelang/src/test/java/org/apache/seata/saga/statelang/domain/StateTypeTest.java
@@ -0,0 +1,52 @@
+/*
+ * 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.saga.statelang.domain;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+public class StateTypeTest {
+
+ @Test
+ public void testStateTypeValues() {
+ assertEquals("ServiceTask", StateType.SERVICE_TASK.getValue());
+ assertEquals("Choice", StateType.CHOICE.getValue());
+ assertEquals("Fail", StateType.FAIL.getValue());
+ assertEquals("Succeed", StateType.SUCCEED.getValue());
+ assertEquals("CompensationTrigger",
StateType.COMPENSATION_TRIGGER.getValue());
+ assertEquals("SubStateMachine",
StateType.SUB_STATE_MACHINE.getValue());
+ assertEquals("CompensateSubMachine",
StateType.SUB_MACHINE_COMPENSATION.getValue());
+ assertEquals("ScriptTask", StateType.SCRIPT_TASK.getValue());
+ assertEquals("LoopStart", StateType.LOOP_START.getValue());
+ }
+
+ @Test
+ public void testGetStateType() {
+ assertEquals(StateType.SERVICE_TASK,
StateType.getStateType("ServiceTask"));
+ assertEquals(StateType.CHOICE, StateType.getStateType("choice"));
+ assertEquals(StateType.SCRIPT_TASK,
StateType.getStateType("SCRIPTTASK"));
+ }
+
+ @Test
+ public void testGetStateTypeWithInvalidValue() {
+ assertThrows(IllegalArgumentException.class, () -> {
+ StateType.getStateType("UnknownType");
+ });
+ }
+}
diff --git
a/saga/seata-saga-statelang/src/test/java/org/apache/seata/saga/statelang/domain/impl/AbstractTaskStateTest.java
b/saga/seata-saga-statelang/src/test/java/org/apache/seata/saga/statelang/domain/impl/AbstractTaskStateTest.java
new file mode 100644
index 0000000000..39f71a3a38
--- /dev/null
+++
b/saga/seata-saga-statelang/src/test/java/org/apache/seata/saga/statelang/domain/impl/AbstractTaskStateTest.java
@@ -0,0 +1,152 @@
+/*
+ * 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.saga.statelang.domain.impl;
+
+import org.apache.seata.saga.statelang.domain.TaskState;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class AbstractTaskStateTest {
+
+ private static class TestAbstractTaskState extends AbstractTaskState {}
+
+ @Test
+ public void testAbstractTaskStateGettersAndSetters() {
+ AbstractTaskState taskState = new TestAbstractTaskState();
+ List<TaskState.Retry> retries = new ArrayList<>();
+ List<TaskState.ExceptionMatch> catches = new ArrayList<>();
+ List<Object> input = new ArrayList<>();
+ Map<String, Object> output = new HashMap<>();
+ Map<String, String> status = new HashMap<>();
+ List<Object> inputExpressions = new ArrayList<>();
+ Map<String, Object> outputExpressions = new HashMap<>();
+ TaskState.Loop loop = new AbstractTaskState.LoopImpl();
+
+ taskState.setCompensateState("compensateState1");
+ taskState.setForCompensation(true);
+ taskState.setRetry(retries);
+ taskState.setCatches(catches);
+ taskState.setInput(input);
+ taskState.setOutput(output);
+ taskState.setStatus(status);
+ taskState.setPersist(false);
+ taskState.setRetryPersistModeUpdate(true);
+ taskState.setCompensatePersistModeUpdate(false);
+ taskState.setInputExpressions(inputExpressions);
+ taskState.setOutputExpressions(outputExpressions);
+ taskState.setLoop(loop);
+
+ assertEquals("compensateState1", taskState.getCompensateState());
+ assertTrue(taskState.isForCompensation());
+ Assertions.assertSame(retries, taskState.getRetry());
+ Assertions.assertSame(catches, taskState.getCatches());
+ Assertions.assertSame(input, taskState.getInput());
+ Assertions.assertSame(output, taskState.getOutput());
+ Assertions.assertSame(status, taskState.getStatus());
+ Assertions.assertFalse(taskState.isPersist());
+ assertTrue(taskState.isRetryPersistModeUpdate());
+ Assertions.assertFalse(taskState.isCompensatePersistModeUpdate());
+ Assertions.assertSame(inputExpressions,
taskState.getInputExpressions());
+ Assertions.assertSame(outputExpressions,
taskState.getOutputExpressions());
+ Assertions.assertSame(loop, taskState.getLoop());
+ }
+
+ @Test
+ public void testSetCompensateState_WithNonBlankValue() {
+ AbstractTaskState taskState = new TestAbstractTaskState();
+ taskState.setForUpdate(false);
+
+ taskState.setCompensateState("compensateState2");
+
+ assertEquals("compensateState2", taskState.getCompensateState());
+ assertTrue(taskState.isForUpdate());
+ }
+
+ @Test
+ public void testSetCompensateState_WithBlankValue() {
+ AbstractTaskState taskState = new TestAbstractTaskState();
+ taskState.setForUpdate(true);
+
+ taskState.setCompensateState("");
+
+ assertEquals("", taskState.getCompensateState());
+ assertTrue(taskState.isForUpdate());
+ }
+
+ @Test
+ public void testRetryImpl() {
+ AbstractTaskState.RetryImpl retry = new AbstractTaskState.RetryImpl();
+ List<String> exceptions = new ArrayList<>();
+ exceptions.add("Exception1");
+ List<Class<? extends Exception>> exceptionClasses = new ArrayList<>();
+ exceptionClasses.add(RuntimeException.class);
+
+ retry.setExceptions(exceptions);
+ retry.setExceptionClasses(exceptionClasses);
+ retry.setIntervalSeconds(1.5);
+ retry.setMaxAttempts(3);
+ retry.setBackoffRate(2.0);
+
+ Assertions.assertSame(exceptions, retry.getExceptions());
+ Assertions.assertSame(exceptionClasses, retry.getExceptionClasses());
+ assertEquals(1.5, retry.getIntervalSeconds());
+ assertEquals(3, retry.getMaxAttempts());
+ assertEquals(2.0, retry.getBackoffRate());
+ }
+
+ @Test
+ public void testExceptionMatchImpl() {
+ AbstractTaskState.ExceptionMatchImpl exceptionMatch = new
AbstractTaskState.ExceptionMatchImpl();
+ List<String> exceptions = new ArrayList<>();
+ exceptions.add("Exception2");
+ List<Class<? extends Exception>> exceptionClasses = new ArrayList<>();
+ exceptionClasses.add(IllegalArgumentException.class);
+
+ exceptionMatch.setExceptions(exceptions);
+ exceptionMatch.setExceptionClasses(exceptionClasses);
+ exceptionMatch.setNext("nextState");
+
+ Assertions.assertSame(exceptions, exceptionMatch.getExceptions());
+ Assertions.assertSame(exceptionClasses,
exceptionMatch.getExceptionClasses());
+ assertEquals("nextState", exceptionMatch.getNext());
+ }
+
+ @Test
+ public void testLoopImpl() {
+ AbstractTaskState.LoopImpl loop = new AbstractTaskState.LoopImpl();
+
+ loop.setParallel(5);
+ loop.setCollection("collectionVar");
+ loop.setElementVariableName("element");
+ loop.setElementIndexName("index");
+ loop.setCompletionCondition("${count > 10}");
+
+ assertEquals(5, loop.getParallel());
+ assertEquals("collectionVar", loop.getCollection());
+ assertEquals("element", loop.getElementVariableName());
+ assertEquals("index", loop.getElementIndexName());
+ assertEquals("${count > 10}", loop.getCompletionCondition());
+ }
+}
diff --git
a/saga/seata-saga-statelang/src/test/java/org/apache/seata/saga/statelang/domain/impl/BaseStateTest.java
b/saga/seata-saga-statelang/src/test/java/org/apache/seata/saga/statelang/domain/impl/BaseStateTest.java
new file mode 100644
index 0000000000..009c5461cc
--- /dev/null
+++
b/saga/seata-saga-statelang/src/test/java/org/apache/seata/saga/statelang/domain/impl/BaseStateTest.java
@@ -0,0 +1,190 @@
+/*
+ * 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.saga.statelang.domain.impl;
+
+import org.apache.seata.saga.statelang.domain.StateMachine;
+import org.apache.seata.saga.statelang.domain.StateType;
+import org.junit.jupiter.api.Assertions;
+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.assertSame;
+
+public class BaseStateTest {
+ private static class TestBaseState extends BaseState {}
+
+ @Test
+ public void testBaseStateAllProperties() {
+ BaseState baseState = new TestBaseState();
+ StateMachine mockStateMachine = new MockStateMachine();
+ Map<String, Object> extensions = new HashMap<>();
+ extensions.put("timeout", 3000);
+ extensions.put("async", false);
+ StateType testType = StateType.SERVICE_TASK;
+
+ baseState.setName("orderCreateState");
+ baseState.setComment("orderCreateStatus");
+ baseState.setNext("orderPayState");
+ baseState.setExtensions(extensions);
+ baseState.setStateMachine(mockStateMachine);
+ baseState.setType(testType);
+
+ assertEquals("orderCreateState", baseState.getName(), "name can not be
matched");
+ assertEquals("orderCreateStatus", baseState.getComment(), "comment can
not be matched");
+ assertEquals("orderPayState", baseState.getNext(), "next can not be
matched");
+ assertSame(extensions, baseState.getExtensions(), "extensions can not
be matched");
+ assertEquals(3000, baseState.getExtensions().get("timeout"),
"extension timeout can not be matched");
+ assertSame(mockStateMachine, baseState.getStateMachine(),
"stateMachine can not be matched");
+ assertEquals(testType, baseState.getType(), "type can not be matched");
+ assertEquals(StateType.SERVICE_TASK, baseState.getType(), "type should
be SERVICE_TASK");
+ }
+
+ @Test
+ public void testBaseStateNullProperties() {
+ BaseState baseState = new TestBaseState();
+
+ baseState.setName(null);
+ baseState.setComment(null);
+ baseState.setNext(null);
+ baseState.setExtensions(null);
+ baseState.setStateMachine(null);
+ baseState.setType(null);
+
+ Assertions.assertNull(baseState.getName(), "name should be null");
+ Assertions.assertNull(baseState.getComment(), "comment should be
null");
+ Assertions.assertNull(baseState.getNext(), "next should be null");
+ Assertions.assertNull(baseState.getExtensions(), "extensions should be
null");
+ Assertions.assertNull(baseState.getStateMachine(), "stateMachine
should be null");
+ Assertions.assertNull(baseState.getType(), "type should be null");
+ }
+
+ private static class MockStateMachine implements StateMachine {
+ private String name;
+ private String id;
+
+ @Override
+ public String getName() {
+ return this.name;
+ }
+
+ @Override
+ public String getComment() {
+ return "mockComment";
+ }
+
+ @Override
+ public String getStartState() {
+ return "mockStartState";
+ }
+
+ @Override
+ public void setStartState(String startState) {}
+
+ @Override
+ public String getVersion() {
+ return "1.0";
+ }
+
+ @Override
+ public void setVersion(String version) {}
+
+ @Override
+ public Map<String, org.apache.seata.saga.statelang.domain.State>
getStates() {
+ return new HashMap<>();
+ }
+
+ @Override
+ public org.apache.seata.saga.statelang.domain.State getState(String
name) {
+ return null;
+ }
+
+ @Override
+ public String getId() {
+ return this.id;
+ }
+
+ @Override
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ @Override
+ public String getTenantId() {
+ return "mockTenantId";
+ }
+
+ @Override
+ public void setTenantId(String tenantId) {}
+
+ @Override
+ public String getAppName() {
+ return "mockAppName";
+ }
+
+ @Override
+ public String getType() {
+ return "STATE_LANG";
+ }
+
+ @Override
+ public Status getStatus() {
+ return Status.AC;
+ }
+
+ @Override
+ public org.apache.seata.saga.statelang.domain.RecoverStrategy
getRecoverStrategy() {
+ return
org.apache.seata.saga.statelang.domain.RecoverStrategy.Compensate;
+ }
+
+ @Override
+ public void
setRecoverStrategy(org.apache.seata.saga.statelang.domain.RecoverStrategy
recoverStrategy) {}
+
+ @Override
+ public String getContent() {
+ return "mockContent";
+ }
+
+ @Override
+ public void setContent(String content) {}
+
+ @Override
+ public boolean isPersist() {
+ return true;
+ }
+
+ @Override
+ public java.util.Date getGmtCreate() {
+ return new java.util.Date();
+ }
+
+ @Override
+ public void setGmtCreate(java.util.Date gmtCreate) {}
+
+ @Override
+ public Boolean isRetryPersistModeUpdate() {
+ return true;
+ }
+
+ @Override
+ public Boolean isCompensatePersistModeUpdate() {
+ return false;
+ }
+ }
+}
diff --git
a/saga/seata-saga-statelang/src/test/java/org/apache/seata/saga/statelang/domain/impl/ChoiceStateImplTest.java
b/saga/seata-saga-statelang/src/test/java/org/apache/seata/saga/statelang/domain/impl/ChoiceStateImplTest.java
new file mode 100644
index 0000000000..d013345362
--- /dev/null
+++
b/saga/seata-saga-statelang/src/test/java/org/apache/seata/saga/statelang/domain/impl/ChoiceStateImplTest.java
@@ -0,0 +1,111 @@
+/*
+ * 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.saga.statelang.domain.impl;
+
+import org.apache.seata.saga.statelang.domain.ChoiceState;
+import org.apache.seata.saga.statelang.domain.StateType;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+/**
+ * Unit tests for ChoiceStateImpl
+ */
+public class ChoiceStateImplTest {
+
+ @Test
+ public void testConstructor() {
+ ChoiceStateImpl choiceState = new ChoiceStateImpl();
+ assertEquals(StateType.CHOICE, choiceState.getType());
+ }
+
+ @Test
+ public void testChoices() {
+ ChoiceStateImpl choiceState = new ChoiceStateImpl();
+ List<ChoiceState.Choice> choices = new ArrayList<>();
+ ChoiceState.Choice choice = new ChoiceStateImpl.ChoiceImpl();
+ choices.add(choice);
+
+ choiceState.setChoices(choices);
+ assertNotNull(choiceState.getChoices());
+ assertEquals(1, choiceState.getChoices().size());
+ Assertions.assertSame(choice, choiceState.getChoices().get(0));
+ }
+
+ @Test
+ public void testDefaultChoice() {
+ ChoiceStateImpl choiceState = new ChoiceStateImpl();
+ String defaultChoice = "defaultNextState";
+
+ choiceState.setDefaultChoice(defaultChoice);
+ assertEquals(defaultChoice, choiceState.getDefault());
+ }
+
+ @Test
+ public void testChoiceEvaluators() {
+ ChoiceStateImpl choiceState = new ChoiceStateImpl();
+ Map<Object, String> evaluators = new HashMap<>();
+ Object key = new Object();
+ String value = "nextState1";
+ evaluators.put(key, value);
+
+ choiceState.setChoiceEvaluators(evaluators);
+ assertNotNull(choiceState.getChoiceEvaluators());
+ assertEquals(1, choiceState.getChoiceEvaluators().size());
+ assertEquals(value, choiceState.getChoiceEvaluators().get(key));
+ }
+
+ @Test
+ public void testChoiceImpl() {
+ ChoiceStateImpl.ChoiceImpl choice = new ChoiceStateImpl.ChoiceImpl();
+ String expression = "${a > b}";
+ String next = "nextState2";
+
+ choice.setExpression(expression);
+ choice.setNext(next);
+
+ assertEquals(expression, choice.getExpression());
+ assertEquals(next, choice.getNext());
+ }
+
+ @Test
+ public void testNullValues() {
+ ChoiceStateImpl choiceState = new ChoiceStateImpl();
+
+ choiceState.setChoices(null);
+ Assertions.assertNull(choiceState.getChoices());
+
+ choiceState.setDefaultChoice(null);
+ Assertions.assertNull(choiceState.getDefault());
+
+ choiceState.setChoiceEvaluators(null);
+ Assertions.assertNull(choiceState.getChoiceEvaluators());
+
+ ChoiceStateImpl.ChoiceImpl choice = new ChoiceStateImpl.ChoiceImpl();
+ choice.setExpression(null);
+ choice.setNext(null);
+ Assertions.assertNull(choice.getExpression());
+ Assertions.assertNull(choice.getNext());
+ }
+}
diff --git
a/saga/seata-saga-statelang/src/test/java/org/apache/seata/saga/statelang/domain/impl/CompensateSubStateMachineStateImplTest.java
b/saga/seata-saga-statelang/src/test/java/org/apache/seata/saga/statelang/domain/impl/CompensateSubStateMachineStateImplTest.java
new file mode 100644
index 0000000000..87912b68a1
--- /dev/null
+++
b/saga/seata-saga-statelang/src/test/java/org/apache/seata/saga/statelang/domain/impl/CompensateSubStateMachineStateImplTest.java
@@ -0,0 +1,34 @@
+/*
+ * 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.saga.statelang.domain.impl;
+
+import org.apache.seata.saga.statelang.domain.StateType;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Unit tests for CompensateSubStateMachineStateImpl
+ */
+public class CompensateSubStateMachineStateImplTest {
+
+ @Test
+ public void testCompensateSubStateMachineStateImpl() {
+ // Test constructor sets correct type
+ CompensateSubStateMachineStateImpl compensateState = new
CompensateSubStateMachineStateImpl();
+ Assertions.assertEquals(StateType.SUB_MACHINE_COMPENSATION,
compensateState.getType());
+ }
+}
diff --git
a/saga/seata-saga-statelang/src/test/java/org/apache/seata/saga/statelang/domain/impl/CompensationTriggerStateImplTest.java
b/saga/seata-saga-statelang/src/test/java/org/apache/seata/saga/statelang/domain/impl/CompensationTriggerStateImplTest.java
new file mode 100644
index 0000000000..ff35c5cb3c
--- /dev/null
+++
b/saga/seata-saga-statelang/src/test/java/org/apache/seata/saga/statelang/domain/impl/CompensationTriggerStateImplTest.java
@@ -0,0 +1,34 @@
+/*
+ * 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.saga.statelang.domain.impl;
+
+import org.apache.seata.saga.statelang.domain.StateType;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Unit tests for CompensationTriggerStateImpl
+ */
+public class CompensationTriggerStateImplTest {
+
+ @Test
+ public void testCompensationTriggerStateImpl() {
+ // Test constructor sets correct type
+ CompensationTriggerStateImpl triggerState = new
CompensationTriggerStateImpl();
+ Assertions.assertEquals(StateType.COMPENSATION_TRIGGER,
triggerState.getType());
+ }
+}
diff --git
a/saga/seata-saga-statelang/src/test/java/org/apache/seata/saga/statelang/domain/impl/FailEndStateImplTest.java
b/saga/seata-saga-statelang/src/test/java/org/apache/seata/saga/statelang/domain/impl/FailEndStateImplTest.java
new file mode 100644
index 0000000000..ae3508a7f3
--- /dev/null
+++
b/saga/seata-saga-statelang/src/test/java/org/apache/seata/saga/statelang/domain/impl/FailEndStateImplTest.java
@@ -0,0 +1,44 @@
+/*
+ * 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.saga.statelang.domain.impl;
+
+import org.apache.seata.saga.statelang.domain.StateType;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Unit tests for FailEndStateImpl
+ */
+public class FailEndStateImplTest {
+
+ @Test
+ public void testFailEndStateImpl() {
+ // Test constructor sets correct type
+ FailEndStateImpl failEndState = new FailEndStateImpl();
+ Assertions.assertEquals(StateType.FAIL, failEndState.getType());
+
+ // Test error code
+ String errorCode = "ERROR_500";
+ failEndState.setErrorCode(errorCode);
+ Assertions.assertEquals(errorCode, failEndState.getErrorCode());
+
+ // Test message
+ String message = "Operation failed";
+ failEndState.setMessage(message);
+ Assertions.assertEquals(message, failEndState.getMessage());
+ }
+}
diff --git
a/saga/seata-saga-statelang/src/test/java/org/apache/seata/saga/statelang/domain/impl/LoopStartStateImplTest.java
b/saga/seata-saga-statelang/src/test/java/org/apache/seata/saga/statelang/domain/impl/LoopStartStateImplTest.java
new file mode 100644
index 0000000000..5f63155571
--- /dev/null
+++
b/saga/seata-saga-statelang/src/test/java/org/apache/seata/saga/statelang/domain/impl/LoopStartStateImplTest.java
@@ -0,0 +1,32 @@
+/*
+ * 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.saga.statelang.domain.impl;
+
+import org.apache.seata.saga.statelang.domain.StateType;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class LoopStartStateImplTest {
+
+ @Test
+ public void testLoopStartStateImpl() {
+ LoopStartStateImpl loopStartState = new LoopStartStateImpl();
+
+ assertEquals(StateType.LOOP_START, loopStartState.getType(),
"LoopStartStateImpl should be LOOP_START");
+ }
+}
diff --git
a/saga/seata-saga-statelang/src/test/java/org/apache/seata/saga/statelang/domain/impl/ScriptTaskStateImplTest.java
b/saga/seata-saga-statelang/src/test/java/org/apache/seata/saga/statelang/domain/impl/ScriptTaskStateImplTest.java
new file mode 100644
index 0000000000..3e184f5257
--- /dev/null
+++
b/saga/seata-saga-statelang/src/test/java/org/apache/seata/saga/statelang/domain/impl/ScriptTaskStateImplTest.java
@@ -0,0 +1,46 @@
+/*
+ * 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.saga.statelang.domain.impl;
+
+import org.apache.seata.saga.statelang.domain.StateType;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+public class ScriptTaskStateImplTest {
+
+ @Test
+ public void testScriptTaskStateImplDefault() {
+ ScriptTaskStateImpl scriptTask = new ScriptTaskStateImpl();
+
+ assertEquals(StateType.SCRIPT_TASK, scriptTask.getType(),
"ScriptTaskStateImpl should be SCRIPT_TASK");
+ assertEquals("groovy", scriptTask.getScriptType(), "default should be
groovy");
+ assertNull(scriptTask.getScriptContent());
+ }
+
+ @Test
+ public void testScriptTaskStateImplCustom() {
+ ScriptTaskStateImpl scriptTask = new ScriptTaskStateImpl();
+
+ scriptTask.setScriptType("python");
+ scriptTask.setScriptContent("print('hello')");
+
+ assertEquals("python", scriptTask.getScriptType());
+ assertEquals("print('hello')", scriptTask.getScriptContent());
+ }
+}
diff --git
a/saga/seata-saga-statelang/src/test/java/org/apache/seata/saga/statelang/domain/impl/ServiceTaskStateImplTest.java
b/saga/seata-saga-statelang/src/test/java/org/apache/seata/saga/statelang/domain/impl/ServiceTaskStateImplTest.java
new file mode 100644
index 0000000000..80d0c92035
--- /dev/null
+++
b/saga/seata-saga-statelang/src/test/java/org/apache/seata/saga/statelang/domain/impl/ServiceTaskStateImplTest.java
@@ -0,0 +1,59 @@
+/*
+ * 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.saga.statelang.domain.impl;
+
+import org.apache.seata.saga.statelang.domain.StateType;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertSame;
+
+public class ServiceTaskStateImplTest {
+
+ @Test
+ public void testServiceTaskStateImplProperties() throws
NoSuchMethodException {
+ ServiceTaskStateImpl serviceTask = new ServiceTaskStateImpl();
+ List<String> paramTypes = Arrays.asList("java.lang.String", "int");
+ Method testMethod =
getClass().getMethod("testServiceTaskStateImplProperties");
+ Map<Object, String> evaluators = new HashMap<>();
+ evaluators.put("SUCCESS", "nextState");
+
+ serviceTask.setServiceType("Dubbo");
+ serviceTask.setServiceName("OrderService");
+ serviceTask.setServiceMethod("createOrder");
+ serviceTask.setParameterTypes(paramTypes);
+ serviceTask.setMethod(testMethod);
+ serviceTask.setStatusEvaluators(evaluators);
+ serviceTask.setAsync(true);
+
+ assertEquals(StateType.SERVICE_TASK, serviceTask.getType(),
"ServiceTaskStateImpl should be SERVICE_TASK");
+ assertEquals("Dubbo", serviceTask.getServiceType());
+ assertEquals("OrderService", serviceTask.getServiceName());
+ assertEquals("createOrder", serviceTask.getServiceMethod());
+ assertSame(paramTypes, serviceTask.getParameterTypes());
+ assertSame(testMethod, serviceTask.getMethod());
+ assertSame(evaluators, serviceTask.getStatusEvaluators());
+ Assertions.assertTrue(serviceTask.isAsync());
+ }
+}
diff --git
a/saga/seata-saga-statelang/src/test/java/org/apache/seata/saga/statelang/domain/impl/StateInstanceImplTest.java
b/saga/seata-saga-statelang/src/test/java/org/apache/seata/saga/statelang/domain/impl/StateInstanceImplTest.java
new file mode 100644
index 0000000000..22d5e4a558
--- /dev/null
+++
b/saga/seata-saga-statelang/src/test/java/org/apache/seata/saga/statelang/domain/impl/StateInstanceImplTest.java
@@ -0,0 +1,105 @@
+/*
+ * 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.saga.statelang.domain.impl;
+
+import org.apache.seata.saga.statelang.domain.ExecutionStatus;
+import org.apache.seata.saga.statelang.domain.StateType;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.util.Date;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertSame;
+
+public class StateInstanceImplTest {
+
+ @Test
+ public void testStateInstanceImplProperties() {
+ StateInstanceImpl instance = new StateInstanceImpl();
+ Date now = new Date();
+ Exception testException = new RuntimeException("test");
+ StateInstanceImpl compensationState = new StateInstanceImpl();
+ compensationState.setStatus(ExecutionStatus.SU);
+
+ instance.setId("TEST_ID");
+ instance.setMachineInstanceId("MACHINE_TEST_ID");
+ instance.setName("TEST_STATE");
+ instance.setType(StateType.SERVICE_TASK);
+ instance.setServiceName("TestService");
+ instance.setServiceMethod("testMethod");
+ instance.setServiceType("SpringBean");
+ instance.setBusinessKey("BUSINESS_123");
+ instance.setGmtStarted(now);
+ instance.setGmtUpdated(now);
+ instance.setGmtEnd(now);
+ instance.setForUpdate(true);
+ instance.setException(testException);
+ instance.setSerializedException("serialized_exception");
+ instance.setInputParams("input");
+ instance.setSerializedInputParams("serialized_input");
+ instance.setOutputParams("output");
+ instance.setSerializedOutputParams("serialized_output");
+ instance.setStateIdCompensatedFor("COMPENSATE_FOR_1");
+ instance.setStateIdRetriedFor("RETRY_FOR_1");
+ instance.setCompensationState(compensationState);
+ instance.setIgnoreStatus(true);
+
+ assertEquals("TEST_ID", instance.getId());
+ assertEquals("MACHINE_TEST_ID", instance.getMachineInstanceId());
+ assertEquals("TEST_STATE", instance.getName());
+ assertEquals(StateType.SERVICE_TASK, instance.getType());
+ assertEquals("TestService", instance.getServiceName());
+ assertEquals("testMethod", instance.getServiceMethod());
+ assertEquals("SpringBean", instance.getServiceType());
+ assertEquals("BUSINESS_123", instance.getBusinessKey());
+ assertSame(now, instance.getGmtStarted());
+ assertSame(now, instance.getGmtUpdated());
+ assertSame(now, instance.getGmtEnd());
+ Assertions.assertTrue(instance.isForUpdate());
+ assertSame(testException, instance.getException());
+ assertEquals("serialized_exception",
instance.getSerializedException());
+ assertEquals("input", instance.getInputParams());
+ assertEquals("serialized_input", instance.getSerializedInputParams());
+ assertEquals("output", instance.getOutputParams());
+ assertEquals("serialized_output",
instance.getSerializedOutputParams());
+ assertEquals("COMPENSATE_FOR_1", instance.getStateIdCompensatedFor());
+ assertEquals("RETRY_FOR_1", instance.getStateIdRetriedFor());
+ assertSame(compensationState, instance.getCompensationState());
+ Assertions.assertTrue(instance.isIgnoreStatus());
+ }
+
+ @Test
+ public void testIsForCompensation() {
+ StateInstanceImpl instance = new StateInstanceImpl();
+
+ Assertions.assertFalse(instance.isForCompensation());
+
+ instance.setStateIdCompensatedFor("COMPENSATE_FOR_1");
+ Assertions.assertTrue(instance.isForCompensation());
+ }
+
+ @Test
+ public void testGetCompensationStatus() {
+ StateInstanceImpl instance = new StateInstanceImpl();
+
+ StateInstanceImpl compensationState = new StateInstanceImpl();
+ compensationState.setStatus(ExecutionStatus.FA);
+ instance.setCompensationState(compensationState);
+ assertEquals(ExecutionStatus.FA, instance.getCompensationStatus());
+ }
+}
diff --git
a/saga/seata-saga-statelang/src/test/java/org/apache/seata/saga/statelang/domain/impl/StateMachineImplTest.java
b/saga/seata-saga-statelang/src/test/java/org/apache/seata/saga/statelang/domain/impl/StateMachineImplTest.java
new file mode 100644
index 0000000000..30effa0527
--- /dev/null
+++
b/saga/seata-saga-statelang/src/test/java/org/apache/seata/saga/statelang/domain/impl/StateMachineImplTest.java
@@ -0,0 +1,53 @@
+/*
+ * 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.saga.statelang.domain.impl;
+
+import org.apache.seata.saga.statelang.domain.State;
+import org.junit.jupiter.api.Test;
+
+import java.util.Date;
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.Mockito.mock;
+
+public class StateMachineImplTest {
+
+ @Test
+ public void testStateMachineImpl() {
+ StateMachineImpl machine = new StateMachineImpl();
+ String testId = "MACHINE_ID_456";
+ String testName = "TestMachine";
+ Date createTime = new Date();
+
+ machine.setId(testId);
+ machine.setName(testName);
+ machine.setGmtCreate(createTime);
+
+ assertEquals(testId, machine.getId());
+ assertEquals(testName, machine.getName());
+ assertEquals(createTime, machine.getGmtCreate());
+
+ State mockState = mock(State.class);
+ String stateName = "State1";
+ machine.putState(stateName, mockState);
+
+ Map<String, State> states = machine.getStates();
+ assertEquals(1, states.size());
+ assertEquals(mockState, machine.getState(stateName));
+ }
+}
diff --git
a/saga/seata-saga-statelang/src/test/java/org/apache/seata/saga/statelang/domain/impl/StateMachineInstanceImplTest.java
b/saga/seata-saga-statelang/src/test/java/org/apache/seata/saga/statelang/domain/impl/StateMachineInstanceImplTest.java
new file mode 100644
index 0000000000..34050f50ce
--- /dev/null
+++
b/saga/seata-saga-statelang/src/test/java/org/apache/seata/saga/statelang/domain/impl/StateMachineInstanceImplTest.java
@@ -0,0 +1,58 @@
+/*
+ * 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.saga.statelang.domain.impl;
+
+import org.apache.seata.saga.statelang.domain.ExecutionStatus;
+import org.apache.seata.saga.statelang.domain.StateInstance;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.util.Date;
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.Mockito.mock;
+
+public class StateMachineInstanceImplTest {
+
+ @Test
+ public void testStateMachineInstanceImpl() {
+ StateMachineInstanceImpl instance = new StateMachineInstanceImpl();
+ Date now = new Date();
+ String testId = "TEST_ID_123";
+ ExecutionStatus status = ExecutionStatus.SU;
+
+ instance.setId(testId);
+ instance.setGmtStarted(now);
+ instance.setStatus(status);
+ instance.setRunning(true);
+
+ assertEquals(testId, instance.getId());
+ assertEquals(now, instance.getGmtStarted());
+ assertEquals(status, instance.getStatus());
+ Assertions.assertTrue(instance.isRunning());
+
+ StateInstance mockState = mock(StateInstance.class);
+ String stateId = "STATE_1";
+ instance.putStateInstance(stateId, mockState);
+
+ Map<String, StateInstance> stateMap = instance.getStateMap();
+ assertEquals(1, stateMap.size());
+ assertEquals(mockState, stateMap.get(stateId));
+ Assertions.assertTrue(instance.getStateList().contains(mockState));
+ }
+}
diff --git
a/saga/seata-saga-statelang/src/test/java/org/apache/seata/saga/statelang/domain/impl/SubStateMachineImplTest.java
b/saga/seata-saga-statelang/src/test/java/org/apache/seata/saga/statelang/domain/impl/SubStateMachineImplTest.java
new file mode 100644
index 0000000000..9ef9e27a35
--- /dev/null
+++
b/saga/seata-saga-statelang/src/test/java/org/apache/seata/saga/statelang/domain/impl/SubStateMachineImplTest.java
@@ -0,0 +1,42 @@
+/*
+ * 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.saga.statelang.domain.impl;
+
+import org.apache.seata.saga.statelang.domain.StateType;
+import org.apache.seata.saga.statelang.domain.TaskState;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.Mockito.mock;
+
+public class SubStateMachineImplTest {
+
+ @Test
+ public void testSubStateMachineImpl() {
+ SubStateMachineImpl subMachine = new SubStateMachineImpl();
+
+ assertEquals(StateType.SUB_STATE_MACHINE, subMachine.getType());
+
+ String machineName = "TestSubMachine";
+ subMachine.setStateMachineName(machineName);
+ assertEquals(machineName, subMachine.getStateMachineName());
+
+ TaskState mockCompensateState = mock(TaskState.class);
+ subMachine.setCompensateStateObject(mockCompensateState);
+ assertEquals(mockCompensateState,
subMachine.getCompensateStateObject());
+ }
+}
diff --git
a/saga/seata-saga-statelang/src/test/java/org/apache/seata/saga/statelang/domain/impl/SucceedEndStateImplTest.java
b/saga/seata-saga-statelang/src/test/java/org/apache/seata/saga/statelang/domain/impl/SucceedEndStateImplTest.java
new file mode 100644
index 0000000000..a2a0a67347
--- /dev/null
+++
b/saga/seata-saga-statelang/src/test/java/org/apache/seata/saga/statelang/domain/impl/SucceedEndStateImplTest.java
@@ -0,0 +1,32 @@
+/*
+ * 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.saga.statelang.domain.impl;
+
+import org.apache.seata.saga.statelang.domain.StateType;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class SucceedEndStateImplTest {
+
+ @Test
+ public void testSucceedEndStateImpl() {
+ SucceedEndStateImpl state = new SucceedEndStateImpl();
+
+ assertEquals(StateType.SUCCEED, state.getType());
+ }
+}
diff --git
a/saga/seata-saga-statelang/src/test/java/org/apache/seata/saga/statelang/parser/StateParserFactoryTest.java
b/saga/seata-saga-statelang/src/test/java/org/apache/seata/saga/statelang/parser/StateParserFactoryTest.java
new file mode 100644
index 0000000000..69561a69ed
--- /dev/null
+++
b/saga/seata-saga-statelang/src/test/java/org/apache/seata/saga/statelang/parser/StateParserFactoryTest.java
@@ -0,0 +1,76 @@
+/*
+ * 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.saga.statelang.parser;
+
+import org.apache.seata.saga.statelang.domain.StateType;
+import org.apache.seata.saga.statelang.parser.impl.ChoiceStateParser;
+import
org.apache.seata.saga.statelang.parser.impl.CompensateSubStateMachineStateParser;
+import
org.apache.seata.saga.statelang.parser.impl.CompensationTriggerStateParser;
+import org.apache.seata.saga.statelang.parser.impl.FailEndStateParser;
+import org.apache.seata.saga.statelang.parser.impl.ScriptTaskStateParser;
+import org.apache.seata.saga.statelang.parser.impl.ServiceTaskStateParser;
+import org.apache.seata.saga.statelang.parser.impl.SubStateMachineParser;
+import org.apache.seata.saga.statelang.parser.impl.SucceedEndStateParser;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * test StateParserFactory
+ */
+public class StateParserFactoryTest {
+
+ @Test
+ public void testGetStateParser_ExistingTypes() {
+ StateParser serviceTaskParser =
StateParserFactory.getStateParser(StateType.SERVICE_TASK);
+ assertNotNull(serviceTaskParser, "ServiceTaskStateParser should not be
null");
+ assertTrue(serviceTaskParser instanceof ServiceTaskStateParser,
"Parser should be ServiceTaskStateParser");
+
+ StateParser choiceParser =
StateParserFactory.getStateParser(StateType.CHOICE);
+ assertNotNull(choiceParser, "ChoiceStateParser should not be null");
+ assertTrue(choiceParser instanceof ChoiceStateParser, "Parser should
be ChoiceStateParser");
+
+ StateParser compensationTriggerParser =
StateParserFactory.getStateParser(StateType.COMPENSATION_TRIGGER);
+ assertNotNull(compensationTriggerParser,
"CompensationTriggerStateParser should not be null");
+ assertTrue(
+ compensationTriggerParser instanceof
CompensationTriggerStateParser,
+ "Parser should be CompensationTriggerStateParser");
+
+ StateParser failEndParser =
StateParserFactory.getStateParser(StateType.FAIL);
+ assertNotNull(failEndParser, "FailEndStateParser should not be null");
+ assertTrue(failEndParser instanceof FailEndStateParser, "Parser should
be FailEndStateParser");
+
+ StateParser succeedEndParser =
StateParserFactory.getStateParser(StateType.SUCCEED);
+ assertNotNull(succeedEndParser, "SucceedEndStateParser should not be
null");
+ assertTrue(succeedEndParser instanceof SucceedEndStateParser, "Parser
should be SucceedEndStateParser");
+
+ StateParser subStateMachineParser =
StateParserFactory.getStateParser(StateType.SUB_STATE_MACHINE);
+ assertNotNull(subStateMachineParser, "SubStateMachineParser should not
be null");
+ assertTrue(subStateMachineParser instanceof SubStateMachineParser,
"Parser should be SubStateMachineParser");
+
+ StateParser subCompensationParser =
StateParserFactory.getStateParser(StateType.SUB_MACHINE_COMPENSATION);
+ assertNotNull(subCompensationParser,
"CompensateSubStateMachineStateParser should not be null");
+ assertTrue(
+ subCompensationParser instanceof
CompensateSubStateMachineStateParser,
+ "Parser should be CompensateSubStateMachineStateParser");
+
+ StateParser scriptTaskParser =
StateParserFactory.getStateParser(StateType.SCRIPT_TASK);
+ assertNotNull(scriptTaskParser, "ScriptTaskStateParser should not be
null");
+ assertTrue(scriptTaskParser instanceof ScriptTaskStateParser, "Parser
should be ScriptTaskStateParser");
+ }
+}
diff --git
a/saga/seata-saga-statelang/src/test/java/org/apache/seata/saga/statelang/parser/utils/DesignerJsonTransformerTest.java
b/saga/seata-saga-statelang/src/test/java/org/apache/seata/saga/statelang/parser/utils/DesignerJsonTransformerTest.java
new file mode 100644
index 0000000000..677fe19c29
--- /dev/null
+++
b/saga/seata-saga-statelang/src/test/java/org/apache/seata/saga/statelang/parser/utils/DesignerJsonTransformerTest.java
@@ -0,0 +1,73 @@
+/*
+ * 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.saga.statelang.parser.utils;
+
+import org.apache.seata.common.exception.FrameworkException;
+import org.apache.seata.saga.statelang.parser.JsonParser;
+import org.apache.seata.saga.statelang.parser.JsonParserFactory;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.Map;
+
+public class DesignerJsonTransformerTest {
+
+ @Test
+ public void testToStandardJsonWithDesignerJson() throws IOException {
+ InputStream inputStream =
getInputStreamByPath("statelang/simple_statemachine.json");
+ String json = IOUtils.toString(inputStream, "UTF-8");
+ JsonParser jsonParser = JsonParserFactory.getJsonParser("jackson");
+ Map<String, Object> designerJson = jsonParser.parse(json, Map.class,
true);
+
+ Map<String, Object> standardJson =
DesignerJsonTransformer.toStandardJson(designerJson);
+
+ Assertions.assertNotNull(standardJson);
+ Assertions.assertTrue(standardJson.containsKey("States"));
+ Assertions.assertTrue(standardJson.containsKey("StartState"));
+ Assertions.assertEquals("simpleTestStateMachine",
standardJson.get("Name"));
+ }
+
+ @Test
+ public void testToStandardJsonWithNonDesignerJson() {
+ Map<String, Object> normalJson = new HashMap<>();
+ normalJson.put("Name", "normal");
+ normalJson.put("States", new HashMap<>());
+
+ Map<String, Object> result =
DesignerJsonTransformer.toStandardJson(normalJson);
+
+ Assertions.assertSame(normalJson, result);
+ }
+
+ @Test
+ public void testGenerateTracingGraphJsonWithNullInstance() {
+ JsonParser parser = JsonParserFactory.getJsonParser("jackson");
+ FrameworkException e = Assertions.assertThrows(
+ FrameworkException.class, () ->
DesignerJsonTransformer.generateTracingGraphJson(null, parser));
+ Assertions.assertEquals("StateMachineInstance is not exits",
e.getMessage());
+ }
+
+ private InputStream getInputStreamByPath(String path) {
+ ClassLoader classLoader =
Thread.currentThread().getContextClassLoader();
+ if (classLoader == null) {
+ classLoader = Thread.currentThread().getClass().getClassLoader();
+ }
+ return classLoader.getResourceAsStream(path);
+ }
+}
diff --git
a/saga/seata-saga-statelang/src/test/java/org/apache/seata/saga/statelang/parser/utils/IOUtilsTest.java
b/saga/seata-saga-statelang/src/test/java/org/apache/seata/saga/statelang/parser/utils/IOUtilsTest.java
new file mode 100644
index 0000000000..925c56e9dd
--- /dev/null
+++
b/saga/seata-saga-statelang/src/test/java/org/apache/seata/saga/statelang/parser/utils/IOUtilsTest.java
@@ -0,0 +1,68 @@
+/*
+ * 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.saga.statelang.parser.utils;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.nio.charset.StandardCharsets;
+
+public class IOUtilsTest {
+
+ @Test
+ public void testToStringWithUtf8() throws IOException {
+ String content = "test the method of toString";
+ ByteArrayInputStream inputStream = new
ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8));
+
+ String result = IOUtils.toString(inputStream, "UTF-8");
+
+ Assertions.assertEquals(content, result);
+ }
+
+ @Test
+ public void testCopyInputStreamToWriter() throws IOException {
+ String content = "Hello World! This is a test for copy method.";
+ ByteArrayInputStream inputStream = new
ByteArrayInputStream(content.getBytes());
+ StringWriter writer = new StringWriter();
+
+ IOUtils.copy(inputStream, writer);
+
+ Assertions.assertEquals(content, writer.toString());
+ }
+
+ @Test
+ public void testCopyReaderToWriter() throws IOException {
+ String content = "Test copy from Reader to Writer";
+ ByteArrayInputStream inputStream = new
ByteArrayInputStream(content.getBytes());
+ StringWriter writer = new StringWriter();
+
+ int count = IOUtils.copy(new java.io.InputStreamReader(inputStream),
writer);
+
+ Assertions.assertEquals(content.length(), count);
+ Assertions.assertEquals(content, writer.toString());
+ }
+
+ @Test
+ public void testToStringWithEmptyStream() throws IOException {
+ ByteArrayInputStream inputStream = new ByteArrayInputStream(new
byte[0]);
+ String result = IOUtils.toString(inputStream, "UTF-8");
+ Assertions.assertEquals("", result);
+ }
+}
diff --git
a/saga/seata-saga-statelang/src/test/java/org/apache/seata/saga/statelang/validator/RuleFactoryTest.java
b/saga/seata-saga-statelang/src/test/java/org/apache/seata/saga/statelang/validator/RuleFactoryTest.java
new file mode 100644
index 0000000000..f140cefd6c
--- /dev/null
+++
b/saga/seata-saga-statelang/src/test/java/org/apache/seata/saga/statelang/validator/RuleFactoryTest.java
@@ -0,0 +1,44 @@
+/*
+ * 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.saga.statelang.validator;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.util.List;
+
+public class RuleFactoryTest {
+
+ @Test
+ public void testGetRules() {
+ List<Rule> rules = RuleFactory.getRules();
+ Assertions.assertNotNull(rules);
+ Assertions.assertFalse(rules.isEmpty(), "Rule list should not be
empty");
+
+ for (Rule rule : rules) {
+ Assertions.assertNotNull(rule.getName(), "Rule name should not be
null");
+ Assertions.assertFalse(rule.getName().trim().isEmpty(), "Rule name
should not be empty");
+ }
+ }
+
+ @Test
+ public void testRuleFactorySingleton() {
+ List<Rule> rules1 = RuleFactory.getRules();
+ List<Rule> rules2 = RuleFactory.getRules();
+ Assertions.assertSame(rules1, rules2, "Rule list should be singleton");
+ }
+}
diff --git
a/saga/seata-saga-statelang/src/test/java/org/apache/seata/saga/statelang/validator/ValidationExceptionTest.java
b/saga/seata-saga-statelang/src/test/java/org/apache/seata/saga/statelang/validator/ValidationExceptionTest.java
new file mode 100644
index 0000000000..70b4d90bfe
--- /dev/null
+++
b/saga/seata-saga-statelang/src/test/java/org/apache/seata/saga/statelang/validator/ValidationExceptionTest.java
@@ -0,0 +1,78 @@
+/*
+ * 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.saga.statelang.validator;
+
+import org.apache.seata.saga.statelang.domain.StateMachine;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+public class ValidationExceptionTest {
+
+ @Test
+ public void testExceptionWithMessage() {
+ Rule mockRule = new TestRule("TestRule", "Please check the state
machine definition");
+ ValidationException exception = new ValidationException(mockRule,
"Invalid state configuration");
+
+ Assertions.assertEquals(
+ "Rule [TestRule]: Invalid state configuration, hints: Please
check the state machine definition",
+ exception.getMessage());
+ }
+
+ @Test
+ public void testExceptionWithCause() {
+ Rule mockRule = new TestRule("ErrorRule", null);
+ Throwable cause = new IllegalArgumentException("Invalid parameter");
+ ValidationException exception = new ValidationException(mockRule,
"Processing failed", cause);
+
+ Assertions.assertEquals("Rule [ErrorRule]: Processing failed",
exception.getMessage());
+ Assertions.assertSame(cause, exception.getCause());
+ Assertions.assertEquals("Invalid parameter",
exception.getCause().getMessage());
+ }
+
+ @Test
+ public void testExceptionWithoutHint() {
+ Rule mockRule = new TestRule("NoHintRule", null);
+ ValidationException exception = new ValidationException(mockRule, "No
hint provided");
+
+ Assertions.assertEquals("Rule [NoHintRule]: No hint provided",
exception.getMessage());
+ }
+
+ private static class TestRule implements Rule {
+ private final String name;
+ private final String hint;
+
+ public TestRule(String name, String hint) {
+ this.name = name;
+ this.hint = hint;
+ }
+
+ @Override
+ public boolean validate(StateMachine stateMachine) {
+ return false;
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public String getHint() {
+ return hint;
+ }
+ }
+}
diff --git
a/saga/seata-saga-statelang/src/test/java/org/apache/seata/saga/statelang/validator/impl/FiniteTerminationRuleTest.java
b/saga/seata-saga-statelang/src/test/java/org/apache/seata/saga/statelang/validator/impl/FiniteTerminationRuleTest.java
new file mode 100644
index 0000000000..0d2857d3f0
--- /dev/null
+++
b/saga/seata-saga-statelang/src/test/java/org/apache/seata/saga/statelang/validator/impl/FiniteTerminationRuleTest.java
@@ -0,0 +1,78 @@
+/*
+ * 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.saga.statelang.validator.impl;
+
+import org.apache.seata.saga.statelang.parser.StateMachineParserFactory;
+import org.apache.seata.saga.statelang.parser.utils.IOUtils;
+import org.apache.seata.saga.statelang.validator.ValidationException;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Unit tests for {@link FiniteTerminationRule}
+ */
+public class FiniteTerminationRuleTest {
+
+ @Test
+ public void testValidFiniteTermination() throws IOException {
+ // Test state machine with valid termination (no infinite loops)
+ InputStream inputStream =
getInputStreamByPath("statelang/simple_statemachine.json");
+ String json = IOUtils.toString(inputStream, "UTF-8");
+
+ Assertions.assertDoesNotThrow(() -> {
+ StateMachineParserFactory.getStateMachineParser(null).parse(json);
+ });
+ }
+
+ @Test
+ public void testSingleStateInfiniteLoop() throws IOException {
+ // Test state machine with single state looping (A -> A)
+ InputStream inputStream =
getInputStreamByPath("statelang/simple_statemachine_with_single_infinite_loop.json");
+ String json = IOUtils.toString(inputStream, "UTF-8");
+
+ Throwable e = Assertions.assertThrows(ValidationException.class, () ->
{
+ StateMachineParserFactory.getStateMachineParser(null).parse(json);
+ });
+ Assertions.assertTrue(
+ e.getMessage().contains("infinite loop") &&
e.getMessage().contains("without outgoing flow to end"));
+ }
+
+ @Test
+ public void testMultiStateInfiniteLoop() throws IOException {
+ // Test state machine with multi-state loop (A -> B -> C -> A)
+ InputStream inputStream =
+
getInputStreamByPath("statelang/simple_statemachine_with_multiple_infinite_loop.json");
+ String json = IOUtils.toString(inputStream, "UTF-8");
+
+ Throwable e = Assertions.assertThrows(ValidationException.class, () ->
{
+ StateMachineParserFactory.getStateMachineParser(null).parse(json);
+ });
+ Assertions.assertTrue(
+ e.getMessage().contains("infinite loop") &&
e.getMessage().contains("without outgoing flow to end"));
+ }
+
+ private InputStream getInputStreamByPath(String path) {
+ ClassLoader classLoader =
Thread.currentThread().getContextClassLoader();
+ if (classLoader == null) {
+ classLoader = Thread.currentThread().getClass().getClassLoader();
+ }
+ return classLoader.getResourceAsStream(path);
+ }
+}
diff --git
a/saga/seata-saga-statelang/src/test/java/org/apache/seata/saga/statelang/validator/impl/NoRecursiveSubStateMachineRuleTest.java
b/saga/seata-saga-statelang/src/test/java/org/apache/seata/saga/statelang/validator/impl/NoRecursiveSubStateMachineRuleTest.java
new file mode 100644
index 0000000000..c766ab987f
--- /dev/null
+++
b/saga/seata-saga-statelang/src/test/java/org/apache/seata/saga/statelang/validator/impl/NoRecursiveSubStateMachineRuleTest.java
@@ -0,0 +1,49 @@
+/*
+ * 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.saga.statelang.validator.impl;
+
+import org.apache.seata.saga.statelang.parser.StateMachineParserFactory;
+import org.apache.seata.saga.statelang.parser.utils.IOUtils;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Unit tests for {@link NoRecursiveSubStateMachineRule}
+ */
+public class NoRecursiveSubStateMachineRuleTest {
+
+ @Test
+ public void testValidSubStateMachine() throws IOException {
+ InputStream inputStream =
getInputStreamByPath("statelang/simple_statemachine.json");
+ String json = IOUtils.toString(inputStream, "UTF-8");
+
+ Assertions.assertDoesNotThrow(() -> {
+ StateMachineParserFactory.getStateMachineParser(null).parse(json);
+ });
+ }
+
+ private InputStream getInputStreamByPath(String path) {
+ ClassLoader classLoader =
Thread.currentThread().getContextClassLoader();
+ if (classLoader == null) {
+ classLoader = Thread.currentThread().getClass().getClassLoader();
+ }
+ return classLoader.getResourceAsStream(path);
+ }
+}
diff --git
a/saga/seata-saga-statelang/src/test/java/org/apache/seata/saga/statelang/validator/impl/StateNameExistsRuleTest.java
b/saga/seata-saga-statelang/src/test/java/org/apache/seata/saga/statelang/validator/impl/StateNameExistsRuleTest.java
new file mode 100644
index 0000000000..8e7f4ab66a
--- /dev/null
+++
b/saga/seata-saga-statelang/src/test/java/org/apache/seata/saga/statelang/validator/impl/StateNameExistsRuleTest.java
@@ -0,0 +1,50 @@
+/*
+ * 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.saga.statelang.validator.impl;
+
+import org.apache.seata.saga.statelang.parser.StateMachineParserFactory;
+import org.apache.seata.saga.statelang.parser.utils.IOUtils;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Unit tests for {@link StateNameExistsRule}
+ */
+public class StateNameExistsRuleTest {
+
+ @Test
+ public void testAllStateNamesExist() throws IOException {
+ // Test state machine with all subsequent states existing
+ InputStream inputStream =
getInputStreamByPath("statelang/simple_statemachine.json");
+ String json = IOUtils.toString(inputStream, "UTF-8");
+
+ Assertions.assertDoesNotThrow(() -> {
+ StateMachineParserFactory.getStateMachineParser(null).parse(json);
+ });
+ }
+
+ private InputStream getInputStreamByPath(String path) {
+ ClassLoader classLoader =
Thread.currentThread().getContextClassLoader();
+ if (classLoader == null) {
+ classLoader = Thread.currentThread().getClass().getClassLoader();
+ }
+ return classLoader.getResourceAsStream(path);
+ }
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]