diff --git a/state_machine_exporter/src/main/java/click/kamil/springstatemachineexporter/analysis/service/VariableTracer.java b/state_machine_exporter/src/main/java/click/kamil/springstatemachineexporter/analysis/service/VariableTracer.java index bc91aa4..3869825 100644 --- a/state_machine_exporter/src/main/java/click/kamil/springstatemachineexporter/analysis/service/VariableTracer.java +++ b/state_machine_exporter/src/main/java/click/kamil/springstatemachineexporter/analysis/service/VariableTracer.java @@ -502,13 +502,13 @@ public class VariableTracer { public String resolveArgumentValue(String argValue, String callerMethod, List path, int pathIndex, Map> callGraph) { if (argValue == null) return null; - if (argValue.contains(".") || argValue.startsWith("new ") || argValue.matches(".*[0-9].*")) { + if (argValue.contains(".") || argValue.startsWith("new ") || argValue.matches(".*[0-9].*") || (argValue.startsWith("\"") && argValue.endsWith("\""))) { return argValue; } String tracedLocal = traceLocalVariable(callerMethod, argValue); if (tracedLocal != null && !tracedLocal.equals(argValue)) { - if (tracedLocal.contains(".") || tracedLocal.startsWith("new ") || tracedLocal.matches(".*[0-9].*") || tracedLocal.contains("(")) { + if (tracedLocal.contains(".") || tracedLocal.startsWith("new ") || tracedLocal.matches(".*[0-9].*") || tracedLocal.contains("(") || (tracedLocal.startsWith("\"") && tracedLocal.endsWith("\""))) { return tracedLocal; } argValue = tracedLocal; diff --git a/state_machine_exporter/src/test/java/click/kamil/springstatemachineexporter/analysis/service/VariableTracerTest.java b/state_machine_exporter/src/test/java/click/kamil/springstatemachineexporter/analysis/service/VariableTracerTest.java new file mode 100644 index 0000000..d96dd28 --- /dev/null +++ b/state_machine_exporter/src/test/java/click/kamil/springstatemachineexporter/analysis/service/VariableTracerTest.java @@ -0,0 +1,69 @@ +package click.kamil.springstatemachineexporter.analysis.service; + +import click.kamil.springstatemachineexporter.analysis.resolver.ConstantResolver; +import click.kamil.springstatemachineexporter.ast.common.CodebaseContext; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; + +class VariableTracerTest { + + @Test + void shouldShortCircuitLiteralStringResolution() { + CodebaseContext context = new CodebaseContext(); + VariableTracer tracer = new VariableTracer(context, new ConstantResolver()); + + // Even with an empty context and call graph, passing a raw String literal should return instantly + // without trying to recursively traverse the path or crash. + String resolved = tracer.resolveArgumentValue("\"PAY_EVENT\"", "com.example.Order.process", List.of("com.example.Order.process"), 0, Map.of()); + + assertThat(resolved).isEqualTo("\"PAY_EVENT\""); + } + + @Test + void shouldBridgePolymorphicParametersPositionally(@TempDir Path tempDir) throws IOException { + Path comFoo = tempDir.resolve("com/foo"); + Files.createDirectories(comFoo); + + // Setup an interface and implementation with DIFFERENT parameter names + Files.writeString(comFoo.resolve("PaymentService.java"), + "package com.foo;\n" + + "public interface PaymentService {\n" + + " void submit(String eventName);\n" + + "}" + ); + Files.writeString(comFoo.resolve("PaymentServiceImpl.java"), + "package com.foo;\n" + + "public class PaymentServiceImpl implements PaymentService {\n" + + " @Override\n" + + " public void submit(String e) {}\n" + + "}" + ); + + CodebaseContext context = new CodebaseContext(); + context.scan(tempDir); + + VariableTracer tracer = new VariableTracer(context, new ConstantResolver()); + + // Simulate tracing from the Interface backwards to the Implementation. + // Because this is a polymorphic bridge, there is NO direct call edge in the AST call graph. + Map params = tracer.buildParameterValuesMap( + "com.foo.PaymentService.submit", + "com.foo.PaymentServiceImpl.submit", + Map.of(), // Empty call graph (no direct edges) + List.of("com.foo.PaymentService.submit", "com.foo.PaymentServiceImpl.submit"), + 0 + ); + + // The tracer should have scanned both ASTs and mapped parameter 0 -> parameter 0 + assertThat(params).hasSize(1); + assertThat(params).containsEntry("e", "eventName"); + } +}