chore: Initial import of FLEX training material
This commit is contained in:
parent
c01246d4f7
commit
12235acc42
1020 changed files with 53940 additions and 0 deletions
|
|
@ -0,0 +1,157 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>de.accso</groupId>
|
||||
<artifactId>flexinale-distributed</artifactId>
|
||||
<version>2024.3.0</version>
|
||||
<relativePath>../pom.xml</relativePath> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
|
||||
<artifactId>flexinale-distributed-test-architecture</artifactId>
|
||||
<version>2024.3.0</version>
|
||||
<name>Flexinale Distributed Test - architecture tests</name>
|
||||
<description>Flexinale - FLEX case-study "film festival", distributed services - architecture tests</description>
|
||||
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.poi</groupId>
|
||||
<artifactId>poi</artifactId>
|
||||
<version>${apache-poi.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.poi</groupId>
|
||||
<artifactId>poi-ooxml</artifactId>
|
||||
<version>${apache-poi.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>de.accso</groupId>
|
||||
<artifactId>flexinale-distributed-besucherportal</artifactId>
|
||||
<version>2024.3.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>de.accso</groupId>
|
||||
<artifactId>flexinale-distributed-common</artifactId>
|
||||
<version>2024.3.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>de.accso</groupId>
|
||||
<artifactId>flexinale-distributed-common</artifactId>
|
||||
<version>2024.3.0</version>
|
||||
<type>test-jar</type>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>de.accso</groupId>
|
||||
<artifactId>flexinale-distributed-security</artifactId>
|
||||
<version>2024.3.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>de.accso</groupId>
|
||||
<artifactId>flexinale-distributed-security_api_contract</artifactId>
|
||||
<version>2024.3.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>de.accso</groupId>
|
||||
<artifactId>flexinale-distributed-backoffice</artifactId>
|
||||
<version>2024.3.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>de.accso</groupId>
|
||||
<artifactId>flexinale-distributed-backoffice_api_contract</artifactId>
|
||||
<version>2024.3.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>de.accso</groupId>
|
||||
<artifactId>flexinale-distributed-ticketing</artifactId>
|
||||
<version>2024.3.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>de.accso</groupId>
|
||||
<artifactId>flexinale-distributed-ticketing_api_contract</artifactId>
|
||||
<version>2024.3.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<version>${maven-jar-plugin.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>test-jar</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>com.github.spotbugs</groupId>
|
||||
<artifactId>spotbugs-maven-plugin</artifactId>
|
||||
<version>${spotbugs-maven-plugin.version}</version>
|
||||
<configuration>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>com.h3xstream.findsecbugs</groupId>
|
||||
<artifactId>findsecbugs-plugin</artifactId>
|
||||
<version>${findsecbugs-maven-plugin.version}</version>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</configuration>
|
||||
<dependencies>
|
||||
<!-- overwrite dependency on spotbugs if you want to specify the version of spotbugs -->
|
||||
<dependency>
|
||||
<groupId>com.github.spotbugs</groupId>
|
||||
<artifactId>spotbugs</artifactId>
|
||||
<version>${spotbugs.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>verify</phase>
|
||||
<goals>
|
||||
<goal>check</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Distributed - Component Dependencies Test" type="JUnit" factoryName="JUnit" folderName="ArchitectureTests Distributed">
|
||||
<module name="flexinale-distributed-test-architecture" />
|
||||
<extension name="coverage">
|
||||
<pattern>
|
||||
<option name="PATTERN" value="architecturetests.*" />
|
||||
<option name="ENABLED" value="true" />
|
||||
</pattern>
|
||||
</extension>
|
||||
<option name="PACKAGE_NAME" value="architecturetests" />
|
||||
<option name="MAIN_CLASS_NAME" value="architecturetests.ComponentDependenciesTest" />
|
||||
<option name="METHOD_NAME" value="" />
|
||||
<option name="TEST_OBJECT" value="class" />
|
||||
<method v="2">
|
||||
<option name="Make" enabled="true" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Distributed - Domain Classes Use Correct Interfaces And Internal Structure Test" type="JUnit" factoryName="JUnit" folderName="ArchitectureTests Distributed">
|
||||
<module name="flexinale-distributed-test-architecture" />
|
||||
<extension name="coverage">
|
||||
<pattern>
|
||||
<option name="PATTERN" value="architecturetests.*" />
|
||||
<option name="ENABLED" value="true" />
|
||||
</pattern>
|
||||
</extension>
|
||||
<option name="PACKAGE_NAME" value="architecturetests" />
|
||||
<option name="MAIN_CLASS_NAME" value="architecturetests.InternalStructureOfDomainClassesUsingCorrectInterfacesAndAttributeTypesTest" />
|
||||
<option name="METHOD_NAME" value="" />
|
||||
<option name="TEST_OBJECT" value="class" />
|
||||
<method v="2">
|
||||
<option name="Make" enabled="true" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Distributed - Entities Use Correct Interfaces And Internal Structure Test" type="JUnit" factoryName="JUnit" folderName="ArchitectureTests Distributed">
|
||||
<module name="flexinale-distributed-test-architecture" />
|
||||
<extension name="coverage">
|
||||
<pattern>
|
||||
<option name="PATTERN" value="architecturetests.*" />
|
||||
<option name="ENABLED" value="true" />
|
||||
</pattern>
|
||||
</extension>
|
||||
<option name="PACKAGE_NAME" value="architecturetests" />
|
||||
<option name="MAIN_CLASS_NAME" value="architecturetests.InternalStructureOfEntityClassesUsingCorrectInterfacesAndAttributeTypesTest" />
|
||||
<option name="METHOD_NAME" value="" />
|
||||
<option name="TEST_OBJECT" value="class" />
|
||||
<method v="2">
|
||||
<option name="Make" enabled="true" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Distributed - Event Classes Use Correct Interfaces And Internal Structure Test" type="JUnit" factoryName="JUnit" folderName="ArchitectureTests Distributed">
|
||||
<module name="flexinale-distributed-test-architecture" />
|
||||
<extension name="coverage">
|
||||
<pattern>
|
||||
<option name="PATTERN" value="architecturetests.*" />
|
||||
<option name="ENABLED" value="true" />
|
||||
</pattern>
|
||||
</extension>
|
||||
<option name="PACKAGE_NAME" value="architecturetests" />
|
||||
<option name="MAIN_CLASS_NAME" value="architecturetests.InternalStructureOfEventClassesUsingCorrectInterfacesAndAttributeTypesTest" />
|
||||
<option name="METHOD_NAME" value="" />
|
||||
<option name="TEST_OBJECT" value="class" />
|
||||
<method v="2">
|
||||
<option name="Make" enabled="true" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Distributed - No Dependencies To Spring Test" type="JUnit" factoryName="JUnit" folderName="ArchitectureTests Distributed">
|
||||
<module name="flexinale-distributed-test-architecture" />
|
||||
<extension name="coverage">
|
||||
<pattern>
|
||||
<option name="PATTERN" value="architecturetests.*" />
|
||||
<option name="ENABLED" value="true" />
|
||||
</pattern>
|
||||
</extension>
|
||||
<option name="PACKAGE_NAME" value="architecturetests" />
|
||||
<option name="MAIN_CLASS_NAME" value="architecturetests.NoDependenciesToSpringTest" />
|
||||
<option name="METHOD_NAME" value="" />
|
||||
<option name="TEST_OBJECT" value="class" />
|
||||
<method v="2">
|
||||
<option name="Make" enabled="true" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Distributed - Onion Dependencies Test" type="JUnit" factoryName="JUnit" folderName="ArchitectureTests Distributed">
|
||||
<module name="flexinale-distributed-test-architecture" />
|
||||
<extension name="coverage">
|
||||
<pattern>
|
||||
<option name="PATTERN" value="architecturetests.*" />
|
||||
<option name="ENABLED" value="true" />
|
||||
</pattern>
|
||||
</extension>
|
||||
<option name="PACKAGE_NAME" value="architecturetests" />
|
||||
<option name="MAIN_CLASS_NAME" value="architecturetests.OnionDependenciesTest" />
|
||||
<option name="METHOD_NAME" value="" />
|
||||
<option name="TEST_OBJECT" value="class" />
|
||||
<method v="2">
|
||||
<option name="Make" enabled="true" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Distributed - TO Classes Use Correct Interfaces And Internal Structure Test" type="JUnit" factoryName="JUnit" folderName="ArchitectureTests Distributed">
|
||||
<module name="flexinale-distributed-test-architecture" />
|
||||
<extension name="coverage">
|
||||
<pattern>
|
||||
<option name="PATTERN" value="architecturetests.*" />
|
||||
<option name="ENABLED" value="true" />
|
||||
</pattern>
|
||||
</extension>
|
||||
<option name="PACKAGE_NAME" value="architecturetests" />
|
||||
<option name="MAIN_CLASS_NAME" value="architecturetests.InternalStructureOfTOClassesUsingCorrectInterfacesAndAttributeTypesTest" />
|
||||
<option name="METHOD_NAME" value="" />
|
||||
<option name="TEST_OBJECT" value="class" />
|
||||
<method v="2">
|
||||
<option name="Make" enabled="true" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
||||
|
|
@ -0,0 +1,339 @@
|
|||
package architecturetests;
|
||||
|
||||
import com.tngtech.archunit.base.DescribedPredicate;
|
||||
import com.tngtech.archunit.core.domain.JavaAnnotation;
|
||||
import com.tngtech.archunit.core.domain.JavaClass;
|
||||
import com.tngtech.archunit.core.domain.JavaClasses;
|
||||
import com.tngtech.archunit.core.domain.JavaField;
|
||||
import com.tngtech.archunit.core.domain.JavaModifier;
|
||||
import com.tngtech.archunit.core.importer.ClassFileImporter;
|
||||
import com.tngtech.archunit.core.importer.ImportOption;
|
||||
import com.tngtech.archunit.lang.ArchCondition;
|
||||
import com.tngtech.archunit.lang.ConditionEvents;
|
||||
import com.tngtech.archunit.lang.SimpleConditionEvent;
|
||||
import de.accso.flexinale.FlexinaleDistributedApplicationBesucherportal;
|
||||
import de.accso.flexinale.FlexinaleDistributedApplicationBackoffice;
|
||||
import de.accso.flexinale.FlexinaleDistributedApplicationTicketing;
|
||||
import de.accso.flexinale.common.shared_kernel.DoNotCheckInArchitectureTests;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public final class ArchUnitHelper {
|
||||
|
||||
static final String PACKAGE_PREFIX = "de.accso.flexinale";
|
||||
static final String PACKAGE_PREFIX_TOCHECK = "de.accso.flexinale";
|
||||
static final JavaClasses allJavaClassesToCheck =
|
||||
new ClassFileImporter()
|
||||
.withImportOption(ImportOption.Predefined.DO_NOT_INCLUDE_TESTS)
|
||||
.importPackages(PACKAGE_PREFIX_TOCHECK);
|
||||
|
||||
static DescribedPredicate<JavaClass> isFlexinaleClass =
|
||||
new DescribedPredicate<>("any Flexinale class") {
|
||||
@Override
|
||||
public boolean test(JavaClass clazz) {
|
||||
return clazz.getPackageName().startsWith(PACKAGE_PREFIX);
|
||||
}
|
||||
};
|
||||
|
||||
static final DescribedPredicate<JavaClass> isFlexinaleTestClassAnnotatedWithDoNotCheckAnnotation =
|
||||
new DescribedPredicate<>("any Flexinale class annotated with DoNotCheckInArchitectureTests") {
|
||||
@Override
|
||||
public boolean test(JavaClass clazz) {
|
||||
return clazz.isAnnotatedWith(DoNotCheckInArchitectureTests.class);
|
||||
}
|
||||
};
|
||||
|
||||
static final DescribedPredicate<JavaClass> isAnyClass =
|
||||
new DescribedPredicate<>("any class (to be ignored always)") {
|
||||
@Override
|
||||
public boolean test(JavaClass clazz) {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
static final DescribedPredicate<JavaClass> isSpringBootTestClass =
|
||||
new DescribedPredicate<>("a Flexinale Spring Boot test class") {
|
||||
@Override
|
||||
public boolean test(JavaClass clazz) {
|
||||
return clazz.isAnnotatedWith(SpringBootTest.class);
|
||||
}
|
||||
};
|
||||
|
||||
static final DescribedPredicate<JavaClass> isSpringBootApplicationMainClass =
|
||||
new DescribedPredicate<>("the Flexinale Spring Boot application main class") {
|
||||
@Override
|
||||
public boolean test(JavaClass clazz) {
|
||||
return
|
||||
clazz.getFullName().equals(FlexinaleDistributedApplicationBesucherportal.class.getName())
|
||||
|| clazz.getFullName().equals(FlexinaleDistributedApplicationBackoffice.class.getName())
|
||||
|| clazz.getFullName().equals(FlexinaleDistributedApplicationTicketing.class.getName())
|
||||
;
|
||||
}
|
||||
};
|
||||
|
||||
static final DescribedPredicate<JavaClass> isSpringClass =
|
||||
new DescribedPredicate<>("a Spring class") {
|
||||
@Override
|
||||
public boolean test(JavaClass clazz) {
|
||||
return clazz.getPackageName().contains("spring");
|
||||
}
|
||||
};
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
static final DescribedPredicate<JavaAnnotation> isSpringAnnotation =
|
||||
new DescribedPredicate<>("a Spring annotation") {
|
||||
@Override
|
||||
public boolean test(final JavaAnnotation anno) {
|
||||
return anno.getType().getName().contains("spring");
|
||||
}
|
||||
};
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
static final DescribedPredicate<JavaAnnotation> isEntityAnnotation =
|
||||
new DescribedPredicate<>("a Entity annotation") {
|
||||
@Override
|
||||
public boolean test(final JavaAnnotation anno) {
|
||||
return anno.getType().getName().contains("jakarta.persistence.Entity");
|
||||
}
|
||||
};
|
||||
|
||||
static ArchCondition<JavaClass> hasFieldOfType(String fieldName, Class<?> fieldType) {
|
||||
return hasFieldOfType(fieldName, fieldType, true);
|
||||
}
|
||||
|
||||
static ArchCondition<JavaClass> hasFieldOfType(String fieldName, Class<?> fieldType,
|
||||
boolean includeDerivedFieldsFromSuperClasses) {
|
||||
return new ArchCondition<>("a field type check") {
|
||||
@Override
|
||||
public void check(JavaClass clazz, ConditionEvents events) {
|
||||
// check all fields (or all field including the derived onces, from superclass / interfaces)
|
||||
Optional<JavaField> optionalField =
|
||||
(includeDerivedFieldsFromSuperClasses ? clazz.getAllFields() : clazz.getFields())
|
||||
.stream()
|
||||
// and retrieve the field with the given name to check
|
||||
.filter(field -> fieldName.equals(field.getName()))
|
||||
.findFirst();
|
||||
|
||||
// if there is not such a field with the given name ...
|
||||
if (optionalField.isEmpty()) {
|
||||
// ... then add message to ArchConditions result
|
||||
String message = String.format("Class %s does not have a field %s", clazz.getName(), fieldName);
|
||||
events.add(SimpleConditionEvent.violated(clazz, message));
|
||||
}
|
||||
else
|
||||
// if there is a field with the given name, but it is not of the given type ...
|
||||
if (!(optionalField.get().getRawType().isEquivalentTo(fieldType))) {
|
||||
// ... then add message to ArchConditions result
|
||||
String message = String.format("Class %s does not have a field %s of type %s",
|
||||
clazz.getName(), fieldName, fieldType.getName());
|
||||
events.add(SimpleConditionEvent.violated(clazz, message));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static ArchCondition<JavaClass> hasFieldsWithSuffixOfType(String fieldNameSuffix, Class<?> fieldType) {
|
||||
return hasFieldsWithSuffixOfType(fieldNameSuffix, fieldType, true);
|
||||
}
|
||||
|
||||
static ArchCondition<JavaClass> hasFieldsWithSuffixOfType(String fieldNameSuffix, Class<?> fieldType,
|
||||
boolean includeDerivedFieldsFromSuperClasses) {
|
||||
return new ArchCondition<>("a field type check") {
|
||||
@Override
|
||||
public void check(JavaClass clazz, ConditionEvents events) {
|
||||
// check all fields (or all field including the derived onces, from superclass / interfaces)
|
||||
List<JavaField> fields =
|
||||
(includeDerivedFieldsFromSuperClasses ? clazz.getAllFields() : clazz.getFields())
|
||||
.stream()
|
||||
// and retrieve the field with the given name to check
|
||||
.filter(field -> field.getName().endsWith(fieldNameSuffix))
|
||||
.toList();
|
||||
|
||||
// if there are fields with the given name suffix, but it is not of the given type ...
|
||||
for (JavaField field: fields) {
|
||||
if (!field.getRawType().isEquivalentTo(fieldType)) {
|
||||
// ... then add message to ArchConditions result
|
||||
String message = String.format("Class %s has a field %s but it is not of type %s",
|
||||
clazz.getName(), field.getName(), fieldType.getName());
|
||||
events.add(SimpleConditionEvent.violated(clazz, message));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static ArchCondition<JavaClass> hasFieldPrivateStatic(String fieldName) {
|
||||
return hasFieldPrivateStatic(fieldName, true);
|
||||
}
|
||||
|
||||
static ArchCondition<JavaClass> hasFieldPrivateStatic(String fieldName,
|
||||
boolean includeDerivedFieldsFromSuperClasses) {
|
||||
return hasFieldModifiers(fieldName, includeDerivedFieldsFromSuperClasses, Set.of(JavaModifier.PRIVATE, JavaModifier.STATIC));
|
||||
}
|
||||
|
||||
static ArchCondition<JavaClass> hasFieldPublicFinal(String fieldName) {
|
||||
return hasFieldPublicFinal(fieldName, true);
|
||||
}
|
||||
|
||||
static ArchCondition<JavaClass> hasFieldPublicFinal(String fieldName,
|
||||
boolean includeDerivedFieldsFromSuperClasses) {
|
||||
return hasFieldModifiers(fieldName, includeDerivedFieldsFromSuperClasses, Set.of(JavaModifier.PUBLIC, JavaModifier.FINAL));
|
||||
}
|
||||
|
||||
static ArchCondition<JavaClass> hasFieldPrivateFinal(String fieldName) {
|
||||
return hasFieldPrivateFinal(fieldName, true);
|
||||
}
|
||||
|
||||
static ArchCondition<JavaClass> hasFieldPrivateFinal(String fieldName,
|
||||
boolean includeDerivedFieldsFromSuperClasses) {
|
||||
return hasFieldModifiers(fieldName, includeDerivedFieldsFromSuperClasses, Set.of(JavaModifier.PRIVATE, JavaModifier.FINAL));
|
||||
}
|
||||
|
||||
private static ArchCondition<JavaClass> hasFieldModifiers(String fieldName,
|
||||
boolean includeDerivedFieldsFromSuperClasses,
|
||||
Set<JavaModifier> modifiers) {
|
||||
return new ArchCondition<>("a field check for public final") {
|
||||
@Override
|
||||
public void check(JavaClass clazz, ConditionEvents events) {
|
||||
// check all fields (or all field including the derived onces, from superclass / interfaces)
|
||||
Optional<JavaField> optionalField =
|
||||
(includeDerivedFieldsFromSuperClasses ? clazz.getAllFields() : clazz.getFields())
|
||||
.stream()
|
||||
// and retrieve the field with the given name to check
|
||||
.filter(field -> fieldName.equals(field.getName()))
|
||||
.findFirst();
|
||||
|
||||
// if there is not such a field with the given name ...
|
||||
if (optionalField.isEmpty()) {
|
||||
// ... then add message to ArchConditions result
|
||||
String message = String.format("Class %s does not have a field %s", clazz.getName(), fieldName);
|
||||
events.add(SimpleConditionEvent.violated(clazz, message));
|
||||
}
|
||||
else
|
||||
// if there is a field with the given name, but it does not have the given modifiers
|
||||
if (!(optionalField.get().getModifiers().containsAll(modifiers))) {
|
||||
// ... then add message to ArchConditions result
|
||||
String message = String.format("Class %s does not have a final field %s with modifiers %s",
|
||||
clazz.getName(), fieldName, modifiers);
|
||||
events.add(SimpleConditionEvent.violated(clazz, message));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static ArchCondition<JavaClass> hasFieldAnnotatedWithType(String fieldName, Class<? extends Annotation> annotationType) {
|
||||
return hasFieldAnnotatedWithType(fieldName, annotationType, true);
|
||||
}
|
||||
|
||||
static ArchCondition<JavaClass> hasFieldAnnotatedWithType(String fieldName, Class<? extends Annotation> annotationType,
|
||||
boolean includeDerivedFieldsFromSuperClasses) {
|
||||
return new ArchCondition<>("a field annotation check") {
|
||||
@Override
|
||||
public void check(JavaClass clazz, ConditionEvents events) {
|
||||
// check all fields (or all field including the derived onces, from superclass / interfaces)
|
||||
Optional<JavaField> optionalField =
|
||||
(includeDerivedFieldsFromSuperClasses ? clazz.getAllFields() : clazz.getFields())
|
||||
.stream()
|
||||
// and retrieve the field with the given name to check
|
||||
.filter(field -> fieldName.equals(field.getName()))
|
||||
.findFirst();
|
||||
|
||||
// if there is not such a field with the given name ...
|
||||
if (optionalField.isEmpty()) {
|
||||
// ... then add message to ArchConditions result
|
||||
String message = String.format("Class %s does not have a field %s", clazz.getName(), fieldName);
|
||||
events.add(SimpleConditionEvent.violated(clazz, message));
|
||||
}
|
||||
else
|
||||
// if there is a field with the given name, but it is not annotated with the annotation type ...
|
||||
if (!(optionalField.get().isAnnotatedWith(annotationType))) {
|
||||
// ... then add message to ArchConditions result
|
||||
String message = String.format("Class %s does not have a field %s annotated with type %s",
|
||||
clazz.getName(), fieldName, annotationType.getName());
|
||||
events.add(SimpleConditionEvent.violated(clazz, message));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static ArchCondition<JavaClass> hasNoFieldsOfAnyType(Set<Class<?>> disallowedFieldTypes, Set<String> fieldsToBeFilteredOut) {
|
||||
return hasNoFieldsOfAnyType(disallowedFieldTypes, fieldsToBeFilteredOut, true);
|
||||
}
|
||||
|
||||
static ArchCondition<JavaClass> hasNoFieldsOfAnyType(Set<Class<?>> disallowedFieldTypes, Set<String> fieldsToBeFilteredOut,
|
||||
boolean includeDerivedFieldsFromSuperClasses) {
|
||||
return new ArchCondition<>("a field type check") {
|
||||
@Override
|
||||
public void check(JavaClass clazz, ConditionEvents events) {
|
||||
// check all fields (or all field including the derived onces, from superclass / interfaces)
|
||||
Set<JavaField> fields = includeDerivedFieldsFromSuperClasses ? clazz.getAllFields() : clazz.getFields();
|
||||
|
||||
// check all retrieved fields of clazz
|
||||
for (JavaField field: fields) {
|
||||
// but do not check the fields explicitely filtered out or annotated
|
||||
if (! ( (field.isAnnotatedWith(DoNotCheckInArchitectureTests.class))
|
||||
|| (fieldsToBeFilteredOut.contains(field.getName()))))
|
||||
{
|
||||
// for any given disallowed field type
|
||||
for (Class<?> fieldType : disallowedFieldTypes) {
|
||||
// check if the field from the clazz is of this disallowed type ...
|
||||
if (field.getRawType().isEquivalentTo(fieldType)) {
|
||||
// ... and if not, add message to ArchConditions result
|
||||
String message = String.format("Class %s has a field %s of type %s",
|
||||
clazz.getName(), field.getName(), fieldType.getSimpleName());
|
||||
events.add(SimpleConditionEvent.violated(clazz, message));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static ArchCondition<JavaClass> hasOnlyFieldsImplementing(Set<Class<?>> interfaceTypes,
|
||||
Set<String> fieldsToBeFilteredOut, String fieldSuffixToBeFilteredOut) {
|
||||
return hasOnlyFieldsImplementing(interfaceTypes, fieldsToBeFilteredOut, fieldSuffixToBeFilteredOut, true);
|
||||
}
|
||||
|
||||
static ArchCondition<JavaClass> hasOnlyFieldsImplementing(Set<Class<?>> interfaceTypes,
|
||||
Set<String> fieldsToBeFilteredOut, String fieldSuffixToBeFilteredOut,
|
||||
boolean includeDerivedFieldsFromSuperClasses) {
|
||||
return new ArchCondition<>("a field implementing interfaces check") {
|
||||
@Override
|
||||
public void check(JavaClass clazz, ConditionEvents events) {
|
||||
// check all fields (or all field including the derived onces, from superclass / interfaces)
|
||||
Set<JavaField> fields = includeDerivedFieldsFromSuperClasses ? clazz.getAllFields() : clazz.getFields();
|
||||
|
||||
// check all retrieved fields of clazz
|
||||
for (JavaField field: fields) {
|
||||
// but do not check the fields explicitely filtered out or annotated
|
||||
if (! ( (field.isAnnotatedWith(DoNotCheckInArchitectureTests.class))
|
||||
|| (fieldsToBeFilteredOut.contains(field.getName()))
|
||||
|| (field.getName().endsWith(fieldSuffixToBeFilteredOut))))
|
||||
{
|
||||
// for any given interface type
|
||||
for (Class<?> interfaceType : interfaceTypes) {
|
||||
// check if the field from the clazz does implement the interface type ...
|
||||
if (!field.getRawType().getAllRawInterfaces()
|
||||
.stream()
|
||||
.map(interfaceClazz -> interfaceClazz.reflect())
|
||||
.toList()
|
||||
.contains(interfaceType))
|
||||
{
|
||||
// ... and if not, add message to ArchConditions result
|
||||
String message = String.format("Class %s has a field %s not implementing type %s but has type %s",
|
||||
clazz.getName(), field.getName(), interfaceType.getSimpleName(), field.getRawType().reflect().getSimpleName());
|
||||
events.add(SimpleConditionEvent.violated(clazz, message));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,290 @@
|
|||
package architecturetests;
|
||||
|
||||
import com.tngtech.archunit.core.domain.JavaClass;
|
||||
import com.tngtech.archunit.core.domain.JavaClasses;
|
||||
import com.tngtech.archunit.core.domain.PackageMatcher;
|
||||
import com.tngtech.archunit.core.importer.ClassFileImporter;
|
||||
import com.tngtech.archunit.library.dependencies.SliceAssignment;
|
||||
import com.tngtech.archunit.library.dependencies.SliceIdentifier;
|
||||
import com.tngtech.archunit.library.dependencies.SlicesRuleDefinition;
|
||||
import de.accso.flexinale.common.shared_kernel.DeveloperMistakeException;
|
||||
import de.accso.flexinale.common.shared_kernel.DoNotCheckInArchitectureTests;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static architecturetests.ArchUnitHelper.PACKAGE_PREFIX;
|
||||
import static architecturetests.ArchUnitHelper.allJavaClassesToCheck;
|
||||
import static com.tngtech.archunit.core.domain.PackageMatcher.TO_GROUPS;
|
||||
import static java.util.Collections.emptyList;
|
||||
import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
public class ComponentDependenciesTest {
|
||||
|
||||
@Test
|
||||
void test_component_dependencies_are_free_of_cycles() {
|
||||
PackageMatchingSliceIdentifierIgnoringDependenciesToApiContract sliceAssignment =
|
||||
new PackageMatchingSliceIdentifierIgnoringDependenciesToApiContract(PACKAGE_PREFIX + ".(*)..");
|
||||
|
||||
// arrange, act, assert
|
||||
SlicesRuleDefinition.slices()
|
||||
.assignedFrom(sliceAssignment)
|
||||
.should()
|
||||
.beFreeOfCycles()
|
||||
.because("no cycles between components allowed")
|
||||
.check(allJavaClassesToCheck);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------------------
|
||||
|
||||
private record IncorrectDependencyFromTo(String fromClazzName, String toClazzName) {
|
||||
@Override
|
||||
public String toString() {
|
||||
return "%s -> %s".formatted(fromClazzName, toClazzName);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_besucherportal_has_only_allowed_dependencies_to_other_flexinale_components() {
|
||||
// arrange
|
||||
FlexinaleComponent cut = FlexinaleComponent.besucherportal;
|
||||
|
||||
// act
|
||||
Set<IncorrectDependencyFromTo> incorrectDependencies =
|
||||
getIncorrectDependenciesFromComponentClazzesOtherThanAllowed(cut,
|
||||
FlexinaleComponent.security_api_contract,
|
||||
FlexinaleComponent.backoffice_api_contract,
|
||||
FlexinaleComponent.ticketing_api_contract,
|
||||
FlexinaleComponent.common);
|
||||
|
||||
// assert
|
||||
assertThat(incorrectDependencies)
|
||||
.withFailMessage("incorrect dependencies found for component %s: %s".formatted(cut.name(), incorrectDependencies))
|
||||
.isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_besucherportal_api_contract_has_only_allowed_dependencies_to_other_flexinale_components() {
|
||||
// arrange
|
||||
FlexinaleComponent cut = FlexinaleComponent.besucherportal_api_contract;
|
||||
|
||||
// act
|
||||
Set<IncorrectDependencyFromTo> incorrectDependencies =
|
||||
getIncorrectDependenciesFromComponentClazzesOtherThanAllowed(cut, FlexinaleComponent.common);
|
||||
|
||||
// assert
|
||||
assertThat(incorrectDependencies)
|
||||
.withFailMessage("incorrect dependencies found for component %s: %s".formatted(cut.name(), incorrectDependencies))
|
||||
.isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_ticketing_has_only_allowed_dependencies_to_other_flexinale_components() {
|
||||
// arrange
|
||||
FlexinaleComponent cut = FlexinaleComponent.ticketing;
|
||||
|
||||
// act
|
||||
Set<IncorrectDependencyFromTo> incorrectDependencies =
|
||||
getIncorrectDependenciesFromComponentClazzesOtherThanAllowed(cut,
|
||||
FlexinaleComponent.besucherportal_api_contract,
|
||||
FlexinaleComponent.security_api_contract,
|
||||
FlexinaleComponent.backoffice_api_contract,
|
||||
FlexinaleComponent.common);
|
||||
|
||||
// assert
|
||||
assertThat(incorrectDependencies)
|
||||
.withFailMessage("incorrect dependencies found for component %s: %s".formatted(cut.name(), incorrectDependencies))
|
||||
.isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_ticketing_api_contract_has_only_allowed_dependencies_to_other_flexinale_components() {
|
||||
// arrange
|
||||
FlexinaleComponent cut = FlexinaleComponent.ticketing_api_contract;
|
||||
|
||||
// act
|
||||
Set<IncorrectDependencyFromTo> incorrectDependencies =
|
||||
getIncorrectDependenciesFromComponentClazzesOtherThanAllowed(cut, FlexinaleComponent.common);
|
||||
|
||||
// assert
|
||||
assertThat(incorrectDependencies)
|
||||
.withFailMessage("incorrect dependencies found for component %s: %s".formatted(cut.name(), incorrectDependencies))
|
||||
.isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_backoffice_has_only_allowed_dependencies_to_other_flexinale_components() {
|
||||
// arrange
|
||||
FlexinaleComponent cut = FlexinaleComponent.backoffice;
|
||||
|
||||
// act
|
||||
Set<IncorrectDependencyFromTo> incorrectDependencies =
|
||||
getIncorrectDependenciesFromComponentClazzesOtherThanAllowed(cut,
|
||||
FlexinaleComponent.security_api_contract,
|
||||
FlexinaleComponent.backoffice_api_contract,
|
||||
FlexinaleComponent.ticketing_api_contract,
|
||||
FlexinaleComponent.common);
|
||||
|
||||
// assert
|
||||
assertThat(incorrectDependencies)
|
||||
.withFailMessage("incorrect dependencies found for component %s: %s".formatted(cut.name(), incorrectDependencies))
|
||||
.isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_security_has_only_allowed_dependencies_to_other_flexinale_components() {
|
||||
// arrange
|
||||
FlexinaleComponent cut = FlexinaleComponent.security;
|
||||
|
||||
// act
|
||||
Set<IncorrectDependencyFromTo> incorrectDependencies =
|
||||
getIncorrectDependenciesFromComponentClazzesOtherThanAllowed(cut,
|
||||
FlexinaleComponent.security_api_contract,
|
||||
FlexinaleComponent.common);
|
||||
|
||||
// assert
|
||||
assertThat(incorrectDependencies)
|
||||
.withFailMessage("incorrect dependencies found for component %s: %s".formatted(cut.name(), incorrectDependencies))
|
||||
.isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_backoffice_api_contract_has_only_allowed_dependencies_to_other_flexinale_components() {
|
||||
// arrange
|
||||
FlexinaleComponent cut = FlexinaleComponent.backoffice_api_contract;
|
||||
|
||||
// act
|
||||
Set<IncorrectDependencyFromTo> incorrectDependencies =
|
||||
getIncorrectDependenciesFromComponentClazzesOtherThanAllowed(cut, FlexinaleComponent.common);
|
||||
|
||||
// assert
|
||||
assertThat(incorrectDependencies)
|
||||
.withFailMessage("incorrect dependencies found for component %s: %s".formatted(cut.name(), incorrectDependencies))
|
||||
.isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_common_has_only_allowed_dependencies_to_self() {
|
||||
// arrange
|
||||
FlexinaleComponent cut = FlexinaleComponent.common;
|
||||
|
||||
// act
|
||||
Set<IncorrectDependencyFromTo> incorrectDependencies =
|
||||
getIncorrectDependenciesFromComponentClazzesOtherThanSelf(cut);
|
||||
|
||||
// assert
|
||||
if (!incorrectDependencies.isEmpty()) {
|
||||
fail("incorrect dependencies found for component %s: %s".formatted(cut.name(), incorrectDependencies));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_security_api_contract_has_only_allowed_dependencies_to_other_flexinale_components() {
|
||||
// arrange
|
||||
FlexinaleComponent cut = FlexinaleComponent.security_api_contract;
|
||||
|
||||
// act
|
||||
Set<IncorrectDependencyFromTo> incorrectDependencies =
|
||||
getIncorrectDependenciesFromComponentClazzesOtherThanAllowed(cut, FlexinaleComponent.common);
|
||||
|
||||
// assert
|
||||
if (!incorrectDependencies.isEmpty()) {
|
||||
fail("incorrect dependencies found for component %s: %s".formatted(cut.name(), incorrectDependencies));
|
||||
}
|
||||
}
|
||||
|
||||
private Set<IncorrectDependencyFromTo>
|
||||
getIncorrectDependenciesFromComponentClazzesOtherThanSelf(FlexinaleComponent fromComponent)
|
||||
{
|
||||
return getIncorrectDependenciesFromComponentClazzesOtherThanAllowed(fromComponent);
|
||||
}
|
||||
|
||||
private Set<IncorrectDependencyFromTo>
|
||||
getIncorrectDependenciesFromComponentClazzesOtherThanAllowed(FlexinaleComponent fromComponent,
|
||||
FlexinaleComponent... toAllowedComponents)
|
||||
{
|
||||
Set<String> toAllowedPackages = Arrays.stream(toAllowedComponents)
|
||||
.map(component -> component.pkg)
|
||||
.collect(Collectors.toSet());
|
||||
// add self as allowed dependency
|
||||
toAllowedPackages.add(fromComponent.pkg);
|
||||
|
||||
Set<IncorrectDependencyFromTo> allIncorrectDependenciesForComponent = new HashSet<>();
|
||||
|
||||
// check all clazzes in the fromComponent
|
||||
JavaClasses fromComponentClazzes = new ClassFileImporter().importPackages(fromComponent.pkg);
|
||||
fromComponentClazzes.forEach(clazz -> {
|
||||
if (!clazz.getFullName().startsWith(fromComponent.pkg))
|
||||
throw new DeveloperMistakeException("wrong clazz filter implemented");
|
||||
|
||||
// for any clazz of the fromComponent: check its outgoing dependencies and keep the wrong ones
|
||||
HashSet<IncorrectDependencyFromTo> incorrectDependenciesForClazz =
|
||||
clazz.getDirectDependenciesFromSelf().stream()
|
||||
.filter(dependency -> {
|
||||
JavaClass targetClazz = dependency.getTargetClass();
|
||||
|
||||
// filter out all clazzes outside of our component scope (like dependencies to java... or org... etc)
|
||||
if (! targetClazz.getPackageName().startsWith(PACKAGE_PREFIX))
|
||||
return false;
|
||||
|
||||
// filter out DoNotCheckInArchitectureTests
|
||||
if (targetClazz.getFullName().equals(DoNotCheckInArchitectureTests.class.getSimpleName()))
|
||||
return false;
|
||||
|
||||
// filter out annotation usages of DoNotCheckInArchitectureTests
|
||||
if (dependency.getOriginClass().isAnnotatedWith(DoNotCheckInArchitectureTests.class))
|
||||
return false;
|
||||
|
||||
// filter out all allowed dependencies
|
||||
for (String toAllowedPackage : toAllowedPackages) {
|
||||
if (targetClazz.getPackageName().startsWith(toAllowedPackage)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// finally now keep all, where the dependency target is not explicitely allowed
|
||||
return true;
|
||||
})
|
||||
.map(it -> new IncorrectDependencyFromTo(it.getOriginClass().getFullName(),
|
||||
it.getTargetClass().getFullName()))
|
||||
.collect(Collectors.toCollection(HashSet::new)); // do not use .toSet() as this results in an UnsupportedOperation exception
|
||||
|
||||
allIncorrectDependenciesForComponent.addAll(incorrectDependenciesForClazz);
|
||||
});
|
||||
|
||||
return allIncorrectDependenciesForComponent;
|
||||
}
|
||||
}
|
||||
|
||||
// copied from ArchUnit's internal class PackageMatchingSliceIdentifier
|
||||
class PackageMatchingSliceIdentifierIgnoringDependenciesToApiContract implements SliceAssignment {
|
||||
private final String packageIdentifier;
|
||||
|
||||
PackageMatchingSliceIdentifierIgnoringDependenciesToApiContract(String packageIdentifier) {
|
||||
this.packageIdentifier = packageIdentifier;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SliceIdentifier getIdentifierOf(JavaClass javaClass) {
|
||||
// ignore dependencies to ..api_contract..
|
||||
if (javaClass.getPackageName().contains("api_contract")) {
|
||||
return SliceIdentifier.ignore();
|
||||
}
|
||||
|
||||
PackageMatcher matcher = PackageMatcher.of(packageIdentifier);
|
||||
Optional<List<String>> result = matcher.match(javaClass.getPackageName()).map(TO_GROUPS);
|
||||
List<String> parts = result.orElse(emptyList());
|
||||
return parts.isEmpty() ? SliceIdentifier.ignore() : SliceIdentifier.of(parts);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return slicesMatchingDescription(packageIdentifier);
|
||||
}
|
||||
|
||||
private static String slicesMatchingDescription(String packageIdentifier) {
|
||||
return "'" + packageIdentifier + "'";
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
package architecturetests;
|
||||
|
||||
import static architecturetests.ArchUnitHelper.PACKAGE_PREFIX;
|
||||
|
||||
enum FlexinaleComponent {
|
||||
besucherportal ("besucherportal", PACKAGE_PREFIX + ".besucherportal"),
|
||||
besucherportal_api_contract("besucherportal_api_contract", PACKAGE_PREFIX + ".besucherportal.api_contract"),
|
||||
security ("security", PACKAGE_PREFIX + ".security"),
|
||||
security_api_contract ("security_api_contract", PACKAGE_PREFIX + ".security.api_contract"),
|
||||
backoffice ("backoffice", PACKAGE_PREFIX + ".backoffice"),
|
||||
backoffice_api_contract ("backoffice_api_contract", PACKAGE_PREFIX + ".backoffice.api_contract"),
|
||||
ticketing ("ticketing", PACKAGE_PREFIX + ".ticketing"),
|
||||
ticketing_api_contract ("ticketing_api_contract", PACKAGE_PREFIX + ".ticketing.api_contract"),
|
||||
common ("common", PACKAGE_PREFIX + ".common");
|
||||
|
||||
final String name;
|
||||
final String pkg;
|
||||
|
||||
FlexinaleComponent(final String name, String pkg) {
|
||||
this.name = name;
|
||||
this.pkg = pkg;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,143 @@
|
|||
package architecturetests;
|
||||
|
||||
import com.tngtech.archunit.lang.ArchRule;
|
||||
import com.tngtech.archunit.lang.EvaluationResult;
|
||||
import com.tngtech.archunit.lang.syntax.ArchRuleDefinition;
|
||||
import com.tngtech.archunit.lang.syntax.elements.GivenClassesConjunction;
|
||||
import de.accso.flexinale.common.shared_kernel.*;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import static architecturetests.ArchUnitHelper.*;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class InternalStructureOfDomainClassesUsingCorrectInterfacesAndAttributeTypesTest {
|
||||
|
||||
static final String PACKAGE_BACKOFFICE_DOMAIN_MODEL = ArchUnitHelper.PACKAGE_PREFIX + ".backoffice.domain.model..";
|
||||
static final String PACKAGE_TICKETING_DOMAIN_MODEL = ArchUnitHelper.PACKAGE_PREFIX + ".ticketing.domain.model..";
|
||||
|
||||
static final GivenClassesConjunction domainClasses = ArchRuleDefinition.classes()
|
||||
.that()
|
||||
.resideInAnyPackage(PACKAGE_BACKOFFICE_DOMAIN_MODEL,
|
||||
PACKAGE_TICKETING_DOMAIN_MODEL)
|
||||
.and()
|
||||
.areNotEnums()
|
||||
.and()
|
||||
.areNotInnerClasses()
|
||||
.and()
|
||||
.areNotNestedClasses()
|
||||
.and()
|
||||
.areNotAnnotatedWith(DoNotCheckInArchitectureTests.class);
|
||||
|
||||
@Test
|
||||
void test_DomainClassesAreImplementingIdentifiableAndVersionableAndEqualsByContent() {
|
||||
// arrange
|
||||
ArchRule archRule = domainClasses
|
||||
|
||||
.should()
|
||||
.implement(Identifiable.class)
|
||||
.andShould()
|
||||
.implement(Versionable.class)
|
||||
.andShould()
|
||||
.implement(EqualsByContent.class);
|
||||
|
||||
// act
|
||||
EvaluationResult evaluationResult = archRule.evaluate(ArchUnitHelper.allJavaClassesToCheck);
|
||||
|
||||
// assert
|
||||
assertThat(evaluationResult.getFailureReport().getDetails()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_DomainClassesHaveIdFieldUsingStrongType() {
|
||||
// arrange
|
||||
String idField = "id";
|
||||
|
||||
ArchRule archRule = domainClasses
|
||||
.should(hasFieldOfType(idField, Identifiable.Id.class));
|
||||
|
||||
// act
|
||||
EvaluationResult evaluationResult = archRule.evaluate(ArchUnitHelper.allJavaClassesToCheck);
|
||||
|
||||
// assert
|
||||
assertThat(evaluationResult.getFailureReport().getDetails()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_DomainClassesHaveVersionFieldUsingStrongType() {
|
||||
// arrange
|
||||
String versionField = "version";
|
||||
|
||||
ArchRule archRule = domainClasses
|
||||
.should(hasFieldOfType(versionField, Versionable.Version.class));
|
||||
|
||||
// act
|
||||
EvaluationResult evaluationResult = archRule.evaluate(ArchUnitHelper.allJavaClassesToCheck);
|
||||
|
||||
// assert
|
||||
assertThat(evaluationResult.getFailureReport().getDetails()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_DomainClassesHaveIdFieldWhichIsPublicFinal() {
|
||||
// arrange
|
||||
String idField = "id";
|
||||
|
||||
ArchRule archRule = domainClasses
|
||||
.should(hasFieldPublicFinal(idField));
|
||||
|
||||
// act
|
||||
EvaluationResult evaluationResult = archRule.evaluate(ArchUnitHelper.allJavaClassesToCheck);
|
||||
|
||||
// assert
|
||||
assertThat(evaluationResult.getFailureReport().getDetails()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_DomainClassesHaveVersionFieldWhichIsPublicFinal() {
|
||||
// arrange
|
||||
String versionField = "version";
|
||||
|
||||
ArchRule archRule = domainClasses
|
||||
.should(hasFieldPublicFinal(versionField));
|
||||
|
||||
// act
|
||||
EvaluationResult evaluationResult = archRule.evaluate(ArchUnitHelper.allJavaClassesToCheck);
|
||||
|
||||
// assert
|
||||
assertThat(evaluationResult.getFailureReport().getDetails()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_DomainClassesHaveOnlyFieldsImplementingRawWrapper() {
|
||||
// arrange
|
||||
Set<Class<?>> interfaceTypes = Set.of(RawWrapper.class);
|
||||
Set<String> fieldsToBeFilteredOut = Set.of();
|
||||
String fieldSuffixToBeFilteredOut = "Id";
|
||||
|
||||
ArchRule archRule = domainClasses
|
||||
.should(hasOnlyFieldsImplementing(interfaceTypes, fieldsToBeFilteredOut, fieldSuffixToBeFilteredOut));
|
||||
|
||||
// act
|
||||
EvaluationResult evaluationResult = archRule.evaluate(ArchUnitHelper.allJavaClassesToCheck);
|
||||
|
||||
// assert
|
||||
assertThat(evaluationResult.getFailureReport().getDetails()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_DomainClassesHaveExternalIdFieldsUsingStrongType() {
|
||||
// arrange
|
||||
String idFieldSuffix = "Id";
|
||||
|
||||
ArchRule archRule = domainClasses
|
||||
.should(hasFieldsWithSuffixOfType(idFieldSuffix, Identifiable.Id.class));
|
||||
|
||||
// act
|
||||
EvaluationResult evaluationResult = archRule.evaluate(ArchUnitHelper.allJavaClassesToCheck);
|
||||
|
||||
// assert
|
||||
assertThat(evaluationResult.getFailureReport().getDetails()).isEmpty();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,108 @@
|
|||
package architecturetests;
|
||||
|
||||
import com.tngtech.archunit.lang.ArchRule;
|
||||
import com.tngtech.archunit.lang.EvaluationResult;
|
||||
import com.tngtech.archunit.lang.syntax.ArchRuleDefinition;
|
||||
import com.tngtech.archunit.lang.syntax.elements.GivenClassesConjunction;
|
||||
import de.accso.flexinale.common.shared_kernel.Identifiable;
|
||||
import de.accso.flexinale.common.shared_kernel.Versionable;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import static architecturetests.ArchUnitHelper.hasFieldAnnotatedWithType;
|
||||
import static architecturetests.ArchUnitHelper.hasFieldOfType;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class InternalStructureOfEntityClassesUsingCorrectInterfacesAndAttributeTypesTest {
|
||||
|
||||
static final GivenClassesConjunction entities = ArchRuleDefinition.classes()
|
||||
.that()
|
||||
.areAnnotatedWith(ArchUnitHelper.isEntityAnnotation);
|
||||
|
||||
@Test
|
||||
void test_NoEntitiesAreOutsidePersistencePackages() {
|
||||
// arrange
|
||||
final String PACKAGE_BACKOFFICE_INFRASTRUCTURE_PERSISTENCE = ArchUnitHelper.PACKAGE_PREFIX + ".backoffice.infrastructure.persistence..";
|
||||
final String PACKAGE_SECURITY_INFRASTRUCTURE_PERSISTENCE = ArchUnitHelper.PACKAGE_PREFIX + ".security.infrastructure.persistence..";
|
||||
final String PACKAGE_TICKETING_INFRASTRUCTURE_PERSISTENCE = ArchUnitHelper.PACKAGE_PREFIX + ".ticketing.infrastructure.persistence..";
|
||||
|
||||
ArchRule archRule = ArchRuleDefinition.noClasses()
|
||||
.that()
|
||||
.resideOutsideOfPackages(PACKAGE_BACKOFFICE_INFRASTRUCTURE_PERSISTENCE,
|
||||
PACKAGE_SECURITY_INFRASTRUCTURE_PERSISTENCE,
|
||||
PACKAGE_TICKETING_INFRASTRUCTURE_PERSISTENCE)
|
||||
.should()
|
||||
.beAnnotatedWith(ArchUnitHelper.isEntityAnnotation);
|
||||
|
||||
// act
|
||||
EvaluationResult evaluationResult = archRule.evaluate(ArchUnitHelper.allJavaClassesToCheck);
|
||||
|
||||
// assert
|
||||
assertThat(evaluationResult.getFailureReport().getDetails()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_EntitiesAreImplementingIdentifiableAndSerializableAndVersionable() {
|
||||
// arrange
|
||||
ArchRule archRule = entities
|
||||
.should()
|
||||
.implement(Identifiable.class)
|
||||
.andShould()
|
||||
.implement(Serializable.class)
|
||||
.andShould()
|
||||
.implement(Versionable.class);
|
||||
|
||||
// act
|
||||
EvaluationResult evaluationResult = archRule.evaluate(ArchUnitHelper.allJavaClassesToCheck);
|
||||
|
||||
// assert
|
||||
assertThat(evaluationResult.getFailureReport().getDetails()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_EntitiesHaveIdFieldUsingRawType() {
|
||||
// arrange
|
||||
String idField = "id";
|
||||
ArchRule archRule = entities
|
||||
.should(hasFieldOfType(idField, String.class)) // Entities should use String for Id (not the Id class)
|
||||
.andShould(hasFieldAnnotatedWithType(idField, jakarta.persistence.Id.class));
|
||||
|
||||
// act
|
||||
EvaluationResult evaluationResult = archRule.evaluate(ArchUnitHelper.allJavaClassesToCheck);
|
||||
|
||||
// assert
|
||||
assertThat(evaluationResult.getFailureReport().getDetails()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_EntitiesHaveVersionFieldUsingRawType() {
|
||||
// arrange
|
||||
String idField = "version";
|
||||
ArchRule archRule = entities
|
||||
.should(hasFieldOfType(idField, Integer.class)) // Entities should use Integer for version (not the Version class)
|
||||
.andShould(hasFieldAnnotatedWithType(idField, jakarta.persistence.Version.class));
|
||||
|
||||
// act
|
||||
EvaluationResult evaluationResult = archRule.evaluate(ArchUnitHelper.allJavaClassesToCheck);
|
||||
|
||||
// assert
|
||||
assertThat(evaluationResult.getFailureReport().getDetails()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_EntitiesHaveEntityAsSuffixClassName() {
|
||||
// arrange
|
||||
String clazzNameSuffix = "Entity";
|
||||
|
||||
ArchRule archRule = entities
|
||||
.should()
|
||||
.haveSimpleNameEndingWith(clazzNameSuffix);
|
||||
|
||||
// act
|
||||
EvaluationResult evaluationResult = archRule.evaluate(ArchUnitHelper.allJavaClassesToCheck);
|
||||
|
||||
// assert
|
||||
assertThat(evaluationResult.getFailureReport().getDetails()).isEmpty();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,217 @@
|
|||
package architecturetests;
|
||||
|
||||
import com.tngtech.archunit.core.domain.JavaModifier;
|
||||
import com.tngtech.archunit.lang.ArchRule;
|
||||
import com.tngtech.archunit.lang.EvaluationResult;
|
||||
import com.tngtech.archunit.lang.syntax.ArchRuleDefinition;
|
||||
import com.tngtech.archunit.lang.syntax.elements.GivenClassesConjunction;
|
||||
import de.accso.flexinale.common.api.event.Event;
|
||||
import de.accso.flexinale.common.shared_kernel.Identifiable;
|
||||
import de.accso.flexinale.common.shared_kernel.RawWrapper;
|
||||
import de.accso.flexinale.common.shared_kernel.Versionable;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Set;
|
||||
|
||||
import static architecturetests.ArchUnitHelper.*;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class InternalStructureOfEventClassesUsingCorrectInterfacesAndAttributeTypesTest {
|
||||
|
||||
static final String PACKAGE_BESUCHERPORTAL_API_EVENTS = ArchUnitHelper.PACKAGE_PREFIX + ".besucherportal.api_contract.event";
|
||||
static final String PACKAGE_BACKOFFICE_API_EVENTS = ArchUnitHelper.PACKAGE_PREFIX + ".backoffice.api_contract.event";
|
||||
static final String PACKAGE_SECURITY_API_EVENTS = ArchUnitHelper.PACKAGE_PREFIX + ".security.api_contract.event";
|
||||
static final String PACKAGE_TICKETING_API_EVENTS = ArchUnitHelper.PACKAGE_PREFIX + ".ticketing.api_contract.event";
|
||||
|
||||
static final GivenClassesConjunction eventClasses = ArchRuleDefinition.classes()
|
||||
.that()
|
||||
.resideInAnyPackage(PACKAGE_BESUCHERPORTAL_API_EVENTS,
|
||||
PACKAGE_BACKOFFICE_API_EVENTS,
|
||||
PACKAGE_SECURITY_API_EVENTS,
|
||||
PACKAGE_TICKETING_API_EVENTS)
|
||||
.and()
|
||||
.areNotEnums()
|
||||
.and()
|
||||
.areNotInnerClasses()
|
||||
.and()
|
||||
.areNotNestedClasses()
|
||||
.and()
|
||||
.doNotHaveModifier(JavaModifier.ABSTRACT) // ignore AbstractEvent
|
||||
.and()
|
||||
.haveSimpleNameNotEndingWith("Events"); // ignore the ...Events classes, as they simply collect the ...Event classes
|
||||
|
||||
@Test
|
||||
void test_EventClassesAreImplementingEvent() {
|
||||
// arrange
|
||||
ArchRule archRule = eventClasses
|
||||
.should()
|
||||
.implement(Event.class);
|
||||
|
||||
// act
|
||||
EvaluationResult evaluationResult = archRule.evaluate(ArchUnitHelper.allJavaClassesToCheck);
|
||||
|
||||
// assert
|
||||
assertThat(evaluationResult.getFailureReport().getDetails()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_EventClassesAreImplementingIdentifiableAndVersionableAndSerializable() {
|
||||
// arrange
|
||||
ArchRule archRule = eventClasses
|
||||
.should()
|
||||
.implement(Identifiable.class)
|
||||
.andShould()
|
||||
.implement(Versionable.class)
|
||||
.andShould()
|
||||
.implement(Serializable.class);
|
||||
|
||||
// act
|
||||
EvaluationResult evaluationResult = archRule.evaluate(ArchUnitHelper.allJavaClassesToCheck);
|
||||
|
||||
// assert
|
||||
assertThat(evaluationResult.getFailureReport().getDetails()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_EventClassesHaveIdFieldUsingStrongType() {
|
||||
// arrange
|
||||
String idField = "id";
|
||||
|
||||
ArchRule archRule = eventClasses
|
||||
.should(hasFieldOfType(idField, Identifiable.Id.class));
|
||||
|
||||
// act
|
||||
EvaluationResult evaluationResult = archRule.evaluate(ArchUnitHelper.allJavaClassesToCheck);
|
||||
|
||||
// assert
|
||||
assertThat(evaluationResult.getFailureReport().getDetails()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_EventClassesHaveCorrelationIdFieldUsingStrongType() {
|
||||
// arrange
|
||||
String correlationIdField = "correlationId";
|
||||
|
||||
ArchRule archRule = eventClasses
|
||||
.should(hasFieldOfType(correlationIdField, Identifiable.Id.class));
|
||||
|
||||
// act
|
||||
EvaluationResult evaluationResult = archRule.evaluate(ArchUnitHelper.allJavaClassesToCheck);
|
||||
|
||||
// assert
|
||||
assertThat(evaluationResult.getFailureReport().getDetails()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_EventClassesHaveVersionFieldUsingStrongType() {
|
||||
// arrange
|
||||
String versionField = "version";
|
||||
|
||||
ArchRule archRule = eventClasses
|
||||
.should(hasFieldOfType(versionField, Versionable.Version.class));
|
||||
|
||||
// act
|
||||
EvaluationResult evaluationResult = archRule.evaluate(ArchUnitHelper.allJavaClassesToCheck);
|
||||
|
||||
// assert
|
||||
assertThat(evaluationResult.getFailureReport().getDetails()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_EventClassesHaveIdFieldWhichIsPublicFinal() {
|
||||
// arrange
|
||||
String idField = "id";
|
||||
|
||||
ArchRule archRule = eventClasses
|
||||
.should(hasFieldPublicFinal(idField));
|
||||
|
||||
// act
|
||||
EvaluationResult evaluationResult = archRule.evaluate(ArchUnitHelper.allJavaClassesToCheck);
|
||||
|
||||
// assert
|
||||
assertThat(evaluationResult.getFailureReport().getDetails()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_EventClassesHaveCorrelationIdFieldWhichIsPublicFinal() {
|
||||
// arrange
|
||||
String correlationIdField = "correlationId";
|
||||
|
||||
ArchRule archRule = eventClasses
|
||||
.should(hasFieldPublicFinal(correlationIdField));
|
||||
|
||||
// act
|
||||
EvaluationResult evaluationResult = archRule.evaluate(ArchUnitHelper.allJavaClassesToCheck);
|
||||
|
||||
// assert
|
||||
assertThat(evaluationResult.getFailureReport().getDetails()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_EventClassesHaveVersionFieldWhichIsPrivateStatic() {
|
||||
// arrange
|
||||
String versionField = "version";
|
||||
|
||||
ArchRule archRule = eventClasses
|
||||
.should(hasFieldPrivateStatic(versionField, false));
|
||||
|
||||
// act
|
||||
EvaluationResult evaluationResult = archRule.evaluate(ArchUnitHelper.allJavaClassesToCheck);
|
||||
|
||||
// assert
|
||||
assertThat(evaluationResult.getFailureReport().getDetails()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_EventClassesHaveEventAsSuffixClassName() {
|
||||
// arrange
|
||||
String clazzNameSuffix = "Event";
|
||||
|
||||
ArchRule archRule = eventClasses
|
||||
.should()
|
||||
.haveSimpleNameEndingWith(clazzNameSuffix);
|
||||
|
||||
// act
|
||||
EvaluationResult evaluationResult = archRule.evaluate(ArchUnitHelper.allJavaClassesToCheck);
|
||||
|
||||
// assert
|
||||
assertThat(evaluationResult.getFailureReport().getDetails()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_EventClassesHaveOnlyFieldsImplementingRawWrapper() {
|
||||
// arrange
|
||||
Set<Class<?>> interfaceTypes = Set.of(RawWrapper.class);
|
||||
Set<String> fieldsToBeFilteredOut = Set.of();
|
||||
String fieldSuffixToBeFilteredOut = "Id";
|
||||
|
||||
ArchRule archRule = eventClasses
|
||||
.should(hasOnlyFieldsImplementing(interfaceTypes, fieldsToBeFilteredOut, fieldSuffixToBeFilteredOut));
|
||||
|
||||
// act
|
||||
EvaluationResult evaluationResult = archRule.evaluate(ArchUnitHelper.allJavaClassesToCheck);
|
||||
|
||||
// assert
|
||||
assertThat(evaluationResult.getFailureReport().getDetails()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_EventClassesHaveExternalIdFieldsUsingStrongType() {
|
||||
//We check here that any attribute with suffix "...Id" in an event class is of type Identifiable.Id.
|
||||
// Actually it would be better to check that events do not 'carry' data by themselves but only in
|
||||
// TO classes wrapping this data. But this is not so easy to do.
|
||||
|
||||
// arrange
|
||||
String idFieldSuffix = "Id";
|
||||
|
||||
ArchRule archRule = eventClasses
|
||||
.should(hasFieldsWithSuffixOfType(idFieldSuffix, Identifiable.Id.class));
|
||||
|
||||
// act
|
||||
EvaluationResult evaluationResult = archRule.evaluate(ArchUnitHelper.allJavaClassesToCheck);
|
||||
|
||||
// assert
|
||||
assertThat(evaluationResult.getFailureReport().getDetails()).isEmpty();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,184 @@
|
|||
package architecturetests;
|
||||
|
||||
import com.tngtech.archunit.lang.ArchRule;
|
||||
import com.tngtech.archunit.lang.EvaluationResult;
|
||||
import com.tngtech.archunit.lang.syntax.ArchRuleDefinition;
|
||||
import com.tngtech.archunit.lang.syntax.elements.GivenClassesConjunction;
|
||||
import de.accso.flexinale.common.shared_kernel.EqualsByContent;
|
||||
import de.accso.flexinale.common.shared_kernel.Identifiable;
|
||||
import de.accso.flexinale.common.shared_kernel.RawWrapper;
|
||||
import de.accso.flexinale.common.shared_kernel.Versionable;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Set;
|
||||
|
||||
import static architecturetests.ArchUnitHelper.*;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class InternalStructureOfTOClassesUsingCorrectInterfacesAndAttributeTypesTest {
|
||||
|
||||
static final String PACKAGE_BACKOFFICE_API_MODEL = ArchUnitHelper.PACKAGE_PREFIX + ".backoffice.api_contract.event.model..";
|
||||
static final String PACKAGE_SECURITY_API_MODEL = ArchUnitHelper.PACKAGE_PREFIX + ".security.api_contract.event.model..";
|
||||
static final String PACKAGE_TICKETING_API_MODEL = ArchUnitHelper.PACKAGE_PREFIX + ".ticketing.api_contract.event.model..";
|
||||
|
||||
static final GivenClassesConjunction toClasses = ArchRuleDefinition.classes()
|
||||
.that()
|
||||
.resideInAnyPackage(PACKAGE_BACKOFFICE_API_MODEL,
|
||||
PACKAGE_SECURITY_API_MODEL,
|
||||
PACKAGE_TICKETING_API_MODEL)
|
||||
.and()
|
||||
.areNotEnums()
|
||||
.and()
|
||||
.areNotInnerClasses()
|
||||
.and()
|
||||
.areNotNestedClasses();
|
||||
|
||||
@Test
|
||||
void test_TOClassesAreImplementingIdentifiableAndVersionableAndEqualsByContentAndSerializable() {
|
||||
// arrange
|
||||
ArchRule archRule = toClasses
|
||||
.should()
|
||||
.implement(Identifiable.class)
|
||||
.andShould()
|
||||
.implement(Versionable.class)
|
||||
.andShould()
|
||||
.implement(EqualsByContent.class)
|
||||
.andShould()
|
||||
.implement(Serializable.class);
|
||||
|
||||
// act
|
||||
EvaluationResult evaluationResult = archRule.evaluate(ArchUnitHelper.allJavaClassesToCheck);
|
||||
|
||||
// assert
|
||||
assertThat(evaluationResult.getFailureReport().getDetails()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_TOClassesAreRecords() {
|
||||
// arrange
|
||||
ArchRule archRule = toClasses
|
||||
.should()
|
||||
.beRecords();
|
||||
|
||||
// act
|
||||
EvaluationResult evaluationResult = archRule.evaluate(ArchUnitHelper.allJavaClassesToCheck);
|
||||
|
||||
// assert
|
||||
assertThat(evaluationResult.getFailureReport().getDetails()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_TOClassesHaveIdFieldUsingStrongType() {
|
||||
// arrange
|
||||
String idField = "id";
|
||||
|
||||
ArchRule archRule = toClasses
|
||||
.should(hasFieldOfType(idField, Identifiable.Id.class));
|
||||
|
||||
// act
|
||||
EvaluationResult evaluationResult = archRule.evaluate(ArchUnitHelper.allJavaClassesToCheck);
|
||||
|
||||
// assert
|
||||
assertThat(evaluationResult.getFailureReport().getDetails()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_TOClassesHaveVersionFieldUsingStrongType() {
|
||||
// arrange
|
||||
String versionField = "version";
|
||||
|
||||
ArchRule archRule = toClasses
|
||||
.should(hasFieldOfType(versionField, Versionable.Version.class));
|
||||
|
||||
// act
|
||||
EvaluationResult evaluationResult = archRule.evaluate(ArchUnitHelper.allJavaClassesToCheck);
|
||||
|
||||
// assert
|
||||
assertThat(evaluationResult.getFailureReport().getDetails()).isEmpty();
|
||||
}
|
||||
|
||||
//@Test
|
||||
void test_TOClassesHaveIdFieldWhichIsPrivateFinal() {
|
||||
// Actually this test is not needed as we test that all TO classes are records, see test_TOClassesAreRecords()
|
||||
// In a record, all its attributes are private final
|
||||
|
||||
// arrange
|
||||
String idField = "id";
|
||||
|
||||
ArchRule archRule = toClasses
|
||||
.should(hasFieldPrivateFinal(idField));
|
||||
|
||||
// act
|
||||
EvaluationResult evaluationResult = archRule.evaluate(ArchUnitHelper.allJavaClassesToCheck);
|
||||
|
||||
// assert
|
||||
assertThat(evaluationResult.getFailureReport().getDetails()).isEmpty();
|
||||
}
|
||||
|
||||
//@Test
|
||||
void test_TOClassesHaveVersionFieldWhichIsPrivateFinal() {
|
||||
// Actually this test is not needed as we test that all TO classes are records, see test_TOClassesAreRecords()
|
||||
// In a record, all its attributes are private final
|
||||
|
||||
// arrange
|
||||
String versionField = "version";
|
||||
|
||||
ArchRule archRule = toClasses
|
||||
.should(hasFieldPrivateFinal(versionField));
|
||||
|
||||
// act
|
||||
EvaluationResult evaluationResult = archRule.evaluate(ArchUnitHelper.allJavaClassesToCheck);
|
||||
|
||||
// assert
|
||||
assertThat(evaluationResult.getFailureReport().getDetails()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_TOClassesHaveTOAsSuffixClassName() {
|
||||
// arrange
|
||||
String clazzNameSuffix = "TO";
|
||||
|
||||
ArchRule archRule = toClasses
|
||||
.should()
|
||||
.haveSimpleNameEndingWith(clazzNameSuffix);
|
||||
|
||||
// act
|
||||
EvaluationResult evaluationResult = archRule.evaluate(ArchUnitHelper.allJavaClassesToCheck);
|
||||
|
||||
// assert
|
||||
assertThat(evaluationResult.getFailureReport().getDetails()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_TOClassesHaveOnlyFieldsImplementingRawWrapper() {
|
||||
// arrange
|
||||
Set<Class<?>> interfaceTypes = Set.of(RawWrapper.class);
|
||||
Set<String> fieldsToBeFilteredOut = Set.of();
|
||||
String fieldSuffixToBeFilteredOut = "Id";
|
||||
|
||||
ArchRule archRule = toClasses
|
||||
.should(hasOnlyFieldsImplementing(interfaceTypes, fieldsToBeFilteredOut, fieldSuffixToBeFilteredOut));
|
||||
|
||||
// act
|
||||
EvaluationResult evaluationResult = archRule.evaluate(ArchUnitHelper.allJavaClassesToCheck);
|
||||
|
||||
// assert
|
||||
assertThat(evaluationResult.getFailureReport().getDetails()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_TOClassesHaveExternalIdFieldsUsingStrongType() {
|
||||
// arrange
|
||||
String idFieldSuffix = "Id";
|
||||
|
||||
ArchRule archRule = toClasses
|
||||
.should(hasFieldsWithSuffixOfType(idFieldSuffix, Identifiable.Id.class));
|
||||
|
||||
// act
|
||||
EvaluationResult evaluationResult = archRule.evaluate(ArchUnitHelper.allJavaClassesToCheck);
|
||||
|
||||
// assert
|
||||
assertThat(evaluationResult.getFailureReport().getDetails()).isEmpty();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
package architecturetests;
|
||||
|
||||
import com.tngtech.archunit.lang.ArchRule;
|
||||
import com.tngtech.archunit.lang.EvaluationResult;
|
||||
import com.tngtech.archunit.lang.syntax.ArchRuleDefinition;
|
||||
import de.accso.flexinale.common.shared_kernel.DoNotCheckInArchitectureTests;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static architecturetests.FlexinaleComponent.*;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class NoDependenciesToSpringTest {
|
||||
|
||||
private static Stream<Arguments> allFlexinaleComponents() {
|
||||
return Stream.of(
|
||||
Arguments.of(besucherportal.name)
|
||||
, Arguments.of(common.name)
|
||||
, Arguments.of(security.name)
|
||||
, Arguments.of(security_api_contract.name)
|
||||
, Arguments.of(backoffice.name)
|
||||
, Arguments.of(backoffice_api_contract.name)
|
||||
, Arguments.of(ticketing.name)
|
||||
, Arguments.of(ticketing_api_contract.name)
|
||||
);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("allFlexinaleComponents")
|
||||
void test_NoDependenciesToSpringInCore(final String component) {
|
||||
// arrange
|
||||
final String componentPackagePrefix = ArchUnitHelper.PACKAGE_PREFIX + "." + component;
|
||||
final String applicationPackage = componentPackagePrefix + ".application..";
|
||||
final String domainPackage = componentPackagePrefix + ".domain..";
|
||||
|
||||
ArchRule archRule = ArchRuleDefinition.noClasses()
|
||||
.that()
|
||||
.areNotAnnotatedWith(DoNotCheckInArchitectureTests.class)
|
||||
.and()
|
||||
.resideInAnyPackage(applicationPackage, domainPackage)
|
||||
.should()
|
||||
.beAnnotatedWith(ArchUnitHelper.isSpringAnnotation)
|
||||
.orShould()
|
||||
.dependOnClassesThat(ArchUnitHelper.isSpringClass);
|
||||
|
||||
// act
|
||||
EvaluationResult evaluationResult = archRule.evaluate(ArchUnitHelper.allJavaClassesToCheck);
|
||||
|
||||
// assert
|
||||
assertThat(evaluationResult.getFailureReport().getDetails()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_NoDependenciesToSpringInSharedKernel() {
|
||||
// arrange
|
||||
final String PACKAGE_SHARED_KERNEL = ArchUnitHelper.PACKAGE_PREFIX + ".common.shared_kernel..";
|
||||
|
||||
ArchRule archRule = ArchRuleDefinition.noClasses()
|
||||
.that()
|
||||
.resideInAnyPackage(PACKAGE_SHARED_KERNEL)
|
||||
.should()
|
||||
.beAnnotatedWith(ArchUnitHelper.isSpringAnnotation)
|
||||
.orShould()
|
||||
.dependOnClassesThat(ArchUnitHelper.isSpringClass);
|
||||
|
||||
// act
|
||||
EvaluationResult evaluationResult = archRule.evaluate(ArchUnitHelper.allJavaClassesToCheck);
|
||||
|
||||
// assert
|
||||
assertThat(evaluationResult.getFailureReport().getDetails()).isEmpty();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,162 @@
|
|||
package architecturetests;
|
||||
|
||||
import com.tngtech.archunit.lang.syntax.ArchRuleDefinition;
|
||||
import com.tngtech.archunit.library.Architectures;
|
||||
import de.accso.flexinale.common.shared_kernel.DoNotCheckInArchitectureTests;
|
||||
import jakarta.transaction.Transactional;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static architecturetests.ArchUnitHelper.*;
|
||||
import static architecturetests.FlexinaleComponent.*;
|
||||
|
||||
public class OnionDependenciesTest {
|
||||
|
||||
record Layer(String layer, String... packages) {}
|
||||
|
||||
private static Stream<Arguments> allFlexinaleComponents() {
|
||||
return Stream.of(
|
||||
Arguments.of(besucherportal.name)
|
||||
, Arguments.of(backoffice.name)
|
||||
, Arguments.of(ticketing.name)
|
||||
//, Arguments.of(common.name) // does not have the onion structure
|
||||
//, Arguments.of(security.name) // does not have the onion structure
|
||||
//, Arguments.of(ticketing_api_contract.name) // does not have the onion structure
|
||||
//, Arguments.of(security_api_contract.name) // does not have the onion structure
|
||||
//, Arguments.of(backoffice_api_contract.name ) // does not have the onion structure
|
||||
);
|
||||
}
|
||||
|
||||
private static Stream<Arguments> allFlexinaleComponentsWithOutBesucherportal() {
|
||||
return Stream.of(
|
||||
Arguments.of(backoffice.name)
|
||||
, Arguments.of(ticketing.name)
|
||||
//, Arguments.of(common.name) // does not have the onion structure
|
||||
//, Arguments.of(security.name) // does not have the onion structure
|
||||
//, Arguments.of(ticketing_api_contract.name) // does not have the onion structure
|
||||
//, Arguments.of(security_api_contract.name) // does not have the onion structure
|
||||
//, Arguments.of(backoffice_api_contract.name ) // does not have the onion structure
|
||||
);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("allFlexinaleComponents")
|
||||
void test_onion_architecture_for_each_component(final String component) {
|
||||
String componentPackagePrefix = PACKAGE_PREFIX + "." + component;
|
||||
|
||||
final Layer apiRest = new Layer("API Rest", componentPackagePrefix + ".api_in.rest..");
|
||||
final Layer apiWeb = new Layer("API Web", componentPackagePrefix + ".api_in.web..");
|
||||
final Layer apiEventIn = new Layer("API Event In", componentPackagePrefix + ".api_in.event..");
|
||||
final Layer apiEventOut = new Layer("API Event Out", componentPackagePrefix + ".api_out.event..");
|
||||
final Layer apiContract = new Layer("API Contract", componentPackagePrefix + ".api_contract..");
|
||||
final Layer application = new Layer("Application", componentPackagePrefix + ".application..");
|
||||
final Layer domainServices = new Layer("DomainServices", componentPackagePrefix + ".domain.services..", componentPackagePrefix + ".domain.dao..");
|
||||
final Layer domainModel = new Layer("DomainModel", componentPackagePrefix + ".domain.model..");
|
||||
final Layer infrastructure = new Layer("Infra", componentPackagePrefix + ".infrastructure");
|
||||
final Layer infraPersistence = new Layer("Infra Persistence", componentPackagePrefix + ".infrastructure.persistence..");
|
||||
final Layer sharedKernel = new Layer("SharedKernel", componentPackagePrefix + ".shared_kernel..");
|
||||
|
||||
Architectures.LayeredArchitecture layeredArchitecture = Architectures.layeredArchitecture()
|
||||
.consideringOnlyDependenciesInAnyPackage(componentPackagePrefix + "..")
|
||||
.withOptionalLayers(true) // needed in modulith-2 and distributed (while commented out in modulith-1)
|
||||
.layer(apiRest.layer) .definedBy(apiRest.packages)
|
||||
.layer(apiWeb.layer) .definedBy(apiWeb.packages)
|
||||
.layer(apiEventIn.layer) .definedBy(apiEventIn.packages)
|
||||
.layer(apiEventOut.layer) .definedBy(apiEventOut.packages)
|
||||
.layer(apiContract.layer) .definedBy(apiContract.packages)
|
||||
.layer(application.layer) .definedBy(application.packages)
|
||||
.layer(domainServices.layer) .definedBy(domainServices.packages)
|
||||
.layer(domainModel.layer) .definedBy(domainModel.packages)
|
||||
.layer(infrastructure.layer) .definedBy(infrastructure.packages)
|
||||
.layer(infraPersistence.layer).definedBy(infraPersistence.packages)
|
||||
.layer(sharedKernel.layer) .definedBy(sharedKernel.packages);
|
||||
|
||||
// shared kernel
|
||||
Architectures.LayeredArchitecture onionArchitecture = layeredArchitecture
|
||||
.whereLayer(sharedKernel.layer).mayNotAccessAnyLayer()
|
||||
|
||||
// outer layer ring: no ingoing dependencies allowed
|
||||
.whereLayer( apiWeb.layer).mayNotBeAccessedByAnyLayer()
|
||||
.whereLayer(apiRest.layer).mayNotBeAccessedByAnyLayer()
|
||||
.whereLayer(infrastructure.layer).mayNotBeAccessedByAnyLayer()
|
||||
|
||||
// web and rest - define outgoing dependencies
|
||||
.whereLayer(apiWeb.layer)
|
||||
.mayOnlyAccessLayers(application.layer, domainModel.layer, sharedKernel.layer)
|
||||
|
||||
.whereLayer(apiRest.layer)
|
||||
.mayOnlyAccessLayers(apiEventOut.layer, application.layer, domainModel.layer, sharedKernel.layer)
|
||||
|
||||
.whereLayer(apiEventIn.layer)
|
||||
.mayOnlyAccessLayers(apiContract.layer, application.layer, domainModel.layer, sharedKernel.layer)
|
||||
|
||||
.whereLayer(apiEventOut.layer)
|
||||
.mayOnlyAccessLayers(apiContract.layer, application.layer, domainModel.layer, sharedKernel.layer)
|
||||
|
||||
// infrastructure - define outgoing dependencies
|
||||
.whereLayer(infrastructure.layer)
|
||||
.mayOnlyAccessLayers(apiEventIn.layer, apiEventOut.layer, application.layer, domainServices.layer, infraPersistence.layer, sharedKernel.layer)
|
||||
|
||||
.whereLayer(infraPersistence.layer)
|
||||
.mayOnlyAccessLayers(domainServices.layer, domainModel.layer, sharedKernel.layer)
|
||||
|
||||
// application - define outgoing dependencies
|
||||
.whereLayer(application.layer)
|
||||
.mayOnlyAccessLayers(domainServices.layer, domainModel.layer, sharedKernel.layer)
|
||||
|
||||
// domain services - define outgoing dependencies
|
||||
.whereLayer(domainServices.layer)
|
||||
.mayOnlyAccessLayers(sharedKernel.layer, domainModel.layer)
|
||||
|
||||
// domain model - define outgoing dependencies
|
||||
.whereLayer(domainModel.layer)
|
||||
.mayOnlyAccessLayers(sharedKernel.layer);
|
||||
|
||||
// check the onion architecture
|
||||
onionArchitecture
|
||||
.ignoreDependency(isSpringBootTestClass, isSpringBootApplicationMainClass)
|
||||
.ignoreDependency(isFlexinaleTestClassAnnotatedWithDoNotCheckAnnotation, isAnyClass)
|
||||
.because("we want to enforce the onion architecture inside each component")
|
||||
.check(allJavaClassesToCheck);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("allFlexinaleComponents")
|
||||
void test_that_only_onion_application_services_are_annotated_with_transactional(final String component) {
|
||||
// arrange
|
||||
String componentPackagePrefix = PACKAGE_PREFIX + "." + component;
|
||||
|
||||
String applicationServicesPackage = componentPackagePrefix + ".application.services..";
|
||||
|
||||
ArchRuleDefinition.classes().that()
|
||||
.resideInAnyPackage(componentPackagePrefix + "..") // only check classes in Component under consideration
|
||||
.and()
|
||||
.areAnnotatedWith(Transactional.class)
|
||||
.should().resideInAnyPackage(applicationServicesPackage)
|
||||
.check(allJavaClassesToCheck);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("allFlexinaleComponentsWithOutBesucherportal")
|
||||
void test_that_all_onion_application_services_are_annotated_with_transactional(final String component) {
|
||||
// arrange
|
||||
String componentPackagePrefix = PACKAGE_PREFIX + "." + component;
|
||||
|
||||
String applicationServicesPackage = componentPackagePrefix + ".application.services..";
|
||||
|
||||
ArchRuleDefinition.classes().that()
|
||||
.resideInAnyPackage(applicationServicesPackage)
|
||||
.and()
|
||||
.haveSimpleNameEndingWith("Service")
|
||||
.and()
|
||||
.areNotAnnotatedWith(DoNotCheckInArchitectureTests.class)
|
||||
.and()
|
||||
.areNotInterfaces()
|
||||
.should().beAnnotatedWith(Transactional.class)
|
||||
.check(allJavaClassesToCheck);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
application.title=FLEXinale as Distributed Services, Test
|
||||
application.version=test
|
||||
spring.banner.location=classpath:/flexinale-banner.txt
|
||||
|
||||
#########################################################################
|
||||
# For endpoint /version
|
||||
#########################################################################
|
||||
build.version=@pom.version@
|
||||
build.date=@maven.build.timestamp@
|
||||
|
||||
#########################################################################
|
||||
# Persistence
|
||||
#########################################################################
|
||||
spring.datasource.url=jdbc:postgresql://127.0.0.1:5432/distributed-test
|
||||
spring.datasource.name=flexinale
|
||||
spring.datasource.username=flexinale
|
||||
spring.datasource.password=flexinale
|
||||
spring.datasource.driver-class-name=org.postgresql.Driver
|
||||
spring.jpa.database=postgresql
|
||||
spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect
|
||||
spring.jpa.generate-ddl=true
|
||||
# spring.jpa.show-sql=true
|
||||
spring.jpa.hibernate.ddl-auto=create
|
||||
server.port=9093
|
||||
|
||||
#########################################################################
|
||||
# Web
|
||||
#########################################################################
|
||||
spring.thymeleaf.prefix=classpath:/templates/
|
||||
spring.thymeleaf.suffix=.html
|
||||
server.error.path=/error
|
||||
|
||||
#########################################################################
|
||||
# flexinale properties
|
||||
#########################################################################
|
||||
# Quote for online kontingent in percent
|
||||
de.accso.flexinale.kontingent.quote.online=33
|
||||
# time in minutes
|
||||
de.accso.flexinale.vorfuehrung.min-zeit-zwischen-vorfuehrungen-in-minuten=30
|
||||
|
|
@ -0,0 +1 @@
|
|||
archRule.failOnEmptyShould = false
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
<configuration>
|
||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>%d{YYYYMMdd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
<logger name="architecturetests" level="INFO"/>
|
||||
<root level="INFO">
|
||||
<appender-ref ref="STDOUT" />
|
||||
</root>
|
||||
</configuration>
|
||||
Loading…
Add table
Add a link
Reference in a new issue