Files
hair/tests/test_gateway_monitor.py
2026-10-15 23:04:07 +08:00

136 lines
4.3 KiB
Python

"""外部监控接口:有在线 worker 才算服务正常;故障模拟只改探活响应。"""
import asyncio
import pytest
from gateway import app as gateway_app
from gateway.app import monitor, monitor_fault, monitor_payload
class _Req:
def __init__(self, body=None, token="secret", raw=None):
self._body = body
self._raw = raw
self.headers = {"X-Internal-Token": token} if token is not None else {}
async def json(self):
if self._raw is not None:
raise self._raw
return self._body
@pytest.fixture(autouse=True)
def _reset_monitor_drill():
gateway_app._monitor_drill["enabled"] = False
yield
gateway_app._monitor_drill["enabled"] = False
def test_monitor_ok_when_any_worker_online():
body = monitor_payload({
"total": 5, "healthy": 1, "primary_total": 3, "primary_healthy": 0,
})
assert body["status"] == "ok"
assert body["workers_healthy"] == 1
assert "message" not in body
def test_monitor_unhealthy_when_none_online():
body = monitor_payload({
"total": 5, "healthy": 0, "primary_total": 3, "primary_healthy": 0,
})
assert body["status"] == "unhealthy"
assert body["message"] == "没有可用的 worker"
assert body["workers_total"] == 5
def test_monitor_unhealthy_when_pool_empty():
body = monitor_payload({"total": 0, "healthy": 0})
assert body["status"] == "unhealthy"
def test_monitor_http_503_when_no_worker(monkeypatch):
monkeypatch.setattr(
"gateway.app._get_pool_status_safe",
lambda: {"total": 2, "healthy": 0, "primary_total": 2, "primary_healthy": 0},
)
resp = asyncio.run(monitor())
assert resp.status_code == 503
assert resp.body
assert b'"status":"unhealthy"' in resp.body or b'"status": "unhealthy"' in resp.body
def test_monitor_http_200_when_worker_online(monkeypatch):
monkeypatch.setattr(
"gateway.app._get_pool_status_safe",
lambda: {"total": 2, "healthy": 2, "primary_total": 2, "primary_healthy": 1},
)
body = asyncio.run(monitor())
assert body["status"] == "ok"
assert body["workers_healthy"] == 2
def test_monitor_payload_simulated_and_really_down():
body = monitor_payload(
{"total": 5, "healthy": 0, "primary_total": 3, "primary_healthy": 0},
simulated=True,
)
assert body["status"] == "unhealthy"
assert body["simulated"] is True
assert body["workers_healthy"] == 0
assert "没有可用的 worker" in body["message"]
def test_monitor_payload_simulated_keeps_real_counts():
body = monitor_payload(
{"total": 5, "healthy": 3, "primary_total": 3, "primary_healthy": 3},
simulated=True,
)
assert body["status"] == "unhealthy"
assert body["simulated"] is True
assert body["workers_healthy"] == 3
assert "模拟故障" in body["message"]
def _patch_token(monkeypatch, password="secret"):
monkeypatch.setattr(
"gateway.config.get_config",
lambda: {"shared_password": password},
)
def test_fault_requires_token(monkeypatch):
_patch_token(monkeypatch)
resp = asyncio.run(monitor_fault(_Req(body={"enabled": True}, token="nope")))
assert resp.status_code == 401
assert gateway_app._monitor_drill["enabled"] is False
def test_fault_rejects_non_bool(monkeypatch):
_patch_token(monkeypatch)
resp = asyncio.run(monitor_fault(_Req(body={"enabled": "true"})))
assert resp.status_code == 400
assert gateway_app._monitor_drill["enabled"] is False
def test_fault_on_makes_monitor_503_then_off_restores(monkeypatch):
monkeypatch.setattr(
"gateway.app._get_pool_status_safe",
lambda: {"total": 5, "healthy": 3, "primary_total": 3, "primary_healthy": 3},
)
_patch_token(monkeypatch)
opened = asyncio.run(monitor_fault(_Req(body={"enabled": True})))
assert opened["simulated"] is True
failed = asyncio.run(monitor())
assert failed.status_code == 503
assert b'"simulated":true' in failed.body or b'"simulated": true' in failed.body
assert b'"workers_healthy":3' in failed.body or b'"workers_healthy": 3' in failed.body
closed = asyncio.run(monitor_fault(_Req(body={"enabled": False})))
assert closed["simulated"] is False
ok = asyncio.run(monitor())
assert ok["status"] == "ok"
assert "simulated" not in ok