diff --git a/.aliases b/.aliases index b7aa5c04..925de39e 100644 --- a/.aliases +++ b/.aliases @@ -90,9 +90,38 @@ alias pg-sql-restore='gunzip --stdout | docker exec -i hsadmin-ng-postgres psql alias fp='grep -r '@Accepts' src | sed -e 's/^.*@/@/g' | sort -u | wc -l' alias gw-spotless='./gradlew spotlessApply -x pitest -x test -x :processResources' -alias gw-test='. .aliases; . .tc-environment; ./gradlew test' alias gw-check='. .aliases; . .tc-environment; gw test check -x pitest' +# HOWTO: run all 'normal' tests (no scenario+import-tests): `gw-test` +# You can also mention specific targets: `gw-test importOfficeData`. +# This will always use the environment from `.tc-environment`. +# +# HOWTO: re-run tests even if no changed can be detected: `gw-test --rerun` +# You can also mention specific targets: `gw-test scenarioTest --rerun`. +# This will always use the environment from `.tc-environment`. +# +# HOWTO: run all tests (unit, integration+acceptance, import and scenario): `gw-test --all` +# You can also re-run all these tests, which will take ~20min: `gw-test --all --rerun` +# This will always use the environment from `.tc-environment`. +# +function _gwTest() { + . .aliases; + . .tc-environment; + if [ "$1" == "--all" ]; then + shift # to remove the --all from $@ + # delierately in separate gradlew-calls to avoid Testcontains-PostgreSQL problem spillover + ./gradlew unitTest "$@" && + ./gradlew officeIntegrationTest bookingIntegrationTest hostingIntegrationTest "$@" && + ./gradlew scenarioTest "$@" && + ./gradlew importOfficeData importHostingAssets "$@"; + elif [ $# -eq 0 ] || [[ $1 == -* ]]; then + ./gradlew test "$@"; + else + ./gradlew "$@"; + fi +} +alias gw-test=_gwTest + alias howto=bin/howto alias cas-curl=bin/cas-curl @@ -107,6 +136,6 @@ if [ ! -f .environment ]; then fi source .environment -alias scenario-reports-upload='./gradlew scenarioTests convertMarkdownToHtml && ssh hsh03-hsngdev@h50.hostsharing.net "rm -f doms/hsngdev.hs-example.de/htdocs-ssl/scenarios/office/*.html" && scp build/doc/scenarios/*.html hsh03-hsngdev@h50.hostsharing.net:doms/hsngdev.hs-example.de/htdocs-ssl/scenarios/office' +alias scenario-reports-upload='./gradlew scenarioTest convertMarkdownToHtml && ssh hsh03-hsngdev@h50.hostsharing.net "rm -f doms/hsngdev.hs-example.de/htdocs-ssl/scenarios/office/*.html" && scp build/doc/scenarios/*.html hsh03-hsngdev@h50.hostsharing.net:doms/hsngdev.hs-example.de/htdocs-ssl/scenarios/office' alias scenario-reports-open='open https://hsngdev.hs-example.de/scenarios/office' diff --git a/Jenkinsfile b/Jenkinsfile index 9b569801..5e41139b 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -35,9 +35,24 @@ pipeline { stage ('Tests') { parallel { - stage('Unit-/Integration/Acceptance-Tests') { + stage('Unit-Tests') { steps { - sh './gradlew check --no-daemon -x pitest -x dependencyCheckAnalyze -x importOfficeData -x importHostingAssets' + sh './gradlew unitTest --no-daemon' + } + } + stage('General-Tests') { + steps { + sh './gradlew generalTest --no-daemon' + } + } + stage('Office-Tests') { + steps { + sh './gradlew officeIntegrationTest --no-daemon' + } + } + stage('Booking+Hosting-Tests') { + steps { + sh './gradlew bookingIntegrationTest hostingIntegrationTest --no-daemon' } } stage('Import-Tests') { @@ -47,7 +62,7 @@ pipeline { } stage ('Scenario-Tests') { steps { - sh './gradlew scenarioTests --no-daemon' + sh './gradlew scenarioTest --no-daemon' } } } diff --git a/README.md b/README.md index ef1fee8a..55b7a159 100644 --- a/README.md +++ b/README.md @@ -51,10 +51,11 @@ Everything is tested on _Ubuntu Linux 22.04_ and _MacOS Monterey (12.4)_. To be able to build and run the Java Spring Boot application, you need the following tools: - Docker 20.x (on MacOS you also need *Docker Desktop* or similar) or Podman -- optionally: PostgreSQL Server 15.5-bookworm +- optionally: PostgreSQL Server 15.5-bookworm, if you want to use the database directly, not just via Docker (see instructions below to install and run in Docker) - The matching Java JDK at will be automatically installed by Gradle toolchain support to `~/.gradle/jdks/`. - You also might need an IDE (e.g. *IntelliJ IDEA* or *Eclipse* or *VS Code* with *[STS](https://spring.io/tools)* and a GUI Frontend for *PostgreSQL* like *Postbird*. +- Python 3 is expected in /usr/bin/python3 if you want to run the `howto` tool (see `bin/howto`) If you have at least Docker and the Java JDK installed in appropriate versions and in your `PATH`, then you can start like this: @@ -64,7 +65,12 @@ If you have at least Docker and the Java JDK installed in appropriate versions a gw # initially downloads the configured Gradle version into the project gw test # compiles and runs unit- and integration-tests - takes >10min even on a fast machine - gw scenarioTests # compiles and scenario-tests - takes ~1min on a decent machine + # `gw test` does NOT run import- and scenario-tests. + # Use `gw-test` instead to make sure .tc-environment is sourced. + gw scenarioTest # compiles and scenario-tests - takes ~1min on a decent machine + # Use `gw-test scenarioTest` instead to make sure .tc-environment is sourced. + + howto test # shows more test information about how to run tests # if the container has not been built yet, run this: pg-sql-run # downloads + runs PostgreSQL in a Docker container on localhost:5432 @@ -437,36 +443,42 @@ Some of these rules are checked with *ArchUnit* unit tests. ### Run Tests from Command Line -Run all tests which have not yet been passed with the current source code: +Run all unit-, integration- and acceptance-tests which have not yet been passed with the current source code: ```shell -gw test +gw test # uses the current environment, especially HSADMINNG_POSTGRES_JDBC_URL +``` + +If the referenced database is not empty, the tests might fail. + +To explicitly use the Testcontainers-environment, run: + +```shell +gw-test # uses the environment from .tc-environment ``` Force running all tests: ```shell -gw cleanTest test +gw-test --rerun ``` +To find more options about running tests, try `howto test`. + ### Spotless Code Formatting Code formatting for Java is checked via *spotless*. -The formatting style can be checked with this command: - -```shell -gw spotlessCheck -``` - -This task is also included in `gw build` and `gw check`. - To apply formatting rules, use: ```shell -gw spotlessApply +gw-spotless ``` +The gradle task spotlessCheck is also included in `gw build` and `gw check`, +thus if the formatting is not compliant to the rules, the build is going to fail. + + ### JaCoCo Test Code Coverage Check This project uses the JaCoCo test code coverage report with limit checks. @@ -508,9 +520,8 @@ This task is also executed as part of `gw check`. #### Remark -In this project, there is little business logic in *Java* code; -most business code is in *plsql* -and *Java* ist mostly used for mapping REST calls to database queries. +In this project, there is a large amount of code is in *plsql*, especially for RBAC. +*Java* ist mostly used for mapping and validating REST calls to database queries. This mapping ist mostly done through *Spring* annotations and other implicit code. Therefore, there are only few unit tests and thus mutation testing has limited value. @@ -603,7 +614,8 @@ and would not need the `rbac.role` table anymore. We would also reduce the depth of the expensive recursive CTE-query. This has to be explored further. -For now, we just keep it in mind and +For now, we just keep it in mind and FIXME + ### The Mapper is Error-Prone @@ -637,7 +649,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=external-db,only -office,without-test-data' ``` These profiles mean: diff --git a/build.gradle b/build.gradle index ad67a152..9f0db4bd 100644 --- a/build.gradle +++ b/build.gradle @@ -255,16 +255,14 @@ licenseReport { } project.tasks.check.dependsOn(checkLicense) - -// HOWTO: run all unit tests: gw test +// 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 'importOfficeData', 'importHostingAssets', 'scenarioTest', 'generalIntegrationTest', - 'officeIntegrationTest', 'bookingIntegrationTest', 'hostingIntegrationTest' + excludeTags 'importOfficeData', 'importHostingAssets', 'scenarioTest' } } @@ -336,6 +334,19 @@ jacocoTestCoverageVerification { } } +// HOWTO: run all unit-tests which don't need a database: gw unitTest +tasks.register('unitTest', Test) { + useJUnitPlatform { + excludeTags 'importOfficeData', '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 { @@ -384,18 +395,6 @@ tasks.register('hostingIntegrationTest', Test) { mustRunAfter spotlessJava } -// HOWTO: run all tests except import- and scenario-tests: gw standardTest -tasks.register('standardTest', Test) { - useJUnitPlatform { - excludeTags 'importOfficeData', 'importHostingAssets', 'scenarioTest' - } - - group 'verification' - description 'runs all tests except import- and scenario-tests' - - mustRunAfter spotlessJava -} - tasks.register('importOfficeData', Test) { useJUnitPlatform { includeTags 'importOfficeData' @@ -418,7 +417,7 @@ tasks.register('importHostingAssets', Test) { mustRunAfter spotlessJava } -tasks.register('scenarioTests', Test) { +tasks.register('scenarioTest', Test) { useJUnitPlatform { includeTags 'scenarioTest' } @@ -518,7 +517,7 @@ tasks.register('convertMarkdownToHtml') { } } } -convertMarkdownToHtml.dependsOn scenarioTests +convertMarkdownToHtml.dependsOn scenarioTest // shortcut for compiling all files tasks.register('compile') { diff --git a/src/test/java/net/hostsharing/hsadminng/rbac/context/ContextIntegrationTests.java b/src/test/java/net/hostsharing/hsadminng/rbac/context/ContextIntegrationTests.java index 7f59d4e3..8b5d7693 100644 --- a/src/test/java/net/hostsharing/hsadminng/rbac/context/ContextIntegrationTests.java +++ b/src/test/java/net/hostsharing/hsadminng/rbac/context/ContextIntegrationTests.java @@ -5,6 +5,7 @@ import net.hostsharing.hsadminng.mapper.Array; import net.hostsharing.hsadminng.mapper.StrictMapper; import net.hostsharing.hsadminng.persistence.EntityManagerWrapper; import net.hostsharing.hsadminng.rbac.test.JpaAttempt; +import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; @@ -19,6 +20,7 @@ import jakarta.servlet.http.HttpServletRequest; import static org.assertj.core.api.Assertions.assertThat; +@Tag("generalIntegrationTest") @DataJpaTest @ComponentScan(basePackageClasses = { Context.class, JpaAttempt.class, EntityManagerWrapper.class, StrictMapper.class }) @DirtiesContext