Browser suite: every service's /metrics, scraped as Prometheus does

Nothing in the browser suite asked ipx, the directory or ipx-play for
their metrics, though production's Prometheus scrapes all three every
15 s and the dashboard and alerts go blank if one stops answering. The
suite's servers get metrics ports (8799, 8797, 8790) and one test reads
each gzipped, checks every sample line is one Prometheus can parse, and
looks for the series that show it works: ipx's request histogram with
the route just asked for, the directory's snapshot from ipx, ipx-play's
build. ipx's metrics.rs went from 9% to 86% of lines run by the suite.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-11-14 03:58:55 +00:00
parent 373f862588
commit 61c38bacc1
2 changed files with 36 additions and 0 deletions

View File

@@ -2883,3 +2883,35 @@ test('an error toast is announced and stays until it is dismissed', async ({ pag
await t.getByRole('button', { name: 'Dismiss' }).click(); await t.getByRole('button', { name: 'Dismiss' }).click();
await expect(t).toHaveCount(0); await expect(t).toHaveCount(0);
}); });
/// A scrape as Prometheus makes it: gzipped (#253), and every sample a name, labels perhaps, and a
/// number, since one line that is not fails the whole scrape.
async function scrape(page: Page, port: number): Promise<string> {
const r = await page.request.get(`http://127.0.0.1:${port}/metrics`, { headers: { 'Accept-Encoding': 'gzip' } });
expect(r.status()).toBe(200);
expect(r.headers()['content-type']).toContain('text/plain');
const text = await r.text();
const bad = text.split('\n').filter(l => l && !l.startsWith('#') && !/^[a-z_:][a-z0-9_:]*(\{.*\})? (-?[0-9.e+-]+|NaN|\+Inf)$/i.test(l));
expect(bad, `port ${port}`).toEqual([]);
return text;
}
test('each service serves its metrics as Prometheus reads them, the requests made counted by route', async ({ page }) => {
// #194, #370, #458: production's Prometheus scrapes these every 15 s; the dashboard and the
// alerts go blank if one stops answering, and nothing else in the suite asks for them.
await page.request.get('/api/feeds');
const ipx = await scrape(page, 8799);
expect(ipx).toMatch(/^# TYPE ipx_http_request_duration_seconds histogram$/m);
expect(ipx).toMatch(/^ipx_http_request_duration_seconds_count\{method="GET",route="\/api\/feeds",status="200"\} [1-9]/m);
expect(ipx).toMatch(/^ipx_build_info\{version="[^"]+"\} 1$/m);
expect(ipx).toMatch(/^ipx_db_connections_max [1-9]/m);
expect(ipx).toMatch(/^ipx_feeds [1-9]/m);
// The suite's ipx shares with its directory, so the directory has taken a snapshot and answered.
const dir = await scrape(page, 8797);
expect(dir).toMatch(/^directory_build_info\{version="[^"]+"\} 1$/m);
expect(dir).toMatch(/^directory_snapshots_total [1-9]/m);
expect(dir).toMatch(/^directory_http_request_duration_seconds_count\{/m);
const play = await scrape(page, 8790);
expect(play).toMatch(/^play_build_info\{version="[^"]+"\} 1$/m);
expect(play).toMatch(/^play_uptime_seconds [0-9]/m);
});

View File

@@ -56,6 +56,8 @@ token = "${TOKEN}"
trusted_header = "X-Test-User" trusted_header = "X-Test-User"
trusted_proxies = ["127.0.0.1"] trusted_proxies = ["127.0.0.1"]
sign_out_url = "/signed-out-by-the-proxy" sign_out_url = "/signed-out-by-the-proxy"
# What Prometheus scrapes in production (#194), read by a test once the others have made requests.
metrics_bind = "127.0.0.1:8799"
[feeds.test-show] [feeds.test-show]
url = "http://127.0.0.1:8792/show.xml" url = "http://127.0.0.1:8792/show.xml"
@@ -82,6 +84,7 @@ auto_download = false
`); `);
fs.writeFileSync(path.join(root, 'directory.toml'), ` fs.writeFileSync(path.join(root, 'directory.toml'), `
bind = "127.0.0.1:8795" bind = "127.0.0.1:8795"
metrics_bind = "127.0.0.1:8797"
database_url_env = "DIRECTORY_DATABASE_URL" database_url_env = "DIRECTORY_DATABASE_URL"
# The fixture feeds are on loopback, which the refresher refuses otherwise (#329). # The fixture feeds are on loopback, which the refresher refuses otherwise (#329).
fetch_internal = ["127.0.0.1"] fetch_internal = ["127.0.0.1"]
@@ -93,6 +96,7 @@ token_sha256 = "${crypto.createHash('sha256').update(DIRECTORY_TOKEN).digest('he
`); `);
fs.writeFileSync(path.join(root, 'play.toml'), ` fs.writeFileSync(path.join(root, 'play.toml'), `
bind = "127.0.0.1:8798" bind = "127.0.0.1:8798"
metrics_bind = "127.0.0.1:8790"
public_url = "${PLAY}" public_url = "${PLAY}"
[[tokens]] [[tokens]]