GH-598 - Invalid dependency into named interface now gets reported properly.

This commit is contained in:
Oliver Drotbohm
2024-05-16 12:34:28 +02:00
parent 6b3e9768d8
commit a6ed890cc0
4 changed files with 60 additions and 2 deletions

View File

@@ -579,6 +579,10 @@ public class ApplicationModule {
.collect(Classes.toClasses());
}
private String getQualifiedName(NamedInterface namedInterface) {
return namedInterface.getQualifiedName(getName());
}
private static Classes filterSpringBeans(JavaPackage source) {
Map<Boolean, List<JavaClass>> collect = source.that(isConfiguration()).stream() //
@@ -971,8 +975,20 @@ public class ApplicationModule {
// Check explicitly defined allowed targets
if (!declaredDependencies.isAllowedDependency(target)) {
var message = "Module '%s' depends on module '%s' via %s -> %s. Allowed targets: %s." //
.formatted(originModule.getName(), targetModule.getName(), source.getName(), target.getName(),
var targetNamedInterfaces = targetModule.getNamedInterfaces()
.getNamedInterfacesContaining(target)
.filter(NamedInterface::isNamed)
.toList();
var targetString = targetNamedInterfaces.isEmpty()
? "module '%s'".formatted(targetModule.getName())
: "named interface(s) '%s'".formatted(
targetNamedInterfaces.stream()
.map(targetModule::getQualifiedName)
.collect(Collectors.joining(", ")));
var message = "Module '%s' depends on %s via %s -> %s. Allowed targets: %s." //
.formatted(originModule.getName(), targetString, source.getName(), target.getName(),
declaredDependencies.toString());
return violations.and(new Violation(message));

View File

@@ -125,6 +125,10 @@ public class NamedInterface implements Iterable<JavaClass> {
return name;
}
String getQualifiedName(String qualifier) {
return qualifier + " :: " + name;
}
/**
* Returns whether this is the unnamed (implicit) {@link NamedInterface}.
*/
@@ -132,6 +136,13 @@ public class NamedInterface implements Iterable<JavaClass> {
return name.equals(UNNAMED_NAME);
}
/**
* Return whether this {@link NamedInterface} has an explicit name.
*/
public boolean isNamed() {
return !isUnnamed();
}
/**
* Returns whether the {@link NamedInterface} contains the given {@link JavaClass}.
*

View File

@@ -138,6 +138,20 @@ public class NamedInterfaces implements Iterable<NamedInterface> {
.orElseThrow(() -> new IllegalStateException("No unnamed interface found!"));
}
/**
* Returns all named interfaces that contain the given type.
*
* @param type must not be {@literal null}.
* @return will never be {@literal null}.
*/
public Stream<NamedInterface> getNamedInterfacesContaining(JavaClass type) {
Assert.notNull(type, "Type must not be null!");
return namedInterfaces.stream()
.filter(it -> it.contains(type));
}
/*
* (non-Javadoc)
* @see java.lang.Iterable#iterator()
@@ -183,6 +197,12 @@ public class NamedInterfaces implements Iterable<NamedInterface> {
return new NamedInterfaces(namedInterfaces);
}
Stream<NamedInterface> getNamedInterfacesContaining(Class<?> type) {
return namedInterfaces.stream()
.filter(it -> it.contains(type));
}
private static NamedInterfaces of(NamedInterface interfaces) {
return new NamedInterfaces(List.of(interfaces));
}

View File

@@ -64,6 +64,17 @@ class NamedInterfacesUnitTests {
.withMessageContaining(InvalidDefaultNamedInterface.class.getSimpleName());
}
@Test // GH-595
void detectsNamedInterfacesATypeIsContainedIn() {
var javaPackage = TestUtils.getPackage(RootType.class);
var interfaces = NamedInterfaces.discoverNamedInterfaces(javaPackage);
assertThat(interfaces.getNamedInterfacesContaining(AdditionalSpiType.class))
.extracting(NamedInterface::getName)
.containsExactlyInAnyOrder("spi", "kpi");
}
private static void assertInterfaceContains(NamedInterfaces interfaces, String name, Class<?>... types) {
var classNames = Arrays.stream(types).map(Class::getName).toArray(String[]::new);