Files
core2026/docs/e2e-orchestrator-tests.md
T

162 lines
6.3 KiB
Markdown

# PM2-Orchestrated E2E Test (Gateway Orchestrator)
This document describes how to run a full end-to-end test using the gateway
orchestrator (PM2) so that `game-api` and `game-engine` run as real processes
and communicate over Redis.
## Goal
Validate the full process boundary:
- gateway orchestrator starts/stops `game-api` and `turn-daemon` via PM2
- `game-api` talks to the daemon over Redis streams
- daemon publishes realtime events over Redis pub/sub
- optional mutation commands flow from API to engine and back
## Where to Implement
Use the existing integration test package and add a PM2-backed test.
- Test file: `tools/integration-tests/test/orchestrator.e2e.test.ts`
- Test helpers: `tools/integration-tests/src/orchestratorHarness.ts`
- Reuse existing integration setup in `docs/integration-tests.md`
Core integration points:
- Orchestrator entrypoint: `app/gateway-api/src/orchestrator/orchestratorServer.ts`
- PM2 manager: `app/gateway-api/src/orchestrator/pm2ProcessManager.ts`
- Process names: `app/gateway-api/src/orchestrator/gatewayOrchestrator.ts:244`
- Redis stream keys: `app/game-api/src/daemon/streamKeys.ts`, `app/game-engine/src/lifecycle/redisCommandStream.ts`
- Turn daemon status endpoint: `app/game-api/src/router/turnDaemon/index.ts`
## Prerequisites
- PM2 is installed and available on PATH.
- `pnpm --filter @sammo-ts/game-api build`
- `pnpm --filter @sammo-ts/game-engine build`
- `pnpm --filter @sammo-ts/gateway-api build` (to run orchestrator in dist)
- Postgres and Redis are running (use a dedicated DB/schema and Redis DB).
Minimum environment variables for the test:
- `DATABASE_URL` or `POSTGRES_*` (both `game-api` and `game-engine` rely on this)
- `GATEWAY_DB_SCHEMA` (gateway profile DB schema)
- `REDIS_URL`
- `GAME_TOKEN_SECRET`
- `GATEWAY_REDIS_PREFIX` (optional but recommended to isolate keys)
- `GATEWAY_WORKSPACE_ROOT` (workspace root with built `dist/`)
- `GATEWAY_WORKTREE_ROOT` (worktree root for builds; can be temp)
- `GATEWAY_TRPC_PATH` (e.g. `/gateway/api/trpc`)
- `GAME_TRPC_PATH` (e.g. `/che/api/trpc`)
- `GAME_API_EVENTS_PATH` (e.g. `/che/api/events`)
For profile alignment:
- `PROFILE` and `SCENARIO` for game server processes
- `TURN_PROFILE_NAME` should match `${PROFILE}:${SCENARIO}`
## Procedure
1) **Reset state**
- Truncate the game schema and gateway schema.
- Flush Redis (or use a dedicated Redis DB index).
2) **Seed the game DB**
- Use `seedProfileDatabase` to seed a scenario and create an admin general.
- Use a fixed `INTEGRATION_WORLD_SEED` to keep deterministic RNG.
3) **Create/Update the gateway profile record**
- Upsert a profile with status `RUNNING` (or `PREOPEN`) and a known `apiPort`.
- Ensure `profileName` is `${profile}:${scenario}`.
4) **Start the orchestrator**
- Run `app/gateway-api/dist/index.js` with `GATEWAY_ROLE=orchestrator`.
- Or instantiate `GatewayOrchestrator` directly and call `start()`.
- Wait for PM2 to report the processes as `online`.
5) **Verify API <-> daemon**
- Exchange a gateway session carrying `superuser`, `admin`, or
`admin.profiles.manage[:<profile>]` for a game access token.
- Call `turnDaemon.status` with that administrator token and expect a
non-null status.
- Send a mutation command that expects a result (e.g., `troop.join`) and
verify `commandResult` is received.
6) **Verify realtime events**
- Trigger a run via `turnDaemon.run` with the administrator token.
- Subscribe to `sammo:${profileName}:realtime:events` and wait for
`turnCompleted`.
7) **Cleanup**
- Stop orchestrator, then `pm2 delete` the processes by name:
`sammo:${profileName}:game-api` and `sammo:${profileName}:turn-daemon`.
- Flush Redis and drop the test schemas.
## What to Check
Mandatory checks:
- PM2 reports both processes as online.
- Authenticated profile administration is required for every
`turnDaemon.run/pause/resume/status` call.
- `turnDaemon.status` responds within the timeout for the administrator token.
- A command with `commandResult` returns a success response.
- A `turnCompleted` realtime event is observed after a `run` command.
Optional checks:
- `worldState.lastTurnTime` advances after a run.
- A mutation command changes DB state (e.g., troop join/exit).
## Notes
- `turnDaemon.run` is fire-and-forget; rely on status or realtime events for
confirmation.
- PM2 is global; use unique `profileName` per test run to avoid collisions.
- Orchestrator starts binaries from `dist/` under `GATEWAY_WORKSPACE_ROOT`.
If build artifacts are missing, PM2 will start and immediately exit.
## HWE GUI lifecycle
`app/gateway-frontend/e2e/hwe-lifecycle.spec.ts` verifies the operational
browser flow with one administrator and two independent user contexts:
1. The administrator logs in, selects `hwe:2`, loads scenario 2 from a fixed
commit, requests a reset, and waits for that exact operation to succeed.
2. The administrator returns to the gateway main page and sees the open HWE
action.
3. Each user logs in with a separate Chromium context, sees the same open HWE
row, creates a general, and reaches the HWE main dashboard without an error.
The test uses an ignored secret directory. It reads `admin_password`,
`user_a_password`, and `user_b_password` from
`SAMMO_LIFECYCLE_SECRET_ROOT`; passwords must not be passed on the command
line. `SAMMO_LIFECYCLE_SOURCE_COMMIT` must be a full commit SHA.
Build the gateway frontend with the public prefix contract before starting its
preview server:
```bash
VITE_APP_BASE_PATH=/gateway \
VITE_GATEWAY_API_URL=/gateway/api/trpc \
VITE_GAME_API_URL_TEMPLATE='/{profile}/api/trpc' \
VITE_GAME_WEB_URL_TEMPLATE='/{profile}/' \
pnpm --filter @sammo-ts/gateway-frontend build
```
Build the HWE frontend with `VITE_APP_BASE_PATH=/hwe`,
`VITE_GAME_API_URL=/hwe/api/trpc`, and
`VITE_GAME_SSE_URL=/hwe/api/events`. Start the isolated gateway API,
orchestrator, both previews, Postgres, and Redis, then run:
```bash
SAMMO_LIFECYCLE_SECRET_ROOT=/path/to/ignored/secrets \
SAMMO_LIFECYCLE_SOURCE_COMMIT="$(git rev-parse HEAD)" \
pnpm --filter @sammo-ts/gateway-frontend test:e2e:hwe-lifecycle
```
The Playwright web server is a local prefix-preserving proxy on port `15140`.
It mirrors the Caddy route contract while keeping all navigation on one
origin. Passing the test means the actual reset/build/seed/PM2 process path and
both user creation flows completed; it is not a mocked API test.