removal and better style
This commit is contained in:
@@ -1,6 +1,9 @@
|
||||
package click.kamil.springstatemachineexporter.ast.app;
|
||||
|
||||
import click.kamil.springstatemachineexporter.ast.app.domain.Action;
|
||||
import click.kamil.springstatemachineexporter.ast.app.domain.Guard;
|
||||
import click.kamil.springstatemachineexporter.ast.app.domain.Transition;
|
||||
import click.kamil.springstatemachineexporter.ast.app.domain.TransitionType;
|
||||
import org.eclipse.jdt.core.dom.*;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
@@ -27,7 +30,7 @@ class AstTransitionParserTest {
|
||||
Transition transition = transitions.getFirst();
|
||||
assertThat(transition)
|
||||
.extracting(Transition::getType)
|
||||
.isEqualTo("withExternal");
|
||||
.isEqualTo(TransitionType.EXTERNAL);
|
||||
assertThat(transition.getSourceStates()).containsExactly("CREATED");
|
||||
assertThat(transition.getTargetStates()).containsExactly("PAID");
|
||||
assertThat(transition)
|
||||
@@ -50,10 +53,12 @@ class AstTransitionParserTest {
|
||||
|
||||
assertThat(transitions).hasSize(1);
|
||||
Transition transition = transitions.getFirst();
|
||||
assertThat(transition)
|
||||
.extracting(Transition::getGuard)
|
||||
assertThat(transition.getGuard())
|
||||
.extracting(Guard::expression)
|
||||
.isEqualTo("amount > 0");
|
||||
assertThat(transition.isLambdaGuard()).isFalse();
|
||||
assertThat(transition.getGuard())
|
||||
.extracting(Guard::isLambda)
|
||||
.isEqualTo(false);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -71,8 +76,8 @@ class AstTransitionParserTest {
|
||||
|
||||
assertThat(transitions).hasSize(1);
|
||||
Transition transition = transitions.getFirst();
|
||||
assertThat(transition.getGuard()).contains("context -> context.getExtendedState().getVariables().get(\"amount\") > 0");
|
||||
assertThat(transition.isLambdaGuard()).isTrue();
|
||||
assertThat(transition.getGuard().expression()).contains("context -> context.getExtendedState().getVariables().get(\"amount\") > 0");
|
||||
assertThat(transition.getGuard().isLambda()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -90,8 +95,12 @@ class AstTransitionParserTest {
|
||||
|
||||
assertThat(transitions).hasSize(1);
|
||||
Transition transition = transitions.getFirst();
|
||||
assertThat(transition.getActions()).containsExactly("processPayment()");
|
||||
assertThat(transition.getIsLambdaActions()).containsExactly(false);
|
||||
assertThat(transition.getActions())
|
||||
.extracting(Action::expression)
|
||||
.containsExactly("processPayment()");
|
||||
assertThat(transition.getActions())
|
||||
.extracting(Action::isLambda)
|
||||
.containsExactly(false);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -109,9 +118,12 @@ class AstTransitionParserTest {
|
||||
|
||||
assertThat(transitions).hasSize(1);
|
||||
Transition transition = transitions.getFirst();
|
||||
assertThat(transition.getActions()).hasSize(1);
|
||||
assertThat(transition.getActions().getFirst()).contains("context -> System.out.println(\"Payment processed\")");
|
||||
assertThat(transition.getIsLambdaActions()).containsExactly(true);
|
||||
assertThat(transition.getActions())
|
||||
.extracting(Action::expression)
|
||||
.anyMatch(s -> s.contains("context -> System.out.println(\"Payment processed\")"));
|
||||
assertThat(transition.getActions())
|
||||
.extracting(Action::isLambda)
|
||||
.containsExactly(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -132,7 +144,7 @@ class AstTransitionParserTest {
|
||||
Transition first = transitions.getFirst();
|
||||
assertThat(first)
|
||||
.extracting(Transition::getType)
|
||||
.isEqualTo("withExternal");
|
||||
.isEqualTo(TransitionType.EXTERNAL);
|
||||
assertThat(first.getSourceStates()).containsExactly("CREATED");
|
||||
assertThat(first.getTargetStates()).containsExactly("PAID");
|
||||
assertThat(first)
|
||||
@@ -142,7 +154,7 @@ class AstTransitionParserTest {
|
||||
Transition second = transitions.get(1);
|
||||
assertThat(second)
|
||||
.extracting(Transition::getType)
|
||||
.isEqualTo("withExternal");
|
||||
.isEqualTo(TransitionType.EXTERNAL);
|
||||
assertThat(second.getSourceStates()).containsExactly("PAID");
|
||||
assertThat(second.getTargetStates()).containsExactly("SHIPPED");
|
||||
assertThat(second)
|
||||
@@ -168,11 +180,11 @@ class AstTransitionParserTest {
|
||||
Transition first = transitions.getFirst();
|
||||
assertThat(first)
|
||||
.extracting(Transition::getType)
|
||||
.isEqualTo("withChoice");
|
||||
.isEqualTo(TransitionType.CHOICE);
|
||||
assertThat(first.getSourceStates()).containsExactly("PAID");
|
||||
assertThat(first.getTargetStates()).containsExactly("SHIPPED");
|
||||
assertThat(first)
|
||||
.extracting(Transition::getGuard)
|
||||
assertThat(first.getGuard())
|
||||
.extracting(Guard::expression)
|
||||
.isEqualTo("isExpress()");
|
||||
assertThat(first)
|
||||
.extracting(Transition::getOrder)
|
||||
@@ -181,11 +193,11 @@ class AstTransitionParserTest {
|
||||
Transition second = transitions.get(1);
|
||||
assertThat(second)
|
||||
.extracting(Transition::getType)
|
||||
.isEqualTo("withChoice");
|
||||
.isEqualTo(TransitionType.CHOICE);
|
||||
assertThat(second.getSourceStates()).containsExactly("PAID");
|
||||
assertThat(second.getTargetStates()).containsExactly("CANCELLED");
|
||||
assertThat(second)
|
||||
.extracting(Transition::getGuard)
|
||||
assertThat(second.getGuard())
|
||||
.extracting(Guard::expression)
|
||||
.isEqualTo("isCancelled()");
|
||||
assertThat(second)
|
||||
.extracting(Transition::getOrder)
|
||||
@@ -194,7 +206,7 @@ class AstTransitionParserTest {
|
||||
Transition third = transitions.get(2);
|
||||
assertThat(third)
|
||||
.extracting(Transition::getType)
|
||||
.isEqualTo("withChoice");
|
||||
.isEqualTo(TransitionType.CHOICE);
|
||||
assertThat(third.getSourceStates()).containsExactly("PAID");
|
||||
assertThat(third.getTargetStates()).containsExactly("PENDING");
|
||||
assertThat(third)
|
||||
@@ -218,13 +230,13 @@ class AstTransitionParserTest {
|
||||
assertThat(transitions).hasSize(3);
|
||||
assertThat(transitions.getFirst())
|
||||
.extracting(Transition::getType)
|
||||
.isEqualTo("withJunction");
|
||||
.isEqualTo(TransitionType.JUNCTION);
|
||||
assertThat(transitions.get(1))
|
||||
.extracting(Transition::getType)
|
||||
.isEqualTo("withJunction");
|
||||
.isEqualTo(TransitionType.JUNCTION);
|
||||
assertThat(transitions.get(2))
|
||||
.extracting(Transition::getType)
|
||||
.isEqualTo("withJunction");
|
||||
.isEqualTo(TransitionType.JUNCTION);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -244,12 +256,14 @@ class AstTransitionParserTest {
|
||||
Transition transition = transitions.getFirst();
|
||||
assertThat(transition)
|
||||
.extracting(Transition::getType)
|
||||
.isEqualTo("withInternal");
|
||||
.isEqualTo(TransitionType.INTERNAL);
|
||||
assertThat(transition.getSourceStates()).containsExactly("PAID");
|
||||
assertThat(transition)
|
||||
.extracting(Transition::getEvent)
|
||||
.isEqualTo("UPDATE");
|
||||
assertThat(transition.getActions()).containsExactly("updateStatus()");
|
||||
assertThat(transition.getActions())
|
||||
.extracting(Action::expression)
|
||||
.containsExactly("updateStatus()");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -269,7 +283,7 @@ class AstTransitionParserTest {
|
||||
Transition transition = transitions.getFirst();
|
||||
assertThat(transition)
|
||||
.extracting(Transition::getType)
|
||||
.isEqualTo("withLocal");
|
||||
.isEqualTo(TransitionType.LOCAL);
|
||||
assertThat(transition.getSourceStates()).containsExactly("PAID");
|
||||
assertThat(transition.getTargetStates()).containsExactly("PROCESSING");
|
||||
assertThat(transition)
|
||||
@@ -294,7 +308,7 @@ class AstTransitionParserTest {
|
||||
Transition transition = transitions.getFirst();
|
||||
assertThat(transition)
|
||||
.extracting(Transition::getType)
|
||||
.isEqualTo("withFork");
|
||||
.isEqualTo(TransitionType.FORK);
|
||||
assertThat(transition.getSourceStates()).containsExactly("PAID");
|
||||
assertThat(transition.getTargetStates()).containsExactlyInAnyOrder("SHIPPING", "BILLING");
|
||||
}
|
||||
@@ -316,7 +330,7 @@ class AstTransitionParserTest {
|
||||
Transition transition = transitions.getFirst();
|
||||
assertThat(transition)
|
||||
.extracting(Transition::getType)
|
||||
.isEqualTo("withJoin");
|
||||
.isEqualTo(TransitionType.JOIN);
|
||||
assertThat(transition.getSourceStates()).containsExactlyInAnyOrder("SHIPPING", "BILLING");
|
||||
assertThat(transition.getTargetStates()).containsExactly("COMPLETED");
|
||||
}
|
||||
@@ -416,9 +430,15 @@ class AstTransitionParserTest {
|
||||
List<Transition> transitions = AstTransitionParser.parseTransitions(method);
|
||||
|
||||
assertThat(transitions).hasSize(3);
|
||||
assertThat(transitions.getFirst().getActions()).containsExactly("shipExpress()");
|
||||
assertThat(transitions.get(1).getActions()).containsExactly("cancelOrder()");
|
||||
assertThat(transitions.get(2).getActions()).containsExactly("defaultAction()");
|
||||
assertThat(transitions.getFirst().getActions())
|
||||
.extracting(Action::expression)
|
||||
.containsExactly("shipExpress()");
|
||||
assertThat(transitions.get(1).getActions())
|
||||
.extracting(Action::expression)
|
||||
.containsExactly("cancelOrder()");
|
||||
assertThat(transitions.get(2).getActions())
|
||||
.extracting(Action::expression)
|
||||
.containsExactly("defaultAction()");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -436,8 +456,10 @@ class AstTransitionParserTest {
|
||||
|
||||
assertThat(transitions).hasSize(1);
|
||||
Transition transition = transitions.getFirst();
|
||||
assertThat(transition.isLambdaGuard()).isTrue();
|
||||
assertThat(transition.getIsLambdaActions()).containsExactly(true);
|
||||
assertThat(transition.getGuard().isLambda()).isTrue();
|
||||
assertThat(transition.getActions())
|
||||
.extracting(Action::isLambda)
|
||||
.containsExactly(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -465,7 +487,9 @@ class AstTransitionParserTest {
|
||||
|
||||
assertThat(transitions).hasSize(1);
|
||||
Transition transition = transitions.getFirst();
|
||||
assertThat(transition.getIsLambdaActions()).containsExactly(true);
|
||||
assertThat(transition.getActions())
|
||||
.extracting(Action::isLambda)
|
||||
.containsExactly(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -272,6 +272,172 @@ class StateMachineAggregatorTest {
|
||||
assertThat(transitions).anyMatch(t -> t.getEvent().equals("E2"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldIgnoreStatesInsideFork() throws IOException {
|
||||
String source = """
|
||||
package com.example;
|
||||
import org.springframework.statemachine.config.builders.StateMachineStateConfigurer;
|
||||
public class ForkStateConfig extends StateMachineConfigurerAdapter {
|
||||
@Override
|
||||
public void configure(StateMachineStateConfigurer states) throws Exception {
|
||||
states
|
||||
.withStates()
|
||||
.initial("CREATED")
|
||||
.fork("PROCESSING")
|
||||
.and()
|
||||
.withStates()
|
||||
.parent("PROCESSING")
|
||||
.initial("SHIPPING")
|
||||
.initial("BILLING")
|
||||
.end("COMPLETED");
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
writeClass("ForkStateConfig", source);
|
||||
context.scan(tempDir);
|
||||
|
||||
TypeDeclaration td = context.getTypeDeclaration("ForkStateConfig");
|
||||
aggregator.aggregateStates(td);
|
||||
|
||||
assertThat(aggregator.getInitialStates()).containsExactly("CREATED");
|
||||
assertThat(aggregator.getEndStates()).containsExactly("COMPLETED");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldHandleNestedForkAndRegion() throws IOException {
|
||||
String source = """
|
||||
package com.example;
|
||||
import org.springframework.statemachine.config.builders.StateMachineStateConfigurer;
|
||||
public class ComplexStateConfig extends StateMachineConfigurerAdapter {
|
||||
@Override
|
||||
public void configure(StateMachineStateConfigurer states) throws Exception {
|
||||
states
|
||||
.withStates()
|
||||
.initial("CREATED")
|
||||
.fork("PROCESSING")
|
||||
.and()
|
||||
.withStates()
|
||||
.parent("PROCESSING")
|
||||
.region("SHIPPING")
|
||||
.and()
|
||||
.withStates()
|
||||
.parent("SHIPPING")
|
||||
.initial("PREPARING")
|
||||
.end("SHIPPED")
|
||||
.and()
|
||||
.withStates()
|
||||
.parent("PROCESSING")
|
||||
.region("BILLING")
|
||||
.and()
|
||||
.withStates()
|
||||
.parent("BILLING")
|
||||
.initial("CALCULATING")
|
||||
.end("BILLED")
|
||||
.and()
|
||||
.withStates()
|
||||
.parent("PROCESSING")
|
||||
.end("COMPLETED");
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
writeClass("ComplexStateConfig", source);
|
||||
context.scan(tempDir);
|
||||
|
||||
TypeDeclaration td = context.getTypeDeclaration("ComplexStateConfig");
|
||||
aggregator.aggregateStates(td);
|
||||
|
||||
assertThat(aggregator.getInitialStates()).containsExactly("CREATED");
|
||||
assertThat(aggregator.getEndStates()).containsExactlyInAnyOrder("COMPLETED", "SHIPPED", "BILLED");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldFollowMethodCallsInOtherClasses() throws IOException {
|
||||
String otherClassSource = """
|
||||
package com.example;
|
||||
public class ExternalTransitions {
|
||||
public static void addCommon(StateMachineTransitionConfigurer transitions) {
|
||||
transitions.withExternal().source("S1").target("S2").event("EXT_E1");
|
||||
}
|
||||
}
|
||||
""";
|
||||
String mainSource = """
|
||||
package com.example;
|
||||
public class MainConfig extends StateMachineConfigurerAdapter {
|
||||
public void configure(StateMachineTransitionConfigurer transitions) throws Exception {
|
||||
ExternalTransitions.addCommon(transitions);
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
writeClass("ExternalTransitions", otherClassSource);
|
||||
writeClass("MainConfig", mainSource);
|
||||
context.scan(tempDir);
|
||||
|
||||
TypeDeclaration td = context.getTypeDeclaration("MainConfig");
|
||||
List<Transition> transitions = aggregator.aggregateTransitions(td);
|
||||
|
||||
// Currently StateMachineAggregator only searches within the hierarchy of the searchStartClass.
|
||||
// It might not find ExternalTransitions.addCommon unless it's static or we handle imports.
|
||||
// Let's see if it works or if we need to improve the aggregator.
|
||||
assertThat(transitions).hasSize(1);
|
||||
assertThat(transitions.get(0).getEvent()).isEqualTo("EXT_E1");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldHandleRecursionSafety() throws IOException {
|
||||
String source = """
|
||||
package com.example;
|
||||
public class RecursiveConfig extends StateMachineConfigurerAdapter {
|
||||
public void configure(StateMachineTransitionConfigurer transitions) throws Exception {
|
||||
transitions.withExternal().source("S1").target("S2").event("E1");
|
||||
recursiveMethod(transitions);
|
||||
}
|
||||
private void recursiveMethod(StateMachineTransitionConfigurer transitions) {
|
||||
transitions.withExternal().source("S2").target("S3").event("E2");
|
||||
recursiveMethod(transitions);
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
writeClass("RecursiveConfig", source);
|
||||
context.scan(tempDir);
|
||||
|
||||
TypeDeclaration td = context.getTypeDeclaration("RecursiveConfig");
|
||||
List<Transition> transitions = aggregator.aggregateTransitions(td);
|
||||
|
||||
// Should not crash and should find both transitions
|
||||
assertThat(transitions).hasSize(2);
|
||||
assertThat(transitions).anyMatch(t -> t.getEvent().equals("E1"));
|
||||
assertThat(transitions).anyMatch(t -> t.getEvent().equals("E2"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldParseTransitionsInsideTryBlock() throws IOException {
|
||||
String source = """
|
||||
package com.example;
|
||||
public class TryConfig extends StateMachineConfigurerAdapter {
|
||||
public void configure(StateMachineTransitionConfigurer transitions) throws Exception {
|
||||
try {
|
||||
transitions.withExternal().source("S1").target("S2").event("TRY_E1");
|
||||
} catch (Exception e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
writeClass("TryConfig", source);
|
||||
context.scan(tempDir);
|
||||
|
||||
TypeDeclaration td = context.getTypeDeclaration("TryConfig");
|
||||
List<Transition> transitions = aggregator.aggregateTransitions(td);
|
||||
|
||||
assertThat(transitions).hasSize(1);
|
||||
assertThat(transitions.get(0).getEvent()).isEqualTo("TRY_E1");
|
||||
}
|
||||
|
||||
private void writeClass(String name, String source) throws IOException {
|
||||
Files.writeString(tempDir.resolve(name + ".java"), source);
|
||||
}
|
||||
|
||||
@@ -1,392 +0,0 @@
|
||||
package click.kamil.springstatemachineexporter.ast.app;
|
||||
|
||||
import org.eclipse.jdt.core.dom.CompilationUnit;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class StateMachineStateConfigurationMethodFinderTest {
|
||||
|
||||
@Test
|
||||
void shouldParseInitialStates() {
|
||||
String source = """
|
||||
public class OrderStateMachineConfig extends EnumStateMachineConfigurerAdapter<OrderStates, OrderEvents> {
|
||||
@Override
|
||||
public void configure(StateMachineStateConfigurer<OrderStates, OrderEvents> states) throws Exception {
|
||||
states
|
||||
.withStates()
|
||||
.initial(OrderStates.CREATED)
|
||||
.end(OrderStates.COMPLETED);
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
CompilationUnit cu = StateMachineStateConfigurationMethodFinder.parse(source);
|
||||
StateMachineStateConfigurationMethodFinder.StateConfigurerVisitor visitor =
|
||||
new StateMachineStateConfigurationMethodFinder.StateConfigurerVisitor();
|
||||
cu.accept(visitor);
|
||||
|
||||
assertThat(visitor.getInitialStates()).containsExactly("OrderStates.CREATED");
|
||||
assertThat(visitor.getEndStates()).containsExactly("OrderStates.COMPLETED");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldParseMultipleInitialStates() {
|
||||
String source = """
|
||||
public class OrderStateMachineConfig extends EnumStateMachineConfigurerAdapter<OrderStates, OrderEvents> {
|
||||
@Override
|
||||
public void configure(StateMachineStateConfigurer<OrderStates, OrderEvents> states) throws Exception {
|
||||
states
|
||||
.withStates()
|
||||
.initial(OrderStates.CREATED)
|
||||
.initial(OrderStates.PENDING)
|
||||
.end(OrderStates.COMPLETED)
|
||||
.end(OrderStates.CANCELLED);
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
CompilationUnit cu = StateMachineStateConfigurationMethodFinder.parse(source);
|
||||
StateMachineStateConfigurationMethodFinder.StateConfigurerVisitor visitor =
|
||||
new StateMachineStateConfigurationMethodFinder.StateConfigurerVisitor();
|
||||
cu.accept(visitor);
|
||||
|
||||
assertThat(visitor.getInitialStates()).containsExactlyInAnyOrder("OrderStates.CREATED", "OrderStates.PENDING");
|
||||
assertThat(visitor.getEndStates()).containsExactlyInAnyOrder("OrderStates.COMPLETED", "OrderStates.CANCELLED");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldIgnoreStatesInsideFork() {
|
||||
String source = """
|
||||
public class OrderStateMachineConfig extends EnumStateMachineConfigurerAdapter<OrderStates, OrderEvents> {
|
||||
@Override
|
||||
public void configure(StateMachineStateConfigurer<OrderStates, OrderEvents> states) throws Exception {
|
||||
states
|
||||
.withStates()
|
||||
.initial(OrderStates.CREATED)
|
||||
.fork(OrderStates.PROCESSING)
|
||||
.and()
|
||||
.withStates()
|
||||
.parent(OrderStates.PROCESSING)
|
||||
.initial(OrderStates.SHIPPING)
|
||||
.initial(OrderStates.BILLING)
|
||||
.end(OrderStates.COMPLETED);
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
CompilationUnit cu = StateMachineStateConfigurationMethodFinder.parse(source);
|
||||
StateMachineStateConfigurationMethodFinder.StateConfigurerVisitor visitor =
|
||||
new StateMachineStateConfigurationMethodFinder.StateConfigurerVisitor();
|
||||
cu.accept(visitor);
|
||||
|
||||
assertThat(visitor.getInitialStates()).containsExactly("OrderStates.CREATED");
|
||||
assertThat(visitor.getEndStates()).containsExactly("OrderStates.COMPLETED");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldIgnoreStatesInsideRegion() {
|
||||
String source = """
|
||||
public class OrderStateMachineConfig extends EnumStateMachineConfigurerAdapter<OrderStates, OrderEvents> {
|
||||
@Override
|
||||
public void configure(StateMachineStateConfigurer<OrderStates, OrderEvents> states) throws Exception {
|
||||
states
|
||||
.withStates()
|
||||
.initial(OrderStates.CREATED)
|
||||
.region(OrderStates.PROCESSING)
|
||||
.and()
|
||||
.withStates()
|
||||
.parent(OrderStates.PROCESSING)
|
||||
.initial(OrderStates.SHIPPING)
|
||||
.initial(OrderStates.BILLING)
|
||||
.end(OrderStates.COMPLETED);
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
CompilationUnit cu = StateMachineStateConfigurationMethodFinder.parse(source);
|
||||
StateMachineStateConfigurationMethodFinder.StateConfigurerVisitor visitor =
|
||||
new StateMachineStateConfigurationMethodFinder.StateConfigurerVisitor();
|
||||
cu.accept(visitor);
|
||||
|
||||
assertThat(visitor.getInitialStates()).containsExactly("OrderStates.CREATED");
|
||||
assertThat(visitor.getEndStates()).containsExactly("OrderStates.COMPLETED");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldParseStatesWithStringLiterals() {
|
||||
String source = """
|
||||
public class OrderStateMachineConfig extends EnumStateMachineConfigurerAdapter<String, String> {
|
||||
@Override
|
||||
public void configure(StateMachineStateConfigurer<String, String> states) throws Exception {
|
||||
states
|
||||
.withStates()
|
||||
.initial("CREATED")
|
||||
.end("COMPLETED");
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
CompilationUnit cu = StateMachineStateConfigurationMethodFinder.parse(source);
|
||||
StateMachineStateConfigurationMethodFinder.StateConfigurerVisitor visitor =
|
||||
new StateMachineStateConfigurationMethodFinder.StateConfigurerVisitor();
|
||||
cu.accept(visitor);
|
||||
|
||||
assertThat(visitor.getInitialStates()).containsExactly("CREATED");
|
||||
assertThat(visitor.getEndStates()).containsExactly("COMPLETED");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldParseStatesWithQuotedStrings() {
|
||||
String source = """
|
||||
public class OrderStateMachineConfig extends EnumStateMachineConfigurerAdapter<String, String> {
|
||||
@Override
|
||||
public void configure(StateMachineStateConfigurer<String, String> states) throws Exception {
|
||||
states
|
||||
.withStates()
|
||||
.initial("CREATED")
|
||||
.end("COMPLETED");
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
CompilationUnit cu = StateMachineStateConfigurationMethodFinder.parse(source);
|
||||
StateMachineStateConfigurationMethodFinder.StateConfigurerVisitor visitor =
|
||||
new StateMachineStateConfigurationMethodFinder.StateConfigurerVisitor();
|
||||
cu.accept(visitor);
|
||||
|
||||
assertThat(visitor.getInitialStates()).containsExactly("CREATED");
|
||||
assertThat(visitor.getEndStates()).containsExactly("COMPLETED");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldIgnoreNonConfigureMethods() {
|
||||
String source = """
|
||||
public class OrderStateMachineConfig extends EnumStateMachineConfigurerAdapter<OrderStates, OrderEvents> {
|
||||
public void someOtherMethod() {
|
||||
states
|
||||
.withStates()
|
||||
.initial(OrderStates.CREATED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(StateMachineStateConfigurer<OrderStates, OrderEvents> states) throws Exception {
|
||||
states
|
||||
.withStates()
|
||||
.initial(OrderStates.PENDING)
|
||||
.end(OrderStates.COMPLETED);
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
CompilationUnit cu = StateMachineStateConfigurationMethodFinder.parse(source);
|
||||
StateMachineStateConfigurationMethodFinder.StateConfigurerVisitor visitor =
|
||||
new StateMachineStateConfigurationMethodFinder.StateConfigurerVisitor();
|
||||
cu.accept(visitor);
|
||||
|
||||
assertThat(visitor.getInitialStates()).containsExactly("OrderStates.PENDING");
|
||||
assertThat(visitor.getEndStates()).containsExactly("OrderStates.COMPLETED");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldHandleEmptyConfigureMethod() {
|
||||
String source = """
|
||||
public class OrderStateMachineConfig extends EnumStateMachineConfigurerAdapter<OrderStates, OrderEvents> {
|
||||
@Override
|
||||
public void configure(StateMachineStateConfigurer<OrderStates, OrderEvents> states) throws Exception {
|
||||
// Empty method
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
CompilationUnit cu = StateMachineStateConfigurationMethodFinder.parse(source);
|
||||
StateMachineStateConfigurationMethodFinder.StateConfigurerVisitor visitor =
|
||||
new StateMachineStateConfigurationMethodFinder.StateConfigurerVisitor();
|
||||
cu.accept(visitor);
|
||||
|
||||
assertThat(visitor.getInitialStates()).isEmpty();
|
||||
assertThat(visitor.getEndStates()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldHandleConfigureMethodWithoutWithStates() {
|
||||
String source = """
|
||||
public class OrderStateMachineConfig extends EnumStateMachineConfigurerAdapter<OrderStates, OrderEvents> {
|
||||
@Override
|
||||
public void configure(StateMachineStateConfigurer<OrderStates, OrderEvents> states) throws Exception {
|
||||
System.out.println("No state configuration here");
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
CompilationUnit cu = StateMachineStateConfigurationMethodFinder.parse(source);
|
||||
StateMachineStateConfigurationMethodFinder.StateConfigurerVisitor visitor =
|
||||
new StateMachineStateConfigurationMethodFinder.StateConfigurerVisitor();
|
||||
cu.accept(visitor);
|
||||
|
||||
assertThat(visitor.getInitialStates()).isEmpty();
|
||||
assertThat(visitor.getEndStates()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldParseComplexStateConfiguration() {
|
||||
String source = """
|
||||
public class OrderStateMachineConfig extends EnumStateMachineConfigurerAdapter<OrderStates, OrderEvents> {
|
||||
@Override
|
||||
public void configure(StateMachineStateConfigurer<OrderStates, OrderEvents> states) throws Exception {
|
||||
states
|
||||
.withStates()
|
||||
.initial(OrderStates.CREATED)
|
||||
.state(OrderStates.PAID)
|
||||
.state(OrderStates.SHIPPED)
|
||||
.end(OrderStates.COMPLETED)
|
||||
.end(OrderStates.CANCELLED)
|
||||
.and()
|
||||
.withStates()
|
||||
.parent(OrderStates.PAID)
|
||||
.initial(OrderStates.PROCESSING)
|
||||
.end(OrderStates.READY);
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
CompilationUnit cu = StateMachineStateConfigurationMethodFinder.parse(source);
|
||||
StateMachineStateConfigurationMethodFinder.StateConfigurerVisitor visitor =
|
||||
new StateMachineStateConfigurationMethodFinder.StateConfigurerVisitor();
|
||||
cu.accept(visitor);
|
||||
|
||||
assertThat(visitor.getInitialStates()).containsExactlyInAnyOrder("OrderStates.CREATED", "OrderStates.PROCESSING");
|
||||
assertThat(visitor.getEndStates()).containsExactlyInAnyOrder("OrderStates.COMPLETED", "OrderStates.CANCELLED", "OrderStates.READY");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldParseStatesWithMethodCalls() {
|
||||
String source = """
|
||||
public class OrderStateMachineConfig extends EnumStateMachineConfigurerAdapter<OrderStates, OrderEvents> {
|
||||
@Override
|
||||
public void configure(StateMachineStateConfigurer<OrderStates, OrderEvents> states) throws Exception {
|
||||
states
|
||||
.withStates()
|
||||
.initial(getInitialState())
|
||||
.end(getEndState());
|
||||
}
|
||||
|
||||
private OrderStates getInitialState() {
|
||||
return OrderStates.CREATED;
|
||||
}
|
||||
|
||||
private OrderStates getEndState() {
|
||||
return OrderStates.COMPLETED;
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
CompilationUnit cu = StateMachineStateConfigurationMethodFinder.parse(source);
|
||||
StateMachineStateConfigurationMethodFinder.StateConfigurerVisitor visitor =
|
||||
new StateMachineStateConfigurationMethodFinder.StateConfigurerVisitor();
|
||||
cu.accept(visitor);
|
||||
|
||||
assertThat(visitor.getInitialStates()).containsExactly("getInitialState()");
|
||||
assertThat(visitor.getEndStates()).containsExactly("getEndState()");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldHandleMultipleConfigureMethods() {
|
||||
String source = """
|
||||
public class OrderStateMachineConfig extends EnumStateMachineConfigurerAdapter<OrderStates, OrderEvents> {
|
||||
@Override
|
||||
public void configure(StateMachineStateConfigurer<OrderStates, OrderEvents> states) throws Exception {
|
||||
states
|
||||
.withStates()
|
||||
.initial(OrderStates.CREATED)
|
||||
.end(OrderStates.COMPLETED);
|
||||
}
|
||||
|
||||
public void configure(StateMachineTransitionConfigurer<OrderStates, OrderEvents> transitions) throws Exception {
|
||||
// This should be ignored
|
||||
states
|
||||
.withStates()
|
||||
.initial(OrderStates.PENDING);
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
CompilationUnit cu = StateMachineStateConfigurationMethodFinder.parse(source);
|
||||
StateMachineStateConfigurationMethodFinder.StateConfigurerVisitor visitor =
|
||||
new StateMachineStateConfigurationMethodFinder.StateConfigurerVisitor();
|
||||
cu.accept(visitor);
|
||||
|
||||
assertThat(visitor.getInitialStates()).containsExactlyInAnyOrder("OrderStates.CREATED", "OrderStates.PENDING");
|
||||
assertThat(visitor.getEndStates()).containsExactly("OrderStates.COMPLETED");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldParseStatesWithEnumValues() {
|
||||
String source = """
|
||||
public class OrderStateMachineConfig extends EnumStateMachineConfigurerAdapter<OrderStates, OrderEvents> {
|
||||
@Override
|
||||
public void configure(StateMachineStateConfigurer<OrderStates, OrderEvents> states) throws Exception {
|
||||
states
|
||||
.withStates()
|
||||
.initial(OrderStates.CREATED)
|
||||
.state(OrderStates.PAID)
|
||||
.state(OrderStates.SHIPPED)
|
||||
.end(OrderStates.COMPLETED);
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
CompilationUnit cu = StateMachineStateConfigurationMethodFinder.parse(source);
|
||||
StateMachineStateConfigurationMethodFinder.StateConfigurerVisitor visitor =
|
||||
new StateMachineStateConfigurationMethodFinder.StateConfigurerVisitor();
|
||||
cu.accept(visitor);
|
||||
|
||||
assertThat(visitor.getInitialStates()).containsExactly("OrderStates.CREATED");
|
||||
assertThat(visitor.getEndStates()).containsExactly("OrderStates.COMPLETED");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldHandleNestedForkAndRegion() {
|
||||
String source = """
|
||||
public class OrderStateMachineConfig extends EnumStateMachineConfigurerAdapter<OrderStates, OrderEvents> {
|
||||
@Override
|
||||
public void configure(StateMachineStateConfigurer<OrderStates, OrderEvents> states) throws Exception {
|
||||
states
|
||||
.withStates()
|
||||
.initial(OrderStates.CREATED)
|
||||
.fork(OrderStates.PROCESSING)
|
||||
.and()
|
||||
.withStates()
|
||||
.parent(OrderStates.PROCESSING)
|
||||
.region(OrderStates.SHIPPING)
|
||||
.and()
|
||||
.withStates()
|
||||
.parent(OrderStates.SHIPPING)
|
||||
.initial(OrderStates.PREPARING)
|
||||
.end(OrderStates.SHIPPED)
|
||||
.and()
|
||||
.withStates()
|
||||
.parent(OrderStates.PROCESSING)
|
||||
.region(OrderStates.BILLING)
|
||||
.and()
|
||||
.withStates()
|
||||
.parent(OrderStates.BILLING)
|
||||
.initial(OrderStates.CALCULATING)
|
||||
.end(OrderStates.BILLED)
|
||||
.and()
|
||||
.withStates()
|
||||
.parent(OrderStates.PROCESSING)
|
||||
.end(OrderStates.COMPLETED);
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
CompilationUnit cu = StateMachineStateConfigurationMethodFinder.parse(source);
|
||||
StateMachineStateConfigurationMethodFinder.StateConfigurerVisitor visitor =
|
||||
new StateMachineStateConfigurationMethodFinder.StateConfigurerVisitor();
|
||||
cu.accept(visitor);
|
||||
|
||||
assertThat(visitor.getInitialStates()).containsExactly("OrderStates.CREATED");
|
||||
assertThat(visitor.getEndStates()).containsExactlyInAnyOrder("OrderStates.COMPLETED", "OrderStates.SHIPPED", "OrderStates.BILLED");
|
||||
}
|
||||
}
|
||||
@@ -1,344 +0,0 @@
|
||||
package click.kamil.springstatemachineexporter.ast.app;
|
||||
|
||||
import org.eclipse.jdt.core.dom.MethodDeclaration;
|
||||
import org.eclipse.jdt.core.dom.SimpleName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class StateMachineTransitionConfigurationMethodFinderTest {
|
||||
|
||||
@Test
|
||||
void shouldFindConfigureMethodInEnumStateMachineConfigurerAdapter() {
|
||||
String source = """
|
||||
public class OrderStateMachineConfig extends EnumStateMachineConfigurerAdapter<OrderStates, OrderEvents> {
|
||||
@Override
|
||||
public void configure(StateMachineTransitionConfigurer<OrderStates, OrderEvents> transitions) throws Exception {
|
||||
transitions
|
||||
.withExternal()
|
||||
.source(OrderStates.CREATED)
|
||||
.target(OrderStates.PAID)
|
||||
.event(OrderEvents.PAY);
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
StateMachineTransitionConfigurationMethodFinder finder = new StateMachineTransitionConfigurationMethodFinder(source);
|
||||
MethodDeclaration method = finder.findConfigureMethod();
|
||||
|
||||
assertThat(method)
|
||||
.extracting(MethodDeclaration::getName)
|
||||
.extracting(SimpleName::getIdentifier)
|
||||
.isEqualTo("configure");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldFindConfigureMethodInStateMachineConfigurerAdapter() {
|
||||
String source = """
|
||||
public class OrderStateMachineConfig extends StateMachineConfigurerAdapter<String, String> {
|
||||
@Override
|
||||
public void configure(StateMachineTransitionConfigurer<String, String> transitions) throws Exception {
|
||||
transitions
|
||||
.withExternal()
|
||||
.source("CREATED")
|
||||
.target("PAID")
|
||||
.event("PAY");
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
StateMachineTransitionConfigurationMethodFinder finder = new StateMachineTransitionConfigurationMethodFinder(source);
|
||||
MethodDeclaration method = finder.findConfigureMethod();
|
||||
|
||||
assertThat(method)
|
||||
.extracting(MethodDeclaration::getName)
|
||||
.extracting(SimpleName::getIdentifier)
|
||||
.isEqualTo("configure");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnNull_whenClassDoesNotExtendStateMachineConfigurer() {
|
||||
String source = """
|
||||
public class OrderStateMachineConfig {
|
||||
public void configure(StateMachineTransitionConfigurer<String, String> transitions) throws Exception {
|
||||
// This should not be found
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
StateMachineTransitionConfigurationMethodFinder finder = new StateMachineTransitionConfigurationMethodFinder(source);
|
||||
MethodDeclaration method = finder.findConfigureMethod();
|
||||
|
||||
assertThat(method).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnNull_whenClassIsInterface() {
|
||||
String source = """
|
||||
public interface OrderStateMachineConfig extends EnumStateMachineConfigurerAdapter<OrderStates, OrderEvents> {
|
||||
void configure(StateMachineTransitionConfigurer<OrderStates, OrderEvents> transitions) throws Exception;
|
||||
}
|
||||
""";
|
||||
|
||||
StateMachineTransitionConfigurationMethodFinder finder = new StateMachineTransitionConfigurationMethodFinder(source);
|
||||
MethodDeclaration method = finder.findConfigureMethod();
|
||||
|
||||
assertThat(method).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnNull_whenConfigureMethodIsNotPublic() {
|
||||
String source = """
|
||||
public class OrderStateMachineConfig extends EnumStateMachineConfigurerAdapter<OrderStates, OrderEvents> {
|
||||
@Override
|
||||
protected void configure(StateMachineTransitionConfigurer<OrderStates, OrderEvents> transitions) throws Exception {
|
||||
// This should not be found
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
StateMachineTransitionConfigurationMethodFinder finder = new StateMachineTransitionConfigurationMethodFinder(source);
|
||||
MethodDeclaration method = finder.findConfigureMethod();
|
||||
|
||||
assertThat(method).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnNull_whenConfigureMethodDoesNotReturnVoid() {
|
||||
String source = """
|
||||
public class OrderStateMachineConfig extends EnumStateMachineConfigurerAdapter<OrderStates, OrderEvents> {
|
||||
@Override
|
||||
public String configure(StateMachineTransitionConfigurer<OrderStates, OrderEvents> transitions) throws Exception {
|
||||
// This should not be found
|
||||
return "result";
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
StateMachineTransitionConfigurationMethodFinder finder = new StateMachineTransitionConfigurationMethodFinder(source);
|
||||
MethodDeclaration method = finder.findConfigureMethod();
|
||||
|
||||
assertThat(method).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnNull_whenConfigureMethodHasWrongNumberOfParameters() {
|
||||
String source = """
|
||||
public class OrderStateMachineConfig extends EnumStateMachineConfigurerAdapter<OrderStates, OrderEvents> {
|
||||
@Override
|
||||
public void configure() throws Exception {
|
||||
// This should not be found
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
StateMachineTransitionConfigurationMethodFinder finder = new StateMachineTransitionConfigurationMethodFinder(source);
|
||||
MethodDeclaration method = finder.findConfigureMethod();
|
||||
|
||||
assertThat(method).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnNull_whenConfigureMethodParameterIsNotStateMachineTransitionConfigurer() {
|
||||
String source = """
|
||||
public class OrderStateMachineConfig extends EnumStateMachineConfigurerAdapter<OrderStates, OrderEvents> {
|
||||
@Override
|
||||
public void configure(StateMachineStateConfigurer<OrderStates, OrderEvents> states) throws Exception {
|
||||
// This should not be found
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
StateMachineTransitionConfigurationMethodFinder finder = new StateMachineTransitionConfigurationMethodFinder(source);
|
||||
MethodDeclaration method = finder.findConfigureMethod();
|
||||
|
||||
assertThat(method).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnNull_whenConfigureMethodDoesNotThrowException() {
|
||||
String source = """
|
||||
public class OrderStateMachineConfig extends EnumStateMachineConfigurerAdapter<OrderStates, OrderEvents> {
|
||||
@Override
|
||||
public void configure(StateMachineTransitionConfigurer<OrderStates, OrderEvents> transitions) {
|
||||
// This should not be found
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
StateMachineTransitionConfigurationMethodFinder finder = new StateMachineTransitionConfigurationMethodFinder(source);
|
||||
MethodDeclaration method = finder.findConfigureMethod();
|
||||
|
||||
assertThat(method).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnNull_whenConfigureMethodThrowsDifferentException() {
|
||||
String source = """
|
||||
public class OrderStateMachineConfig extends EnumStateMachineConfigurerAdapter<OrderStates, OrderEvents> {
|
||||
@Override
|
||||
public void configure(StateMachineTransitionConfigurer<OrderStates, OrderEvents> transitions) throws RuntimeException {
|
||||
// This should not be found
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
StateMachineTransitionConfigurationMethodFinder finder = new StateMachineTransitionConfigurationMethodFinder(source);
|
||||
MethodDeclaration method = finder.findConfigureMethod();
|
||||
|
||||
assertThat(method).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldFindConfigureMethodWithMultipleExceptions() {
|
||||
String source = """
|
||||
public class OrderStateMachineConfig extends EnumStateMachineConfigurerAdapter<OrderStates, OrderEvents> {
|
||||
@Override
|
||||
public void configure(StateMachineTransitionConfigurer<OrderStates, OrderEvents> transitions) throws Exception, RuntimeException {
|
||||
transitions
|
||||
.withExternal()
|
||||
.source(OrderStates.CREATED)
|
||||
.target(OrderStates.PAID)
|
||||
.event(OrderEvents.PAY);
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
StateMachineTransitionConfigurationMethodFinder finder = new StateMachineTransitionConfigurationMethodFinder(source);
|
||||
MethodDeclaration method = finder.findConfigureMethod();
|
||||
|
||||
assertThat(method)
|
||||
.extracting(MethodDeclaration::getName)
|
||||
.extracting(SimpleName::getIdentifier)
|
||||
.isEqualTo("configure");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldFindConfigureMethodWithParameterizedType() {
|
||||
String source = """
|
||||
public class OrderStateMachineConfig extends EnumStateMachineConfigurerAdapter<OrderStates, OrderEvents> {
|
||||
@Override
|
||||
public void configure(StateMachineTransitionConfigurer<OrderStates, OrderEvents> transitions) throws Exception {
|
||||
transitions
|
||||
.withExternal()
|
||||
.source(OrderStates.CREATED)
|
||||
.target(OrderStates.PAID)
|
||||
.event(OrderEvents.PAY);
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
StateMachineTransitionConfigurationMethodFinder finder = new StateMachineTransitionConfigurationMethodFinder(source);
|
||||
MethodDeclaration method = finder.findConfigureMethod();
|
||||
|
||||
assertThat(method)
|
||||
.extracting(MethodDeclaration::getName)
|
||||
.extracting(SimpleName::getIdentifier)
|
||||
.isEqualTo("configure");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnNull_whenMultipleConfigureMethodsExist() {
|
||||
String source = """
|
||||
public class OrderStateMachineConfig extends EnumStateMachineConfigurerAdapter<OrderStates, OrderEvents> {
|
||||
@Override
|
||||
public void configure(StateMachineStateConfigurer<OrderStates, OrderEvents> states) throws Exception {
|
||||
// State configuration
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(StateMachineTransitionConfigurer<OrderStates, OrderEvents> transitions) throws Exception {
|
||||
// Transition configuration
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
StateMachineTransitionConfigurationMethodFinder finder = new StateMachineTransitionConfigurationMethodFinder(source);
|
||||
MethodDeclaration method = finder.findConfigureMethod();
|
||||
|
||||
assertThat(method)
|
||||
.extracting(MethodDeclaration::getName)
|
||||
.extracting(SimpleName::getIdentifier)
|
||||
.isEqualTo("configure");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldHandleClassWithNoMethods() {
|
||||
String source = """
|
||||
public class OrderStateMachineConfig extends EnumStateMachineConfigurerAdapter<OrderStates, OrderEvents> {
|
||||
// No methods
|
||||
}
|
||||
""";
|
||||
|
||||
StateMachineTransitionConfigurationMethodFinder finder = new StateMachineTransitionConfigurationMethodFinder(source);
|
||||
MethodDeclaration method = finder.findConfigureMethod();
|
||||
|
||||
assertThat(method).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldHandleClassWithOtherMethods() {
|
||||
String source = """
|
||||
public class OrderStateMachineConfig extends EnumStateMachineConfigurerAdapter<OrderStates, OrderEvents> {
|
||||
public void someOtherMethod() {
|
||||
// This should be ignored
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(StateMachineTransitionConfigurer<OrderStates, OrderEvents> transitions) throws Exception {
|
||||
// This should be found
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
StateMachineTransitionConfigurationMethodFinder finder = new StateMachineTransitionConfigurationMethodFinder(source);
|
||||
MethodDeclaration method = finder.findConfigureMethod();
|
||||
|
||||
assertThat(method)
|
||||
.extracting(MethodDeclaration::getName)
|
||||
.extracting(SimpleName::getIdentifier)
|
||||
.isEqualTo("configure");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldHandleClassWithGenericParameters() {
|
||||
String source = """
|
||||
public class OrderStateMachineConfig<S, E> extends EnumStateMachineConfigurerAdapter<S, E> {
|
||||
@Override
|
||||
public void configure(StateMachineTransitionConfigurer<S, E> transitions) throws Exception {
|
||||
// Generic configuration
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
StateMachineTransitionConfigurationMethodFinder finder = new StateMachineTransitionConfigurationMethodFinder(source);
|
||||
MethodDeclaration method = finder.findConfigureMethod();
|
||||
|
||||
assertThat(method)
|
||||
.extracting(MethodDeclaration::getName)
|
||||
.extracting(SimpleName::getIdentifier)
|
||||
.isEqualTo("configure");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldHandleClassWithAnnotations() {
|
||||
String source = """
|
||||
@Configuration
|
||||
public class OrderStateMachineConfig extends EnumStateMachineConfigurerAdapter<OrderStates, OrderEvents> {
|
||||
@Override
|
||||
@Bean
|
||||
public void configure(StateMachineTransitionConfigurer<OrderStates, OrderEvents> transitions) throws Exception {
|
||||
// Configuration with annotations
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
StateMachineTransitionConfigurationMethodFinder finder = new StateMachineTransitionConfigurationMethodFinder(source);
|
||||
MethodDeclaration method = finder.findConfigureMethod();
|
||||
|
||||
assertThat(method)
|
||||
.extracting(MethodDeclaration::getName)
|
||||
.extracting(SimpleName::getIdentifier)
|
||||
.isEqualTo("configure");
|
||||
}
|
||||
}
|
||||
@@ -97,4 +97,53 @@ class CodebaseContextTest {
|
||||
assertThat(entryPoints).hasSize(1);
|
||||
assertThat(entryPoints.get(0).getName().getIdentifier()).isEqualTo("LeafConfig");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldFindBeanMethodsReturningStateMachine() throws IOException {
|
||||
String source = """
|
||||
package com.example;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.statemachine.StateMachine;
|
||||
public class MyApp {
|
||||
@Bean
|
||||
public StateMachine<String, String> stateMachine() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public StateMachine<String, String> notABean() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
""";
|
||||
Files.writeString(tempDir.resolve("MyApp.java"), source);
|
||||
|
||||
CodebaseContext context = new CodebaseContext();
|
||||
context.scan(tempDir);
|
||||
|
||||
var beanMethods = context.findBeanMethodsReturning(java.util.Set.of("StateMachine"));
|
||||
assertThat(beanMethods).hasSize(1);
|
||||
assertThat(beanMethods.get(0).getName().getIdentifier()).isEqualTo("stateMachine");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldFindBeanMethodsReturningStateMachineWithQualifiedName() throws IOException {
|
||||
String source = """
|
||||
package com.example;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
public class MyApp {
|
||||
@Bean
|
||||
public org.springframework.statemachine.StateMachine stateMachine() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
""";
|
||||
Files.writeString(tempDir.resolve("MyApp.java"), source);
|
||||
|
||||
CodebaseContext context = new CodebaseContext();
|
||||
context.scan(tempDir);
|
||||
|
||||
var beanMethods = context.findBeanMethodsReturning(java.util.Set.of("StateMachine"));
|
||||
assertThat(beanMethods).hasSize(1);
|
||||
assertThat(beanMethods.get(0).getName().getIdentifier()).isEqualTo("stateMachine");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,314 +0,0 @@
|
||||
package click.kamil.springstatemachineexporter.ast.in;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy;
|
||||
|
||||
class AstFileFinderTest {
|
||||
|
||||
private static final String CLASS_WITH_ANNOTATION = """
|
||||
@Service
|
||||
public class TestService {
|
||||
public void doSomething() {
|
||||
// implementation
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
private static final String CLASS_WITH_MULTIPLE_ANNOTATIONS = """
|
||||
@Service
|
||||
@Component
|
||||
public class TestService {
|
||||
public void doSomething() {
|
||||
// implementation
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
private static final String INTERFACE_WITH_ANNOTATION = """
|
||||
@FunctionalInterface
|
||||
public interface TestInterface {
|
||||
void doSomething();
|
||||
}
|
||||
""";
|
||||
|
||||
private static final String CLASS_WITHOUT_ANNOTATION = """
|
||||
public class TestService {
|
||||
public void doSomething() {
|
||||
// implementation
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
private static final String CLASS_WITH_DIFFERENT_ANNOTATION = """
|
||||
@Repository
|
||||
public class TestRepository {
|
||||
public void doSomething() {
|
||||
// implementation
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
private static final String CLASS_WITH_FULLY_QUALIFIED_ANNOTATION = """
|
||||
@org.springframework.stereotype.Service
|
||||
public class TestService {
|
||||
public void doSomething() {
|
||||
// implementation
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
private static final String MULTIPLE_CLASSES = """
|
||||
@Service
|
||||
public class TestService {
|
||||
public void doSomething() {
|
||||
// implementation
|
||||
}
|
||||
}
|
||||
|
||||
public class AnotherClass {
|
||||
// no annotation
|
||||
}
|
||||
""";
|
||||
|
||||
private static final String CLASS_WITH_ANNOTATION_AND_FIELDS = """
|
||||
@Service
|
||||
public class TestService {
|
||||
private String name;
|
||||
|
||||
public void doSomething() {
|
||||
// implementation
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
private static final String EMPTY_SOURCE = "";
|
||||
|
||||
private static final String INVALID_JAVA_SYNTAX = """
|
||||
@Service
|
||||
public class TestService {
|
||||
// missing closing brace
|
||||
""";
|
||||
|
||||
@Test
|
||||
void shouldReturnTrue_whenClassHasMatchingAnnotation() {
|
||||
AstFileFinder finder = new AstFileFinder();
|
||||
List<String> annotationNames = List.of("Service");
|
||||
|
||||
boolean result = finder.hasClassWithAnnotation(CLASS_WITH_ANNOTATION, annotationNames);
|
||||
|
||||
assertThat(result).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnTrue_whenClassHasMultipleMatchingAnnotations() {
|
||||
AstFileFinder finder = new AstFileFinder();
|
||||
List<String> annotationNames = List.of("Service", "Component");
|
||||
|
||||
boolean result = finder.hasClassWithAnnotation(CLASS_WITH_MULTIPLE_ANNOTATIONS, annotationNames);
|
||||
|
||||
assertThat(result).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnFalse_whenClassHasNoAnnotation() {
|
||||
AstFileFinder finder = new AstFileFinder();
|
||||
List<String> annotationNames = List.of("Service");
|
||||
|
||||
boolean result = finder.hasClassWithAnnotation(CLASS_WITHOUT_ANNOTATION, annotationNames);
|
||||
|
||||
assertThat(result).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnFalse_whenClassHasDifferentAnnotation() {
|
||||
AstFileFinder finder = new AstFileFinder();
|
||||
List<String> annotationNames = List.of("Service");
|
||||
|
||||
boolean result = finder.hasClassWithAnnotation(CLASS_WITH_DIFFERENT_ANNOTATION, annotationNames);
|
||||
|
||||
assertThat(result).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnTrue_whenClassHasFullyQualifiedAnnotation() {
|
||||
AstFileFinder finder = new AstFileFinder();
|
||||
List<String> annotationNames = List.of("org.springframework.stereotype.Service");
|
||||
|
||||
boolean result = finder.hasClassWithAnnotation(CLASS_WITH_FULLY_QUALIFIED_ANNOTATION, annotationNames);
|
||||
|
||||
assertThat(result).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnTrue_whenMultipleClassesAndOneHasAnnotation() {
|
||||
AstFileFinder finder = new AstFileFinder();
|
||||
List<String> annotationNames = List.of("Service");
|
||||
|
||||
boolean result = finder.hasClassWithAnnotation(MULTIPLE_CLASSES, annotationNames);
|
||||
|
||||
assertThat(result).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnFalse_whenInterfaceHasAnnotation() {
|
||||
AstFileFinder finder = new AstFileFinder();
|
||||
List<String> annotationNames = List.of("FunctionalInterface");
|
||||
|
||||
boolean result = finder.hasClassWithAnnotation(INTERFACE_WITH_ANNOTATION, annotationNames);
|
||||
|
||||
assertThat(result).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnFalse_whenEmptyAnnotationList() {
|
||||
AstFileFinder finder = new AstFileFinder();
|
||||
List<String> annotationNames = Collections.emptyList();
|
||||
|
||||
boolean result = finder.hasClassWithAnnotation(CLASS_WITH_ANNOTATION, annotationNames);
|
||||
|
||||
assertThat(result).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnFalse_whenEmptySource() {
|
||||
AstFileFinder finder = new AstFileFinder();
|
||||
List<String> annotationNames = List.of("Service");
|
||||
|
||||
boolean result = finder.hasClassWithAnnotation(EMPTY_SOURCE, annotationNames);
|
||||
|
||||
assertThat(result).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldThrowException_whenNullAnnotationList() {
|
||||
AstFileFinder finder = new AstFileFinder();
|
||||
|
||||
// when & then
|
||||
assertThatThrownBy(() ->
|
||||
finder.hasClassWithAnnotation(CLASS_WITH_ANNOTATION, null)
|
||||
).isInstanceOf(NullPointerException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnTrue_whenClassHasAnnotationAndFields() {
|
||||
AstFileFinder finder = new AstFileFinder();
|
||||
List<String> annotationNames = List.of("Service");
|
||||
|
||||
boolean result = finder.hasClassWithAnnotation(CLASS_WITH_ANNOTATION_AND_FIELDS, annotationNames);
|
||||
|
||||
assertThat(result).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldHandleInvalidJavaSyntax() {
|
||||
AstFileFinder finder = new AstFileFinder();
|
||||
List<String> annotationNames = List.of("Service");
|
||||
|
||||
boolean result = finder.hasClassWithAnnotation(INVALID_JAVA_SYNTAX, annotationNames);
|
||||
|
||||
// AST parser might still create a valid AST even with syntax errors
|
||||
// The result depends on how the parser handles the invalid syntax
|
||||
assertThat(result).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnTrue_whenAnnotationNameMatchesCaseInsensitive() {
|
||||
AstFileFinder finder = new AstFileFinder();
|
||||
List<String> annotationNames = List.of("service"); // lowercase
|
||||
|
||||
boolean result = finder.hasClassWithAnnotation(CLASS_WITH_ANNOTATION, annotationNames);
|
||||
|
||||
assertThat(result).isFalse(); // AST parser is case-sensitive
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnTrue_whenMultipleAnnotationNamesProvided() {
|
||||
AstFileFinder finder = new AstFileFinder();
|
||||
List<String> annotationNames = List.of("Component", "Service", "Repository");
|
||||
|
||||
boolean result = finder.hasClassWithAnnotation(CLASS_WITH_ANNOTATION, annotationNames);
|
||||
|
||||
assertThat(result).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnFalse_whenNoMatchingAnnotationNames() {
|
||||
AstFileFinder finder = new AstFileFinder();
|
||||
List<String> annotationNames = List.of("Component", "Repository", "Controller");
|
||||
|
||||
boolean result = finder.hasClassWithAnnotation(CLASS_WITH_ANNOTATION, annotationNames);
|
||||
|
||||
assertThat(result).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnFalse_whenSourceContainsOnlyComments() {
|
||||
String sourceWithComments = """
|
||||
// This is a comment
|
||||
/* Another comment */
|
||||
/** JavaDoc comment */
|
||||
""";
|
||||
AstFileFinder finder = new AstFileFinder();
|
||||
List<String> annotationNames = List.of("Service");
|
||||
|
||||
boolean result = finder.hasClassWithAnnotation(sourceWithComments, annotationNames);
|
||||
|
||||
assertThat(result).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnFalse_whenSourceContainsOnlyPackageDeclaration() {
|
||||
String sourceWithPackage = "package com.example;";
|
||||
AstFileFinder finder = new AstFileFinder();
|
||||
List<String> annotationNames = List.of("Service");
|
||||
|
||||
boolean result = finder.hasClassWithAnnotation(sourceWithPackage, annotationNames);
|
||||
|
||||
assertThat(result).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnTrue_whenFunctionReturnsTargetType() {
|
||||
String source = """
|
||||
public class Test {
|
||||
@Bean
|
||||
public StateMachine<S, E> build() { return null; }
|
||||
}
|
||||
""";
|
||||
AstFileFinder finder = new AstFileFinder();
|
||||
boolean result = finder.hasFunctionReturningType(source, Set.of("StateMachine"));
|
||||
assertThat(result).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnFalse_whenNoFunctionReturnsTargetType() {
|
||||
String source = """
|
||||
public class Test {
|
||||
@Bean
|
||||
public void build() {}
|
||||
}
|
||||
""";
|
||||
AstFileFinder finder = new AstFileFinder();
|
||||
boolean result = finder.hasFunctionReturningType(source, Set.of("StateMachine"));
|
||||
assertThat(result).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnTrue_whenFunctionReturnsQualifiedTargetType() {
|
||||
String source = """
|
||||
public class Test {
|
||||
@Bean
|
||||
public org.springframework.statemachine.StateMachine build() { return null; }
|
||||
}
|
||||
""";
|
||||
AstFileFinder finder = new AstFileFinder();
|
||||
boolean result = finder.hasFunctionReturningType(source, Set.of("StateMachine"));
|
||||
assertThat(result).isTrue();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user