feat: backend service
This commit is contained in:
+60
-184
@@ -1,191 +1,67 @@
|
||||
# Integration Tests
|
||||
# integration
|
||||
|
||||
`integration` owns only true inter-service black-box tests.
|
||||
Each suite must raise real service processes, speak only over public HTTP/gRPC/Redis contracts, and avoid imports from `internal/...` packages of tested services.
|
||||
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`.
|
||||
|
||||
## 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
|
||||
|
||||
```bash
|
||||
go test ./integration/...
|
||||
```
|
||||
|
||||
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`. 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
|
||||
|
||||
```text
|
||||
integration/
|
||||
├── README.md
|
||||
├── authsessionmail/
|
||||
│ ├── authsession_mail_test.go
|
||||
│ └── harness_test.go
|
||||
├── gatewayauthsessionmail/
|
||||
│ ├── gateway_authsession_mail_test.go
|
||||
│ └── harness_test.go
|
||||
├── gatewayauthsessionusermail/
|
||||
│ └── gateway_authsession_user_mail_test.go
|
||||
├── authsessionuser/
|
||||
│ ├── authsession_user_test.go
|
||||
│ └── harness_test.go
|
||||
├── gatewayauthsession/
|
||||
│ ├── harness_test.go
|
||||
│ └── gateway_authsession_test.go
|
||||
├── gatewayauthsessionuser/
|
||||
│ ├── gateway_authsession_user_test.go
|
||||
│ └── harness_test.go
|
||||
├── gatewayuser/
|
||||
│ ├── gateway_user_test.go
|
||||
│ └── harness_test.go
|
||||
├── notificationgateway/
|
||||
│ └── notification_gateway_test.go
|
||||
├── notificationmail/
|
||||
│ └── notification_mail_test.go
|
||||
├── notificationuser/
|
||||
│ └── notification_user_test.go
|
||||
├── lobbyuser/
|
||||
│ └── lobby_user_test.go
|
||||
├── lobbynotification/
|
||||
│ ├── lobby_notification_test.go
|
||||
│ └── race_name_intents_test.go
|
||||
├── lobbyrtm/
|
||||
│ ├── harness_test.go
|
||||
│ └── lobby_rtm_test.go
|
||||
├── go.mod
|
||||
├── go.sum
|
||||
└── internal/
|
||||
├── contracts/
|
||||
│ ├── gatewayv1/
|
||||
│ │ └── contract.go
|
||||
│ └── userv1/
|
||||
│ └── contract.go
|
||||
└── harness/
|
||||
├── binary.go
|
||||
├── dockernetwork.go
|
||||
├── engineimage.go
|
||||
├── keys.go
|
||||
├── mail_stub.go
|
||||
├── process.go
|
||||
├── redis_container.go
|
||||
├── rtmanagerservice.go
|
||||
├── smtp_capture.go
|
||||
└── user_stub.go
|
||||
```
|
||||
- `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.
|
||||
|
||||
## Rules
|
||||
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.
|
||||
|
||||
- Keep suites black-box. Do not import `galaxy/gateway/internal/...`, `galaxy/authsession/internal/...`, or any other service-owned internal package.
|
||||
- Start real binaries from `cmd/...` and talk to them only through their published HTTP, gRPC, and Redis contracts.
|
||||
- Put boundary-specific orchestration and assertions into the owning suite package, not into shared helpers.
|
||||
- Put only generic process/runtime utilities into `internal/harness`.
|
||||
- Put only public-contract helpers into `internal/contracts/...`.
|
||||
## Determinism
|
||||
|
||||
## Current Boundary Suites
|
||||
|
||||
- `gatewayauthsession` verifies the integration boundary between real `Edge Gateway` and real `Auth / Session Service`.
|
||||
- `authsessionuser` verifies the integration boundary between real `Auth / Session Service` and real `User Service`.
|
||||
- `authsessionmail` verifies the integration boundary between real `Auth / Session Service` and real `Mail Service`.
|
||||
- `gatewayauthsessionmail` verifies the public auth flow across real `Edge Gateway`, real `Auth / Session Service`, and real `Mail Service`.
|
||||
- `gatewayuser` verifies the direct authenticated self-service boundary between real `Edge Gateway` and real `User Service`.
|
||||
- `gatewayauthsessionuser` verifies the full public-auth plus authenticated-account chain across real `Edge Gateway`, real `Auth / Session Service`, and real `User Service`.
|
||||
- `notificationgateway` verifies that real `Notification Service` push
|
||||
publication is consumed and fanned out by real `Edge Gateway` for all
|
||||
user-facing push types.
|
||||
- `notificationmail` verifies that real `Notification Service` template-mode
|
||||
mail publication is consumed by real `Mail Service` for all notification
|
||||
email types.
|
||||
- `notificationuser` verifies that real `Notification Service` enriches
|
||||
recipients through real `User Service` and preserves Redis stream progress
|
||||
semantics for missing or temporarily unavailable users.
|
||||
- `gatewayauthsessionusermail` verifies the full public registration chain
|
||||
across real `Edge Gateway`, real `Auth / Session Service`, real
|
||||
`User Service`, and real `Mail Service`, including the regression that
|
||||
auth-code mail bypasses `notification:intents`.
|
||||
- `lobbyuser` verifies the synchronous eligibility boundary between real
|
||||
`Game Lobby` and real `User Service`, including the happy path,
|
||||
permanent_block rejection, unknown user, and transient User Service
|
||||
unavailability.
|
||||
- `lobbynotification` verifies the producer side of `Game Lobby →
|
||||
notification:intents`, covering all eleven `lobby.*` intent types from
|
||||
applications, invites, member operations, runtime pause, cascade
|
||||
membership block, and the three race-name intents emitted by capability
|
||||
evaluation at game finish and by self-service registration.
|
||||
- `lobbyrtm` verifies the asynchronous boundary between real
|
||||
`Game Lobby` and real `Runtime Manager` end-to-end against a real
|
||||
Docker daemon: start_job → engine container → success job_result →
|
||||
game `running`; cascade-blocked owner → stop_job(cancelled) → engine
|
||||
stopped; missing image → failure job_result + admin notification
|
||||
intent → game `start_failed`. Skips automatically on hosts without
|
||||
Docker.
|
||||
|
||||
The current fast suites still use one isolated `miniredis` instance plus either
|
||||
real downstream processes or external stateful HTTP stubs where appropriate.
|
||||
`authsessionmail`, `gatewayauthsessionmail`, `notificationgateway`,
|
||||
`notificationmail`, `notificationuser`, `gatewayauthsessionusermail`,
|
||||
`lobbyuser`, `lobbynotification`, and `lobbyrtm` are the deliberate
|
||||
exceptions: they use one real Redis container through
|
||||
`testcontainers-go`, because those boundaries must exercise real Redis
|
||||
stream, persistence, or scheduling behavior. `lobbyrtm` additionally
|
||||
needs a real Docker daemon and the `galaxy/game` engine image.
|
||||
`authsessionmail` additionally contains one targeted SMTP-capture scenario for
|
||||
the real `smtp` provider path, while `gatewayauthsessionmail` keeps `Mail
|
||||
Service` in `stub` mode and extracts the confirmation code through the trusted
|
||||
operator delivery surface.
|
||||
|
||||
## Running
|
||||
|
||||
Run from the module directory:
|
||||
|
||||
```bash
|
||||
cd integration
|
||||
go test ./gatewayauthsession/...
|
||||
go test ./authsessionuser/...
|
||||
go test ./authsessionmail/...
|
||||
go test ./gatewayauthsessionmail/...
|
||||
go test ./gatewayuser/...
|
||||
go test ./gatewayauthsessionuser/...
|
||||
go test ./notificationgateway/...
|
||||
go test ./notificationmail/...
|
||||
go test ./notificationuser/...
|
||||
go test ./gatewayauthsessionusermail/...
|
||||
go test ./lobbyuser/...
|
||||
go test ./lobbynotification/...
|
||||
go test ./lobbyrtm/...
|
||||
```
|
||||
|
||||
Useful regression commands after boundary changes:
|
||||
|
||||
```bash
|
||||
go test ./gatewayauthsession/...
|
||||
go test ./authsessionuser/...
|
||||
go test ./authsessionmail/...
|
||||
go test ./gatewayauthsessionmail/...
|
||||
go test ./gatewayuser/...
|
||||
go test ./gatewayauthsessionuser/...
|
||||
go test ./notificationgateway/...
|
||||
go test ./notificationmail/...
|
||||
go test ./notificationuser/...
|
||||
go test ./gatewayauthsessionusermail/...
|
||||
go test ./lobbyuser/...
|
||||
go test ./lobbynotification/...
|
||||
go test ./lobbyrtm/...
|
||||
cd ../gateway && go test ./...
|
||||
cd ../authsession && go test ./... -run GatewayCompatibility
|
||||
cd ../user && go test ./...
|
||||
```
|
||||
|
||||
Do not use `go test ./...` from the repository root. The repository is organized through `go.work`, so verification should stay module-scoped.
|
||||
|
||||
## Adding A New Boundary Suite
|
||||
|
||||
1. Create `integration/<boundary>/` for the new inter-service boundary.
|
||||
2. Keep suite-local fixtures, scenario helpers, and assertion helpers inside that package.
|
||||
3. Reuse `internal/harness` only for generic concerns such as binary build/run, ports, keys, Redis, and shared external stubs.
|
||||
4. Add new helpers to `internal/contracts/<contract>/` only when they describe a reusable public wire contract.
|
||||
5. Prefer fast deterministic infrastructure by default: in-memory test doubles, `httptest` stubs, and `miniredis`.
|
||||
|
||||
## Real Redis Suites
|
||||
|
||||
Fast suites stay on `miniredis` by default.
|
||||
When one boundary explicitly needs real Redis semantics, prefer a package-local
|
||||
container setup through `testcontainers-go` plus reusable helpers in
|
||||
`internal/harness`, as done by `authsessionmail` and
|
||||
`gatewayauthsessionmail`.
|
||||
|
||||
Current rule of thumb:
|
||||
|
||||
- use `miniredis` when the boundary does not depend on Redis persistence or
|
||||
scheduling behavior
|
||||
- use `testcontainers-go` only when the real Redis process materially changes
|
||||
the behavior being verified
|
||||
- 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.
|
||||
|
||||
Reference in New Issue
Block a user