def test_dashboard_metrics_are_persisted_counts(ops_client): response = ops_client.get("/api/v1/dashboard") assert response.status_code == 200 body = response.json() metrics = body["metrics"] total = ( metrics["available"] + metrics["rented"] + metrics["cleaning"] + metrics["maintenance"] + metrics["blocked"] ) assert total == 50 assert metrics["open_quality_issues"] >= 1 assert metrics["pending_or_failed_workflows"] >= 1 def test_dashboard_attention_items_link_to_records(ops_client): response = ops_client.get("/api/v1/dashboard") body = response.json() assert len(body["attention_items"]) > 0 for item in body["attention_items"]: assert item["link_ref"] assert item["severity"] in ("low", "medium", "high") def test_dashboard_attention_items_expose_localizable_signals_not_raw_text(ops_client): """The dashboard subtext used to be raw, untranslated evidence text (and for most seeded issues, the meaningless placeholder 'Synthetic deterministic seed issue'). The API must never emit prose here -- only stable signal codes + params, exactly like the data-quality issue detail page, for the frontend to localize.""" response = ops_client.get("/api/v1/dashboard") body = response.json() assert len(body["attention_items"]) > 0 for item in body["attention_items"]: assert "detail" not in item assert len(item["evidence_signals"]) > 0 for signal in item["evidence_signals"]: assert signal["code"] assert signal["code"] != "Synthetic deterministic seed issue" def test_dashboard_recent_automation_capped_at_five(ops_client): response = ops_client.get("/api/v1/dashboard") body = response.json() assert len(body["recent_automation"]) == 5