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.
4.4 KiB
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.
# 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:
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
- Bootstrap. Posts the admin-creation request to
/api/bootstrapwith the token scraped from the server log. - Login (UI). Drives the login form via Playwright; verifies the dashboard loads with a session cookie set.
- 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.
- Accept. POSTs
/api/pending-hosts/{id}/acceptwith 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.
- Online + auto-init. Polls
/api/hostsuntil the new host isstatus=online. Auto-init runs as part of this — the first dispatched job after creds save isrestic init. - Run backup. Submits the host detail page's
Run nowform; expectsHX-Redirectto the live job page. - Verify. Polls
/api/hostsuntil the host'slast_backup_statusflips tosucceeded. - Metrics. Scrapes
/metricsand asserts the server-gauge + build-info lines are present (the compose stack opens the endpoint viaRM_METRICS_TRUSTED_CIDR=0.0.0.0/0).
CI workflow
.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 agentfor connection errors anddocker compose logs serverfor any 4xx on/api/agents/announce. - Backup hangs in
running. The agent shells out torestic; check the live job log athttp://127.0.0.1:8080/jobs/<id>(still up after a failed test as long as you didn'tdown -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.