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.
57 lines
1.7 KiB
Python
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
|