diff --git a/lib/taskana-core/src/test/java/pro/taskana/ArchitectureTest.java b/lib/taskana-core/src/test/java/pro/taskana/ArchitectureTest.java index 38dc5e26e..5e840ad55 100644 --- a/lib/taskana-core/src/test/java/pro/taskana/ArchitectureTest.java +++ b/lib/taskana-core/src/test/java/pro/taskana/ArchitectureTest.java @@ -1,16 +1,25 @@ package pro.taskana; import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.classes; +import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noClasses; import static com.tngtech.archunit.library.GeneralCodingRules.NO_CLASSES_SHOULD_ACCESS_STANDARD_STREAMS; import static com.tngtech.archunit.library.GeneralCodingRules.NO_CLASSES_SHOULD_THROW_GENERIC_EXCEPTIONS; import static com.tngtech.archunit.library.dependencies.SlicesRuleDefinition.slices; +import static org.junit.jupiter.api.DynamicTest.dynamicTest; import com.tngtech.archunit.core.domain.JavaClasses; import com.tngtech.archunit.core.importer.ClassFileImporter; +import com.tngtech.archunit.core.importer.ImportOption.DoNotIncludeTests; import com.tngtech.archunit.lang.ArchRule; +import java.util.Collection; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import java.util.stream.Stream; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.DynamicTest; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestFactory; /** * Test architecture of classes in taskana. For more info and examples see @@ -22,22 +31,13 @@ class ArchitectureTest { @BeforeAll static void init() throws ClassNotFoundException { // time intensive operation should only be done once - importedClasses = new ClassFileImporter().importPackages("pro.taskana"); + importedClasses = + new ClassFileImporter() + .withImportOption(new DoNotIncludeTests()) + .importPackages("pro.taskana"); } - @Test @Disabled - void mapperShouldBePlacedInMappingsPackage() { - ArchRule myRule = - classes() - .that() - .haveSimpleNameEndingWith("Mapper") - .should() - .resideInAPackage("..mappings.."); - - myRule.check(importedClasses); - } - @Test void apiClassesShouldNotDependOnInternalClasses() { ArchRule myRule = @@ -47,7 +47,7 @@ class ArchitectureTest { .and() .haveSimpleNameNotEndingWith("BulkOperationResults") .and() - .resideInAPackage("..api") + .resideInAPackage("..api..") .should() .onlyDependOnClassesThat() .resideOutsideOfPackage("..internal.."); @@ -69,8 +69,7 @@ class ArchitectureTest { @Test void onlyExceptionsShouldResideInExceptionPackage() { ArchRule myRule = - classes().that().resideInAPackage("..exceptions").should() - .beAssignableTo(Throwable.class); + classes().that().resideInAPackage("..exceptions").should().beAssignableTo(Throwable.class); myRule.check(importedClasses); } @@ -84,20 +83,94 @@ class ArchitectureTest { NO_CLASSES_SHOULD_ACCESS_STANDARD_STREAMS.check(importedClasses); } - @Test + /** + * Solution would be nice, but is not achievable without big changes + * https://www.archunit.org/userguide/html/000_Index.html#_cycle_checks + */ @Disabled + @Test void freeOfCycles() { - ArchRule myRule = slices().matching("pro.taskana.(*)..") - .should().beFreeOfCycles(); + ArchRule myRule = slices().matching("pro.taskana.(*)..").should().beFreeOfCycles(); myRule.check(importedClasses); } + /** + * Test for cycles with subpackages + * https://www.archunit.org/userguide/html/000_Index.html#_cycle_checks + */ + @TestFactory + Collection freeOfCycles_subpackages() { + + Stream packagesToTest = + Stream.of( + "pro.taskana.common.internal.(*)..", + "pro.taskana.common.api.(*)..", + "pro.taskana.classification.api.(*)..", + "pro.taskana.classification.internal.(*)..", + "pro.taskana.history.api.(*)..", + "pro.taskana.history.internal.(*)..", + // to be fixed soon + // "pro.taskana.report.api.(*)..", + "pro.taskana.report.internal.(*)..", + "pro.taskana.task.api.(*)..", + "pro.taskana.task.internal.(*)..", + "pro.taskana.workbasket.api.(*)..", + "pro.taskana.workbasket.internal.(*).."); + return packagesToTest + .map( + p -> + dynamicTest( + p.replaceAll(Pattern.quote("pro.taskana."), "") + " is free of cycles", + () -> slices().matching(p).should().beFreeOfCycles().check(importedClasses))) + .collect(Collectors.toList()); + } + + @Disabled("TBD") @Test - @Disabled - void freeOfCyclicDependencies() { - ArchRule myRule = slices().matching("pro.taskana.(*)..").should() - .notDependOnEachOther(); + void commonClassesShouldNotDependOnOtherDomainClasses() { + ArchRule myRule = + noClasses() + .that() + .resideInAPackage("..common..") + .should() + .dependOnClassesThat() + .resideInAnyPackage( + "..classification..", "..history..", "..report..", "..task..", "..workbasket.."); myRule.check(importedClasses); } + @TestFactory + Collection classesShouldNotDependOnReportDomainClasses() { + + Stream packagesToTest = + Stream.of( + "..workbasket..", + "..history..", + // tasks are to be fixed ... + // "..task..", + "..classification.."); + return packagesToTest + .map( + p -> + dynamicTest( + "Domain " + + p.replaceAll(Pattern.quote("."), "") + + " should not depend on reports", + () -> + noClasses() + .that() + .resideInAPackage(p) + .should() + .dependOnClassesThat() + .resideInAnyPackage("..report..") + .check(importedClasses))) + .collect(Collectors.toList()); + } + + @Disabled + @Test + void freeOfCyclicDependencies() { + ArchRule myRule = slices().matching("pro.taskana.(*)..").should().notDependOnEachOther(); + myRule.check(importedClasses); + } }