Fix map-routed dispatcher events to respect path-bound keys.
Resolve static map lookups along the call path, guard enum widening when keys are bound, and add isolation tests for warmed-cache map routing. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,128 @@
|
||||
package click.kamil.springstatemachineexporter.analysis.service;
|
||||
|
||||
import click.kamil.springstatemachineexporter.analysis.model.CallChain;
|
||||
import click.kamil.springstatemachineexporter.analysis.model.EntryPoint;
|
||||
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;
|
||||
|
||||
/**
|
||||
* Guards map-routed endpoint resolution and per-machine polymorphic event isolation when the
|
||||
* call graph and accessor caches are reused across multiple {@link HeuristicCallGraphEngine#findChains}
|
||||
* invocations on the same {@link CodebaseContext}.
|
||||
*/
|
||||
class PerformanceCacheMultiMachineIsolationTest {
|
||||
|
||||
private static final String ORDER_CONTROLLER = "com.example.OrderController";
|
||||
private static final String SHIPMENT_CONTROLLER = "com.example.ShipmentController";
|
||||
private static final String ORDER_MACHINE = "com.example.OrderMachine";
|
||||
private static final String SHIPMENT_MACHINE = "com.example.ShipmentMachine";
|
||||
|
||||
@Test
|
||||
void mapRoutedEndpointsShouldResolveSingleEventPerLiteralEndpoint(@TempDir Path tempDir) throws Exception {
|
||||
String source = """
|
||||
package com.example;
|
||||
import java.util.Map;
|
||||
public class OrderController {
|
||||
OrderMachine sm;
|
||||
public void payOrder() { sm.sendEvent(Routes.ROUTES.get("order.pay")); }
|
||||
public void shipOrder() { sm.sendEvent(Routes.ROUTES.get("order.ship")); }
|
||||
}
|
||||
class Routes {
|
||||
static final Map<String, OrderEvent> ROUTES = Map.of(
|
||||
"order.pay", OrderEvent.PAY,
|
||||
"order.ship", OrderEvent.SHIP
|
||||
);
|
||||
}
|
||||
enum OrderEvent { PAY, SHIP, CANCEL }
|
||||
class OrderMachine { public void sendEvent(OrderEvent e) {} }
|
||||
""";
|
||||
Files.writeString(tempDir.resolve("App.java"), source);
|
||||
CodebaseContext context = new CodebaseContext();
|
||||
context.scan(tempDir);
|
||||
|
||||
HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
|
||||
TriggerPoint trigger = TriggerPoint.builder()
|
||||
.className(ORDER_MACHINE)
|
||||
.methodName("sendEvent")
|
||||
.event("e")
|
||||
.build();
|
||||
|
||||
EntryPoint payEntry = EntryPoint.builder().className(ORDER_CONTROLLER).methodName("payOrder").build();
|
||||
EntryPoint shipEntry = EntryPoint.builder().className(ORDER_CONTROLLER).methodName("shipOrder").build();
|
||||
|
||||
CallChain payChain = engine.findChains(List.of(payEntry), List.of(trigger)).get(0);
|
||||
CallChain shipChain = engine.findChains(List.of(shipEntry), List.of(trigger)).get(0);
|
||||
|
||||
assertThat(payChain.getTriggerPoint().getPolymorphicEvents()).containsExactly("OrderEvent.PAY");
|
||||
assertThat(shipChain.getTriggerPoint().getPolymorphicEvents()).containsExactly("OrderEvent.SHIP");
|
||||
|
||||
assertThat(context.getCache()).containsKey("heuristicCallGraph");
|
||||
|
||||
CallChain payAgain = engine.findChains(List.of(payEntry), List.of(trigger)).get(0);
|
||||
CallChain shipAgain = engine.findChains(List.of(shipEntry), List.of(trigger)).get(0);
|
||||
|
||||
assertThat(payAgain.getTriggerPoint().getPolymorphicEvents())
|
||||
.isEqualTo(payChain.getTriggerPoint().getPolymorphicEvents());
|
||||
assertThat(shipAgain.getTriggerPoint().getPolymorphicEvents())
|
||||
.isEqualTo(shipChain.getTriggerPoint().getPolymorphicEvents());
|
||||
}
|
||||
|
||||
@Test
|
||||
void separateMachinesShouldNotCrossContaminateMapRoutedResolution(@TempDir Path tempDir) throws Exception {
|
||||
String source = """
|
||||
package com.example;
|
||||
import java.util.Map;
|
||||
public class OrderController {
|
||||
OrderMachine orderMachine;
|
||||
public void pay() { orderMachine.sendEvent(OrderRoutes.ROUTES.get("order.pay")); }
|
||||
}
|
||||
public class ShipmentController {
|
||||
ShipmentMachine shipmentMachine;
|
||||
public void dispatch() { shipmentMachine.sendEvent(ShipmentRoutes.ROUTES.get("shipment.dispatch")); }
|
||||
}
|
||||
class OrderRoutes {
|
||||
static final Map<String, OrderEvent> ROUTES = Map.of("order.pay", OrderEvent.PAY);
|
||||
}
|
||||
class ShipmentRoutes {
|
||||
static final Map<String, ShipmentEvent> ROUTES = Map.of("shipment.dispatch", ShipmentEvent.DISPATCH);
|
||||
}
|
||||
enum OrderEvent { PAY }
|
||||
enum ShipmentEvent { DISPATCH }
|
||||
class OrderMachine { public void sendEvent(OrderEvent e) {} }
|
||||
class ShipmentMachine { public void sendEvent(ShipmentEvent e) {} }
|
||||
""";
|
||||
Files.writeString(tempDir.resolve("App.java"), source);
|
||||
CodebaseContext context = new CodebaseContext();
|
||||
context.scan(tempDir);
|
||||
|
||||
HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
|
||||
|
||||
EntryPoint orderEntry = EntryPoint.builder().className(ORDER_CONTROLLER).methodName("pay").build();
|
||||
EntryPoint shipmentEntry = EntryPoint.builder().className(SHIPMENT_CONTROLLER).methodName("dispatch").build();
|
||||
TriggerPoint orderTrigger = TriggerPoint.builder()
|
||||
.className(ORDER_MACHINE)
|
||||
.methodName("sendEvent")
|
||||
.event("e")
|
||||
.build();
|
||||
TriggerPoint shipmentTrigger = TriggerPoint.builder()
|
||||
.className(SHIPMENT_MACHINE)
|
||||
.methodName("sendEvent")
|
||||
.event("e")
|
||||
.build();
|
||||
|
||||
engine.findChains(List.of(orderEntry), List.of(orderTrigger));
|
||||
CallChain shipmentChain = engine.findChains(List.of(shipmentEntry), List.of(shipmentTrigger)).get(0);
|
||||
assertThat(shipmentChain.getTriggerPoint().getPolymorphicEvents()).containsExactly("ShipmentEvent.DISPATCH");
|
||||
|
||||
CallChain orderChain = engine.findChains(List.of(orderEntry), List.of(orderTrigger)).get(0);
|
||||
assertThat(orderChain.getTriggerPoint().getPolymorphicEvents()).containsExactly("OrderEvent.PAY");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user