Add automatic architecture verification 22/121322/6
authorBruno Sakoto <bruno.sakoto@bell.ca>
Wed, 12 May 2021 12:37:16 +0000 (08:37 -0400)
committerBruno Sakoto <bruno.sakoto@bell.ca>
Mon, 17 May 2021 15:11:37 +0000 (11:11 -0400)
Introduce verification for dependencies and layers.

Issue-ID: CPS-381
Signed-off-by: Bruno Sakoto <bruno.sakoto@bell.ca>
Change-Id: I948439ee5bcba2d41ccba3028d62a728babc83da

pom.xml
src/test/java/org/onap/cps/temporal/architecture/DependencyArchitectureTest.java [new file with mode: 0644]
src/test/java/org/onap/cps/temporal/architecture/LayeredArchitectureTest.java [new file with mode: 0644]

diff --git a/pom.xml b/pom.xml
index 9488873..c38b565 100755 (executable)
--- a/pom.xml
+++ b/pom.xml
 
     <properties>
         <app>org.onap.cps.temporal.Application</app>
-        <cps.checkstyle.version>1.0.1</cps.checkstyle.version>
-        <cps.spotbugs.version>1.0.1</cps.spotbugs.version>
         <docker.repository.pull>nexus3.onap.org:10001/</docker.repository.pull>
         <docker.repository.push>nexus3.onap.org:10003/</docker.repository.push>
         <image.base>${docker.repository.pull}onap/integration-java11:8.0.0</image.base>
         <image.name>${docker.repository.push}onap/cps-temporal</image.name>
-        <hibernate-types.version>2.10.0</hibernate-types.version>
         <java.version>11</java.version>
-        <jib-maven-plugin.version>3.0.0</jib-maven-plugin.version>
         <maven.build.timestamp.format>yyyyMMdd'T'HHmmss'Z'</maven.build.timestamp.format>
         <minimum-coverage>0.8</minimum-coverage>
+        <!-- Application dependencies versions -->
+        <spring-boot-dependencies.version>2.3.8.RELEASE</spring-boot-dependencies.version>
+        <hibernate-types.version>2.10.0</hibernate-types.version>
+        <liquibase-core.version>4.3.2</liquibase-core.version>
+        <!-- Tests dependencies versions -->
+        <spock-bom.version>2.0-M4-groovy-3.0</spock-bom.version>
+        <groovy.version>3.0.7</groovy.version>
+        <junit-jupiter.version>1.15.2</junit-jupiter.version>
+        <testcontainers-postgresql.version>1.15.2</testcontainers-postgresql.version>
+        <archunit-junit5.version>0.18.0</archunit-junit5.version>
+        <!-- Plugins and plugins dependencies versions -->
+        <spring-boot-maven-plugin.version>2.3.3.RELEASE</spring-boot-maven-plugin.version>
+        <gmavenplus-plugin.version>1.12.1</gmavenplus-plugin.version>
+        <jib-maven-plugin.version>3.0.0</jib-maven-plugin.version>
         <oparent.version>3.2.0</oparent.version>
+        <cps.checkstyle.version>1.0.1</cps.checkstyle.version>
+        <cps.spotbugs.version>1.0.1</cps.spotbugs.version>
         <spotbugs-maven-plugin.version>4.1.3</spotbugs-maven-plugin.version>
-        <spotbugs.slf4j.version>1.8.0-beta4</spotbugs.slf4j.version>
         <spotbugs.version>4.2.0</spotbugs.version>
+        <spotbugs.slf4j.version>1.8.0-beta4</spotbugs.slf4j.version>
+        <bug-pattern.version>1.5.0</bug-pattern.version>
     </properties>
 
     <dependencyManagement>
             <dependency>
                 <groupId>org.springframework.boot</groupId>
                 <artifactId>spring-boot-dependencies</artifactId>
-                <version>2.3.8.RELEASE</version>
+                <version>${spring-boot-dependencies.version}</version>
                 <type>pom</type>
                 <scope>import</scope>
             </dependency>
             <dependency>
                 <groupId>org.spockframework</groupId>
                 <artifactId>spock-bom</artifactId>
-                <version>2.0-M4-groovy-3.0</version>
+                <version>${spock-bom.version}</version>
                 <type>pom</type>
                 <scope>import</scope>
             </dependency>
         <dependency>
             <groupId>org.liquibase</groupId>
             <artifactId>liquibase-core</artifactId>
-            <version>4.3.2</version>
+            <version>${liquibase-core.version}</version>
         </dependency>
         <dependency>
             <groupId>org.projectlombok</groupId>
         <dependency>
             <groupId>org.codehaus.groovy</groupId>
             <artifactId>groovy</artifactId>
-            <version>3.0.7</version>
+            <version>${groovy.version}</version>
         </dependency>
         <dependency>
             <groupId>org.springframework.boot</groupId>
         <dependency>
             <groupId>org.testcontainers</groupId>
             <artifactId>junit-jupiter</artifactId>
-            <version>1.15.2</version>
+            <version>${junit-jupiter.version}</version>
             <scope>test</scope>
         </dependency>
         <dependency>
             <groupId>org.testcontainers</groupId>
             <artifactId>postgresql</artifactId>
-            <version>1.15.2</version>
+            <version>${testcontainers-postgresql.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.tngtech.archunit</groupId>
+            <artifactId>archunit-junit5</artifactId>
+            <version>${archunit-junit5.version}</version>
             <scope>test</scope>
         </dependency>
     </dependencies>
             <plugin>
                 <groupId>org.springframework.boot</groupId>
                 <artifactId>spring-boot-maven-plugin</artifactId>
-                <version>2.3.3.RELEASE</version>
+                <version>${spring-boot-maven-plugin.version}</version>
             </plugin>
             <plugin>
                 <!-- The gmavenplus plugin is used to compile Groovy code. To learn more about this plugin,
                 visit https://github.com/groovy/GMavenPlus/wiki -->
                 <groupId>org.codehaus.gmavenplus</groupId>
                 <artifactId>gmavenplus-plugin</artifactId>
-                <version>1.12.1</version>
+                <version>${gmavenplus-plugin.version}</version>
                 <executions>
                     <execution>
                         <goals>
                         <plugin>
                             <groupId>jp.skypencil.findbugs.slf4j</groupId>
                             <artifactId>bug-pattern</artifactId>
-                            <version>1.5.0</version>
+                            <version>${bug-pattern.version}</version>
                         </plugin>
                     </plugins>
                     <!--
diff --git a/src/test/java/org/onap/cps/temporal/architecture/DependencyArchitectureTest.java b/src/test/java/org/onap/cps/temporal/architecture/DependencyArchitectureTest.java
new file mode 100644 (file)
index 0000000..8e4cfd5
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (c) 2021 Bell Canada.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.temporal.architecture;
+
+import static com.tngtech.archunit.library.DependencyRules.NO_CLASSES_SHOULD_DEPEND_UPPER_PACKAGES;
+import static com.tngtech.archunit.library.dependencies.SlicesRuleDefinition.slices;
+
+import com.tngtech.archunit.core.importer.ImportOption;
+import com.tngtech.archunit.junit.AnalyzeClasses;
+import com.tngtech.archunit.junit.ArchTest;
+import com.tngtech.archunit.lang.ArchRule;
+
+/**
+ * Test class responsible for dependencies validations.
+ */
+@AnalyzeClasses(packages = "org.onap.cps.temporal", importOptions = { ImportOption.DoNotIncludeTests.class })
+public class DependencyArchitectureTest {
+
+    @ArchTest
+    static final ArchRule noCyclesRule =
+            slices().matching("org.onap.cps.temporal.(**)..").should().beFreeOfCycles();
+
+    @ArchTest
+    static final ArchRule noUpperPackageDependencyRule = NO_CLASSES_SHOULD_DEPEND_UPPER_PACKAGES;
+
+}
diff --git a/src/test/java/org/onap/cps/temporal/architecture/LayeredArchitectureTest.java b/src/test/java/org/onap/cps/temporal/architecture/LayeredArchitectureTest.java
new file mode 100644 (file)
index 0000000..d47e8a5
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (c) 2021 Bell Canada.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.temporal.architecture;
+
+import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.classes;
+import static com.tngtech.archunit.library.Architectures.layeredArchitecture;
+
+import com.tngtech.archunit.core.importer.ImportOption;
+import com.tngtech.archunit.junit.AnalyzeClasses;
+import com.tngtech.archunit.junit.ArchTest;
+import com.tngtech.archunit.lang.ArchRule;
+
+/**
+ * Test class responsible for layered architecture.
+ */
+@AnalyzeClasses(packages = "org.onap.cps.temporal", importOptions = { ImportOption.DoNotIncludeTests.class })
+public class LayeredArchitectureTest {
+
+    private static final String CONTROLLER_PACKAGE = "org.onap.cps.temporal.controller..";
+    private static final String SERVICE_PACKAGE = "org.onap.cps.temporal.service..";
+    private static final String REPOSITORY_PACKAGE = "org.onap.cps.temporal.repository..";
+
+    // 'access' catches only violations by real accesses,
+    // i.e. accessing a field, calling a method; compare 'dependOn' further down
+
+    @ArchTest
+    public static final ArchRule layeredArchitectureRule =
+            layeredArchitecture()
+                    .layer("Controller").definedBy(CONTROLLER_PACKAGE)
+                    .layer("Service").definedBy(SERVICE_PACKAGE)
+                    .layer("Repository").definedBy(REPOSITORY_PACKAGE)
+                    .whereLayer("Controller").mayNotBeAccessedByAnyLayer()
+                    .whereLayer("Service").mayOnlyBeAccessedByLayers("Controller")
+                    .whereLayer("Repository").mayOnlyBeAccessedByLayers("Service");
+
+    // 'dependOn' catches a wider variety of violations,
+    // e.g. having fields of type, having method parameters of type, extending type ...
+
+    @ArchTest
+    static final ArchRule controllerDependencyRule =
+            classes().that().resideInAPackage(CONTROLLER_PACKAGE)
+                    .should().onlyHaveDependentClassesThat()
+                    .resideInAPackage(CONTROLLER_PACKAGE);
+
+    @ArchTest
+    static final ArchRule serviceDependencyRule =
+            classes().that().resideInAPackage(SERVICE_PACKAGE)
+                    .should().onlyHaveDependentClassesThat()
+                    .resideInAnyPackage(CONTROLLER_PACKAGE, SERVICE_PACKAGE);
+
+    @ArchTest
+    static final ArchRule repositoryDependencyRule =
+            classes().that().resideInAPackage(REPOSITORY_PACKAGE)
+                    .should().onlyHaveDependentClassesThat()
+                    .resideInAnyPackage(SERVICE_PACKAGE, REPOSITORY_PACKAGE);
+
+}
\ No newline at end of file