a
This commit is contained in:
@@ -100,6 +100,9 @@ public final class BooleanConstraintEvaluator {
|
|||||||
if (isIncompleteDottedName(boundValue)) {
|
if (isIncompleteDottedName(boundValue)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (TruncatedEnumRefReconstructor.looksLikePackageTruncatedEnumRef(boundValue)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
if (boundValue.contains("(") && !boundValue.contains("valueOf")) {
|
if (boundValue.contains("(") && !boundValue.contains("valueOf")) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -879,6 +879,14 @@ public final class MachineEnumCanonicalizer {
|
|||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (context != null) {
|
||||||
|
String reconstructed = TruncatedEnumRefReconstructor.reconstruct(stripped, enumTypeFqn, context);
|
||||||
|
if (reconstructed != null && !reconstructed.equals(stripped)) {
|
||||||
|
stripped = reconstructed;
|
||||||
|
value = reconstructed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (stripped.startsWith(".") || stripped.endsWith(".") || stripped.contains("..")) {
|
if (stripped.startsWith(".") || stripped.endsWith(".") || stripped.contains("..")) {
|
||||||
// Truncated/malformed refs (e.g. com.example.DomainCommand.) are not canonical.
|
// Truncated/malformed refs (e.g. com.example.DomainCommand.) are not canonical.
|
||||||
return value;
|
return value;
|
||||||
|
|||||||
@@ -0,0 +1,224 @@
|
|||||||
|
package click.kamil.springstatemachineexporter.analysis.resolver;
|
||||||
|
|
||||||
|
import click.kamil.springstatemachineexporter.ast.common.CodebaseContext;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Repairs truncated enum references produced by older resolution bugs, e.g.
|
||||||
|
* {@code com.example.PAY} (package + constant, type segment dropped) →
|
||||||
|
* {@code com.example.OrderEvent.PAY} when the match is unique or matches a preferred type.
|
||||||
|
*/
|
||||||
|
public final class TruncatedEnumRefReconstructor {
|
||||||
|
|
||||||
|
private static final Pattern ENUM_CONSTANT = Pattern.compile("[A-Z_][A-Z0-9_]*");
|
||||||
|
|
||||||
|
private TruncatedEnumRefReconstructor() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return reconstructed FQN when uniquely determined; otherwise the original {@code value}
|
||||||
|
*/
|
||||||
|
public static String reconstruct(String value, CodebaseContext context) {
|
||||||
|
return reconstruct(value, null, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param preferredEnumTypeFqn optional machine/event type that disambiguates when several enums
|
||||||
|
* share the same constant name under a package prefix
|
||||||
|
* @return reconstructed FQN when uniquely determined; otherwise the original {@code value}
|
||||||
|
*/
|
||||||
|
public static String reconstruct(String value, String preferredEnumTypeFqn, CodebaseContext context) {
|
||||||
|
if (value == null || value.isBlank() || context == null) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
String stripped = stripQuotes(value.trim());
|
||||||
|
if (stripped.isBlank() || stripped.contains("(") || stripped.contains(")")) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
if (stripped.startsWith(".") || stripped.contains("..")) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type-only trailing dot (com.example.OrderEvent.) — no constant to recover.
|
||||||
|
if (stripped.endsWith(".")) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
int lastDot = stripped.lastIndexOf('.');
|
||||||
|
if (lastDot < 0) {
|
||||||
|
return reconstructBareConstant(stripped, preferredEnumTypeFqn, context, value);
|
||||||
|
}
|
||||||
|
if (lastDot == 0 || lastDot >= stripped.length() - 1) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
String prefix = stripped.substring(0, lastDot);
|
||||||
|
String constant = stripped.substring(lastDot + 1);
|
||||||
|
if (!ENUM_CONSTANT.matcher(constant).matches()) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Already a known Type.CONSTANT (simple or FQN) — normalize to indexed FQN when possible.
|
||||||
|
if (isKnownEnumType(prefix, context)) {
|
||||||
|
String canonical = canonicalConstantRef(prefix, constant, context);
|
||||||
|
return canonical != null ? canonical : value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Preferred type: classic truncation com.example.PAY + preferred com.example.OrderEvent.
|
||||||
|
if (preferredEnumTypeFqn != null
|
||||||
|
&& !preferredEnumTypeFqn.isBlank()
|
||||||
|
&& enumHasConstant(preferredEnumTypeFqn, constant, context)
|
||||||
|
&& prefixMatchesPreferredType(prefix, preferredEnumTypeFqn)) {
|
||||||
|
return preferredEnumTypeFqn + "." + constant;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> matches = findConstantsUnderPrefix(prefix, constant, context);
|
||||||
|
if (matches.size() == 1) {
|
||||||
|
return matches.get(0);
|
||||||
|
}
|
||||||
|
if (preferredEnumTypeFqn != null) {
|
||||||
|
for (String match : matches) {
|
||||||
|
if (match.startsWith(preferredEnumTypeFqn + ".")) {
|
||||||
|
return match;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* True when {@code value} looks like the old package+CONSTANT truncation
|
||||||
|
* ({@code com.example.PAY}) rather than a real {@code Type.CONSTANT} or routing key.
|
||||||
|
*/
|
||||||
|
public static boolean looksLikePackageTruncatedEnumRef(String value) {
|
||||||
|
if (value == null || value.isBlank() || value.contains("(")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
String stripped = stripQuotes(value.trim());
|
||||||
|
int lastDot = stripped.lastIndexOf('.');
|
||||||
|
if (lastDot <= 0 || lastDot >= stripped.length() - 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
String constant = stripped.substring(lastDot + 1);
|
||||||
|
if (!ENUM_CONSTANT.matcher(constant).matches()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
String prefix = stripped.substring(0, lastDot);
|
||||||
|
int prevDot = prefix.lastIndexOf('.');
|
||||||
|
String lastSegment = prevDot >= 0 ? prefix.substring(prevDot + 1) : prefix;
|
||||||
|
return !lastSegment.isEmpty() && Character.isLowerCase(lastSegment.charAt(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String reconstructBareConstant(
|
||||||
|
String constant, String preferredEnumTypeFqn, CodebaseContext context, String original) {
|
||||||
|
if (!ENUM_CONSTANT.matcher(constant).matches() || preferredEnumTypeFqn == null) {
|
||||||
|
return original;
|
||||||
|
}
|
||||||
|
if (enumHasConstant(preferredEnumTypeFqn, constant, context)) {
|
||||||
|
return preferredEnumTypeFqn + "." + constant;
|
||||||
|
}
|
||||||
|
return original;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean prefixMatchesPreferredType(String prefix, String preferredEnumTypeFqn) {
|
||||||
|
if (prefix.equals(preferredEnumTypeFqn)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
String preferredPackage = packageOf(preferredEnumTypeFqn);
|
||||||
|
if (prefix.equals(preferredPackage)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return preferredEnumTypeFqn.startsWith(prefix + ".");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<String> findConstantsUnderPrefix(String prefix, String constant, CodebaseContext context) {
|
||||||
|
LinkedHashSet<String> matches = new LinkedHashSet<>();
|
||||||
|
for (Map.Entry<String, List<String>> entry : context.getEnumValuesMap().entrySet()) {
|
||||||
|
String enumFqn = entry.getKey();
|
||||||
|
if (!enumUnderPrefix(enumFqn, prefix)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
String candidate = enumFqn + "." + constant;
|
||||||
|
List<String> values = entry.getValue();
|
||||||
|
if (values == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (values.contains(candidate)
|
||||||
|
|| values.stream().anyMatch(v -> v != null && v.endsWith("." + constant))) {
|
||||||
|
matches.add(candidate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new ArrayList<>(matches);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean enumUnderPrefix(String enumFqn, String prefix) {
|
||||||
|
if (enumFqn == null || prefix == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (enumFqn.equals(prefix)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (enumFqn.startsWith(prefix + ".")) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return prefix.equals(packageOf(enumFqn));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isKnownEnumType(String typeName, CodebaseContext context) {
|
||||||
|
if (typeName == null || typeName.isBlank()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (context.getEnumValuesMap().containsKey(typeName)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
List<String> values = context.getEnumValues(typeName);
|
||||||
|
return values != null && !values.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean enumHasConstant(String enumTypeFqn, String constant, CodebaseContext context) {
|
||||||
|
List<String> values = context.getEnumValues(enumTypeFqn);
|
||||||
|
if (values == null || values.isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
String suffix = "." + constant;
|
||||||
|
for (String value : values) {
|
||||||
|
if (value != null && (value.endsWith(suffix) || value.equals(constant))) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String canonicalConstantRef(String typeName, String constant, CodebaseContext context) {
|
||||||
|
List<String> values = context.getEnumValues(typeName);
|
||||||
|
if (values == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String suffix = "." + constant;
|
||||||
|
for (String value : values) {
|
||||||
|
if (value != null && value.endsWith(suffix)) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String packageOf(String typeFqn) {
|
||||||
|
int dot = typeFqn.lastIndexOf('.');
|
||||||
|
return dot > 0 ? typeFqn.substring(0, dot) : "";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String stripQuotes(String value) {
|
||||||
|
if (value.length() >= 2
|
||||||
|
&& ((value.startsWith("\"") && value.endsWith("\""))
|
||||||
|
|| (value.startsWith("'") && value.endsWith("'")))) {
|
||||||
|
return value.substring(1, value.length() - 1);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -595,9 +595,9 @@ public abstract class AbstractCallGraphEngine implements CallGraphEngine {
|
|||||||
String literal = sl.getLiteralValue();
|
String literal = sl.getLiteralValue();
|
||||||
if (literal != null && !literal.isBlank()) {
|
if (literal != null && !literal.isBlank()) {
|
||||||
if (enumTypeName != null) {
|
if (enumTypeName != null) {
|
||||||
polymorphicEvents.add(enumTypeName + "." + literal);
|
polymorphicEvents.add(reconstructEnumRef(enumTypeName + "." + literal));
|
||||||
} else {
|
} else {
|
||||||
polymorphicEvents.add(literal);
|
polymorphicEvents.add(reconstructEnumRef(literal));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
methodName = null;
|
methodName = null;
|
||||||
@@ -606,9 +606,9 @@ public abstract class AbstractCallGraphEngine implements CallGraphEngine {
|
|||||||
String constant = qn.getName().getIdentifier();
|
String constant = qn.getName().getIdentifier();
|
||||||
if (constant != null && !constant.isBlank()) {
|
if (constant != null && !constant.isBlank()) {
|
||||||
if (enumTypeName != null) {
|
if (enumTypeName != null) {
|
||||||
polymorphicEvents.add(enumTypeName + "." + constant);
|
polymorphicEvents.add(reconstructEnumRef(enumTypeName + "." + constant));
|
||||||
} else {
|
} else {
|
||||||
polymorphicEvents.add(constant);
|
polymorphicEvents.add(reconstructEnumRef(constant));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
methodName = null;
|
methodName = null;
|
||||||
@@ -617,9 +617,9 @@ public abstract class AbstractCallGraphEngine implements CallGraphEngine {
|
|||||||
String resolvedConstant = resolveEnumConstantFromArgument(sn.getIdentifier(), path, callGraph);
|
String resolvedConstant = resolveEnumConstantFromArgument(sn.getIdentifier(), path, callGraph);
|
||||||
if (resolvedConstant != null && !resolvedConstant.isBlank()) {
|
if (resolvedConstant != null && !resolvedConstant.isBlank()) {
|
||||||
if (enumTypeName != null) {
|
if (enumTypeName != null) {
|
||||||
polymorphicEvents.add(enumTypeName + "." + resolvedConstant);
|
polymorphicEvents.add(reconstructEnumRef(enumTypeName + "." + resolvedConstant));
|
||||||
} else {
|
} else {
|
||||||
polymorphicEvents.add(resolvedConstant);
|
polymorphicEvents.add(reconstructEnumRef(resolvedConstant));
|
||||||
}
|
}
|
||||||
methodName = null;
|
methodName = null;
|
||||||
handledStaticEnum = true;
|
handledStaticEnum = true;
|
||||||
@@ -2896,6 +2896,11 @@ public abstract class AbstractCallGraphEngine implements CallGraphEngine {
|
|||||||
return !simple.isEmpty() && Character.isUpperCase(simple.charAt(0));
|
return !simple.isEmpty() && Character.isUpperCase(simple.charAt(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String reconstructEnumRef(String value) {
|
||||||
|
return click.kamil.springstatemachineexporter.analysis.resolver.TruncatedEnumRefReconstructor
|
||||||
|
.reconstruct(value, context);
|
||||||
|
}
|
||||||
|
|
||||||
private String resolveEnumConstantFromArgument(String argName, List<String> path, Map<String, List<CallEdge>> callGraph) {
|
private String resolveEnumConstantFromArgument(String argName, List<String> path, Map<String, List<CallEdge>> callGraph) {
|
||||||
if (argName == null || path == null || path.size() < 2) {
|
if (argName == null || path == null || path.size() < 2) {
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package click.kamil.springstatemachineexporter.analysis.service;
|
|||||||
import click.kamil.springstatemachineexporter.analysis.model.CallEdge;
|
import click.kamil.springstatemachineexporter.analysis.model.CallEdge;
|
||||||
import click.kamil.springstatemachineexporter.analysis.resolver.BooleanConstraintEvaluator;
|
import click.kamil.springstatemachineexporter.analysis.resolver.BooleanConstraintEvaluator;
|
||||||
import click.kamil.springstatemachineexporter.analysis.resolver.ConstantResolver;
|
import click.kamil.springstatemachineexporter.analysis.resolver.ConstantResolver;
|
||||||
|
import click.kamil.springstatemachineexporter.analysis.resolver.TruncatedEnumRefReconstructor;
|
||||||
import click.kamil.springstatemachineexporter.ast.common.CodebaseContext;
|
import click.kamil.springstatemachineexporter.ast.common.CodebaseContext;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@@ -189,7 +190,10 @@ public class PathBindingEvaluator {
|
|||||||
&& FunctionalInterfaceTypes.isFunctionalInterface(typeResolver.getParameterType(target, i))) {
|
&& FunctionalInterfaceTypes.isFunctionalInterface(typeResolver.getParameterType(target, i))) {
|
||||||
List<String> constants = variableTracer.resolveConstantsFromCallSiteArgument(caller, target, i, edge);
|
List<String> constants = variableTracer.resolveConstantsFromCallSiteArgument(caller, target, i, edge);
|
||||||
if (constants.size() == 1) {
|
if (constants.size() == 1) {
|
||||||
bindings.put(paramName, constants.get(0));
|
String constant = maybeReconstruct(constants.get(0));
|
||||||
|
if (shouldStoreBinding(paramName, constant)) {
|
||||||
|
bindings.put(paramName, constant);
|
||||||
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -207,6 +211,10 @@ public class PathBindingEvaluator {
|
|||||||
if (BooleanConstraintEvaluator.isIncompleteDottedName(resolved)) {
|
if (BooleanConstraintEvaluator.isIncompleteDottedName(resolved)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
// Unrepaired package+CONSTANT truncation (com.example.PAY) is not a trustworthy binding.
|
||||||
|
if (TruncatedEnumRefReconstructor.looksLikePackageTruncatedEnumRef(resolved)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
if (resolved.contains("(") && !isEnumLikeConstant(resolved)) {
|
if (resolved.contains("(") && !isEnumLikeConstant(resolved)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -230,25 +238,32 @@ public class PathBindingEvaluator {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (rawValue.startsWith("\"") || isEnumLikeConstant(rawValue)) {
|
if (rawValue.startsWith("\"") || isEnumLikeConstant(rawValue)) {
|
||||||
return normalizeLiteral(rawValue);
|
return maybeReconstruct(normalizeLiteral(rawValue));
|
||||||
}
|
}
|
||||||
if (bindings.containsKey(rawValue)) {
|
if (bindings.containsKey(rawValue)) {
|
||||||
return bindings.get(rawValue);
|
return maybeReconstruct(bindings.get(rawValue));
|
||||||
}
|
}
|
||||||
String traced = variableTracer.traceLocalVariable(callerFqn, rawValue, bindings);
|
String traced = variableTracer.traceLocalVariable(callerFqn, rawValue, bindings);
|
||||||
if (traced != null && !traced.isBlank()) {
|
if (traced != null && !traced.isBlank()) {
|
||||||
if (traced.startsWith("\"") || isEnumLikeConstant(traced)) {
|
if (traced.startsWith("\"") || isEnumLikeConstant(traced)) {
|
||||||
return traced;
|
return maybeReconstruct(traced);
|
||||||
}
|
}
|
||||||
String fromCall = expressionResolver.resolveMethodCallFromSource(callerFqn, rawValue, bindings);
|
String fromCall = expressionResolver.resolveMethodCallFromSource(callerFqn, rawValue, bindings);
|
||||||
if (fromCall != null) {
|
if (fromCall != null) {
|
||||||
return fromCall;
|
return maybeReconstruct(fromCall);
|
||||||
}
|
}
|
||||||
if (!traced.equals(rawValue)) {
|
if (!traced.equals(rawValue)) {
|
||||||
return traced;
|
return maybeReconstruct(traced);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return rawValue;
|
return maybeReconstruct(rawValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String maybeReconstruct(String value) {
|
||||||
|
if (value == null || context == null) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
return TruncatedEnumRefReconstructor.reconstruct(value, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String normalizeLiteral(String value) {
|
private static String normalizeLiteral(String value) {
|
||||||
|
|||||||
@@ -185,6 +185,17 @@ class MachineEnumCanonicalizerTest {
|
|||||||
.isEqualTo(truncated);
|
.isEqualTo(truncated);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldReconstructPackageTruncatedEnumConstant(@TempDir Path tempDir) throws IOException {
|
||||||
|
writeSampleConfig(tempDir);
|
||||||
|
CodebaseContext context = new CodebaseContext();
|
||||||
|
context.scan(tempDir);
|
||||||
|
|
||||||
|
assertThat(MachineEnumCanonicalizer.canonicalizeLabel(
|
||||||
|
"com.example.order.PAY", "com.example.order.OrderEvent", context))
|
||||||
|
.isEqualTo("com.example.order.OrderEvent.PAY");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void shouldDenormalizeCorruptedPackageValueOfTriggerExpression() {
|
void shouldDenormalizeCorruptedPackageValueOfTriggerExpression() {
|
||||||
String corrupted = "com.abcd.aaa.MyEvent.valueOf(event)";
|
String corrupted = "com.abcd.aaa.MyEvent.valueOf(event)";
|
||||||
|
|||||||
@@ -0,0 +1,77 @@
|
|||||||
|
package click.kamil.springstatemachineexporter.analysis.resolver;
|
||||||
|
|
||||||
|
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 static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
class TruncatedEnumRefReconstructorTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldReconstructPackageTruncatedConstantWhenUnique(@TempDir Path tempDir) throws IOException {
|
||||||
|
CodebaseContext context = scanOrderAndDoc(tempDir, false);
|
||||||
|
|
||||||
|
assertThat(TruncatedEnumRefReconstructor.reconstruct("com.example.PAY", context))
|
||||||
|
.isEqualTo("com.example.OrderEvent.PAY");
|
||||||
|
assertThat(TruncatedEnumRefReconstructor.looksLikePackageTruncatedEnumRef("com.example.PAY"))
|
||||||
|
.isTrue();
|
||||||
|
assertThat(TruncatedEnumRefReconstructor.looksLikePackageTruncatedEnumRef("com.example.OrderEvent.PAY"))
|
||||||
|
.isFalse();
|
||||||
|
assertThat(TruncatedEnumRefReconstructor.looksLikePackageTruncatedEnumRef("order.pay"))
|
||||||
|
.isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldUsePreferredTypeWhenConstantIsAmbiguousAcrossEnums(@TempDir Path tempDir) throws IOException {
|
||||||
|
CodebaseContext context = scanOrderAndDoc(tempDir, true);
|
||||||
|
|
||||||
|
// Both OrderEvent and DocumentEvent have PAY under com.example
|
||||||
|
assertThat(TruncatedEnumRefReconstructor.reconstruct("com.example.PAY", context))
|
||||||
|
.isEqualTo("com.example.PAY");
|
||||||
|
assertThat(TruncatedEnumRefReconstructor.reconstruct(
|
||||||
|
"com.example.PAY", "com.example.OrderEvent", context))
|
||||||
|
.isEqualTo("com.example.OrderEvent.PAY");
|
||||||
|
assertThat(TruncatedEnumRefReconstructor.reconstruct(
|
||||||
|
"com.example.PAY", "com.example.DocumentEvent", context))
|
||||||
|
.isEqualTo("com.example.DocumentEvent.PAY");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldNotInventConstantForTrailingDotTypeRef(@TempDir Path tempDir) throws IOException {
|
||||||
|
CodebaseContext context = scanOrderAndDoc(tempDir, false);
|
||||||
|
|
||||||
|
assertThat(TruncatedEnumRefReconstructor.reconstruct("com.example.OrderEvent.", context))
|
||||||
|
.isEqualTo("com.example.OrderEvent.");
|
||||||
|
assertThat(TruncatedEnumRefReconstructor.reconstruct(
|
||||||
|
"com.example.OrderEvent.", "com.example.OrderEvent", context))
|
||||||
|
.isEqualTo("com.example.OrderEvent.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldCanonicalizeTruncatedPolyEventViaMachineEnumCanonicalizer(@TempDir Path tempDir) throws IOException {
|
||||||
|
CodebaseContext context = scanOrderAndDoc(tempDir, false);
|
||||||
|
|
||||||
|
assertThat(MachineEnumCanonicalizer.canonicalizeLabel(
|
||||||
|
"com.example.PAY", "com.example.OrderEvent", context))
|
||||||
|
.isEqualTo("com.example.OrderEvent.PAY");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static CodebaseContext scanOrderAndDoc(Path tempDir, boolean documentAlsoHasPay) throws IOException {
|
||||||
|
Path dir = tempDir.resolve("com/example");
|
||||||
|
Files.createDirectories(dir);
|
||||||
|
Files.writeString(dir.resolve("OrderEvent.java"),
|
||||||
|
"package com.example;\npublic enum OrderEvent { PAY, SHIP }\n");
|
||||||
|
String docConstants = documentAlsoHasPay ? "PAY, ARCHIVE" : "ARCHIVE";
|
||||||
|
Files.writeString(dir.resolve("DocumentEvent.java"),
|
||||||
|
"package com.example;\npublic enum DocumentEvent { " + docConstants + " }\n");
|
||||||
|
|
||||||
|
CodebaseContext context = new CodebaseContext();
|
||||||
|
context.scan(tempDir);
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -93,7 +93,7 @@ class PathBindingEvaluatorTest {
|
|||||||
Map<String, String> payBindings = evaluator.traceBindings(rawPaths.get(0), graph, pathFinder);
|
Map<String, String> payBindings = evaluator.traceBindings(rawPaths.get(0), graph, pathFinder);
|
||||||
assertThat(payBindings)
|
assertThat(payBindings)
|
||||||
.as("pay path bindings, compatible=%s", payPathCompatible)
|
.as("pay path bindings, compatible=%s", payPathCompatible)
|
||||||
.containsEntry("command", "DomainCommand.ORDER_PAY");
|
.containsEntry("command", "com.example.DomainCommand.ORDER_PAY");
|
||||||
|
|
||||||
EntryPoint entry = EntryPoint.builder().className("com.example.ApiController").methodName("pay").build();
|
EntryPoint entry = EntryPoint.builder().className("com.example.ApiController").methodName("pay").build();
|
||||||
TriggerPoint trigger = TriggerPoint.builder()
|
TriggerPoint trigger = TriggerPoint.builder()
|
||||||
|
|||||||
Reference in New Issue
Block a user