# End-to-end test harness

The e2e harness stands up the full production-shaped stack
(server + agent + rest-server) in Docker Compose and drives it
through Playwright. CI runs it on every PR; operators can run it
locally too.

## Files

```
e2e/
├── compose.e2e.yml         compose stack: server + rest-server + agent
├── Dockerfile.agent        Linux container for the agent (alpine + restic)
├── agent-entrypoint.sh     decides between announce / token-enrol / run
└── playwright/
    ├── package.json
    ├── playwright.config.ts
    └── tests/
        ├── lib/server.ts   bootstrap, login, accept, poll helpers
        └── smoke.spec.ts   happy-path: enrol → backup → succeeded
```

## Local run

Prerequisites: Docker + Docker Compose, and `npx` for Playwright.

```sh
# 1. Build + bring up the stack (server, rest-server, source data).
docker compose -f e2e/compose.e2e.yml up --build -d server rest-server source-fixture

# 2. Wait for the server, then scrape the bootstrap token from the log.
until curl -fsS http://127.0.0.1:8080/api/version >/dev/null; do sleep 1; done
RM_BOOTSTRAP_TOKEN=$(docker compose -f e2e/compose.e2e.yml logs server \
    | grep -Eo '[a-zA-Z0-9_-]{40,}' | head -1)
export RM_BOOTSTRAP_TOKEN

# 3. Start the agent (it announces against the running server).
docker compose -f e2e/compose.e2e.yml up -d agent

# 4. Install + run Playwright.
cd e2e/playwright
npm install
npx playwright install --with-deps chromium
npx playwright test
```

When the test passes you'll see:

```
Running 2 tests using 1 worker
  ✓  smoke: enrol-via-announce → backup › happy path completes in under a minute (47s)
  ✓  smoke: scrape /metrics › metrics endpoint exposes the host gauge (180ms)

  2 passed (47.5s)
```

Tear-down:

```sh
docker compose -f e2e/compose.e2e.yml down -v
```

`-v` removes the named volumes too — important between runs because
the rest-server volume holds an initialised repo and the
agent-config volume holds a stale bearer.

## What the test exercises

1. **Bootstrap.** Posts the admin-creation request to
   `/api/bootstrap` with the token scraped from the server log.
2. **Login (UI).** Drives the login form via Playwright; verifies
   the dashboard loads with a session cookie set.
3. **Pending host appears.** Polls the dashboard for the inline
   accept form generated by the announcing agent; reads the
   pending-id out of its action URL.
4. **Accept.** POSTs `/api/pending-hosts/{id}/accept` with the
   rest-server URL + repo password. The server mints a Host row
   + bearer + AEAD-encrypted creds and pushes the bearer down
   the still-open pending WebSocket.
5. **Online + auto-init.** Polls `/api/hosts` until the new host
   is `status=online`. Auto-init runs as part of this — the
   first dispatched job after creds save is `restic init`.
6. **Run backup.** Submits the host detail page's `Run now`
   form; expects `HX-Redirect` to the live job page.
7. **Verify.** Polls `/api/hosts` until the host's
   `last_backup_status` flips to `succeeded`.
8. **Metrics.** Scrapes `/metrics` and asserts the
   server-gauge + build-info lines are present (the compose
   stack opens the endpoint via `RM_METRICS_TRUSTED_CIDR=0.0.0.0/0`).

## CI workflow

[`.gitea/workflows/e2e.yml`](../.gitea/workflows/e2e.yml) runs the
suite on every PR into `main`. On failure it dumps the last 200
lines of each container log as a workflow annotation and uploads
the Playwright HTML report as an artefact.

## When tests fail

- **Pending host never appears.** Agent container probably
  couldn't reach the server. Check `docker compose logs agent`
  for connection errors and `docker compose logs server` for
  any 4xx on `/api/agents/announce`.
- **Backup hangs in `running`.** The agent shells out to
  `restic`; check the live job log at
  `http://127.0.0.1:8080/jobs/<id>` (still up after a
  failed test as long as you didn't `down -v`).
- **`RM_BOOTSTRAP_TOKEN not set`.** The server log scrape
  matched the wrong line or the token regex is too tight. The
  server prints the token on a line starting with `    ` (four
  spaces) inside a banner; widen the regex if your server log
  format changes.

## Adding new tests

The harness is intentionally flat — one `*.spec.ts` per
scenario. Reuse the helpers in `lib/server.ts` and avoid
duplicating bootstrap / login boilerplate. Heavy fixtures
(custom users, OIDC IdP) belong in their own compose override
file rather than complicating `compose.e2e.yml`.
