Gate shared-service multi-attach to provable shared infrastructure.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,187 @@
|
||||
package click.kamil.springstatemachineexporter.analysis.enricher.routing;
|
||||
|
||||
import click.kamil.springstatemachineexporter.analysis.model.CallChain;
|
||||
import click.kamil.springstatemachineexporter.analysis.model.TriggerPoint;
|
||||
import click.kamil.springstatemachineexporter.ast.common.CodebaseContext;
|
||||
import click.kamil.springstatemachineexporter.model.Event;
|
||||
import click.kamil.springstatemachineexporter.model.State;
|
||||
import click.kamil.springstatemachineexporter.model.Transition;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class SharedServiceRoutingPolicyTest {
|
||||
|
||||
private final HeuristicBeanResolutionEngine engine = new HeuristicBeanResolutionEngine();
|
||||
|
||||
@Test
|
||||
void commonOrderServiceChainIsProvablySharedInfrastructure() {
|
||||
CallChain chain = CallChain.builder()
|
||||
.triggerPoint(TriggerPoint.builder().event("PROCESS").build())
|
||||
.methodChain(List.of(
|
||||
"com.example.CommonOrderController.post()",
|
||||
"com.example.CommonOrderService.processOrderEvent()"))
|
||||
.build();
|
||||
|
||||
assertThat(SharedServiceRoutingPolicy.isProvablySharedInfrastructure(chain)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void electronicsVerticalChainIsNotProvablySharedInfrastructure() {
|
||||
CallChain chain = CallChain.builder()
|
||||
.triggerPoint(TriggerPoint.builder().event("ASSEMBLE").build())
|
||||
.methodChain(List.of(
|
||||
"com.example.electronics.ElectronicsOrderController.post()",
|
||||
"com.example.electronics.ElectronicsOrderService.processOrderEvent()"))
|
||||
.build();
|
||||
|
||||
assertThat(SharedServiceRoutingPolicy.isProvablySharedInfrastructure(chain)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void chainWithProvenTypeFqnsIsNotProvablySharedInfrastructure() {
|
||||
CallChain chain = CallChain.builder()
|
||||
.triggerPoint(TriggerPoint.builder()
|
||||
.event("com.example.OrderEvent.PAY")
|
||||
.eventTypeFqn("com.example.OrderEvent")
|
||||
.stateTypeFqn("com.example.OrderState")
|
||||
.build())
|
||||
.methodChain(List.of("com.example.SharedDispatcher.payOrder()"))
|
||||
.build();
|
||||
|
||||
assertThat(SharedServiceRoutingPolicy.isProvablySharedInfrastructure(chain)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void sharedInfrastructureMayMultiAttachOnSharedEventInMultiMachineContext(@TempDir Path tempDir) throws Exception {
|
||||
Files.writeString(tempDir.resolve("OrderConfig.java"), """
|
||||
package com.example;
|
||||
@org.springframework.statemachine.config.EnableStateMachine
|
||||
public class OrderStateMachineConfig
|
||||
extends org.springframework.statemachine.config.StateMachineConfigurerAdapter<String, String> {}
|
||||
""");
|
||||
Files.writeString(tempDir.resolve("DocumentConfig.java"), """
|
||||
package com.example;
|
||||
@org.springframework.statemachine.config.EnableStateMachine
|
||||
public class DocumentStateMachineConfig
|
||||
extends org.springframework.statemachine.config.StateMachineConfigurerAdapter<String, String> {}
|
||||
""");
|
||||
|
||||
CodebaseContext context = new CodebaseContext();
|
||||
context.scan(tempDir);
|
||||
|
||||
CallChain chain = CallChain.builder()
|
||||
.triggerPoint(TriggerPoint.builder().event("PROCESS").build())
|
||||
.methodChain(List.of(
|
||||
"com.example.CommonOrderController.post()",
|
||||
"com.example.CommonOrderService.processOrderEvent()"))
|
||||
.build();
|
||||
|
||||
Transition transition = new Transition();
|
||||
transition.setSourceStates(List.of(State.of("NEW", "NEW")));
|
||||
transition.setTargetStates(List.of(State.of("PROCESSED", "PROCESSED")));
|
||||
transition.setEvent(Event.of("PROCESS", "PROCESS"));
|
||||
List<Transition> machineTransitions = List.of(transition);
|
||||
|
||||
assertThat(engine.hasProvenMachineAffinity(
|
||||
chain, "com.example.OrderStateMachineConfig", context, machineTransitions)).isTrue();
|
||||
assertThat(engine.hasProvenMachineAffinity(
|
||||
chain, "com.example.DocumentStateMachineConfig", context, machineTransitions)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void verticalChainFailsClosedOnEventMatchInMultiMachineContext(@TempDir Path tempDir) throws Exception {
|
||||
Files.writeString(tempDir.resolve("ElectronicsConfig.java"), """
|
||||
package com.example.electronics;
|
||||
@org.springframework.statemachine.config.EnableStateMachine
|
||||
public class ElectronicsStateMachineConfig
|
||||
extends org.springframework.statemachine.config.StateMachineConfigurerAdapter<String, String> {}
|
||||
""");
|
||||
Files.writeString(tempDir.resolve("ComputerConfig.java"), """
|
||||
package com.example.computerstore;
|
||||
@org.springframework.statemachine.config.EnableStateMachine
|
||||
public class ComputerStateMachineConfig
|
||||
extends org.springframework.statemachine.config.StateMachineConfigurerAdapter<String, String> {}
|
||||
""");
|
||||
|
||||
CodebaseContext context = new CodebaseContext();
|
||||
context.scan(tempDir);
|
||||
|
||||
CallChain chain = CallChain.builder()
|
||||
.triggerPoint(TriggerPoint.builder().event("ASSEMBLE").build())
|
||||
.methodChain(List.of(
|
||||
"com.example.electronics.ElectronicsOrderController.post()",
|
||||
"com.example.electronics.ElectronicsOrderService.processOrderEvent()"))
|
||||
.build();
|
||||
|
||||
Transition transition = new Transition();
|
||||
transition.setSourceStates(List.of(State.of("NEW", "NEW")));
|
||||
transition.setTargetStates(List.of(State.of("ASSEMBLED", "ASSEMBLED")));
|
||||
transition.setEvent(Event.of("ASSEMBLE", "ASSEMBLE"));
|
||||
List<Transition> machineTransitions = List.of(transition);
|
||||
|
||||
assertThat(engine.hasProvenMachineAffinity(
|
||||
chain, "com.example.electronics.ElectronicsStateMachineConfig", context, machineTransitions))
|
||||
.isTrue();
|
||||
assertThat(engine.hasProvenMachineAffinity(
|
||||
chain, "com.example.computerstore.ComputerStateMachineConfig", context, machineTransitions))
|
||||
.isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void provenDistinctTypesStillRouteToSingleMachine(@TempDir Path tempDir) throws Exception {
|
||||
Files.writeString(tempDir.resolve("SharedDispatcher.java"), """
|
||||
package com.example;
|
||||
public class SharedDispatcher {
|
||||
void payOrder() {
|
||||
org.springframework.statemachine.StateMachine<OrderState, OrderEvent> machine = null;
|
||||
machine.sendEvent(OrderEvent.PAY);
|
||||
}
|
||||
void submitDocument() {
|
||||
org.springframework.statemachine.StateMachine<DocumentState, DocumentEvent> machine = null;
|
||||
machine.sendEvent(DocumentEvent.SUBMIT);
|
||||
}
|
||||
}
|
||||
enum OrderEvent { PAY }
|
||||
enum OrderState { NEW, PAID }
|
||||
enum DocumentEvent { SUBMIT }
|
||||
enum DocumentState { DRAFT }
|
||||
class OrderStateMachineConfiguration
|
||||
extends org.springframework.statemachine.config.StateMachineConfigurerAdapter<OrderState, OrderEvent> {}
|
||||
class DocumentStateMachineConfiguration
|
||||
extends org.springframework.statemachine.config.StateMachineConfigurerAdapter<DocumentState, DocumentEvent> {}
|
||||
""");
|
||||
|
||||
CodebaseContext context = new CodebaseContext();
|
||||
context.setResolveBindings(true);
|
||||
context.scan(tempDir);
|
||||
|
||||
CallChain orderChain = CallChain.builder()
|
||||
.triggerPoint(TriggerPoint.builder()
|
||||
.event("com.example.OrderEvent.PAY")
|
||||
.eventTypeFqn("com.example.OrderEvent")
|
||||
.stateTypeFqn("com.example.OrderState")
|
||||
.className("com.example.SharedDispatcher")
|
||||
.methodName("payOrder")
|
||||
.build())
|
||||
.methodChain(List.of("com.example.SharedDispatcher.payOrder()"))
|
||||
.build();
|
||||
|
||||
Transition payTransition = new Transition();
|
||||
payTransition.setSourceStates(List.of(State.of("NEW", "NEW")));
|
||||
payTransition.setTargetStates(List.of(State.of("PAID", "PAID")));
|
||||
payTransition.setEvent(Event.of("PAY", "PAY"));
|
||||
|
||||
assertThat(engine.hasProvenMachineAffinity(
|
||||
orderChain, "com.example.OrderStateMachineConfiguration", context, List.of(payTransition)))
|
||||
.isTrue();
|
||||
assertThat(engine.hasProvenMachineAffinity(
|
||||
orderChain, "com.example.DocumentStateMachineConfiguration", context, List.of(payTransition)))
|
||||
.isFalse();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user