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
200 lines
7.9 KiB
Python
200 lines
7.9 KiB
Python
"""
|
|
Tests for PostgresIncidentStore, AutoIncidentStore, and INCIDENT_BACKEND=auto logic.
|
|
"""
|
|
import os
|
|
import sys
|
|
import json
|
|
import time
|
|
import threading
|
|
import unittest
|
|
from pathlib import Path
|
|
from unittest.mock import MagicMock, patch, PropertyMock
|
|
import tempfile
|
|
|
|
ROOT = Path(__file__).resolve().parent.parent
|
|
ROUTER = ROOT / "services" / "router"
|
|
if str(ROUTER) not in sys.path:
|
|
sys.path.insert(0, str(ROUTER))
|
|
|
|
|
|
class TestPostgresIncidentStore:
|
|
"""Unit tests for PostgresIncidentStore using mocked psycopg2."""
|
|
|
|
def _make_store(self):
|
|
"""Create a PostgresIncidentStore with a mocked DB connection."""
|
|
mock_psycopg2 = MagicMock()
|
|
mock_conn = MagicMock()
|
|
mock_conn.closed = False
|
|
mock_psycopg2.connect.return_value = mock_conn
|
|
mock_cursor = MagicMock()
|
|
mock_conn.cursor.return_value = mock_cursor
|
|
|
|
with patch.dict("sys.modules", {"psycopg2": mock_psycopg2, "psycopg2.extras": MagicMock()}):
|
|
from importlib import reload
|
|
import incident_store
|
|
reload(incident_store)
|
|
store = incident_store.PostgresIncidentStore("postgresql://test:test@localhost/test")
|
|
store._local = threading.local()
|
|
store._local.conn = mock_conn
|
|
return store, mock_cursor, mock_conn
|
|
|
|
def test_create_incident(self):
|
|
store, mock_cursor, _ = self._make_store()
|
|
result = store.create_incident({
|
|
"service": "gateway",
|
|
"severity": "P1",
|
|
"title": "Test incident",
|
|
"started_at": "2025-01-01T00:00:00Z",
|
|
})
|
|
assert result["status"] == "open"
|
|
assert result["id"].startswith("inc_")
|
|
assert mock_cursor.execute.called
|
|
|
|
def test_get_incident_not_found(self):
|
|
store, mock_cursor, _ = self._make_store()
|
|
mock_cursor.fetchone.return_value = None
|
|
result = store.get_incident("nonexistent")
|
|
assert result is None
|
|
|
|
def test_list_incidents_with_filters(self):
|
|
store, mock_cursor, _ = self._make_store()
|
|
mock_cursor.description = [("id",), ("workspace_id",), ("service",), ("env",),
|
|
("severity",), ("status",), ("title",), ("summary",),
|
|
("started_at",), ("ended_at",), ("created_by",),
|
|
("created_at",), ("updated_at",)]
|
|
mock_cursor.fetchall.return_value = []
|
|
result = store.list_incidents({"service": "gateway", "status": "open"}, limit=10)
|
|
assert isinstance(result, list)
|
|
sql_call = mock_cursor.execute.call_args[0][0]
|
|
assert "service=%s" in sql_call
|
|
assert "status=%s" in sql_call
|
|
|
|
def test_close_incident(self):
|
|
store, mock_cursor, _ = self._make_store()
|
|
mock_cursor.fetchone.return_value = ("inc_test",)
|
|
result = store.close_incident("inc_test", "2025-01-02T00:00:00Z", "Fixed")
|
|
assert result["status"] == "closed"
|
|
|
|
def test_append_event(self):
|
|
store, mock_cursor, _ = self._make_store()
|
|
result = store.append_event("inc_test", "note", "test message", {"key": "val"})
|
|
assert result["type"] == "note"
|
|
assert mock_cursor.execute.called
|
|
|
|
|
|
class TestAutoIncidentStore:
|
|
"""Tests for AutoIncidentStore with Postgres → JSONL fallback."""
|
|
|
|
def test_fallback_on_pg_failure(self):
|
|
from incident_store import AutoIncidentStore
|
|
with tempfile.TemporaryDirectory() as tmpdir:
|
|
store = AutoIncidentStore(
|
|
pg_dsn="postgresql://invalid:invalid@localhost:1/none",
|
|
jsonl_dir=tmpdir,
|
|
)
|
|
result = store.create_incident({
|
|
"service": "test-svc",
|
|
"title": "Test fallback",
|
|
"severity": "P2",
|
|
"started_at": "2025-01-01T00:00:00Z",
|
|
})
|
|
assert result["id"].startswith("inc_")
|
|
assert store._using_fallback is True
|
|
assert store.active_backend() == "jsonl_fallback"
|
|
|
|
def test_recovery_resets_after_interval(self):
|
|
from incident_store import AutoIncidentStore
|
|
with tempfile.TemporaryDirectory() as tmpdir:
|
|
store = AutoIncidentStore(
|
|
pg_dsn="postgresql://invalid:invalid@localhost:1/none",
|
|
jsonl_dir=tmpdir,
|
|
)
|
|
store.create_incident({
|
|
"service": "test",
|
|
"title": "Initial fail",
|
|
"severity": "P2",
|
|
})
|
|
assert store._using_fallback is True
|
|
|
|
store._fallback_since = time.monotonic() - 400
|
|
store._maybe_recover()
|
|
assert store._using_fallback is False
|
|
|
|
def test_active_backend_reflects_state(self):
|
|
from incident_store import AutoIncidentStore
|
|
with tempfile.TemporaryDirectory() as tmpdir:
|
|
store = AutoIncidentStore(
|
|
pg_dsn="postgresql://invalid:invalid@localhost:1/none",
|
|
jsonl_dir=tmpdir,
|
|
)
|
|
assert store.active_backend() == "postgres"
|
|
|
|
store._using_fallback = True
|
|
assert store.active_backend() == "jsonl_fallback"
|
|
|
|
def test_list_and_get_after_fallback(self):
|
|
from incident_store import AutoIncidentStore
|
|
with tempfile.TemporaryDirectory() as tmpdir:
|
|
store = AutoIncidentStore(
|
|
pg_dsn="postgresql://invalid:invalid@localhost:1/none",
|
|
jsonl_dir=tmpdir,
|
|
)
|
|
inc = store.create_incident({
|
|
"service": "api",
|
|
"title": "Test list",
|
|
"severity": "P1",
|
|
})
|
|
inc_id = inc["id"]
|
|
store.append_event(inc_id, "note", "some event")
|
|
|
|
fetched = store.get_incident(inc_id)
|
|
assert fetched is not None
|
|
assert fetched["service"] == "api"
|
|
|
|
listed = store.list_incidents()
|
|
assert any(i["id"] == inc_id for i in listed)
|
|
|
|
|
|
class TestCreateStoreFactory:
|
|
"""Tests for _create_store() with INCIDENT_BACKEND env var."""
|
|
|
|
def test_backend_memory(self):
|
|
from incident_store import _create_store, MemoryIncidentStore
|
|
with patch.dict(os.environ, {"INCIDENT_BACKEND": "memory"}):
|
|
store = _create_store()
|
|
assert isinstance(store, MemoryIncidentStore)
|
|
|
|
def test_backend_jsonl_default(self):
|
|
from incident_store import _create_store, JsonlIncidentStore
|
|
env = {"INCIDENT_BACKEND": "jsonl", "INCIDENT_JSONL_DIR": "/tmp/test_inc"}
|
|
with patch.dict(os.environ, env, clear=False):
|
|
store = _create_store()
|
|
assert isinstance(store, JsonlIncidentStore)
|
|
|
|
def test_backend_auto_with_dsn(self):
|
|
from incident_store import _create_store, AutoIncidentStore
|
|
env = {"INCIDENT_BACKEND": "auto", "DATABASE_URL": "postgresql://x:x@localhost/test"}
|
|
with patch.dict(os.environ, env, clear=False):
|
|
store = _create_store()
|
|
assert isinstance(store, AutoIncidentStore)
|
|
|
|
def test_backend_auto_without_dsn(self):
|
|
from incident_store import _create_store, JsonlIncidentStore
|
|
env = {"INCIDENT_BACKEND": "auto"}
|
|
env_clear = {k: v for k, v in os.environ.items()
|
|
if k not in ("DATABASE_URL", "INCIDENT_DATABASE_URL")}
|
|
env_clear["INCIDENT_BACKEND"] = "auto"
|
|
with patch.dict(os.environ, env_clear, clear=True):
|
|
store = _create_store()
|
|
assert isinstance(store, JsonlIncidentStore)
|
|
|
|
def test_backend_postgres_without_dsn_falls_back(self):
|
|
from incident_store import _create_store, JsonlIncidentStore
|
|
env = {"INCIDENT_BACKEND": "postgres", "INCIDENT_JSONL_DIR": "/tmp/test_inc_pg"}
|
|
env_clear = {k: v for k, v in os.environ.items()
|
|
if k not in ("DATABASE_URL", "INCIDENT_DATABASE_URL")}
|
|
env_clear.update(env)
|
|
with patch.dict(os.environ, env_clear, clear=True):
|
|
store = _create_store()
|
|
assert isinstance(store, JsonlIncidentStore)
|