# integration End-to-end test suite for the Galaxy platform. The suite drives `gateway` from outside and verifies behaviour at the public boundary while `backend` and `galaxy/game` run as Docker containers managed by the test process via `testcontainers-go`. For cross-cutting testing principles (unit vs integration boundaries, why testcontainers tests pin no-op observability providers, why infrastructure failures in this suite fail loudly instead of skipping) see [`docs/TESTING.md`](../docs/TESTING.md). This README focuses on the integration-specific runbook: prerequisites, entry points, labels, and per-test fixtures. ## Prerequisites - A reachable Docker daemon (`DOCKER_HOST` or the local socket). - Go toolchain matching the workspace `go.work` directive. - Network access for the first run (`postgres:16-alpine`, `axllent/mailpit`, `redis:7-alpine` images are pulled). Subsequent runs reuse the local image cache. ## Run The recommended entry points are the Makefile targets: ```bash make -C integration preclean # idempotent leftover cleanup make -C integration integration # preclean + serial test run make -C integration integration-step # preclean + one-test-at-a-time ``` `preclean` removes stale containers and locally-built images from earlier runs; it never touches testcontainers-pulled service images (`postgres:16-alpine`, `axllent/mailpit`, `redis:7-alpine`, `testcontainers/ryuk`), so the cache stays warm. The cleanup keys off labels: - `org.testcontainers=true` — every container/network created by `testcontainers-go` (our backend/gateway/game and the postgres / redis / mailpit / ryuk service containers). - `galaxy.backend=1` — engine instances spawned by backend's runtime adapter directly on the host Docker daemon (see `backend/internal/dockerclient/types.go`). - `galaxy.test.kind=integration-image` — local builds of `galaxy/{backend,gateway,game}:integration` produced by `testenv/images.go`. `integration` runs every test in the module sequentially (`-p=1 -parallel=1`) — recommended default on a slow / shared Docker. `integration-step` runs them one at a time with a fresh preclean before each test and stops on the first failure; useful to isolate a flake or build up to a full pass without losing context to subsequent tests. Direct `go test ./integration/...` still works but does not pre-clean or serialise the suite; use it only on a hand-cleaned Docker. The suite builds three Docker images on demand from the workspace sources: - `galaxy/backend:integration` (`backend/Dockerfile`), - `galaxy/gateway:integration` (`gateway/Dockerfile`), - `galaxy/game:integration` (`game/Dockerfile`). Each image is built once per `go test` invocation, guarded by a `sync.Once` inside `testenv`, and stamped with the `galaxy.test.kind=integration-image` label so `preclean` can wipe it on the next run. The first cold run is slow (~2–3 min on a developer machine); subsequent runs reuse the layer cache. ## Skipping Tests skip with a clear message when the Docker daemon is unreachable. Subsuites that require a live engine container (`lobby_flow_test.go`) also skip when the `galaxy/game` image cannot be built. ## Layout - `testenv/` — fixtures: Postgres, Redis, mailpit, GeoLite2 mmdb, image builders, backend/gateway runners, signed gRPC client (built on top of the public `galaxy/gateway/authn` package, no duplicated canonical-bytes code), mailpit HTTP client, `EnrollPilots` helper for runtime-driven scenarios that need ≥10 members, platform bootstrap. - `*_test.go` — one file per cross-service scenario. The runtime-driven tests (`runtime_lifecycle_test.go`, `engine_command_proxy_test.go`) honour the engine's production contract `len(races) >= 10`: each registers ten extra pilots with synthetic `Player01..Player10` race names and matching emails, has the owner invite each one, and has each pilot redeem the invite before admin force-start. Cold runs add ~30 s for the ten extra mailpit round-trips on top of the engine image build. ## Determinism - Each test calls `Bootstrap(t)` to spin up a dedicated Postgres, Redis, mailpit, backend and gateway. Cross-test contamination is not possible. - Tests do not call `t.Parallel()`. Docker resource pressure makes parallel suites flaky on commodity hardware. - Gateway anti-abuse and body-size limits are loosened for the bulk of scenarios (so legitimate flows are not rate-limited mid-test) and intentionally tightened in `gateway_edge_test.go` so each protective mechanism can be observed firing.