5925d09e8b
Two issues uncovered by the page-snapshot dump after the agent state-dir fix: * The host page server-renders `Run backup now` as disabled while repo_status != ready, and the page has no live-refresh on that field. The test was navigating right after status flipped to 'online' but before auto-init had completed (~3s later), so the rendered HTML still showed init_running and the click was a no-op. Wait for repo_status === 'ready' before navigating. * playwright.config.ts pinned the per-test timeout at 60s, but the test itself uses 60s + 120s of internal waits. Bump to 240s so the test fails on real regressions instead of timing out on its own internal budget. Renamed the test description away from "under a minute" since it overpromises against the new timeout. The performance SLO belongs in a separate test if we want to assert it.
88 lines
3.6 KiB
TypeScript
88 lines
3.6 KiB
TypeScript
// End-to-end smoke: bootstrap → accept pending host → run backup → see succeeded.
|
|
//
|
|
// The compose stack stands up a server, a sibling rest-server, and an
|
|
// agent in announce-and-approve mode. This test drives the operator
|
|
// path through the UI (login + dashboard) and the API
|
|
// (accept + run-now + poll for terminal) — UI for the human surfaces,
|
|
// API for the deterministic ones.
|
|
|
|
import { test, expect } from '@playwright/test';
|
|
import {
|
|
baseURL,
|
|
bootstrapAdmin,
|
|
loginViaUI,
|
|
waitForPendingHostID,
|
|
acceptPending,
|
|
waitForHostStatus,
|
|
getSessionCookie,
|
|
} from './lib/server';
|
|
|
|
test.describe('smoke: enrol-via-announce → backup', () => {
|
|
test('happy path: enrol → accept → backup → succeeded', async ({ page, request }) => {
|
|
const { username, password } = await bootstrapAdmin(request);
|
|
await loginViaUI(page, username, password);
|
|
|
|
// Dashboard renders.
|
|
await expect(page.locator('main')).toContainText(/host|fleet|pending/i, { timeout: 10_000 });
|
|
|
|
// Pending host appears (the agent container has been
|
|
// announcing since startup).
|
|
const pendingID = await waitForPendingHostID(page);
|
|
const cookie = await getSessionCookie(page);
|
|
|
|
// Accept with the rest-server creds. compose's rest-server runs
|
|
// --no-auth, so any credentials work; restic still demands a
|
|
// password to encrypt the repo.
|
|
await acceptPending(request, cookie, pendingID, {
|
|
url: 'rest:http://rest-server:8000/',
|
|
password: 'e2e-repo-password',
|
|
});
|
|
|
|
// Wait for the host to come online AND for auto-init to
|
|
// finish. Coming online happens as soon as the agent's
|
|
// bearer-authed WS attaches (~1s after accept); repo_status
|
|
// flips to 'ready' once the auto-init job completes (a
|
|
// couple of seconds later). Loading the host page before
|
|
// that leaves the Run-backup button disabled because the
|
|
// server-rendered HTML reflects the still-in-progress init,
|
|
// and the page has no live-refresh on that field.
|
|
const readyHost = await waitForHostStatus(
|
|
request, cookie,
|
|
(h) => h.status === 'online' && h.repo_status === 'ready',
|
|
90_000,
|
|
);
|
|
expect(readyHost.id).toBeTruthy();
|
|
|
|
// Trigger a backup via the UI form-post (HX-Redirect to /jobs/{id}).
|
|
await page.goto(`${baseURL}/hosts/${readyHost.id}`);
|
|
await Promise.all([
|
|
page.waitForURL(/\/jobs\//),
|
|
page.locator('form[action$="/run-backup"] button[type="submit"]').first().click(),
|
|
]);
|
|
|
|
// Wait for the host's last_backup_status to flip to 'succeeded'.
|
|
// The job page itself is harder to assert on (it uses
|
|
// server-pushed updates and a reload-on-finish pattern); the
|
|
// host record is the source of truth and is what the dashboard
|
|
// surfaces.
|
|
const finishedHost = await waitForHostStatus(
|
|
request, cookie,
|
|
(h) => h.id === readyHost.id && h.last_backup_status === 'succeeded',
|
|
120_000,
|
|
);
|
|
expect(finishedHost.last_backup_status).toBe('succeeded');
|
|
});
|
|
});
|
|
|
|
test.describe('smoke: scrape /metrics', () => {
|
|
test('metrics endpoint exposes the host gauge', async ({ request }) => {
|
|
// Compose sets RM_METRICS_TRUSTED_CIDR=0.0.0.0/0 so the
|
|
// endpoint is open to the test runner.
|
|
const res = await request.get(`${baseURL}/metrics`);
|
|
expect(res.status()).toBe(200);
|
|
const body = await res.text();
|
|
expect(body).toContain('rm_hosts_total');
|
|
expect(body).toContain('rm_build_info{');
|
|
});
|
|
});
|