more fixes 5

This commit is contained in:
2026-06-20 12:52:19 +02:00
parent 9ca8b2fe37
commit 76ac143370
4 changed files with 440 additions and 80 deletions

View File

@@ -229,14 +229,14 @@ class TransitionLinkerEnricherTest {
CallChain chain = CallChain.builder()
.triggerPoint(TriggerPoint.builder().event("ASSEMBLE").build())
.methodChain(List.of(
"com.example.ElectronicsOrderController.post()",
"com.example.ElectronicsOrderService.processOrderEvent()"
"com.example.electronics.ElectronicsOrderController.post()",
"com.example.electronics.ElectronicsOrderService.processOrderEvent()"
))
.build();
// Testing against Electronics SM - should match
AnalysisResult resultElectronics = AnalysisResult.builder()
.name("ElectronicsOwnDeliveryStateMachineConfiguration")
.name("com.example.electronics.ElectronicsOwnDeliveryStateMachineConfiguration")
.transitions(List.of(t1))
.metadata(CodebaseMetadata.builder().callChains(List.of(chain)).build())
.build();
@@ -247,7 +247,7 @@ class TransitionLinkerEnricherTest {
// Testing against ComputerStore SM - should NOT match because chain has Electronics service
AnalysisResult resultComputer = AnalysisResult.builder()
.name("OwnDeliveryStateMachineConfiguration") // the non-electronics one
.name("com.example.computerstore.OwnDeliveryStateMachineConfiguration") // the non-electronics one
.transitions(List.of(t1))
.metadata(CodebaseMetadata.builder().callChains(List.of(chain)).build())
.build();
@@ -275,7 +275,7 @@ class TransitionLinkerEnricherTest {
.build();
AnalysisResult resultElectronics = AnalysisResult.builder()
.name("ElectronicsOwnDeliveryStateMachineConfiguration")
.name("com.example.electronics.ElectronicsOwnDeliveryStateMachineConfiguration")
.transitions(List.of(t1))
.metadata(CodebaseMetadata.builder().callChains(List.of(chain)).build())
.build();
@@ -287,7 +287,7 @@ class TransitionLinkerEnricherTest {
assertThat(updatedChainElec.getMatchedTransitions()).hasSize(1);
AnalysisResult resultComputer = AnalysisResult.builder()
.name("OwnDeliveryStateMachineConfiguration")
.name("com.example.computerstore.OwnDeliveryStateMachineConfiguration")
.transitions(List.of(t1))
.metadata(CodebaseMetadata.builder().callChains(List.of(chain)).build())
.build();
@@ -299,4 +299,83 @@ class TransitionLinkerEnricherTest {
assertThat(updatedChainComp.getMatchedTransitions()).hasSize(1);
}
@Test
void shouldFilterCrossStateMachineRoutingWithMultipleVerticals() {
Transition t1 = new Transition();
t1.setSourceStates(List.of(State.of("NEW", "NEW")));
t1.setTargetStates(List.of(State.of("ASSEMBLED", "ASSEMBLED")));
t1.setEvent(Event.of("ASSEMBLE", "ASSEMBLE"));
// Chain 1: Electronics
CallChain chainElec = CallChain.builder()
.triggerPoint(TriggerPoint.builder().event("ASSEMBLE").build())
.methodChain(List.of("com.example.electronics.ElectronicsOrderController.post()"))
.build();
// Chain 2: Furniture
CallChain chainFurn = CallChain.builder()
.triggerPoint(TriggerPoint.builder().event("ASSEMBLE").build())
.methodChain(List.of("com.example.furniture.FurnitureOrderController.post()"))
.build();
// Chain 3: Groceries
CallChain chainGroc = CallChain.builder()
.triggerPoint(TriggerPoint.builder().event("ASSEMBLE").build())
.methodChain(List.of("com.example.groceries.GroceriesOrderController.post()"))
.build();
// SM 1: Electronics
AnalysisResult resElec = AnalysisResult.builder()
.name("com.example.electronics.ElectronicsOwnDeliveryStateMachineConfiguration")
.transitions(List.of(t1))
.metadata(CodebaseMetadata.builder().callChains(List.of(chainElec, chainFurn, chainGroc)).build())
.build();
// SM 2: Furniture
AnalysisResult resFurn = AnalysisResult.builder()
.name("com.example.furniture.FurnitureOwnDeliveryStateMachineConfiguration")
.transitions(List.of(t1))
.metadata(CodebaseMetadata.builder().callChains(List.of(chainElec, chainFurn, chainGroc)).build())
.build();
// SM 3: Groceries
AnalysisResult resGroc = AnalysisResult.builder()
.name("com.example.groceries.GroceriesStateMachineConfiguration")
.transitions(List.of(t1))
.metadata(CodebaseMetadata.builder().callChains(List.of(chainElec, chainFurn, chainGroc)).build())
.build();
// SM 4: Computer Store (The catch-all base one in computerstore package)
AnalysisResult resComp = AnalysisResult.builder()
.name("com.example.computerstore.OwnDeliveryStateMachineConfiguration")
.transitions(List.of(t1))
.metadata(CodebaseMetadata.builder().callChains(List.of(chainElec, chainFurn, chainGroc)).build())
.build();
// Test Electronics SM - Should only keep Electronics Chain
enricher.enrich(resElec, null, null);
assertThat(resElec.getMetadata().getCallChains().get(0).getMatchedTransitions()).hasSize(1); // Elec
assertThat(resElec.getMetadata().getCallChains().get(1).getMatchedTransitions()).isNullOrEmpty(); // Furn
assertThat(resElec.getMetadata().getCallChains().get(2).getMatchedTransitions()).isNullOrEmpty(); // Groc
// Test Furniture SM - Should only keep Furniture Chain
enricher.enrich(resFurn, null, null);
assertThat(resFurn.getMetadata().getCallChains().get(0).getMatchedTransitions()).isNullOrEmpty(); // Elec
assertThat(resFurn.getMetadata().getCallChains().get(1).getMatchedTransitions()).hasSize(1); // Furn
assertThat(resFurn.getMetadata().getCallChains().get(2).getMatchedTransitions()).isNullOrEmpty(); // Groc
// Test Groceries SM - Should only keep Groceries Chain
enricher.enrich(resGroc, null, null);
assertThat(resGroc.getMetadata().getCallChains().get(0).getMatchedTransitions()).isNullOrEmpty(); // Elec
assertThat(resGroc.getMetadata().getCallChains().get(1).getMatchedTransitions()).isNullOrEmpty(); // Furn
assertThat(resGroc.getMetadata().getCallChains().get(2).getMatchedTransitions()).hasSize(1); // Groc
// Test ComputerStore SM - Should reject ALL because none belong to computerstore
enricher.enrich(resComp, null, null);
assertThat(resComp.getMetadata().getCallChains().get(0).getMatchedTransitions()).isNullOrEmpty(); // Elec
assertThat(resComp.getMetadata().getCallChains().get(1).getMatchedTransitions()).isNullOrEmpty(); // Furn
assertThat(resComp.getMetadata().getCallChains().get(2).getMatchedTransitions()).isNullOrEmpty(); // Groc
}
}

View File

@@ -1723,4 +1723,137 @@ class CallGraphBuilderTest {
org.assertj.core.api.Assertions.assertThat(chains.get(0).getTriggerPoint().getPolymorphicEvents()).contains("THIS_CALL_CONSTANT");
}
@Test
void shouldResolveConstantsFromDynamicMethodAssignmentInConstructor() throws IOException {
String source = """
package com.example;
public class OrderController {
private OrderService service;
public void handleStatus() {
service.process(new OrderCancelledEvent().getType());
}
}
class OrderCancelledEvent {
private String cancelEventType;
public OrderCancelledEvent() {
this.cancelEventType = assertMatchingCancelEventType("some_sender");
}
private String assertMatchingCancelEventType(String sender) {
return "CANCELLED_BY_SENDER";
}
public String getType() { return cancelEventType; }
}
class OrderService {
public void process(String event) {}
}
""";
Path tempDir = Files.createTempDirectory("callgraph_dynamic_rhs");
Files.writeString(tempDir.resolve("OrderConfig.java"), source);
CodebaseContext context = new CodebaseContext();
context.scan(tempDir);
CallGraphBuilder builder = new CallGraphBuilder(context);
EntryPoint entryPoint = EntryPoint.builder().className("com.example.OrderController").methodName("handleStatus").build();
TriggerPoint trigger = TriggerPoint.builder().className("com.example.OrderService").methodName("process").event("event").build();
List<CallChain> chains = builder.findChains(List.of(entryPoint), List.of(trigger));
org.assertj.core.api.Assertions.assertThat(chains).hasSize(1);
org.assertj.core.api.Assertions.assertThat(chains.get(0).getTriggerPoint().getPolymorphicEvents()).contains("CANCELLED_BY_SENDER");
}
@Test
void shouldResolveConstantsFromAbstractSuperclass() throws IOException {
String source = """
package com.example;
public class OrderController {
private OrderService service;
public void handleStatus() {
service.process(new PostAcceptanceOrderModificationsRejectedEvent().getType());
}
}
abstract class AbstractPostAcceptanceOrderModificationsEvent {
private String event;
public AbstractPostAcceptanceOrderModificationsEvent(String event) {
this.event = event;
}
public String getType() { return event; }
}
class PostAcceptanceOrderModificationsRejectedEvent extends AbstractPostAcceptanceOrderModificationsEvent {
public PostAcceptanceOrderModificationsRejectedEvent() {
super("POST_ACCEPTANCE_REJECTED");
}
}
class OrderService {
public void process(String event) {}
}
""";
Path tempDir = Files.createTempDirectory("callgraph_post_acc");
Files.writeString(tempDir.resolve("OrderConfig.java"), source);
CodebaseContext context = new CodebaseContext();
context.scan(tempDir);
CallGraphBuilder builder = new CallGraphBuilder(context);
EntryPoint entryPoint = EntryPoint.builder().className("com.example.OrderController").methodName("handleStatus").build();
TriggerPoint trigger = TriggerPoint.builder().className("com.example.OrderService").methodName("process").event("event").build();
List<CallChain> chains = builder.findChains(List.of(entryPoint), List.of(trigger));
org.assertj.core.api.Assertions.assertThat(chains).hasSize(1);
org.assertj.core.api.Assertions.assertThat(chains.get(0).getTriggerPoint().getPolymorphicEvents()).contains("POST_ACCEPTANCE_REJECTED");
}
@Test
void shouldResolveConstantsFromFieldDeclarationsAndInitializers() throws IOException {
String source = """
package com.example;
public class OrderController {
private OrderService service;
public void handleStatus1() {
service.process(new EventWithFieldDecl().getType());
}
public void handleStatus2() {
service.process(new EventWithInitBlock().getType());
}
}
class EventWithFieldDecl {
private String event = "FIELD_DECL";
public String getType() { return event; }
}
class EventWithInitBlock {
private String event;
{
event = "INIT_BLOCK";
}
public String getType() { return event; }
}
class OrderService {
public void process(String event) {}
}
""";
Path tempDir = Files.createTempDirectory("callgraph_field_init");
Files.writeString(tempDir.resolve("OrderConfig.java"), source);
CodebaseContext context = new CodebaseContext();
context.scan(tempDir);
CallGraphBuilder builder = new CallGraphBuilder(context);
EntryPoint ep1 = EntryPoint.builder().className("com.example.OrderController").methodName("handleStatus1").build();
EntryPoint ep2 = EntryPoint.builder().className("com.example.OrderController").methodName("handleStatus2").build();
TriggerPoint trigger = TriggerPoint.builder().className("com.example.OrderService").methodName("process").event("event").build();
List<CallChain> chains = builder.findChains(List.of(ep1, ep2), List.of(trigger));
org.assertj.core.api.Assertions.assertThat(chains).hasSize(2);
java.util.List<String> allEvents = new java.util.ArrayList<>();
chains.forEach(c -> allEvents.addAll(c.getTriggerPoint().getPolymorphicEvents()));
org.assertj.core.api.Assertions.assertThat(allEvents).containsExactlyInAnyOrder("FIELD_DECL", "INIT_BLOCK");
}
}