spec(grafana-metric-gateway): verify + close GM-115 + reconcile tracking
Add 3 test cases (startup old-config validation warning; status auth_failed for 401/403) closing the GM-115 PARTIAL. Write apply-progress.md, tick all 33 tasks, add verify-report.md (15/16 PASS, 1 PARTIAL->PASS). Gates green: 331+ pytest, ruff clean, npm build+lint 0 errors, 151 vitest.
This commit is contained in:
@@ -5,10 +5,12 @@ without requiring real remote connections.
|
||||
"""
|
||||
|
||||
import json
|
||||
import logging
|
||||
from types import SimpleNamespace
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
import requests
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
from media_library_viewer_api.clients.ssh import CommandResult
|
||||
@@ -787,3 +789,59 @@ class TestPrometheusStatus:
|
||||
assert data["up"] is True
|
||||
assert data["version"] == "ok"
|
||||
assert data["service_id"] == "p1"
|
||||
|
||||
@pytest.mark.parametrize("status_code", [401, 403])
|
||||
def test_prometheus_status_returns_auth_failure_message(self, test_client, status_code):
|
||||
# GM-110: a 401/403 from the Grafana gateway must surface as an
|
||||
# auth-related error, not a crash and not a generic gateway error.
|
||||
service = ServiceRecord(
|
||||
id="p1",
|
||||
service_type="prometheus",
|
||||
name="Prometheus",
|
||||
config={"grafana_url": "http://grafana:3000", "datasource_uid": "prometheus"},
|
||||
secrets={"grafana_api_key": "bad-key"},
|
||||
)
|
||||
auth_error = requests.HTTPError(
|
||||
f"{status_code} Client Error",
|
||||
response=MagicMock(status_code=status_code),
|
||||
)
|
||||
with (
|
||||
patch(f"{_MON}.resolve_service_record", return_value=service),
|
||||
patch(f"{_MON}.requests.post", side_effect=auth_error),
|
||||
):
|
||||
response = test_client.get("/api/monitoring/prometheus-status")
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert not data["up"]
|
||||
assert data["error"] == "auth_failed"
|
||||
assert data["service_id"] == "p1"
|
||||
|
||||
|
||||
class TestPrometheusStartupValidation:
|
||||
"""GM-113: startup warns (never crashes) about old-shape prometheus services."""
|
||||
|
||||
def test_old_shape_prometheus_service_logs_migration_warning(self, tmp_path, caplog):
|
||||
from media_library_viewer_api.main import _validate_prometheus_gateway_config
|
||||
|
||||
# Seed a prometheus service persisted with the OLD config shape: a
|
||||
# ``base_url`` and no ``grafana_url`` (pre-gateway migration).
|
||||
store = SettingsStore(tmp_path / "settings.sqlite")
|
||||
store.upsert_service(
|
||||
{
|
||||
"service_type": "prometheus",
|
||||
"name": "Legacy Prometheus",
|
||||
"config": {"base_url": "https://prometheus.example.com"},
|
||||
"enabled": True,
|
||||
}
|
||||
)
|
||||
|
||||
with (
|
||||
patch("media_library_viewer_api.main.get_settings_store", return_value=store),
|
||||
caplog.at_level(logging.WARNING, logger="media_library_viewer_api.main"),
|
||||
):
|
||||
# Must not raise even though the service uses the deprecated shape.
|
||||
_validate_prometheus_gateway_config()
|
||||
|
||||
# Best-effort validator logs a migration hint referencing grafana_url.
|
||||
assert "grafana_url" in caplog.text
|
||||
assert any(record.levelno == logging.WARNING for record in caplog.records)
|
||||
|
||||
Reference in New Issue
Block a user