Align enricher pipeline with context-aware enum canonicalization

Use label-only canonicalization for shared-infrastructure triggers, resolve valueOf enum types from JDT bindings when available, and allow trusted valueOf widens using the machine event type when the trigger omits type FQNs.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-14 06:13:34 +02:00
parent 5c0d9c466d
commit 5bc65be30d
6 changed files with 112 additions and 10 deletions

View File

@@ -60,6 +60,20 @@ class CallChainLinkPolicyTest {
assertThat(CallChainLinkPolicy.shouldFailClosedOnAmbiguousCallGraphWiden(trigger)).isFalse();
}
@Test
void shouldAllowTrustedWidenUsingMachineEventTypeWhenTriggerTypeIsNull() {
TriggerPoint trigger = TriggerPoint.builder()
.ambiguous(true)
.event("OrderEvent.valueOf(eventStr)")
.polymorphicEvents(List.of("com.example.OrderEvent.PAY", "com.example.OrderEvent.SHIP"))
.build();
assertThat(CallChainLinkPolicy.isTrustedEnumPolymorphicWiden(
trigger, "com.example.OrderEvent")).isTrue();
assertThat(CallChainLinkPolicy.shouldFailClosedOnAmbiguousCallGraphWiden(
trigger, "com.example.OrderEvent")).isFalse();
}
@Test
void shouldNotTrustImportStylePolymorphicWidenWithoutPackage() {
TriggerPoint trigger = TriggerPoint.builder()

View File

@@ -1,6 +1,7 @@
package click.kamil.springstatemachineexporter.analysis.service;
import click.kamil.springstatemachineexporter.analysis.enricher.TransitionLinkerEnricher;
import click.kamil.springstatemachineexporter.analysis.enricher.TriggerCanonicalizationEnricher;
import click.kamil.springstatemachineexporter.analysis.model.AnalysisResult;
import click.kamil.springstatemachineexporter.analysis.model.CallChain;
import click.kamil.springstatemachineexporter.analysis.model.CodebaseMetadata;
@@ -224,5 +225,39 @@ class AmbiguousSimpleEnumLinkingTest {
assertThat(linked.getMatchedTransitions()).isNullOrEmpty();
assertThat(linked.getLinkResolution()).isIn(LinkResolution.NO_MATCH, LinkResolution.AMBIGUOUS_WIDEN);
}
@Test
void shouldNotRewriteAmbiguousImportStylePolyDuringTriggerCanonicalization(@TempDir Path tempDir) throws Exception {
Files.createDirectories(tempDir.resolve("a"));
Files.createDirectories(tempDir.resolve("b"));
Files.writeString(tempDir.resolve("a/OrderEvent.java"),
"package a; public enum OrderEvent { PAY }");
Files.writeString(tempDir.resolve("b/OrderEvent.java"),
"package b; public enum OrderEvent { CANCEL }");
CodebaseContext context = new CodebaseContext();
context.scan(tempDir);
TriggerPoint trigger = TriggerPoint.builder()
.event("OrderEvent.valueOf(eventStr)")
.polymorphicEvents(List.of("OrderEvent.PAY", "OrderEvent.SHIP"))
.ambiguous(true)
.build();
AnalysisResult result = AnalysisResult.builder()
.name("com.example.OrderStateMachineConfig")
.eventTypeFqn("a.OrderEvent")
.transitions(List.of())
.metadata(CodebaseMetadata.builder()
.callChains(List.of(CallChain.builder().triggerPoint(trigger).build()))
.build())
.build();
new TriggerCanonicalizationEnricher().enrich(result, context, null);
TriggerPoint canonical = result.getMetadata().getCallChains().get(0).getTriggerPoint();
assertThat(canonical.getPolymorphicEvents())
.containsExactly("OrderEvent.PAY", "OrderEvent.SHIP");
}
}