diff --git a/tests/ui/app.spec.ts b/tests/ui/app.spec.ts index 18b6a51..2c55ce8 100644 --- a/tests/ui/app.spec.ts +++ b/tests/ui/app.spec.ts @@ -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 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 { + 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); +}); diff --git a/tests/ui/global-setup.ts b/tests/ui/global-setup.ts index a9d6d02..b0237ef 100644 --- a/tests/ui/global-setup.ts +++ b/tests/ui/global-setup.ts @@ -56,6 +56,8 @@ token = "${TOKEN}" trusted_header = "X-Test-User" trusted_proxies = ["127.0.0.1"] 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] url = "http://127.0.0.1:8792/show.xml" @@ -82,6 +84,7 @@ auto_download = false `); fs.writeFileSync(path.join(root, 'directory.toml'), ` bind = "127.0.0.1:8795" +metrics_bind = "127.0.0.1:8797" database_url_env = "DIRECTORY_DATABASE_URL" # The fixture feeds are on loopback, which the refresher refuses otherwise (#329). 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'), ` bind = "127.0.0.1:8798" +metrics_bind = "127.0.0.1:8790" public_url = "${PLAY}" [[tokens]]