Public Access
Make the end-to-end suite self-contained with Testcontainers
It needed a hand-started stack and an opt-in flag, so it ran on one machine and never in CI. It now brings up PostgreSQL, Keycloak and an OpenSSH server itself, applies the committed migrations and starts the API as a child process, which makes it part of the ordinary test run at ~25s. The API runs as a process rather than through WebApplicationFactory. The client builds its own HttpClient for a URL the user typed, so there is no seam to hand a test handler through without inventing one that exists only for tests — and a test host would replace the entry point, Kestrel and the content root, so it would never prove that Program.cs composes or that the committed appsettings is found and layered in the documented order. Running out of the API's own output directory is what makes its configuration real. The suite still consumes what ships: the realm file from deploy/keycloak, the EF migrations, the API's own appsettings. Only Oidc:Authority is overridden, because the container's port is assigned at start. Falsified by reintroducing the wildcard-port redirect URI the realm once had — Keycloak rejects the authorization request and the suite fails at sign-in, which is what proves the committed file is the one imported. Skipping the migration step likewise fails, and the failure names the pending migration. A fresh Keycloak per run also sidesteps the --import-realm trap: editing the realm file and rerunning now always tests the edit. DodoDbContextFactory gains a Create(connectionString) so the fixture and dotnet ef place the migrations history table in exactly one place. If they disagreed the API would report every migration pending, which is how the readiness gate catches it.
This commit is contained in:
@@ -74,6 +74,10 @@ dotnet build DodoSSH.slnx
|
||||
dotnet test DodoSSH.slnx
|
||||
```
|
||||
|
||||
The tests need a Docker daemon. Everything that touches the database, the identity provider or an SSH
|
||||
server uses Testcontainers rather than a stub or a shared instance, so there is nothing to start first and
|
||||
nothing to clean up after — but with no daemon those suites fail rather than skip.
|
||||
|
||||
Run the API locally:
|
||||
|
||||
```bash
|
||||
@@ -85,32 +89,28 @@ Development — `/openapi/v1.json`.
|
||||
|
||||
### End-to-end verification
|
||||
|
||||
One suite runs against a real server rather than a stub, and it is opt-in because it needs the stack:
|
||||
One suite runs against a real server rather than a stub. It needs a Docker daemon and nothing else, so it
|
||||
is part of the ordinary test run:
|
||||
|
||||
```bash
|
||||
docker compose -f deploy/docker-compose.dev.yml up -d
|
||||
dotnet test tests/DodoSSH.SystemTests
|
||||
```
|
||||
|
||||
```bash
|
||||
dotnet ef database update --project src/DodoSSH.Infrastructure
|
||||
```
|
||||
It brings up PostgreSQL, Keycloak and an OpenSSH server in containers, applies the committed migrations,
|
||||
starts the API as a child process out of its own build output, and then drives the real client: sign in
|
||||
through Keycloak, enroll, unlock, create a host, sync it, read it back on a second simulated machine,
|
||||
unlock again with no network, and open a shell on the `sshd`. Roughly 25 seconds once the images are
|
||||
pulled.
|
||||
|
||||
```bash
|
||||
dotnet run --project src/DodoSSH.Api
|
||||
```
|
||||
What makes it worth its weight is that it consumes the artefacts that ship — the realm file from
|
||||
`deploy/keycloak`, the EF migrations, the API's own `appsettings` — rather than a fixture written to match
|
||||
them. On its first run it found a loopback redirect URI the realm registered in a form Keycloak rejects,
|
||||
and a JSON configuration gap that made the whole sync surface unreachable from the real client while every
|
||||
other test passed. Both are the same class of bug: two sides of a stub agreeing with each other about
|
||||
something the specification never said.
|
||||
|
||||
```bash
|
||||
DODOSSH_E2E=1 dotnet test tests/DodoSSH.SystemTests
|
||||
```
|
||||
|
||||
It signs in through a real Keycloak, enrolls, unlocks, creates a host, syncs it, reads it back on a second
|
||||
simulated machine, unlocks again with no network, and opens a shell on a real `sshd`. Skipped otherwise,
|
||||
with a message naming the commands above.
|
||||
|
||||
It is worth its weight: on its first run it found a loopback redirect URI the realm registered in a form
|
||||
Keycloak rejects, and a JSON configuration gap that made the whole sync surface unreachable from the real
|
||||
client while every other test passed. Both are the same class of bug — two sides of a stub agreeing with
|
||||
each other about something the specification never said.
|
||||
The one value it cannot take from a committed file is `Oidc:Authority`, since the container's port is
|
||||
assigned at start. Everything that authority points at is still the real realm.
|
||||
|
||||
Development and testing are currently **Windows-only**. Anything known or suspected to differ on
|
||||
Linux and macOS is tracked in [`docs/platform-flags.md`](docs/platform-flags.md), along with the
|
||||
|
||||
Reference in New Issue
Block a user