Native image en testen
GraalVM native image is het productie-artefact
Het Docker-image bevat een GraalVM native image (./gradlew nativeCompile, Dockerfile.native),
en dat legt beperkingen op aan hoe je code schrijft:
- Gebruik nooit
@ConditionalOnPropertyvoor een feature die op runtime togglebaar moet zijn. Bean-conditions worden geëvalueerd en bevroren op het build-moment van het native image; een conditionele bean wordt uit het productie-image gebakken en geen property krijgt hem terug. Check de vlag in plaats daarvan binnen de bean — de Typesense-handlers zijn het referentievoorbeeld (hoofdstuk 6). - Reflectie- en resource-hints worden met de hand bijgehouden, per module onder
src/main/resources/META-INF/native-image/. Niets genereert ze; er eentje vergeten merk je pas in het native image, niet in JVM-runs. - Een properties-bestand dat via
spring.config.importgeïmporteerd wordt heeft een entry nodig in deresource-config.jsonvan zijn module (ziesearch/src/main/resources/META-INF/native-image/). - AWT werkt in het native image, maar alleen met handmatig aangeleverde JNI-hints. PDFBox
raakt AWT zelfs voor een simpele paginatelling (de statische init van
PDDocumentwarmt het kleursubsysteem op), en de AWT-native-libraries van de JDK zoeken klassen, velden en methodes op via JNI op runtime — elk daarvan moet indisk/.../native-image/jni-config.jsonstaan, anders faalt de lookup en sneuvelt de class-initializer voor de rest van het procesleven. De charset-vlag-H:+AddAllCharsets(serverbuild.gradle) hoort bij hetzelfde verhaal: PDFBox'BaseParserwil Windows-1252, dat het image standaard weglaat. De gegenereerdelibawt*.so- bestanden komen naast de binary terecht inbuild/native/nativeCompileen reizen mee (Dockerfile.nativekopieert de hele map).
Databaseschema-discipline
Prod draait hibernate.ddl-auto=validate: een entity-wijziging zonder bijpassende
Flyway-migratie laat de startup falen. Migraties staan in
database/src/main/resources/db/migration/V<n>__<name>.sql (laatste: V23), zijn
forward-only en worden ook als apart image uitgeleverd (Dockerfile.migrations). Bewerk nooit
een migratie die ergens al toegepast is.
Testen
-
Unit-tests zijn JUnit 5 + Mockito; Mockito draait als
-javaagent, aangesloten in de root-build.gradle. -
jimfs(in-memory filesystem) draagt de disk-/bestandspad-tests. -
ffmpeg moet op
PATHstaan — de transcoder-tests shellen ernaar uit, en CI installeert het vóór de build. -
Integratietests zijn geen aparte source set of task. Ze draaien onder de normale
test-task, heten*IntegrationTesten gebruiken Testcontainers PostgreSQL (en RabbitMQ voor de dead-letter-flow). Ze zijn geannoteerd met@Testcontainers(disabledWithoutDocker = true), dus ze worden stilletjes overgeslagen als er geen container-runtime bereikbaar is — een groene./gradlew testbewijst niet dat ze gedraaid hebben. Lokaal:systemctl --user start podman.socketDOCKER_HOST=unix:///run/user/$UID/podman/podman.sock ./gradlew test
Handige commando's:
./gradlew build # alle modules bouwen
./gradlew :core:test # één module
./gradlew :worker:test --tests "app.ister.worker.…" # één klasse
CI en kwaliteitsbewaking
CI draait ./gradlew build check jacocoTestReport sonar. Er is geen aparte linter of formatter —
de kwaliteitsbewaking is SonarCloud (org.sonarqube) plus Jacoco-coverage, allebei onderdeel van
check.
Commits en releases
Commit-subjects moeten Conventional Commits zijn
(feat(scope): …, fix: …, !/BREAKING CHANGE: voor breaking); een commit-lint-job laat de
PR anders falen, en .github/workflows/release.yml leidt er zowel de versiebump als de release
notes uit af — zie CONTRIBUTING.md.
Releases worden nachtelijks en automatisch gemaakt. Bump nooit met de hand version in
build.gradle: main draagt altijd een -SNAPSHOT, en alleen de getagde release-commit draagt
een schone versie. docker-publish.yml tagt elke push naar main met de gradle-projectversie naast
main; de server- en migrations-images krijgen dezelfde tag, zodat downstream-pins in de pas
blijven lopen.