finalize test suites and improve documentation

This commit is contained in:
Michael Hoennig 2025-01-21 14:06:50 +01:00
parent 645fde6f87
commit 0e29958bf9
5 changed files with 99 additions and 42 deletions

View File

@ -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 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-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' 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 howto=bin/howto
alias cas-curl=bin/cas-curl alias cas-curl=bin/cas-curl
@ -107,6 +136,6 @@ if [ ! -f .environment ]; then
fi fi
source .environment 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' alias scenario-reports-open='open https://hsngdev.hs-example.de/scenarios/office'

21
Jenkinsfile vendored
View File

@ -35,9 +35,24 @@ pipeline {
stage ('Tests') { stage ('Tests') {
parallel { parallel {
stage('Unit-/Integration/Acceptance-Tests') { stage('Unit-Tests') {
steps { 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') { stage('Import-Tests') {
@ -47,7 +62,7 @@ pipeline {
} }
stage ('Scenario-Tests') { stage ('Scenario-Tests') {
steps { steps {
sh './gradlew scenarioTests --no-daemon' sh './gradlew scenarioTest --no-daemon'
} }
} }
} }

View File

@ -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: 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 - 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) (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/`. - 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*. - 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: 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 # 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 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: # if the container has not been built yet, run this:
pg-sql-run # downloads + runs PostgreSQL in a Docker container on localhost:5432 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 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 ```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: Force running all tests:
```shell ```shell
gw cleanTest test gw-test --rerun
``` ```
To find more options about running tests, try `howto test`.
### Spotless Code Formatting ### Spotless Code Formatting
Code formatting for Java is checked via *spotless*. 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: To apply formatting rules, use:
```shell ```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 ### JaCoCo Test Code Coverage Check
This project uses the JaCoCo test code coverage report with limit checks. 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 #### Remark
In this project, there is little business logic in *Java* code; In this project, there is a large amount of code is in *plsql*, especially for RBAC.
most business code is in *plsql* *Java* ist mostly used for mapping and validating REST calls to database queries.
and *Java* ist mostly used for mapping REST calls to database queries.
This mapping ist mostly done through *Spring* annotations and other implicit code. 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. 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. We would also reduce the depth of the expensive recursive CTE-query.
This has to be explored further. 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 ### The Mapper is Error-Prone
@ -637,7 +649,7 @@ howto
Add `--args='--spring.profiles.active=...` with the wanted profile selector: Add `--args='--spring.profiles.active=...` with the wanted profile selector:
```sh ```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: These profiles mean:

View File

@ -255,16 +255,14 @@ licenseReport {
} }
project.tasks.check.dependsOn(checkLicense) project.tasks.check.dependsOn(checkLicense)
// HOWTO: run all tests except import- and scenario-tests: gw test
// HOWTO: run all unit tests: gw test
test { test {
finalizedBy jacocoTestReport // generate report after tests finalizedBy jacocoTestReport // generate report after tests
excludes = [ excludes = [
'net.hostsharing.hsadminng.**.generated.**', 'net.hostsharing.hsadminng.**.generated.**',
] ]
useJUnitPlatform { useJUnitPlatform {
excludeTags 'importOfficeData', 'importHostingAssets', 'scenarioTest', 'generalIntegrationTest', excludeTags 'importOfficeData', 'importHostingAssets', 'scenarioTest'
'officeIntegrationTest', 'bookingIntegrationTest', 'hostingIntegrationTest'
} }
} }
@ -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. // HOWTO: run all integration tests which are not specific to a module, like base, rbac, config etc.
tasks.register('generalIntegrationTest', Test) { tasks.register('generalIntegrationTest', Test) {
useJUnitPlatform { useJUnitPlatform {
@ -384,18 +395,6 @@ tasks.register('hostingIntegrationTest', Test) {
mustRunAfter spotlessJava 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) { tasks.register('importOfficeData', Test) {
useJUnitPlatform { useJUnitPlatform {
includeTags 'importOfficeData' includeTags 'importOfficeData'
@ -418,7 +417,7 @@ tasks.register('importHostingAssets', Test) {
mustRunAfter spotlessJava mustRunAfter spotlessJava
} }
tasks.register('scenarioTests', Test) { tasks.register('scenarioTest', Test) {
useJUnitPlatform { useJUnitPlatform {
includeTags 'scenarioTest' includeTags 'scenarioTest'
} }
@ -518,7 +517,7 @@ tasks.register('convertMarkdownToHtml') {
} }
} }
} }
convertMarkdownToHtml.dependsOn scenarioTests convertMarkdownToHtml.dependsOn scenarioTest
// shortcut for compiling all files // shortcut for compiling all files
tasks.register('compile') { tasks.register('compile') {

View File

@ -5,6 +5,7 @@ import net.hostsharing.hsadminng.mapper.Array;
import net.hostsharing.hsadminng.mapper.StrictMapper; import net.hostsharing.hsadminng.mapper.StrictMapper;
import net.hostsharing.hsadminng.persistence.EntityManagerWrapper; import net.hostsharing.hsadminng.persistence.EntityManagerWrapper;
import net.hostsharing.hsadminng.rbac.test.JpaAttempt; import net.hostsharing.hsadminng.rbac.test.JpaAttempt;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; 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; import static org.assertj.core.api.Assertions.assertThat;
@Tag("generalIntegrationTest")
@DataJpaTest @DataJpaTest
@ComponentScan(basePackageClasses = { Context.class, JpaAttempt.class, EntityManagerWrapper.class, StrictMapper.class }) @ComponentScan(basePackageClasses = { Context.class, JpaAttempt.class, EntityManagerWrapper.class, StrictMapper.class })
@DirtiesContext @DirtiesContext