pryscraper/tests/test_metrics.py
cryptorugmunch 648640e1c8 feat(api): expose /metrics endpoint as Prometheus text format
The /metrics endpoint was wired in observability.py (REQUEST_COUNT,
REQUEST_LATENCY, SCRAPE_COUNT, SCRAPE_LATENCY, etc.) but never exposed
as an HTTP route. This commit:

- Adds @app.get("/metrics") returning Prometheus text exposition format
  when prometheus_client is installed, JSON fallback otherwise
- Adds /metrics to _AUTH_PUBLIC_PATHS so it works without PRY_API_KEY
- Adds /metrics to X402Middleware.free_paths so scrapers can poll
- Adds tests/test_metrics.py covering 200, content-type, and counter increments

Closes the Cinnabox feat/metrics-endpoint work (uncommitted). Brings
observability story from 80% to 100%.

Audit items 9 and 13.
Tests: 598 passed, 1 skipped.
2026-07-06 10:32:03 +02:00

57 lines
1.7 KiB
Python

# SPDX-License-Identifier: MIT
# Copyright (c) 2026 Rug Munch Media LLC
#
# Part of Pry - https://git.rugmunch.io/RugMunchMedia/pryscraper
# Licensed under MIT. See LICENSE.
"""Tests for the /metrics endpoint and Prometheus instrumentation."""
import pytest
from fastapi.testclient import TestClient
from api import app
from observability import SCRAPE_COUNT, track_scrape
@pytest.fixture
def client() -> TestClient:
return TestClient(app)
def test_metrics_returns_200(client: TestClient) -> None:
resp = client.get("/metrics")
assert resp.status_code == 200
def test_metrics_content_type_prometheus(client: TestClient) -> None:
resp = client.get("/metrics")
assert resp.headers.get("content-type", "").startswith("text/plain")
def test_metrics_contains_expected_metric_names(client: TestClient) -> None:
"""Verify HELP lines for pry_requests_total and pry_scrapes_total appear."""
resp = client.get("/metrics")
assert "# HELP pry_requests_total" in resp.text
assert "# HELP pry_scrapes_total" in resp.text
def test_metrics_contains_scrapes_counter_after_track(client: TestClient) -> None:
with track_scrape(method="test_meth"):
pass
resp = client.get("/metrics")
assert "pry_scrapes_total" in resp.text
def test_scrape_counters_increment_after_track() -> None:
if SCRAPE_COUNT is None:
pytest.skip("prometheus_client not available")
with track_scrape(method="test_metrics_inc"):
pass
found = False
for sample in SCRAPE_COUNT.collect():
for s in sample.samples:
if s.labels.get("method") == "test_metrics_inc":
assert s.value >= 1
found = True
assert found