Files
steve bb4ed3502d P5: OSS readiness — docs site, contributor onboarding, e2e harness
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.
2026-05-08 20:08:23 +01:00

4.4 KiB
Raw Permalink Blame History

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

  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 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.