Extract machine routing helpers and gate Spring DI to ambiguous generics.
Keep package heuristics for single-machine codebases and fail closed only when String/Object generic triggers are provably ambiguous across multiple beans. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,114 @@
|
||||
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 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 SpringInjectionRoutingTest {
|
||||
|
||||
@Test
|
||||
void shouldRouteByQualifierWithoutPackageNameHeuristics(@TempDir Path tempDir) throws Exception {
|
||||
Files.writeString(tempDir.resolve("PaymentService.java"), """
|
||||
package com.example;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.statemachine.StateMachine;
|
||||
import org.springframework.stereotype.Service;
|
||||
@Service
|
||||
public class PaymentService {
|
||||
@Qualifier("paymentStateMachine")
|
||||
private final StateMachine<String, String> stateMachine;
|
||||
public PaymentService(StateMachine<String, String> stateMachine) {
|
||||
this.stateMachine = stateMachine;
|
||||
}
|
||||
public void capture() {
|
||||
stateMachine.sendEvent("CAPTURE");
|
||||
}
|
||||
}
|
||||
@org.springframework.context.annotation.Configuration
|
||||
@org.springframework.statemachine.config.EnableStateMachine(name = "paymentStateMachine")
|
||||
class PaymentStateMachineConfig
|
||||
extends org.springframework.statemachine.config.StateMachineConfigurerAdapter<String, String> {}
|
||||
@org.springframework.context.annotation.Configuration
|
||||
@org.springframework.statemachine.config.EnableStateMachine(name = "orderStateMachine")
|
||||
class OrderStateMachineConfig
|
||||
extends org.springframework.statemachine.config.StateMachineConfigurerAdapter<String, String> {}
|
||||
""");
|
||||
|
||||
CodebaseContext context = new CodebaseContext();
|
||||
context.setResolveBindings(true);
|
||||
context.scan(tempDir);
|
||||
|
||||
HeuristicBeanResolutionEngine engine = new HeuristicBeanResolutionEngine();
|
||||
CallChain chain = CallChain.builder()
|
||||
.triggerPoint(TriggerPoint.builder()
|
||||
.className("com.example.PaymentService")
|
||||
.methodName("capture")
|
||||
.lineNumber(12)
|
||||
.event("CAPTURE")
|
||||
.eventTypeFqn("java.lang.String")
|
||||
.stateTypeFqn("java.lang.String")
|
||||
.stateMachineId("paymentStateMachine")
|
||||
.build())
|
||||
.methodChain(List.of("com.example.PaymentService.capture()"))
|
||||
.build();
|
||||
|
||||
assertThat(engine.isRoutedToCorrectMachine(
|
||||
chain, "com.example.PaymentStateMachineConfig", context)).isTrue();
|
||||
assertThat(engine.isRoutedToCorrectMachine(
|
||||
chain, "com.example.OrderStateMachineConfig", context)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldFailClosedForAmbiguousStringStateMachineWithoutQualifier(@TempDir Path tempDir) throws Exception {
|
||||
Files.writeString(tempDir.resolve("OrderService.java"), """
|
||||
package com.example;
|
||||
import org.springframework.statemachine.StateMachine;
|
||||
import org.springframework.stereotype.Service;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class OrderService {
|
||||
private final StateMachine<String, String> stateMachine;
|
||||
public void finish() {
|
||||
stateMachine.sendEvent("FINISH");
|
||||
}
|
||||
}
|
||||
@org.springframework.context.annotation.Configuration
|
||||
@org.springframework.statemachine.config.EnableStateMachine(name = "machineA")
|
||||
class MachineAConfig
|
||||
extends org.springframework.statemachine.config.StateMachineConfigurerAdapter<String, String> {}
|
||||
@org.springframework.context.annotation.Configuration
|
||||
@org.springframework.statemachine.config.EnableStateMachine(name = "machineB")
|
||||
class MachineBConfig
|
||||
extends org.springframework.statemachine.config.StateMachineConfigurerAdapter<String, String> {}
|
||||
""");
|
||||
|
||||
CodebaseContext context = new CodebaseContext();
|
||||
context.setResolveBindings(true);
|
||||
context.scan(tempDir);
|
||||
|
||||
HeuristicBeanResolutionEngine engine = new HeuristicBeanResolutionEngine();
|
||||
CallChain chain = CallChain.builder()
|
||||
.triggerPoint(TriggerPoint.builder()
|
||||
.className("com.example.OrderService")
|
||||
.methodName("finish")
|
||||
.lineNumber(11)
|
||||
.event("FINISH")
|
||||
.eventTypeFqn("java.lang.String")
|
||||
.stateTypeFqn("java.lang.String")
|
||||
.build())
|
||||
.methodChain(List.of("com.example.OrderService.finish()"))
|
||||
.build();
|
||||
|
||||
assertThat(engine.isRoutedToCorrectMachine(chain, "com.example.MachineAConfig", context)).isFalse();
|
||||
assertThat(engine.isRoutedToCorrectMachine(chain, "com.example.MachineBConfig", context)).isFalse();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user