more fixes 2

This commit is contained in:
2026-06-20 07:57:01 +02:00
parent 3ac057618f
commit c37aa92c78
4 changed files with 266 additions and 12 deletions

View File

@@ -192,4 +192,30 @@ class TransitionLinkerEnricherTest {
CallChain updatedChain = result.getMetadata().getCallChains().get(0);
assertThat(updatedChain.getMatchedTransitions()).hasSize(1);
}
}
@Test
void shouldNotFalsePositiveOnSubstringContains() {
// This test proves that the heuristic is entirely removed and false positives are eliminated
Transition t1 = new Transition();
t1.setSourceStates(List.of(State.of("NEW", "NEW")));
t1.setTargetStates(List.of(State.of("PAID", "PAID")));
t1.setEvent(Event.of("PAY", "PAY"));
// Trigger point sends "PAYMENT" which contains "PAY". Under the old heuristics, this would falsely match!
CallChain chain = CallChain.builder()
.triggerPoint(TriggerPoint.builder().event("PAYMENT").build())
.build();
AnalysisResult result = AnalysisResult.builder()
.transitions(List.of(t1))
.metadata(CodebaseMetadata.builder().callChains(List.of(chain)).build())
.build();
enricher.enrich(result, null, null);
// It should NOT match, because "PAYMENT" != "PAY" strictly.
CallChain updatedChain = result.getMetadata().getCallChains().get(0);
assertThat(updatedChain.getMatchedTransitions()).isNullOrEmpty();
}
}

View File

@@ -1337,4 +1337,187 @@ class CallGraphBuilderTest {
org.assertj.core.api.Assertions.assertThat(chains.get(0).getTriggerPoint().getPolymorphicEvents()).containsExactlyInAnyOrder("STRING_LITERAL_EVENT");
}
@Test
void shouldResolveConstantsFromInlineInstantiation() throws IOException {
String source = """
package com.example;
public class OrderController {
private OrderService service;
public void handleStatus() {
service.process(new DomainEvent().getEvent());
}
}
class DomainEvent {
public String getEvent() { return "INLINE_EVENT"; }
}
class OrderService {
public void process(String event) {}
}
""";
Path tempDir = Files.createTempDirectory("callgraph_inline_inst");
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()).containsExactlyInAnyOrder("INLINE_EVENT");
}
@Test
void shouldResolveConstantsFromInlineInstantiationWithArguments() throws IOException {
String source = """
package com.example;
public class OrderController {
private OrderService service;
public void handleStatus() {
service.process(new DomainEvent("PAYLOAD_ID").getEvent());
}
}
class DomainEvent {
private String id;
public DomainEvent(String id) { this.id = id; }
public String getEvent() { return "INLINE_EVENT_WITH_ARGS"; }
}
class OrderService {
public void process(String event) {}
}
""";
Path tempDir = Files.createTempDirectory("callgraph_inline_inst_args");
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()).containsExactlyInAnyOrder("INLINE_EVENT_WITH_ARGS");
}
@Test
void shouldResolveConstantsFromInlineInstantiationWithGenerics() throws IOException {
String source = """
package com.example;
public class OrderController {
private OrderService service;
public void handleStatus() {
service.process(new DomainEvent<String>().getEvent());
}
}
class DomainEvent<T> {
public String getEvent() { return "INLINE_EVENT_WITH_GENERICS"; }
}
class OrderService {
public void process(String event) {}
}
""";
Path tempDir = Files.createTempDirectory("callgraph_inline_inst_gen");
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()).containsExactlyInAnyOrder("INLINE_EVENT_WITH_GENERICS");
}
@Test
void shouldResolveConstantsFromInlineInstantiationInheritedMethod() throws IOException {
String source = """
package com.example;
public class OrderController {
private OrderService service;
public void handleStatus() {
service.process(new SpecificDomainEvent().getEvent());
}
}
abstract class BaseDomainEvent {
public String getEvent() { return "BASE_EVENT_RETURN"; }
}
class SpecificDomainEvent extends BaseDomainEvent {
// no getEvent here, it inherits it!
}
class OrderService {
public void process(String event) {}
}
""";
Path tempDir = Files.createTempDirectory("callgraph_inline_inst_hier");
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()).containsExactlyInAnyOrder("BASE_EVENT_RETURN");
}
@Test
void shouldResolveConstantsFromInlineInstantiationWithComplexArguments() throws IOException {
String source = """
package com.example;
public class OrderController {
private OrderService service;
public void handleStatus(Order o, String abc) {
service.process(new DomainEvent(o.getId(), abc).getEvent());
}
}
class Order {
public String getId() { return "123"; }
}
class DomainEvent {
private String id;
private String extra;
public DomainEvent(String id, String extra) { this.id = id; this.extra = extra; }
public String getEvent() { return "INLINE_EVENT_COMPLEX_ARGS"; }
}
class OrderService {
public void process(String event) {}
}
""";
Path tempDir = Files.createTempDirectory("callgraph_inline_inst_complex_args");
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()).containsExactlyInAnyOrder("INLINE_EVENT_COMPLEX_ARGS");
}
}