diff --git a/README.md b/README.md index 4eccc6ec..1997dcf9 100644 --- a/README.md +++ b/README.md @@ -315,7 +315,7 @@ If you have figured out how it works, please add instructions above this section ##### Build Settings Go to [Gradle Settings}(jetbrains://idea/settings?name=Build%2C+Execution%2C+Deployment--Build+Tools--Gradle) and select "Build and run using" and "Run tests using" both to "gradle". -Otherwise, settings from `build.gradle`, like compiler arguments, are not applied when compiling through *IntelliJ IDEA*. +Otherwise, settings from `build.gradle.kts`, like compiler arguments, are not applied when compiling through *IntelliJ IDEA*. ##### Annotation Processor @@ -368,8 +368,8 @@ You can explore the prototype as follows: `build/` Output directory for gradle build results. Ignored by git. -`build.gradle` - Gradle build-file. Contains dependencies and build configurations. +`build.gradle.kts` + Gradle build-file (Kotlin-Script). Contains dependencies and build configurations. `doc/` Contains project documentation. @@ -503,7 +503,7 @@ gw jacocoTestReport ``` This task is also automatically run after `gw test`. -It is configured in [build.gradle](build.gradle). +It is configured in [build.gradle.kts](build.gradle.kts). A report is generated under [build/reports/jacoco/tests/test/index.html](./build/reports/jacoco/test/html/index.html). @@ -525,7 +525,7 @@ It can be executed with: gw pitest ``` -Classes to be scanned, tests to be executed and thresholds are configured in [build.gradle](build.gradle). +Classes to be scanned, tests to be executed and thresholds are configured in [build.gradle.kts](build.gradle.kts). A report is generated under [build/reports/pitest/index.html](./build/reports/pitest/index.html). A link to the report is also printed after the `pitest` run. @@ -561,7 +561,7 @@ gw dependencyCheckAnalyze ``` This task is also included in `gw build` and `gw check`. -It is configured in [build.gradle](build.gradle). +It is configured in [build.gradle.kts](build.gradle.kts). Often vulnerability reports don't apply to our use cases. Therefore, reports can be [suppressed](./etc/owasp-dependency-check-suppression.xml). @@ -663,7 +663,7 @@ howto Add `--args='--spring.profiles.active=...` with the wanted profile selector: ```sh -gw bootRun --args='--spring.profiles.active=external-db,only-office,without-test-data' +gw bootRun --args='--spring.profiles.active=fakeCasAuthenticator,external-db,only-office,without-test-data' ``` These profiles mean: @@ -837,6 +837,12 @@ By default, `gw bootRun` starts the application on port 8080. This port can be changed in [src/main/resources/application.yml](src/main/resources/application.yml) through the property `server.port`. +Or on the command line, add ` --server.port=...` to the `--args` parameter of the `bootRun` task, e.g.: + +```sh +gw bootRun --args='--spring.profiles.active=dev,fakeCasAuthenticator,complete,test-data --server.port=8888' +``` + ### How to Use a Persistent Database for Integration Tests? Usually, the `DataJpaTest` integration tests run against a database in a temporary docker container. diff --git a/build.gradle b/build.gradle deleted file mode 100644 index ef8d6631..00000000 --- a/build.gradle +++ /dev/null @@ -1,514 +0,0 @@ -plugins { - id 'java' - id 'org.springframework.boot' version '3.4.2' - id 'io.spring.dependency-management' version '1.1.7' // manages implicit dependencies - id 'io.openapiprocessor.openapi-processor' version '2023.2' // generates Controller-interface and resources from API-spec - id 'com.github.jk1.dependency-license-report' version '2.9' // checks dependency-license compatibility - id "org.owasp.dependencycheck" version "12.0.1" // checks dependencies for known vulnerabilities - id "com.diffplug.spotless" version "7.0.2" // formats + checks formatting for source-code - id 'jacoco' // determines code-coverage of tests - id 'info.solidsoft.pitest' version '1.15.0' // performs mutation testing - id 'se.patrikerdes.use-latest-versions' version '0.2.18' // updates module and plugin versions - id 'com.github.ben-manes.versions' version '0.52.0' // determines which dependencies have updates -} - -// HOWTO: find out which dependency versions are managed by Spring Boot: -// https://docs.spring.io/spring-boot/appendix/dependency-versions/coordinates.html - -group = 'net.hostsharing' -version = '0.0.1-SNAPSHOT' - -wrapper { - distributionType = Wrapper.DistributionType.BIN - gradleVersion = '8.5' -} - -// TODO.impl: self-attaching is deprecated, see: -// https://javadoc.io/doc/org.mockito/mockito-core/latest/org/mockito/Mockito.html#0.3 - -configurations { - compileOnly { - extendsFrom annotationProcessor - } - testCompile { - extendsFrom testAnnotationProcessor - - // Only JUNit 5 (Jupiter) should be used at compile time. - // For runtime it's still needed by testcontainers, though. - exclude group: 'junit', module: 'junit' - exclude group: 'org.junit.vintage', module: 'junit-vintage-engine' - } -} - -repositories { - mavenCentral() - maven { url 'https://repo.spring.io/milestone' } - maven { url 'https://repo.spring.io/snapshot' } -} - -java { - toolchain { - languageVersion = JavaLanguageVersion.of(21) - vendor = JvmVendorSpec.ADOPTIUM - implementation = JvmImplementation.VENDOR_SPECIFIC - } -} - -ext { - set('testcontainersVersion', "1.17.3") -} - -dependencies { - implementation 'org.springframework.boot:spring-boot-starter-data-jpa' - implementation 'org.springframework.boot:spring-boot-starter-data-rest' - implementation 'org.springframework.boot:spring-boot-starter-jdbc' - implementation 'org.springframework.boot:spring-boot-starter-web' - implementation 'org.springframework.boot:spring-boot-starter-validation' - implementation 'org.springframework.boot:spring-boot-starter-actuator' - implementation 'org.springframework.boot:spring-boot-starter-security' - implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.8.5' - implementation 'com.github.gavlyukovskiy:datasource-proxy-spring-boot-starter:1.10.0' - implementation 'org.postgresql:postgresql' - implementation 'org.liquibase:liquibase-core' - implementation 'io.hypersistence:hypersistence-utils-hibernate-63:3.9.0' - implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310' - implementation 'org.openapitools:jackson-databind-nullable:0.2.6' - implementation 'org.apache.commons:commons-text:1.13.0' - implementation 'net.java.dev.jna:jna:5.16.0' - implementation 'org.modelmapper:modelmapper:3.2.2' - implementation 'org.iban4j:iban4j:3.2.10-RELEASE' - implementation 'org.reflections:reflections:0.10.2' - - compileOnly 'org.projectlombok:lombok' - testCompileOnly 'org.projectlombok:lombok' - - // TODO.impl: version conflict with SpringDoc, check later and re-enable if fixed - // developmentOnly 'org.springframework.boot:spring-boot-devtools' - - annotationProcessor 'org.projectlombok:lombok' - testAnnotationProcessor 'org.projectlombok:lombok' - - testImplementation 'org.springframework.boot:spring-boot-starter-test' - testImplementation 'org.testcontainers:testcontainers' - testImplementation 'org.testcontainers:junit-jupiter' - testImplementation 'org.junit.jupiter:junit-jupiter' - testImplementation 'org.testcontainers:postgresql' - testImplementation 'com.tngtech.archunit:archunit-junit5:1.3.0' - testImplementation 'io.rest-assured:spring-mock-mvc' - testImplementation 'org.hamcrest:hamcrest-core' - testImplementation 'org.pitest:pitest-junit5-plugin:1.2.1' - testImplementation 'org.junit.jupiter:junit-jupiter-api' - testImplementation 'org.wiremock:wiremock-standalone:3.10.0' -} - -dependencyManagement { - imports { - mavenBom "org.testcontainers:testcontainers-bom:${testcontainersVersion}" - } -} - -// Java Compiler Options -tasks.withType(JavaCompile) { - options.compilerArgs += [ - "-parameters" // keep parameter names => no need for @Param for SpringData - ] -} - -// Configure tests -tasks.named('test') { - useJUnitPlatform() - jvmArgs '-Duser.language=en' - jvmArgs '-Duser.country=US' -} - -// OpenAPI Source Code Generation -openapiProcessor { - springRoot { - processorName 'spring' - processor 'io.openapiprocessor:openapi-processor-spring:2022.5' - apiPath "$projectDir/src/main/resources/api-definition/api-definition.yaml" - mapping "$projectDir/src/main/resources/api-definition/api-mappings.yaml" - targetDir "$buildDir/generated/sources/openapi-javax" - showWarnings true - openApiNullable true - } - springRbac { - processorName 'spring' - processor 'io.openapiprocessor:openapi-processor-spring:2022.5' - apiPath "$projectDir/src/main/resources/api-definition/rbac/rbac.yaml" - mapping "$projectDir/src/main/resources/api-definition/rbac/api-mappings.yaml" - targetDir "$buildDir/generated/sources/openapi-javax" - showWarnings true - openApiNullable true - } - springTest { - processorName 'spring' - processor 'io.openapiprocessor:openapi-processor-spring:2022.5' - apiPath "$projectDir/src/main/resources/api-definition/test/test.yaml" - mapping "$projectDir/src/main/resources/api-definition/test/api-mappings.yaml" - targetDir "$buildDir/generated/sources/openapi-javax" - showWarnings true - openApiNullable true - } - springHsOffice { - processorName 'spring' - processor 'io.openapiprocessor:openapi-processor-spring:2022.5' - apiPath "$projectDir/src/main/resources/api-definition/hs-office/hs-office.yaml" - mapping "$projectDir/src/main/resources/api-definition/hs-office/api-mappings.yaml" - targetDir "$buildDir/generated/sources/openapi-javax" - showWarnings true - openApiNullable true - } - springHsBooking { - processorName 'spring' - processor 'io.openapiprocessor:openapi-processor-spring:2022.5' - apiPath "$projectDir/src/main/resources/api-definition/hs-booking/hs-booking.yaml" - mapping "$projectDir/src/main/resources/api-definition/hs-booking/api-mappings.yaml" - targetDir "$buildDir/generated/sources/openapi-javax" - showWarnings true - openApiNullable true - } - springHsHosting { - processorName 'spring' - processor 'io.openapiprocessor:openapi-processor-spring:2022.5' - apiPath "$projectDir/src/main/resources/api-definition/hs-hosting/hs-hosting.yaml" - mapping "$projectDir/src/main/resources/api-definition/hs-hosting/api-mappings.yaml" - targetDir "$buildDir/generated/sources/openapi-javax" - showWarnings true - openApiNullable true - } -} -sourceSets.main.java.srcDir 'build/generated/sources/openapi' - -abstract class ProcessSpring extends DefaultTask {} - -tasks.register('processSpring', ProcessSpring) -['processSpringRoot', - 'processSpringRbac', - 'processSpringTest', - 'processSpringHsOffice', - 'processSpringHsBooking', - 'processSpringHsHosting' -].each { - project.tasks.processSpring.dependsOn it -} -project.tasks.processResources.dependsOn processSpring -project.tasks.compileJava.dependsOn processSpring - -// Rename javax to jakarta in OpenApi generated java files because -// io.openapiprocessor.openapi-processor 2022.5 does not yet support the openapiprocessor useSpringBoot3 config option. -// TODO.impl: Upgrade to io.openapiprocessor.openapi-processor >= 2024.2 -// and use either `bean-validation: true` in api-mapping.yaml or `useSpringBoot3 true` (not sure where exactly). -task openApiGenerate(type: Copy) { - from "$buildDir/generated/sources/openapi-javax" - into "$buildDir/generated/sources/openapi" - filter { line -> line.replaceAll('javax', 'jakarta') } -} -compileJava.source "$buildDir/generated/sources/openapi" -compileJava.dependsOn openApiGenerate -openApiGenerate.dependsOn processSpring - -// Spotless Code Formatting -spotless { - java { - removeUnusedImports() - leadingTabsToSpaces(4) - endWithNewline() - toggleOffOn() - - target fileTree(rootDir) { - include '**/*.java' - exclude '**/generated/**/*.java' - } - } -} -project.tasks.check.dependsOn(spotlessCheck) -// HACK: no idea why spotless uses the output of these tasks, but we get warnings without those -project.tasks.spotlessJava.dependsOn( - tasks.generateLicenseReport, - // tasks.pitest, TODO.test: PiTest currently does not work, needs to be fixed - tasks.jacocoTestReport, - tasks.processResources, - tasks.processTestResources) - -// OWASP Dependency Security Test -dependencyCheck { - nvd { - apiKey = project.properties['OWASP_API_KEY'] // set it in ~/.gradle/gradle.properties - delay = 16000 - } - format = 'ALL' - suppressionFile = 'etc/owasp-dependency-check-suppression.xml' - failOnError = true - failBuildOnCVSS = 5 -} -project.tasks.check.dependsOn(dependencyCheckAnalyze) -project.tasks.dependencyCheckAnalyze.doFirst { // Why not doLast? See README.md! - println "OWASP Dependency Security Report: file:///${project.rootDir}/build/reports/dependency-check-report.html" -} - - -// License Check -licenseReport { - excludeBoms = true - allowedLicensesFile = new File("$projectDir/etc/allowed-licenses.json") -} -project.tasks.check.dependsOn(checkLicense) - -// HOWTO: run all tests except import- and scenario-tests: gw test -test { - finalizedBy jacocoTestReport // generate report after tests - excludes = [ - 'net.hostsharing.hsadminng.**.generated.**', - ] - useJUnitPlatform { - excludeTags 'importHostingAssets', 'scenarioTest' - } -} - -// JaCoCo Test Code Coverage for unit-tests -jacoco { - toolVersion = "0.8.10" -} -jacocoTestReport { - dependsOn test - afterEvaluate { - classDirectories.setFrom(files(classDirectories.files.collect { - fileTree(dir: it, exclude: [ - "net/hostsharing/hsadminng/**/generated/**/*.class", - "net/hostsharing/hsadminng/hs/HsadminNgApplication.class" - ]) - })) - } - doFirst { // Why not doLast? See README.md! - println "HTML Jacoco Test Code Coverage Report: file://${reports.html.outputLocation.get()}/index.html" - } -} -project.tasks.check.dependsOn(jacocoTestCoverageVerification) -jacocoTestCoverageVerification { - violationRules { - rule { - limit { - minimum = 0.80 // TODO.test: improve instruction coverage - } - } - - // element: PACKAGE, BUNDLE, CLASS, SOURCEFILE or METHOD - // counter: INSTRUCTION, BRANCH, LINE, COMPLEXITY, METHOD, or CLASS - // value: TOTALCOUNT, COVEREDCOUNT, MISSEDCOUNT, COVEREDRATIO or MISSEDRATIO - - rule { - element = 'CLASS' - excludes = [ - 'net.hostsharing.hsadminng.**.generated.**', - 'net.hostsharing.hsadminng.rbac.test.dom.TestDomainEntity', - 'net.hostsharing.hsadminng.HsadminNgApplication', - 'net.hostsharing.hsadminng.ping.PingController', - 'net.hostsharing.hsadminng.rbac.generator.*', - 'net.hostsharing.hsadminng.rbac.grant.RbacGrantsDiagramService', - 'net.hostsharing.hsadminng.rbac.grant.RbacGrantsDiagramService.Node', - 'net.hostsharing.hsadminng.**.*Repository', - 'net.hostsharing.hsadminng.mapper.Mapper' - ] - - limit { - counter = 'LINE' - value = 'COVEREDRATIO' - minimum = 0.75 // TODO.test: improve line coverage - } - } - rule { - element = 'METHOD' - excludes = [ - 'net.hostsharing.hsadminng.**.generated.**', - 'net.hostsharing.hsadminng.HsadminNgApplication.main', - 'net.hostsharing.hsadminng.ping.PingController.*' - ] - - limit { - counter = 'BRANCH' - value = 'COVEREDRATIO' - minimum = 0.00 // TODO.test: improve branch coverage - } - } - } -} - -// HOWTO: run all unit-tests which don't need a database: gw-test unitTest -tasks.register('unitTest', Test) { - useJUnitPlatform { - excludeTags 'importHostingAssets', 'scenarioTest', 'generalIntegrationTest', - 'officeIntegrationTest', 'bookingIntegrationTest', 'hostingIntegrationTest' - } - - group 'verification' - description 'runs all unit-tests which do not need a database' - - mustRunAfter spotlessJava -} - -// HOWTO: run all integration tests which are not specific to a module, like base, rbac, config etc. -tasks.register('generalIntegrationTest', Test) { - useJUnitPlatform { - includeTags 'generalIntegrationTest' - } - - group 'verification' - description 'runs integration tests which are not specific to a module, like base, rbac, config etc.' - - mustRunAfter spotlessJava -} - -// HOWTO: run all integration tests of the office module: gw-test officeIntegrationTest -tasks.register('officeIntegrationTest', Test) { - useJUnitPlatform { - includeTags 'officeIntegrationTest' - } - - group 'verification' - description 'runs integration tests of the office module' - - mustRunAfter spotlessJava -} - -// HOWTO: run all integration tests of the booking module: gw-test bookingIntegrationTest -tasks.register('bookingIntegrationTest', Test) { - useJUnitPlatform { - includeTags 'bookingIntegrationTest' - } - - group 'verification' - description 'runs integration tests of the booking module' - - mustRunAfter spotlessJava -} - -// HOWTO: run all integration tests of the hosting module: gw-test hostingIntegrationTest -tasks.register('hostingIntegrationTest', Test) { - useJUnitPlatform { - includeTags 'hostingIntegrationTest' - } - - group 'verification' - description 'runs integration tests of the hosting module' - - mustRunAfter spotlessJava -} - -tasks.register('importHostingAssets', Test) { - useJUnitPlatform { - includeTags 'importHostingAssets' - } - - group 'verification' - description 'run the import jobs as tests' - - mustRunAfter spotlessJava -} - -tasks.register('scenarioTest', Test) { - useJUnitPlatform { - includeTags 'scenarioTest' - } - - group 'verification' - description 'run the import jobs as tests' - - mustRunAfter spotlessJava -} - -// pitest mutation testing -pitest { - targetClasses = ['net.hostsharing.hsadminng.**'] - excludedClasses = [ - 'net.hostsharing.hsadminng.config.**', - // 'net.hostsharing.hsadminng.**.*Controller', - 'net.hostsharing.hsadminng.**.generated.**' - ] - - targetTests = ['net.hostsharing.hsadminng.**.*UnitTest', 'net.hostsharing.hsadminng.**.*RestTest'] - excludedTestClasses = ['**AcceptanceTest*', '**IntegrationTest*', '**ImportHostingAssets'] - - pitestVersion = '1.17.0' - junit5PluginVersion = '1.1.0' - - threads = 4 - - // As Java unit tests are pretty pointless in our case, this maybe makes not much sense. - mutationThreshold = 71 - coverageThreshold = 57 - testStrengthThreshold = 87 - - outputFormats = ['XML', 'HTML'] - timestampedReports = false -} -// project.tasks.check.dependsOn(project.tasks.pitest) TODO.test: PiTest currently does not work, needs to be fixed -project.tasks.pitest.doFirst { // Why not doLast? See README.md! - println "PiTest Mutation Report: file:///${project.rootDir}/build/reports/pitest/index.html" -} - - -// Dependency Versions Upgrade -useLatestVersions { - finalizedBy check -} - -def isNonStable = { String version -> - def stableKeyword = ['RELEASE', 'FINAL', 'GA'].any { it -> version.toUpperCase().contains(it) } - def regex = /^[0-9,.v-]+(-r)?$/ - return !stableKeyword && !(version ==~ regex) -} - -tasks.named("dependencyUpdates").configure { - rejectVersionIf { - isNonStable(it.candidate.version) - } -} - - -// Generate HTML from Markdown scenario-test-reports using Pandoc: -tasks.register('convertMarkdownToHtml') { - description = 'Generates HTML from Markdown scenario-test-reports using Pandoc.' - group = 'Conversion' - - // Define the template file and input directory - def templateFile = file('doc/scenarios/.template.html') - - // Task configuration and execution - doFirst { - // Check if pandoc is installed - try { - exec { - commandLine 'pandoc', '--version' - } - } catch (Exception) { - throw new GradleException("Pandoc is not installed or not found in the system path.") - } - - // Check if the template file exists - if (!templateFile.exists()) { - throw new GradleException("Template file 'doc/scenarios/.template.html' not found.") - } - } - - doLast { - // Gather all Markdown files in the current directory - fileTree(dir: '.', include: 'build/doc/scenarios/*.md').each { file -> - // Corrected way to create the output file path - def outputFile = new File(file.parent, file.name.replaceAll(/\.md$/, '.html')) - - // Execute pandoc for each markdown file - exec { - commandLine 'pandoc', file.absolutePath, '--template', templateFile.absolutePath, '-o', outputFile.absolutePath - } - - println "Converted ${file.name} to ${outputFile.name}" - } - } -} -convertMarkdownToHtml.dependsOn scenarioTest - -// shortcut for compiling all files -tasks.register('compile') { - dependsOn 'compileJava', 'compileTestJava' -} diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 00000000..33500bcc --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,632 @@ +import com.github.jk1.license.render.ReportRenderer +import com.github.jk1.license.render.InventoryHtmlReportRenderer +import com.github.jk1.license.filter.DependencyFilter +import com.github.jk1.license.filter.LicenseBundleNormalizer +import org.gradle.kotlin.dsl.* +import com.diffplug.gradle.spotless.SpotlessExtension +import info.solidsoft.gradle.pitest.PitestPluginExtension +import org.gradle.api.tasks.testing.Test +import org.gradle.api.tasks.Copy +import org.owasp.dependencycheck.gradle.extension.DependencyCheckExtension +import com.github.benmanes.gradle.versions.updates.DependencyUpdatesTask +import java.io.File +import org.gradle.api.GradleException +import org.gradle.api.tasks.compile.JavaCompile +import org.gradle.jvm.toolchain.JavaLanguageVersion +import org.gradle.jvm.toolchain.JvmVendorSpec +import org.gradle.jvm.toolchain.JvmImplementation +import io.spring.gradle.dependencymanagement.dsl.DependencyManagementExtension +import org.gradle.testing.jacoco.tasks.JacocoReport +import org.gradle.testing.jacoco.tasks.JacocoCoverageVerification +import org.gradle.api.tasks.wrapper.Wrapper +import java.io.FileOutputStream + +// Import specific license report task/extension if needed (adjust class name if necessary) +// import com.github.jk1.gradle.license.LicenseReportExtension +// import com.github.jk1.gradle.license.LicenseReportTask + +plugins { + java + id("org.springframework.boot") version "3.4.4" + id("io.spring.dependency-management") version "1.1.7" // manages implicit dependencies + id("io.openapiprocessor.openapi-processor") version "2023.2" // generates Controller-interface and resources from API-spec + id("com.github.jk1.dependency-license-report") version "2.9" // checks dependency-license compatibility + id("org.owasp.dependencycheck") version "12.1.1" // checks dependencies for known vulnerabilities + id("com.diffplug.spotless") version "7.0.2" // formats + checks formatting for source-code + jacoco // determines code-coverage of tests + id("info.solidsoft.pitest") version "1.15.0" // performs mutation testing + id("se.patrikerdes.use-latest-versions") version "0.2.18" // updates module and plugin versions + id("com.github.ben-manes.versions") version "0.52.0" // determines which dependencies have updates +} + +// HOWTO: find out which dependency versions are managed by Spring Boot: +// https://docs.spring.io/spring-boot/appendix/dependency-versions/coordinates.html + +group = "net.hostsharing" +version = "0.0.1-SNAPSHOT" + +tasks.named("wrapper") { + distributionType = Wrapper.DistributionType.BIN + gradleVersion = "8.5" +} + +// TODO.impl: self-attaching is deprecated, see: +// https://javadoc.io/doc/org.mockito/mockito-core/latest/org/mockito/Mockito.html#0.3 + +configurations { + compileOnly { + extendsFrom(configurations.annotationProcessor.get()) + } + + // Only JUnit 5 (Jupiter) should be used at compile time. + // TODO.test: For runtime, JUnit 4 is still needed by testcontainers < v2, which is not released yet. + // testCompileOnly { + // exclude(group = "junit", module = "junit") + // exclude(group = "org.junit.vintage", module = "junit-vintage-engine") + // } +} + +repositories { + mavenCentral() + maven { url = uri("https://repo.spring.io/milestone") } +} + +java { + toolchain { + languageVersion.set(JavaLanguageVersion.of(21)) + vendor.set(JvmVendorSpec.ADOPTIUM) + implementation.set(JvmImplementation.VENDOR_SPECIFIC) + } +} + +// Use extra properties delegate for type safety +val testcontainersVersion by extra("1.20.6") + +dependencies { + implementation("org.springframework.boot:spring-boot-starter-data-jpa") + implementation("org.springframework.boot:spring-boot-starter-data-rest") + implementation("org.springframework.boot:spring-boot-starter-jdbc") + implementation("org.springframework.boot:spring-boot-starter-web") + implementation("org.springframework.boot:spring-boot-starter-validation") + implementation("org.springframework.boot:spring-boot-starter-actuator") + implementation("org.springframework.boot:spring-boot-starter-security") + implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:2.8.6") + implementation("com.github.gavlyukovskiy:datasource-proxy-spring-boot-starter:1.11.0") + implementation("org.postgresql:postgresql") + implementation("org.liquibase:liquibase-core") + implementation("io.hypersistence:hypersistence-utils-hibernate-63:3.9.9") + implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310") + implementation("org.openapitools:jackson-databind-nullable:0.2.6") + implementation("org.apache.commons:commons-text:1.13.0") + implementation("net.java.dev.jna:jna:5.17.0") + implementation("org.modelmapper:modelmapper:3.2.2") + implementation("org.iban4j:iban4j:3.2.11-RELEASE") + implementation("org.reflections:reflections:0.10.2") + + compileOnly("org.projectlombok:lombok") + testCompileOnly("org.projectlombok:lombok") + + developmentOnly("org.springframework.boot:spring-boot-devtools") + + annotationProcessor("org.projectlombok:lombok") + testAnnotationProcessor("org.projectlombok:lombok") + + testImplementation("org.springframework.boot:spring-boot-starter-test") + testImplementation("org.testcontainers:testcontainers") + testImplementation("org.testcontainers:junit-jupiter") + testImplementation("org.junit.jupiter:junit-jupiter") + testImplementation("org.testcontainers:postgresql") + testImplementation("com.tngtech.archunit:archunit-junit5:1.4.0") + testImplementation("io.rest-assured:spring-mock-mvc") + testImplementation("org.hamcrest:hamcrest-core") + testImplementation("org.pitest:pitest-junit5-plugin:1.2.2") + testImplementation("org.junit.jupiter:junit-jupiter-api") + testImplementation("org.wiremock:wiremock-standalone:3.12.1") +} + +// Configure dependency management using the extension +configure { + imports { + mavenBom("org.testcontainers:testcontainers-bom:$testcontainersVersion") + } +} + +// Java Compiler Options +tasks.withType { + options.compilerArgs.add("-parameters") // keep parameter names => no need for @Param for SpringData +} + +// Configure tests +tasks.named("test") { + useJUnitPlatform { + excludeTags("importHostingAssets", "scenarioTest") + } + jvmArgs("-Duser.language=en", "-Duser.country=US") + // The 'excludes' property seems deprecated/less common in Kotlin DSL for Test tasks. + // Use filtering or other mechanisms if needed, or keep if it works. + // For filtering based on package/class name patterns: + filter { + excludeTestsMatching("net.hostsharing.hsadminng.**.generated.**") + // Add more exclude patterns if needed + } + finalizedBy(tasks.named("jacocoTestReport")) // generate report after tests +} + +// OpenAPI Source Code Generation +openapiProcessor { + process("springRoot") { + processorName("spring") + processor("io.openapiprocessor:openapi-processor-spring:2022.5") + apiPath(project.file("src/main/resources/api-definition/api-definition.yaml").path) + prop("mapping", project.file("src/main/resources/api-definition/api-mappings.yaml").path) + prop("showWarnings", true) + prop("openApiNullable", true) + targetDir(layout.buildDirectory.dir("generated/sources/openapi-javax").get().asFile.path) + } + + process("springRbac") { + processorName("spring") + processor("io.openapiprocessor:openapi-processor-spring:2022.5") + apiPath(project.file("src/main/resources/api-definition/rbac/rbac.yaml").path) + prop("mapping", project.file("src/main/resources/api-definition/rbac/api-mappings.yaml").path) + prop("showWarnings", true) + prop("openApiNullable", true) + targetDir(layout.buildDirectory.dir("generated/sources/openapi-javax").get().asFile.path) + } + + process("springTest") { + processorName("spring") + processor("io.openapiprocessor:openapi-processor-spring:2022.5") + apiPath(project.file("src/main/resources/api-definition/test/test.yaml").path) + prop("mapping", project.file("src/main/resources/api-definition/test/api-mappings.yaml").path) + prop("showWarnings", true) + prop("openApiNullable", true) + targetDir(layout.buildDirectory.dir("generated/sources/openapi-javax").get().asFile.path) + } + + process("springHsOffice") { + processorName("spring") + processor("io.openapiprocessor:openapi-processor-spring:2022.5") + apiPath(project.file("src/main/resources/api-definition/hs-office/hs-office.yaml").path) + prop("mapping", project.file("src/main/resources/api-definition/hs-office/api-mappings.yaml").path) + prop("showWarnings", true) + prop("openApiNullable", true) + targetDir(layout.buildDirectory.dir("generated/sources/openapi-javax").get().asFile.path) + } + + process("springHsBooking") { + processorName("spring") + processor("io.openapiprocessor:openapi-processor-spring:2022.5") + apiPath(project.file("src/main/resources/api-definition/hs-booking/hs-booking.yaml").path) + prop("mapping", project.file("src/main/resources/api-definition/hs-booking/api-mappings.yaml").path) + prop("showWarnings", true) + prop("openApiNullable", true) + targetDir(layout.buildDirectory.dir("generated/sources/openapi-javax").get().asFile.path) + } + + process("springHsHosting") { + processorName("spring") + processor("io.openapiprocessor:openapi-processor-spring:2022.5") + apiPath(project.file("src/main/resources/api-definition/hs-hosting//hs-hosting.yaml").path) + prop("mapping", project.file("src/main/resources/api-definition/hs-hosting/api-mappings.yaml").path) + prop("showWarnings", true) + prop("openApiNullable", true) + targetDir(layout.buildDirectory.dir("generated/sources/openapi-javax").get().asFile.path) + } +} + +// Add generated sources to the main source set +sourceSets.main.get().java.srcDir(layout.buildDirectory.dir("generated/sources/openapi")) + +// Define an abstract task class (if needed for type safety, otherwise can be skipped) +// abstract class ProcessSpring : DefaultTask() + +// Register an aggregate task for all OpenAPI generation tasks +val processSpring = tasks.register("processSpring") { + group = "openapi" + description = "Runs all OpenAPI generation tasks" + // Depend on individual processor tasks (names are derived from the configuration block names) + dependsOn( + "processSpringRoot", + "processSpringRbac", + "processSpringTest", + "processSpringHsOffice", + "processSpringHsBooking", + "processSpringHsHosting" + ) +} + +// Ensure resources and compilation depend on the aggregate task +tasks.processResources { + dependsOn(processSpring) +} +tasks.compileJava { + dependsOn(processSpring) +} + + +// Rename javax to jakarta in OpenApi generated java files +// TODO.impl: Upgrade to io.openapiprocessor.openapi-processor >= 2024.2 +// and use either `bean-validation: true` in api-mapping.yaml or `useSpringBoot3 true` +val openApiGenerate = tasks.register("openApiGenerate") { + from(layout.buildDirectory.dir("generated/sources/openapi-javax")) + into(layout.buildDirectory.dir("generated/sources/openapi")) + filter { line: String -> line.replace("javax", "jakarta") } + dependsOn(processSpring) // Ensure generation happens first +} + +// Ensure compileJava uses the renamed sources and depends on the renaming task +tasks.compileJava { + source(layout.buildDirectory.dir("generated/sources/openapi")) + dependsOn(openApiGenerate) +} + + +// Spotless Code Formatting +configure { + java { + // Configure formatting steps + removeUnusedImports() + leadingTabsToSpaces(4) + endWithNewline() + toggleOffOn() + + // Target files + target(fileTree(rootDir) { + include("**/*.java") + exclude("**/generated/**/*.java", "build/**", ".gradle/**") // Add build/.gradle excludes + }) + } +} +tasks.check { + dependsOn(tasks.named("spotlessCheck")) +} +// HACK: no idea why spotless uses the output of these tasks, but we get warnings without those +tasks.named("spotlessJava") { + dependsOn( + tasks.named("generateLicenseReport"), + // tasks.named("pitest"), // TODO.test: PiTest currently does not work, needs to be fixed + tasks.named("jacocoTestReport"), + tasks.named("processResources"), + tasks.named("processTestResources") + ) +} + + +// OWASP Dependency Security Test +configure { + nvd { + apiKey = project.findProperty("OWASP_API_KEY")?.toString() // set it in ~/.gradle/gradle.properties + delay = 16000 // Milliseconds + } + format = org.owasp.dependencycheck.reporting.ReportGenerator.Format.ALL.name + suppressionFiles.add("etc/owasp-dependency-check-suppression.xml") // Use suppressionFiles collection + failOnError = true + failBuildOnCVSS = 5.0f // Use float value +} +tasks.check { + dependsOn(tasks.named("dependencyCheckAnalyze")) +} +tasks.named("dependencyCheckAnalyze") { + doFirst { // Why not doLast? See README.md! + println("OWASP Dependency Security Report: file://${project.rootDir}/build/reports/dependency-check-report.html") + } +} + + +licenseReport { + renderers = arrayOf(InventoryHtmlReportRenderer("report.html", "Backend")) + filters = arrayOf(LicenseBundleNormalizer()) + excludeBoms = true + allowedLicensesFile = project.file("etc/allowed-licenses.json") +} +tasks.check { + // Ensure the task name 'checkLicense' is correct for the plugin + dependsOn(tasks.named("checkLicense")) +} + + +// JaCoCo Test Code Coverage for unit-tests +configure { + toolVersion = "0.8.10" +} + +tasks.named("jacocoTestReport") { + dependsOn(tasks.named("test")) // Depends on the main test task + reports { + xml.required.set(true) // Common requirement for CI/CD + csv.required.set(false) + html.outputLocation.set(layout.buildDirectory.dir("reports/jacoco/test/html")) + } + + classDirectories.setFrom(files(sourceSets.main.get().output.classesDirs.map { dir -> + fileTree(dir) { + exclude( + "net/hostsharing/hsadminng/**/generated/**/*.class", + "net/hostsharing/hsadminng/hs/HsadminNgApplication.class" + ) + } + })) + + doFirst { // Why not doLast? See README.md! + println("HTML Jacoco Test Code Coverage Report: file://${reports.html.outputLocation.get().asFile}/index.html") + } +} + +tasks.check { + dependsOn(tasks.named("jacocoTestCoverageVerification")) +} + +tasks.named("jacocoTestCoverageVerification") { + dependsOn(tasks.named("jacocoTestReport")) // Ensure report is generated first + violationRules { + rule { + limit { + minimum = "0.80".toBigDecimal() // TODO.test: improve instruction coverage + } + } + + // element: PACKAGE, BUNDLE, CLASS, SOURCEFILE or METHOD + // counter: INSTRUCTION, BRANCH, LINE, COMPLEXITY, METHOD, or CLASS + // value: TOTALCOUNT, COVEREDCOUNT, MISSEDCOUNT, COVEREDRATIO or MISSEDRATIO + + rule { + element = "CLASS" + excludes = listOf( + "net.hostsharing.hsadminng.**.generated.**", + "net.hostsharing.hsadminng.rbac.test.dom.TestDomainEntity", + "net.hostsharing.hsadminng.HsadminNgApplication", + "net.hostsharing.hsadminng.ping.PingController", + "net.hostsharing.hsadminng.rbac.generator.*", + "net.hostsharing.hsadminng.rbac.grant.RbacGrantsDiagramService", + "net.hostsharing.hsadminng.rbac.grant.RbacGrantsDiagramService\$Node", // Use $ for inner class + "net.hostsharing.hsadminng.**.*Repository", + "net.hostsharing.hsadminng.mapper.Mapper" + ) + + limit { + counter = "LINE" + value = "COVEREDRATIO" + minimum = "0.75".toBigDecimal() // TODO.test: improve line coverage + } + } + rule { + element = "METHOD" + excludes = listOf( + "net.hostsharing.hsadminng.**.generated.**", + "net.hostsharing.hsadminng.HsadminNgApplication.main", + "net.hostsharing.hsadminng.ping.PingController.*" + ) + + limit { + counter = "BRANCH" + value = "COVEREDRATIO" + minimum = "0.00".toBigDecimal() // TODO.test: improve branch coverage + } + } + } +} + + +// HOWTO: run all unit-tests which don't need a database: gw-test unitTest +tasks.register("unitTest") { + useJUnitPlatform { + excludeTags( + "importHostingAssets", "scenarioTest", "generalIntegrationTest", + "officeIntegrationTest", "bookingIntegrationTest", "hostingIntegrationTest" + ) + } + + group = "verification" + description = "runs all unit-tests which do not need a database" + + mustRunAfter(tasks.named("spotlessJava")) +} + +// HOWTO: run all integration tests which are not specific to a module, like base, rbac, config etc. +tasks.register("generalIntegrationTest") { + useJUnitPlatform { + includeTags("generalIntegrationTest") + } + + group = "verification" + description = "runs integration tests which are not specific to a module, like base, rbac, config etc." + + mustRunAfter(tasks.named("spotlessJava")) +} + +// HOWTO: run all integration tests of the office module: gw-test officeIntegrationTest +tasks.register("officeIntegrationTest") { + useJUnitPlatform { + includeTags("officeIntegrationTest") + } + + group = "verification" + description = "runs integration tests of the office module" + + mustRunAfter(tasks.named("spotlessJava")) +} + +// HOWTO: run all integration tests of the booking module: gw-test bookingIntegrationTest +tasks.register("bookingIntegrationTest") { + useJUnitPlatform { + includeTags("bookingIntegrationTest") + } + + group = "verification" + description = "runs integration tests of the booking module" + + mustRunAfter(tasks.named("spotlessJava")) +} + +// HOWTO: run all integration tests of the hosting module: gw-test hostingIntegrationTest +tasks.register("hostingIntegrationTest") { + useJUnitPlatform { + includeTags("hostingIntegrationTest") + } + + group = "verification" + description = "runs integration tests of the hosting module" + + mustRunAfter(tasks.named("spotlessJava")) +} + +tasks.register("importHostingAssets") { + useJUnitPlatform { + includeTags("importHostingAssets") + } + + group = "verification" + description = "run the import jobs as tests" + + mustRunAfter(tasks.named("spotlessJava")) +} + +tasks.register("scenarioTest") { + useJUnitPlatform { + includeTags("scenarioTest") + } + + group = "verification" + description = "run the import jobs as tests" // Description seems copied, adjust if needed + + mustRunAfter(tasks.named("spotlessJava")) +} + + +// pitest mutation testing +configure { + targetClasses.set(listOf("net.hostsharing.hsadminng.**")) // Use .set() for Property> + excludedClasses.set( + listOf( + "net.hostsharing.hsadminng.config.**", + // "net.hostsharing.hsadminng.**.*Controller", + "net.hostsharing.hsadminng.**.generated.**" + ) + ) + + targetTests.set(listOf("net.hostsharing.hsadminng.**.*UnitTest", "net.hostsharing.hsadminng.**.*RestTest")) + excludedTestClasses.set(listOf("**AcceptanceTest*", "**IntegrationTest*", "**ImportHostingAssets")) + + // Check if these are Property or direct assignment + // pitestVersion.set("1.17.0") // If Property + // junit5PluginVersion.set("1.1.0") // If Property + // Otherwise, direct assignment might work if the extension allows it, or check plugin docs. + pitestVersion = "1.17.0" // Assuming direct assignment works + junit5PluginVersion = "1.1.0" // Assuming direct assignment works + + threads.set(4) + + // As Java unit tests are pretty pointless in our case, this maybe makes not much sense. + mutationThreshold.set(71) + coverageThreshold.set(57) + testStrengthThreshold.set(87) + + outputFormats.set(listOf("XML", "HTML")) + timestampedReports.set(false) +} +// tasks.check { dependsOn(tasks.named("pitest")) } // TODO.test: PiTest currently does not work, needs to be fixed +tasks.named("pitest") { + doFirst { // Why not doLast? See README.md! + println("PiTest Mutation Report: file://${project.rootDir}/build/reports/pitest/index.html") + } +} + + +// Dependency Versions Upgrade +// Ensure the task name 'useLatestVersions' is correct +tasks.named("useLatestVersions") { + finalizedBy(tasks.check) +} + +// Define the stability check function at the top level or within an object +val isNonStable = { version: String -> + val stableKeyword = listOf("RELEASE", "FINAL", "GA").any { version.uppercase().contains(it) } + val regex = """^[0-9,.v-]+(-r)?$""".toRegex() // Use Kotlin Regex syntax + !stableKeyword && !version.matches(regex) +} + +tasks.named("dependencyUpdates") { + // rejectVersionIf expects a closure that returns true if the version should be rejected + rejectVersionIf { + isNonStable(candidate.version) + } +} + + +// Generate HTML from Markdown scenario-test-reports using Pandoc: +tasks.register("convertMarkdownToHtml") { + description = "Generates HTML from Markdown scenario-test-reports using Pandoc." + group = "Conversion" + + // Define the template file using project.file + val templateFile = project.file("doc/scenarios/.template.html") + // Define input directory using layout property + val inputDir = layout.buildDirectory.dir("doc/scenarios") + + // Use inputs and outputs for better up-to-date checks + inputs.file(templateFile).withPathSensitivity(PathSensitivity.NONE) + inputs.dir(inputDir).withPathSensitivity(PathSensitivity.RELATIVE) + outputs.dir(inputDir) // Output HTMLs will be in the same directory + + doFirst { + // Check if pandoc is installed using exec and capturing output/errors + val result = project.exec { + commandLine("pandoc", "--version") + isIgnoreExitValue = true // Don't fail the build immediately + standardOutput = FileOutputStream(File.createTempFile("pandoc_check", ".out")) // Redirect output + errorOutput = FileOutputStream(File.createTempFile("pandoc_check", ".err")) // Redirect error + } + if (result.exitValue != 0) { + throw GradleException("Pandoc is not installed or not found in the system path. Please install Pandoc.") + } + + // Check if the template file exists + if (!templateFile.exists()) { + throw GradleException("Template file '$templateFile' not found.") + } + // Ensure input directory exists (Gradle handles this implicitly usually, but explicit check is fine) + if (!inputDir.get().asFile.exists()) { + logger.warn("Input directory ${inputDir.get().asFile} does not exist, skipping Pandoc conversion.") + // Potentially disable the task or skip doLast if input dir missing + enabled = false // Example: disable task if input dir doesn't exist yet + } + } + + doLast { + // Check if input dir exists again, in case it was created between doFirst and doLast + if (!inputDir.get().asFile.exists()) { + logger.warn("Input directory ${inputDir.get().asFile} still does not exist, skipping Pandoc conversion.") + return@doLast // Skip execution + } + + // Gather all Markdown files in the input directory + project.fileTree(inputDir) { + include("*.md") + }.forEach { file -> + // Create the output file path in the same directory + val outputFile = File(file.parentFile, file.name.replace(".md", ".html")) + + // Execute pandoc for each markdown file + project.exec { + commandLine( + "pandoc", + file.absolutePath, + "--template", templateFile.absolutePath, + "-o", outputFile.absolutePath + ) + } + println("Converted ${file.name} to ${outputFile.name}") + } + } + // Ensure this task runs after scenario tests have potentially generated the markdown files + dependsOn(tasks.named("scenarioTest")) +} + + +// shortcut for compiling all files +tasks.register("compile") { + group = "build" + description = "Compiles main and test Java sources." + dependsOn("compileJava", "compileTestJava") +} diff --git a/etc/owasp-dependency-check-suppression.xml b/etc/owasp-dependency-check-suppression.xml index 52fe065c..a55b90e4 100644 --- a/etc/owasp-dependency-check-suppression.xml +++ b/etc/owasp-dependency-check-suppression.xml @@ -7,14 +7,4 @@ ^pkg:maven/org\.pitest/pitest\-command\-line@.*$ cpe:/a:line:line - - - ^pkg:maven/ch\.qos\.logback/logback-core@.*$ - cpe:/a:qos:logback - CVE-2024-12798 - - diff --git a/src/test/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemControllerRestTest.java b/src/test/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemControllerRestTest.java index 17558d42..9f9553ae 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemControllerRestTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/booking/item/HsBookingItemControllerRestTest.java @@ -14,7 +14,6 @@ import net.hostsharing.hsadminng.config.DisableSecurityConfig; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; -import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.boot.test.context.TestConfiguration; @@ -23,7 +22,6 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Import; import org.springframework.http.MediaType; import org.springframework.test.context.ActiveProfiles; -import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; @@ -47,7 +45,6 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. @WebMvcTest(HsBookingItemController.class) @Import({StrictMapper.class, JsonObjectMapperConfiguration.class, DisableSecurityConfig.class, MessageTranslator.class}) -@RunWith(SpringRunner.class) @ActiveProfiles("test") class HsBookingItemControllerRestTest { diff --git a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetControllerRestTest.java b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetControllerRestTest.java index 972b8807..b3016485 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetControllerRestTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/hosting/asset/HsHostingAssetControllerRestTest.java @@ -17,7 +17,6 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.EnumSource; -import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.boot.test.context.TestConfiguration; @@ -26,7 +25,6 @@ import org.springframework.context.annotation.Import; import org.springframework.http.MediaType; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.bean.override.mockito.MockitoBean; -import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; @@ -57,7 +55,6 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. @WebMvcTest(HsHostingAssetController.class) @Import({ StrictMapper.class, JsonObjectMapperConfiguration.class, DisableSecurityConfig.class, MessageTranslator.class }) -@RunWith(SpringRunner.class) @ActiveProfiles("test") public class HsHostingAssetControllerRestTest { diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionControllerRestTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionControllerRestTest.java index 2855f34c..318cf556 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionControllerRestTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/coopassets/HsOfficeCoopAssetsTransactionControllerRestTest.java @@ -16,14 +16,12 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.EnumSource; -import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.context.annotation.Import; import org.springframework.http.MediaType; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.bean.override.mockito.MockitoBean; -import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; @@ -58,7 +56,6 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. JsonObjectMapperConfiguration.class, DisableSecurityConfig.class }) @ActiveProfiles("test") -@RunWith(SpringRunner.class) class HsOfficeCoopAssetsTransactionControllerRestTest { // If you need to run just a single test-case in this data-driven test-method, set SINGLE_TEST_CASE_EXECUTION to true! diff --git a/src/test/java/net/hostsharing/hsadminng/ping/PingControllerRestTest.java b/src/test/java/net/hostsharing/hsadminng/ping/PingControllerRestTest.java index 5715befd..3d584838 100644 --- a/src/test/java/net/hostsharing/hsadminng/ping/PingControllerRestTest.java +++ b/src/test/java/net/hostsharing/hsadminng/ping/PingControllerRestTest.java @@ -8,14 +8,12 @@ import net.hostsharing.hsadminng.config.MessagesResourceConfig; import net.hostsharing.hsadminng.context.Context; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.EnumSource; -import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.context.annotation.Import; import org.springframework.http.MediaType; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.bean.override.mockito.MockitoBean; -import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; @@ -29,7 +27,6 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. MessageTranslator.class, JsonObjectMapperConfiguration.class, DisableSecurityConfig.class }) -@RunWith(SpringRunner.class) @ActiveProfiles("test") class PingControllerRestTest { diff --git a/src/test/java/net/hostsharing/hsadminng/rbac/role/RbacRoleControllerRestTest.java b/src/test/java/net/hostsharing/hsadminng/rbac/role/RbacRoleControllerRestTest.java index cd7b12b3..07db7f2d 100644 --- a/src/test/java/net/hostsharing/hsadminng/rbac/role/RbacRoleControllerRestTest.java +++ b/src/test/java/net/hostsharing/hsadminng/rbac/role/RbacRoleControllerRestTest.java @@ -7,14 +7,12 @@ import net.hostsharing.hsadminng.persistence.EntityManagerWrapper; import net.hostsharing.hsadminng.config.DisableSecurityConfig; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.test.context.bean.override.mockito.MockitoBean; import org.springframework.context.annotation.Import; import org.springframework.http.MediaType; import org.springframework.test.context.ActiveProfiles; -import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; @@ -34,7 +32,6 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. @WebMvcTest(RbacRoleController.class) @Import({StrictMapper.class, DisableSecurityConfig.class, MessageTranslator.class}) @ActiveProfiles("test") -@RunWith(SpringRunner.class) class RbacRoleControllerRestTest { @Autowired diff --git a/src/test/java/net/hostsharing/hsadminng/rbac/subject/RbacSubjectControllerRestTest.java b/src/test/java/net/hostsharing/hsadminng/rbac/subject/RbacSubjectControllerRestTest.java index 6d7f30fe..840ace5e 100644 --- a/src/test/java/net/hostsharing/hsadminng/rbac/subject/RbacSubjectControllerRestTest.java +++ b/src/test/java/net/hostsharing/hsadminng/rbac/subject/RbacSubjectControllerRestTest.java @@ -6,14 +6,12 @@ import net.hostsharing.hsadminng.mapper.StrictMapper; import net.hostsharing.hsadminng.persistence.EntityManagerWrapper; import net.hostsharing.hsadminng.config.DisableSecurityConfig; import org.junit.jupiter.api.Test; -import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.test.context.bean.override.mockito.MockitoBean; import org.springframework.context.annotation.Import; import org.springframework.http.MediaType; import org.springframework.test.context.ActiveProfiles; -import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; @@ -30,7 +28,6 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. @WebMvcTest(RbacSubjectController.class) @Import({StrictMapper.class, DisableSecurityConfig.class, MessageTranslator.class}) @ActiveProfiles("test") -@RunWith(SpringRunner.class) class RbacSubjectControllerRestTest { @Autowired