You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
chore(test): port keycloak-version unit test to testcontainers-python
Replace the 350-line hand-rolled podman orchestration with a
~70-line testcontainers-python test that pulls each supported
Keycloak major (18 through 26) as an upstream image, waits on a
log marker, and runs the plugin from the host against the exposed
port.
Upsides compared to the old run file:
- No more hardcoded EOL-date regexes. The old test baked
`EOL 2022-07-27 -30d [WARNING]` style assertions into the regex,
which broke every time wall-clock time moved the EOL-offset
around. The new assertions check "version string is reported"
and "state is one of OK/WARN/CRIT".
- Declarative image list, single `IMAGES = [...]` block. Adding a
new major means a single line.
- Uses the `Listening on:` log marker as the readiness probe
instead of `time.sleep(3) + time.sleep(30)`.
- Runs the plugin from the host, so the upstream Keycloak image is
enough - no need to maintain per-version Containerfiles that
install Python into the service image.
- 213s total for 9 versions end-to-end (was 20+ minutes when the
old run actually completed - the py39 tox run timed out on the
first container).
The helper behind this is `lib.lftest.run_container()`, shipped in
linuxfabrik-lib 2026041201.
CONTRIBUTING's "Container-based tests" section is rewritten
accordingly: reference example, guidance against state-shifting
assertions, `DOCKER_HOST` / `TESTCONTAINERS_RYUK_DISABLED` setup
for rootless podman.
If you want to implement unit tests based on containers, the following rules apply:
857
+
For checks that must run against a real service (Keycloak, Redis, a database, a web API), use the `lib.lftest.run_container()` helper from the `linuxfabrik-lib` package. It wraps [testcontainers-python](https://testcontainers-python.readthedocs.io/) so that container lifecycle, port exposure, environment variables and log-based readiness waits are declarative rather than hand-rolled podman orchestration.
858
858
859
-
* Each container file does everything necessary to set up a running environment for the check plugin (e.g. install Python if you want to run the plugin inside the container).
860
-
* The `./run` unit test simply calls podman and, for each containerfile found, builds the container, injects the libs and the check plugin, and runs the tests - but does not modify the container in any other way.
861
-
* See the `keycloak-version` plugin for how to do this.
862
-
*`tools/run-unit-tests` auto-detects container tests by looking for a `containerfiles/` subdirectory, so `--no-container` / `--only-container` filter correctly without any per-plugin annotation.
859
+
Minimal example (see `check-plugins/keycloak-version/unit-test/run` for the full reference):
860
+
861
+
```python
862
+
import subprocess
863
+
import sys
864
+
import unittest
865
+
866
+
sys.path.append('..')
867
+
868
+
import lib.lftest
869
+
from lib.globals importSTATE_OK, STATE_WARN, STATE_CRIT
***Pull upstream images whenever possible.** You do not need a custom `Containerfile` that injects Python into the service image, because the plugin runs from the host and connects to the container via the exposed port. That is the common case for API-driven checks.
910
+
***Wait on a log marker, not a sleep.** The `wait_log` argument takes a substring that the service writes to stdout/stderr when it is ready (e.g. `Listening on:` for Keycloak, `ready for connections.` for MariaDB). Use `wait_log_timeout` for services that take longer than 2 minutes to start.
911
+
***Do not hardcode state-shifting assertions.** If the plugin reports something that depends on today's date (EOL windows, "last seen N days ago", "expires in X days"), assert only that the plugin returned a valid state (any of `STATE_OK`, `STATE_WARN`, `STATE_CRIT`) and that the output contains the expected version / service identifier. Locking in a specific state will break the test every time the calendar moves past a boundary.
912
+
***Multi-version matrix** goes in an `IMAGES` list at the top of the test file, iterated via `self.subTest(image=...)`. Add a new major release at the bottom of the list when it becomes available upstream.
913
+
***Rootless podman**: testcontainers-python works, but the Ryuk cleanup container needs to be disabled. Set `TESTCONTAINERS_RYUK_DISABLED=true` and `DOCKER_HOST=unix:///run/user/$UID/podman/podman.sock` before running the tests. A lightweight wrapper can live in `tools/run-container-tests` to set these for you.
914
+
***Do not run container tests via `tox`.** They are integration tests and belong in `tools/run-container-tests`, not in the multi-Python matrix. `tools/run-unit-tests` detects them automatically by inspecting the `run` file for `podman` or `testcontainers` references.
915
+
***Keep hand-rolled podman orchestration out of new tests.** If you find a plugin that still builds containers via `subprocess.run(['podman', 'build', ...])`, migrate it to `lib.lftest.run_container()`; the old pattern is being retired.
0 commit comments