"""告警引擎与运营看板单元测试(内存 SQLite)。""" from datetime import datetime, timedelta from types import SimpleNamespace from unittest.mock import patch from sqlmodel import Session, SQLModel, create_engine, select from app.models import (Campaign, Notification, PointAccount, TaskQueueItem) from app import ops def _session(): engine = create_engine("sqlite://", connect_args={"check_same_thread": False}) SQLModel.metadata.create_all(engine) return Session(engine) def test_alert_dedup_within_window(): with _session() as s: assert ops.raise_alert(s, "queue-dead", "队列告警:1 个死信 [queue-dead]") is True assert ops.raise_alert(s, "queue-dead", "队列告警:2 个死信 [queue-dead]") is False rows = s.exec(select(Notification)).all() assert len(rows) == 1 def test_alert_dedup_expires(): with _session() as s: old = (datetime.now() - timedelta(minutes=15)).isoformat() s.add(Notification(type="alert", ref_id=0, message="队列告警 [queue-dead]", created_at=old)) s.commit() assert ops.raise_alert(s, "queue-dead", "队列告警 [queue-dead]") is True def test_sweep_fires_dead_letter_alert(): with _session() as s: s.add(TaskQueueItem(task_key="run:1", status="dead", error="x", attempt=3)) s.commit() fired = ops.run_alert_sweep(s) assert "queue-dead" in fired alerts = s.exec(select(Notification).where(Notification.type == "alert")).all() assert any("死信" in a.message for a in alerts) def test_sweep_fires_low_points(): with _session() as s: s.add(PointAccount(user_id=1, organization_id=1, balance_points=10)) s.commit() fired = ops.run_alert_sweep(s) assert "points-low" in fired def test_sweep_healthy_no_alerts(): with _session() as s: # 99.9 万点:高于 points-low(1000) 也高于充值提醒阈值(100万以下不算健康) s.add(PointAccount(user_id=1, organization_id=1, balance_points=2_000_000)) s.commit() fired = ops.run_alert_sweep(s) assert fired == [] def test_dashboard_shape(): with _session() as s: s.add(PointAccount(user_id=1, organization_id=1, balance_points=500000)) s.add(Campaign(name="任务A", status="done", has_failure=False)) s.commit() data = ops.ops_dashboard(s) assert set(data.keys()) >= {"queue", "campaigns", "cost_7d", "knowledge", "providers", "dead_letter", "recent_alerts"} assert data["campaigns"]["done"] == 1 # 点数池已从看板移除(用户要求:运营看板不出现点数余额) assert "points" not in data["cost_7d"] def test_dashboard_isolation_for_non_admin(): """普通用户只能看到自己的任务/用量/告警,且拿不到系统级信息。""" with _session() as s: s.add(Campaign(name="我的任务", status="done", has_failure=False, owner_user_id=7, organization_id=1)) s.add(Campaign(name="别人的任务", status="done", has_failure=False, owner_user_id=8, organization_id=1)) s.commit() mine = ops.ops_dashboard(s, owner_user_id=7) assert mine["scope"] == "self" assert mine["campaigns"]["total"] == 1 and mine["campaigns"]["done"] == 1 # 系统级信息对普通用户置空 assert mine["providers"] == [] and mine["backup"] == {}