Match HTML sidebar chains by entry point name when paths differ.
Expanded REST endpoints share the same controller method, so class/method matching grouped unrelated call chains; prefer entry point name when present and cover with enterprise HTML tests. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -394,10 +394,20 @@
|
||||
return isLifecycleChain(c) || isHighlightableChain(c) || isUnresolvedExternalChain(c);
|
||||
}
|
||||
|
||||
function entryPointsMatch(chainEntryPoint, sidebarEntryPoint) {
|
||||
if (!chainEntryPoint || !sidebarEntryPoint) {
|
||||
return false;
|
||||
}
|
||||
if (chainEntryPoint.name && sidebarEntryPoint.name) {
|
||||
return chainEntryPoint.name === sidebarEntryPoint.name;
|
||||
}
|
||||
return chainEntryPoint.className === sidebarEntryPoint.className
|
||||
&& chainEntryPoint.methodName === sidebarEntryPoint.methodName;
|
||||
}
|
||||
|
||||
function chainsForEntryPoint(ep) {
|
||||
return (metadata.metadata.callChains || []).filter(c =>
|
||||
c.entryPoint.className === ep.className &&
|
||||
c.entryPoint.methodName === ep.methodName &&
|
||||
entryPointsMatch(c.entryPoint, ep) &&
|
||||
isSidebarChain(c)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -13,16 +13,16 @@ import java.util.List;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Enterprise dedicated REST endpoints must produce HTML explorer link keys end-to-end;
|
||||
* generic {@code {event}} dispatchers remain unresolved external.
|
||||
* Enterprise dedicated and expanded generic REST endpoints must produce HTML explorer link keys end-to-end.
|
||||
*/
|
||||
class EnterpriseDedicatedEndpointHtmlTest {
|
||||
|
||||
private static final String PAY_ENDPOINT = "POST /api/machine/order/pay";
|
||||
private static final String GENERIC_ENDPOINT = "POST /api/machine/{machineType}/transition/{event}";
|
||||
private static final String ORDER_PAY = "POST /api/machine/ORDER/transition/PAY";
|
||||
private static final String ORDER_SHIP = "POST /api/machine/ORDER/transition/SHIP";
|
||||
|
||||
@Test
|
||||
void htmlShouldExposeLinkKeysForDedicatedPayEndpoint(@TempDir Path tempDir) throws Exception {
|
||||
void htmlShouldExposeLinkKeysForDedicatedAndExpandedOrderEndpoints(@TempDir Path tempDir) throws Exception {
|
||||
Path enterprise = findProjectRoot().resolve("state_machines/state_machine_enterprise");
|
||||
ExportService exportService = new ExportService(List.of(new HtmlExporter(), new JsonExporter()));
|
||||
exportService.runExporter(enterprise, tempDir, List.of("html"), true, List.of(), null, null,
|
||||
@@ -33,11 +33,14 @@ class EnterpriseDedicatedEndpointHtmlTest {
|
||||
String html = Files.readString(htmlFile);
|
||||
|
||||
assertThat(html).contains(PAY_ENDPOINT);
|
||||
assertThat(html).contains(ORDER_PAY);
|
||||
assertThat(html).contains(ORDER_SHIP);
|
||||
assertThat(html).contains("function entryPointsMatch(chainEntryPoint, sidebarEntryPoint)");
|
||||
assertThat(html).contains("\"linkResolution\" : \"RESOLVED\"");
|
||||
assertThat(html).contains("#link_");
|
||||
assertThat(html).contains("\"linkKey\"");
|
||||
assertThat(html).contains(GENERIC_ENDPOINT);
|
||||
assertThat(html).contains("linkResolution === 'UNRESOLVED_EXTERNAL'");
|
||||
assertThat(html).contains("#link_OrderState_NEW__OrderEvent_PAY");
|
||||
assertThat(html).contains("#link_OrderState_PENDING__OrderEvent_SHIP");
|
||||
assertThat(html).contains("\"linkKey\" : \"OrderState_NEW__OrderEvent_PAY\"");
|
||||
assertThat(html).contains("\"linkKey\" : \"OrderState_PENDING__OrderEvent_SHIP\"");
|
||||
}
|
||||
|
||||
private static Path findOrderMachineHtml(Path outputDir) throws Exception {
|
||||
|
||||
@@ -410,6 +410,78 @@ class HtmlTransitionLinkConsistencyTest {
|
||||
assertThat(html).contains("#link_OrderState_NEW__OrderEvent_PAY");
|
||||
}
|
||||
|
||||
@Test
|
||||
void htmlShouldMatchCallChainsByEntryPointNameWhenControllerMethodIsShared() {
|
||||
EntryPoint payTransition = EntryPoint.builder()
|
||||
.type(EntryPoint.Type.REST)
|
||||
.name("POST /api/machine/ORDER/transition/PAY")
|
||||
.className("com.example.web.StateMachineController")
|
||||
.methodName("transition")
|
||||
.build();
|
||||
EntryPoint shipTransition = EntryPoint.builder()
|
||||
.type(EntryPoint.Type.REST)
|
||||
.name("POST /api/machine/ORDER/transition/SHIP")
|
||||
.className("com.example.web.StateMachineController")
|
||||
.methodName("transition")
|
||||
.build();
|
||||
|
||||
CallChain payChain = CallChain.builder()
|
||||
.entryPoint(payTransition)
|
||||
.linkResolution(LinkResolution.RESOLVED)
|
||||
.triggerPoint(TriggerPoint.builder()
|
||||
.event("com.example.order.OrderEvent.PAY")
|
||||
.build())
|
||||
.matchedTransitions(List.of(MatchedTransition.builder()
|
||||
.sourceState("com.example.order.OrderState.NEW")
|
||||
.targetState("com.example.order.OrderState.PAID")
|
||||
.event("com.example.order.OrderEvent.PAY")
|
||||
.linkKey("OrderState_NEW__OrderEvent_PAY")
|
||||
.build()))
|
||||
.build();
|
||||
CallChain shipChain = CallChain.builder()
|
||||
.entryPoint(shipTransition)
|
||||
.linkResolution(LinkResolution.RESOLVED)
|
||||
.triggerPoint(TriggerPoint.builder()
|
||||
.event("com.example.order.OrderEvent.SHIP")
|
||||
.build())
|
||||
.matchedTransitions(List.of(MatchedTransition.builder()
|
||||
.sourceState("com.example.order.OrderState.PAID")
|
||||
.targetState("com.example.order.OrderState.SHIPPED")
|
||||
.event("com.example.order.OrderEvent.SHIP")
|
||||
.linkKey("OrderState_PAID__OrderEvent_SHIP")
|
||||
.build()))
|
||||
.build();
|
||||
|
||||
AnalysisResult result = AnalysisResult.builder()
|
||||
.name("com.example.config.OrderStateMachineConfiguration")
|
||||
.stateTypeFqn("com.example.order.OrderState")
|
||||
.eventTypeFqn("com.example.order.OrderEvent")
|
||||
.transitions(List.of(
|
||||
shortTransition(
|
||||
"com.example.order.OrderEvent.PAY",
|
||||
"com.example.order.OrderState.NEW",
|
||||
"com.example.order.OrderState.PAID"),
|
||||
shortTransition(
|
||||
"com.example.order.OrderEvent.SHIP",
|
||||
"com.example.order.OrderState.PAID",
|
||||
"com.example.order.OrderState.SHIPPED")))
|
||||
.startStates(Set.of("com.example.order.OrderState.NEW"))
|
||||
.endStates(Set.of("com.example.order.OrderState.SHIPPED"))
|
||||
.metadata(CodebaseMetadata.builder()
|
||||
.entryPoints(List.of(payTransition, shipTransition))
|
||||
.callChains(List.of(payChain, shipChain))
|
||||
.build())
|
||||
.build();
|
||||
|
||||
String html = new HtmlExporter().export(result, ExportOptions.builder().includeMetadataPane(true).build());
|
||||
|
||||
assertThat(html).contains("function entryPointsMatch(chainEntryPoint, sidebarEntryPoint)");
|
||||
assertThat(html).contains("POST /api/machine/ORDER/transition/PAY");
|
||||
assertThat(html).contains("POST /api/machine/ORDER/transition/SHIP");
|
||||
assertThat(html).contains("\"linkKey\" : \"OrderState_NEW__OrderEvent_PAY\"");
|
||||
assertThat(html).contains("\"linkKey\" : \"OrderState_PAID__OrderEvent_SHIP\"");
|
||||
}
|
||||
|
||||
@Test
|
||||
void ambiguousWidenHtmlShouldExposeMatchedTransitionLinkKeys() {
|
||||
EntryPoint ambiguousEp = EntryPoint.builder()
|
||||
|
||||
Reference in New Issue
Block a user