Files
microdao-daarion/tests/test_monitor_status.py
Apple 129e4ea1fc feat(platform): add new services, tools, tests and crews modules
New router intelligence modules (26 files): alert_ingest/store, audit_store,
architecture_pressure, backlog_generator/store, cost_analyzer, data_governance,
dependency_scanner, drift_analyzer, incident_* (5 files), llm_enrichment,
platform_priority_digest, provider_budget, release_check_runner, risk_* (6 files),
signature_state_store, sofiia_auto_router, tool_governance

New services:
- sofiia-console: Dockerfile, adapters/, monitor/nodes/ops/voice modules, launchd, react static
- memory-service: integration_endpoints, integrations, voice_endpoints, static UI
- aurora-service: full app suite (analysis, job_store, orchestrator, reporting, schemas, subagents)
- sofiia-supervisor: new supervisor service
- aistalk-bridge-lite: Telegram bridge lite
- calendar-service: CalDAV calendar service with reminders
- mlx-stt-service / mlx-tts-service: Apple Silicon speech services
- binance-bot-monitor: market monitor service
- node-worker: STT/TTS memory providers

New tools (9): agent_email, browser_tool, contract_tool, observability_tool,
oncall_tool, pr_reviewer_tool, repo_tool, safe_code_executor, secure_vault

New crews: agromatrix_crew (10 modules: depth_classifier, doc_facts, doc_focus,
farm_state, light_reply, llm_factory, memory_manager, proactivity, reflection_engine,
session_context, style_adapter, telemetry)

Tests: 85+ test files for all new modules
Made-with: Cursor
2026-03-03 07:14:14 -08:00

357 lines
14 KiB
Python

"""
tests/test_monitor_status.py — Tests for GET /monitor/status on router.
Covers:
- test_monitor_status_basic : returns required fields, router_ok=True
- test_monitor_status_no_secrets : no DSN/password/key in response
- test_monitor_status_rbac_prod : 403 when wrong key in prod
- test_monitor_status_rbac_dev : 200 even without key in dev
- test_monitor_status_partial_fail : returns data even if incidents/slo unavailable
- test_monitor_status_rate_limit : 429 after 60 rpm
- test_healthz_alias : /healthz returns same as /health
"""
from __future__ import annotations
import json
import os
import sys
import importlib
import unittest
from pathlib import Path
from unittest.mock import MagicMock, patch
# Ensure router modules are importable
sys.path.insert(0, str(Path(__file__).resolve().parent.parent / "services" / "router"))
# ── Minimal FastAPI test client setup ─────────────────────────────────────────
def _make_test_client():
"""Import router main and return TestClient (skips if deps missing)."""
try:
from fastapi.testclient import TestClient
import main as router_main
return TestClient(router_main.app, raise_server_exceptions=False)
except Exception as exc:
return None, str(exc)
# ── Helpers ───────────────────────────────────────────────────────────────────
def _call_monitor(client, headers=None):
return client.get("/monitor/status", headers=headers or {})
def _call_health(client):
return client.get("/health")
def _call_healthz(client):
return client.get("/healthz")
# ─────────────────────────────────────────────────────────────────────────────
# Tests
# ─────────────────────────────────────────────────────────────────────────────
class TestMonitorStatusBasic(unittest.TestCase):
def setUp(self):
result = _make_test_client()
if isinstance(result, tuple):
self.skipTest(f"Cannot import router main: {result[1]}")
self.client = result
def test_returns_200(self):
r = _call_monitor(self.client)
self.assertEqual(r.status_code, 200, r.text[:200])
def test_required_fields_present(self):
r = _call_monitor(self.client)
d = r.json()
required = ["node_id", "ts", "heartbeat_age_s", "router_ok", "backends"]
for field in required:
self.assertIn(field, d, f"missing field: {field}")
def test_router_ok_true(self):
"""Router self-reports as OK if endpoint responds."""
r = _call_monitor(self.client)
d = r.json()
self.assertTrue(d["router_ok"])
def test_backends_has_alerts(self):
r = _call_monitor(self.client)
be = r.json().get("backends", {})
self.assertIn("alerts", be)
self.assertIn("incidents", be)
def test_heartbeat_age_nonnegative(self):
r = _call_monitor(self.client)
age = r.json().get("heartbeat_age_s")
self.assertIsNotNone(age)
self.assertGreaterEqual(age, 0)
def test_warnings_is_list(self):
r = _call_monitor(self.client)
warnings = r.json().get("warnings", [])
self.assertIsInstance(warnings, list)
class TestMonitorStatusNoSecrets(unittest.TestCase):
"""Ensure no DSN, passwords, or keys leak in the response."""
FORBIDDEN_PATTERNS = [
"postgresql://", "postgres://", "mongodb://", "mysql://",
"password", "passwd", "secret", "dsn", "DATABASE_URL",
"QDRANT_", "AWS_SECRET", "API_KEY=", "token=",
]
def setUp(self):
result = _make_test_client()
if isinstance(result, tuple):
self.skipTest(f"Cannot import router main: {result[1]}")
self.client = result
def test_no_secrets_in_response(self):
r = _call_monitor(self.client)
body = r.text.lower()
for pat in self.FORBIDDEN_PATTERNS:
self.assertNotIn(pat.lower(), body,
f"Potential secret pattern '{pat}' found in /monitor/status response")
def test_backends_values_are_short_identifiers(self):
"""Backend values should be short labels like 'postgres', 'auto', 'memory' — not DSNs."""
r = _call_monitor(self.client)
backends = r.json().get("backends", {})
for key, value in backends.items():
if value and value != "unknown":
self.assertLess(len(str(value)), 64,
f"backends.{key} value looks too long (possible DSN): {value[:80]}")
self.assertNotIn("://", str(value),
f"backends.{key} contains URL scheme (possible DSN): {value[:80]}")
class TestMonitorStatusRBAC(unittest.TestCase):
def setUp(self):
result = _make_test_client()
if isinstance(result, tuple):
self.skipTest(f"Cannot import router main: {result[1]}")
self.client = result
def test_dev_no_key_returns_200(self):
"""In dev env (no API key set), /monitor/status is accessible without auth."""
with patch.dict(os.environ, {"ENV": "dev", "SUPERVISOR_API_KEY": ""}):
r = _call_monitor(self.client)
self.assertEqual(r.status_code, 200)
def test_prod_no_key_still_200_when_no_key_configured(self):
"""If SUPERVISOR_API_KEY is not set, even prod allows access (graceful)."""
with patch.dict(os.environ, {"ENV": "prod", "SUPERVISOR_API_KEY": ""}):
r = _call_monitor(self.client)
self.assertEqual(r.status_code, 200)
def test_prod_wrong_key_returns_403(self):
"""In prod with a configured API key, wrong key → 403."""
with patch.dict(os.environ, {"ENV": "prod", "SUPERVISOR_API_KEY": "secret-key-123"}):
r = _call_monitor(self.client, headers={"X-Monitor-Key": "wrong-key"})
self.assertEqual(r.status_code, 403)
def test_prod_correct_key_returns_200(self):
"""In prod, correct X-Monitor-Key → 200."""
with patch.dict(os.environ, {"ENV": "prod", "SUPERVISOR_API_KEY": "secret-key-123"}):
r = _call_monitor(self.client, headers={"X-Monitor-Key": "secret-key-123"})
self.assertEqual(r.status_code, 200)
def test_prod_bearer_token_accepted(self):
"""In prod, Authorization: Bearer <key> is also accepted."""
with patch.dict(os.environ, {"ENV": "prod", "SUPERVISOR_API_KEY": "secret-key-123"}):
r = _call_monitor(self.client, headers={"Authorization": "Bearer secret-key-123"})
self.assertEqual(r.status_code, 200)
class TestMonitorStatusPartialFail(unittest.TestCase):
"""Even if incidents or SLO store fails, /monitor/status must return 200 with partial data."""
def setUp(self):
result = _make_test_client()
if isinstance(result, tuple):
self.skipTest(f"Cannot import router main: {result[1]}")
self.client = result
def test_incident_store_error_is_non_fatal(self):
"""If incident_store raises, open_incidents is None and warning is added."""
with patch.dict("sys.modules", {"incident_store": None}):
# Force import error on incident_store within the endpoint
r = _call_monitor(self.client)
# Must still return 200
self.assertEqual(r.status_code, 200)
d = r.json()
# open_incidents can be null but endpoint must not crash
self.assertIn("open_incidents", d)
def test_alert_store_error_is_non_fatal(self):
"""If alert_store.compute_loop_slo raises, alerts_loop_slo is None and warning added."""
with patch.dict("sys.modules", {"alert_store": None}):
r = _call_monitor(self.client)
self.assertEqual(r.status_code, 200)
d = r.json()
self.assertIn("alerts_loop_slo", d)
def test_partial_data_has_warnings(self):
"""When stores are unavailable, warnings list should be non-empty."""
# Simulate both stores failing by patching the imports inside the function
import main as router_main
orig_get_is = None
orig_get_as = None
try:
import incident_store as _is_mod
orig_get_is = _is_mod.get_incident_store
def _bad_is():
raise RuntimeError("simulated incident_store failure")
_is_mod.get_incident_store = _bad_is
except ImportError:
pass
try:
import alert_store as _as_mod
orig_get_as = _as_mod.get_alert_store
def _bad_as():
raise RuntimeError("simulated alert_store failure")
_as_mod.get_alert_store = _bad_as
except ImportError:
pass
try:
r = _call_monitor(self.client)
self.assertEqual(r.status_code, 200)
d = r.json()
warnings = d.get("warnings", [])
self.assertIsInstance(warnings, list)
finally:
# Restore
try:
if orig_get_is:
import incident_store as _is_mod
_is_mod.get_incident_store = orig_get_is
if orig_get_as:
import alert_store as _as_mod
_as_mod.get_alert_store = orig_get_as
except Exception:
pass
class TestMonitorStatusRateLimit(unittest.TestCase):
def setUp(self):
result = _make_test_client()
if isinstance(result, tuple):
self.skipTest(f"Cannot import router main: {result[1]}")
self.client = result
def test_rate_limit_after_60_requests(self):
"""After 60 requests from same IP within 60s, should get 429."""
import main as router_main
# Reset the rate bucket for test isolation
if hasattr(router_main.monitor_status, "_buckets"):
router_main.monitor_status._buckets.clear()
# Fire 60 — all should pass
for i in range(60):
r = _call_monitor(self.client)
self.assertIn(r.status_code, (200, 403),
f"Expected 200/403 on request {i+1}, got {r.status_code}")
# 61st should be rate limited
r = _call_monitor(self.client)
self.assertEqual(r.status_code, 429, "Expected 429 after 60 rpm")
def tearDown(self):
# Always reset bucket after test
try:
import main as router_main
if hasattr(router_main.monitor_status, "_buckets"):
router_main.monitor_status._buckets.clear()
except Exception:
pass
class TestHealthzAlias(unittest.TestCase):
"""GET /healthz should return same structure as GET /health."""
def setUp(self):
result = _make_test_client()
if isinstance(result, tuple):
self.skipTest(f"Cannot import router main: {result[1]}")
self.client = result
def test_healthz_returns_200(self):
r = _call_healthz(self.client)
self.assertEqual(r.status_code, 200)
def test_healthz_has_status_ok(self):
r = _call_healthz(self.client)
self.assertEqual(r.json().get("status"), "ok")
def test_healthz_same_fields_as_health(self):
rh = _call_health(self.client)
rz = _call_healthz(self.client)
health_keys = set(rh.json().keys())
healthz_keys = set(rz.json().keys())
self.assertEqual(health_keys, healthz_keys,
f"healthz keys differ from health: {health_keys ^ healthz_keys}")
class TestMonitorRbacMatrixEntitlement(unittest.TestCase):
"""Verify rbac_tools_matrix.yml contains tools.monitor.read in correct roles."""
def _load_matrix(self):
import yaml as _yaml
paths = [
Path(__file__).resolve().parent.parent / "config" / "rbac_tools_matrix.yml",
Path("config/rbac_tools_matrix.yml"),
]
for p in paths:
if p.exists():
with open(p) as f:
return _yaml.safe_load(f)
self.skipTest("rbac_tools_matrix.yml not found")
def test_monitor_tool_defined(self):
m = self._load_matrix()
tools = m.get("tools", {})
self.assertIn("monitor_tool", tools, "monitor_tool missing from rbac matrix")
def test_monitor_status_action_has_entitlement(self):
m = self._load_matrix()
ents = (
m.get("tools", {})
.get("monitor_tool", {})
.get("actions", {})
.get("status", {})
.get("entitlements", [])
)
self.assertIn("tools.monitor.read", ents)
def test_agent_cto_has_monitor_read(self):
m = self._load_matrix()
cto_ents = m.get("role_entitlements", {}).get("agent_cto", [])
self.assertIn("tools.monitor.read", cto_ents)
def test_agent_monitor_has_monitor_read(self):
m = self._load_matrix()
ents = m.get("role_entitlements", {}).get("agent_monitor", [])
self.assertIn("tools.monitor.read", ents)
def test_agent_oncall_has_monitor_read(self):
m = self._load_matrix()
ents = m.get("role_entitlements", {}).get("agent_oncall", [])
self.assertIn("tools.monitor.read", ents)
if __name__ == "__main__":
unittest.main(verbosity=2)