82931684eb
CI / Test (server-http) (pull_request) Successful in 21s
CI / Test (rest) (pull_request) Successful in 23s
CI / Test (store) (pull_request) Successful in 36s
CI / Lint (pull_request) Successful in 27s
CI / Build (windows/amd64) (pull_request) Successful in 26s
CI / Build (linux/arm64) (pull_request) Successful in 23s
CI / Build (linux/amd64) (pull_request) Successful in 1m24s
e2e / Playwright vs docker-compose (pull_request) Failing after 2m52s
P5-01 — Documentation site under docs/book/ rendered with mdBook
(downloaded via Makefile, same static-binary pattern as Tailwind).
Structured chapters: getting started, concepts, operations,
security, reference. `make docs` / `make docs-watch`. Generated
output gitignored.
P5-02 — CONTRIBUTING.md rewritten from placeholder to a full
guide. CODE_OF_CONDUCT.md adapted from Contributor Covenant for a
single-maintainer project. .gitea/issue_template/{bug,feature}.md
and PULL_REQUEST_TEMPLATE.md.
P5-04 — Six README screenshots captured live from a fresh server
bootstrap (login, empty dashboard, add-host, alerts, settings,
audit log). README rewritten to centre the screenshot grid and
link out to the docs site.
P5-05 — SECURITY.md with disclosure policy (3-day ack, 30-day
default window), scope in/out, threat-model summary, operator
hardening checklist. Mirrored as a docs-site chapter.
P5-06 — End-to-end test harness. e2e/compose.e2e.yml brings up
server + sibling Linux agent (alpine + restic) + restic/rest-server.
Agent uses announce-and-approve so Playwright can drive the full
operator flow: bootstrap → login → accept pending → backup →
verify terminal status. Second spec scrapes /metrics to assert
the P6-04 endpoint surface. .gitea/workflows/e2e.yml runs on every
PR; local how-to in docs/e2e.md.
121 lines
4.4 KiB
Markdown
121 lines
4.4 KiB
Markdown
# 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`.
|