Improve REST path binding for enum path variables and valueOf

Treat enum @PathVariable parameters as internal triggers, expand multiple path placeholders via cartesian bindings, derive event cases from proven Enum.valueOf chains, and add enterprise HTML regression coverage.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-14 06:52:27 +02:00
parent 24fe8dfe00
commit 5d432e8893
5 changed files with 295 additions and 8 deletions

View File

@@ -198,4 +198,50 @@ class EntryPointBindingExpanderTest {
assertThat(variants).extracting(map -> map.get("machineType"))
.containsExactlyInAnyOrder("ORDER", "DOCUMENT");
}
@Test
void shouldExpandEventPathVariableFromPreservedEnumValueOfParam(@TempDir Path tempDir) throws Exception {
String source = """
package com.example;
public class OrderController {
OrderGateway gateway;
public void transition(String event) {
gateway.trigger(event);
}
}
class OrderGateway {
void trigger(String event) {
OrderEvent.valueOf(event.toUpperCase());
}
}
enum OrderEvent { PAY, SHIP }
""";
Files.writeString(tempDir.resolve("App.java"), source);
CodebaseContext context = new CodebaseContext();
context.setResolveBindings(true);
context.scan(tempDir);
HeuristicCallGraphEngine engine = new HeuristicCallGraphEngine(context);
Map<String, List<click.kamil.springstatemachineexporter.analysis.model.CallEdge>> graph = engine.buildCallGraph();
EntryPoint entryPoint = EntryPoint.builder()
.className("com.example.OrderController")
.methodName("transition")
.name("POST /api/order/{event}")
.parameters(List.of(EntryPoint.Parameter.builder()
.name("event")
.type("String")
.annotations(List.of("PathVariable"))
.build()))
.build();
List<Map<String, String>> variants =
EntryPointBindingExpander.expandPathVariableBindings(entryPoint, context, graph);
assertThat(variants).extracting(map -> map.get("event"))
.containsExactlyInAnyOrder("PAY", "SHIP");
assertThat(EntryPointBindingExpander.withResolvedPath(entryPoint, variants.get(0)).getName())
.isEqualTo("POST /api/order/PAY");
}
}

View File

@@ -35,6 +35,43 @@ class ExternalTriggerPolicyTest {
entryPoint, trigger, "com.example.Api.trigger", "event", null)).isTrue();
}
@Test
void pathVariableEnumParameterShouldBeInternal(@TempDir Path tempDir) throws Exception {
String source = """
package com.example;
import org.springframework.web.bind.annotation.*;
public class Api {
@PostMapping("/api/order/{event}")
public void transition(@PathVariable OrderEvent event) {
fire(event);
}
void fire(OrderEvent event) {}
}
enum OrderEvent { PAY, SHIP }
""";
Files.writeString(tempDir.resolve("Api.java"), source);
CodebaseContext context = new CodebaseContext();
context.scan(tempDir);
EntryPoint entryPoint = EntryPoint.builder()
.type(EntryPoint.Type.REST)
.name("POST /api/order/{event}")
.className("com.example.Api")
.methodName("transition")
.parameters(List.of(EntryPoint.Parameter.builder()
.name("event")
.type("com.example.OrderEvent")
.annotations(List.of("PathVariable"))
.build()))
.build();
TriggerPoint trigger = TriggerPoint.builder()
.event("event")
.build();
assertThat(ExternalTriggerPolicy.isExternalFromSource(
entryPoint, trigger, "com.example.Api.transition", "event", context)).isFalse();
}
@Test
void requestBodyEnumShouldBeInternal(@TempDir Path tempDir) throws Exception {
String source = """