"""Integration: the Prometheus dashboard widget (#5691) draws a target's stored series with the bundled Fabled Archives template, the picker lists the targets and templates, and a widget whose target is gone says so. Requires STEWARD_DATABASE_URL.""" from __future__ import annotations import asyncio import importlib import os import time import uuid import pytest pytestmark = pytest.mark.integration @pytest.fixture def app(): if not os.environ.get("STEWARD_DATABASE_URL"): pytest.skip("needs Postgres") from steward.app import create_app return create_app(testing=False) def _prom(sub): """A submodule of the prometheus plugin as the app loaded it (see test_prometheus_scrape).""" from steward.core.plugin_manager import get_loaded_plugin_modules loaded = get_loaded_plugin_modules().get("prometheus") return importlib.import_module(f"{loaded.__name__ if loaded else 'plugins.prometheus'}.{sub}") def _series(name: str) -> list[tuple[str, str, float]]: """(resource, metric, value) as a scrape of Archives stores them.""" rows = [(name, "fabledarchives_data_disk_free_bytes", 17.8e12), (name, "fabledarchives_data_disk_size_bytes", 46e12), (name, "fabledarchives_files", 94116.0), (name, "fabledarchives_files_checked", 93310.0), (name, "fabledarchives_malware_held_files", 2.0), (name, "fabledarchives_notification_channels_failing", 0.0), (f"{name}:version=2026.10.12.1717,channel=dev,commit=3605d0a", "fabledarchives_build_info", 1.0), (f"{name}:person=Bryan,person_id=9,kind=files", "fabledarchives_person_stored_bytes", 4.7e11)] for state in ("disabled", "fresh", "late", "missing", "waiting"): rows.append((f"{name}:target=Steward,target_id=1,state={state}", "fabledarchives_backup_target_state", float(state == "late"))) for state in ("ok", "failing", "late", "running"): rows.append((f"{name}:job=Nightly,job_id=5,state={state}", "fabledarchives_replication_job_state", float(state == "ok"))) return rows def test_the_widget_draws_a_target_with_a_template_and_the_picker_lists_them(app): from steward.core.alerts import record_metrics from steward.core.widgets import WIDGET_REGISTRY from steward.dashboard.routes import with_options from steward.models.metrics import PluginMetric from steward.models.users import User, UserRole PrometheusTarget = _prom("models").PrometheusTarget tag = uuid.uuid4().hex[:8] name = f"arch-{tag}" async def go(): uid = str(uuid.uuid4()) async with app.db_sessionmaker() as s: async with s.begin(): s.add(User(id=uid, username=f"pw-{tag}", email=f"pw-{tag}@test", password_hash="x", role=UserRole.viewer, is_active=True)) target = PrometheusTarget(name=name, url=f"http://{name}/metrics") s.add(target) await s.flush() target_id = target.id await record_metrics(s, [PluginMetric(source_module="prometheus", resource_name=r, metric_name=m, value=v) for r, m, v in _series(name)]) async with app.db_sessionmaker() as db: picker = await with_options(db, WIDGET_REGISTRY["prometheus"]) client = app.test_client() async with client.session_transaction() as sess: sess.update(user_id=uid, user_role="viewer", username=f"pw-{tag}", login_at=time.time(), tz="UTC") ok = await client.get(f"/plugins/prometheus/widget?target={target_id}&template=bundled:fabled_archives") page = " ".join((await ok.get_data(as_text=True)).split()) gone = await client.get("/plugins/prometheus/widget?target=no-such-target&template=bundled:fabled_archives") async with app.db_sessionmaker() as s: async with s.begin(): await s.delete(await s.get(PrometheusTarget, target_id)) return target_id, picker, ok.status_code, page, gone.status_code, await gone.get_data(as_text=True) target_id, picker, status, page, gone_status, gone = asyncio.run(go()) targets, templates = picker["params"] assert {"value": target_id, "label": name} in targets["options"] assert {"value": "bundled:fabled_archives", "label": "Fabled Archives (built in)"} in templates["options"] assert status == 200 # The late backup target and the held malware are the problems, the late target first. assert "2 need a look. Backup targets: Steward is late." in page assert page.index("2 need a look") < page.index("Backup targets") < page.index("Replication") assert "16.2 TiB of 41.8 TiB" in page and "93,310 of 94,116" in page assert "version 2026.10.12.1717 · channel dev · commit 3605d0a" in page assert f'{name}' in page assert gone_status == 200 and "The target this widget showed has been deleted." in gone