121 lines
4.3 KiB
Python

import json
import threading
import unittest
import urllib.request
from urllib.error import HTTPError
from detector.api import APIServer
from detector.app import DetectorApp
from detector.config import Config
class TestAPI(unittest.TestCase):
def setUp(self):
config = Config.from_env()
config.http_addr = "127.0.0.1:0"
self.app = DetectorApp(config)
self.app.seed_demo_rules()
self.app.start()
self.api = APIServer(self.app, "127.0.0.1:0")
self.port = self.api.server.server_address[1]
self.base = f"http://127.0.0.1:{self.port}"
self.thread = threading.Thread(target=self.api.serve_forever, daemon=True)
self.thread.start()
def tearDown(self):
self.api.shutdown()
self.api.server.server_close()
self.app.stop()
def _request(self, method, path, body=None):
data = json.dumps(body).encode("utf-8") if body is not None else None
req = urllib.request.Request(
self.base + path,
data=data,
method=method,
headers={"Content-Type": "application/json"},
)
try:
with urllib.request.urlopen(req, timeout=5) as resp:
return resp.status, json.loads(resp.read().decode("utf-8"))
except HTTPError as exc:
return exc.code, json.loads(exc.read().decode("utf-8"))
def test_healthz(self):
status, body = self._request("GET", "/healthz")
self.assertEqual(status, 200)
self.assertEqual(body["data"]["status"], "ok")
def test_list_rules(self):
status, body = self._request("GET", "/api/v1/rules")
self.assertEqual(status, 200)
self.assertGreaterEqual(body["data"]["total"], 3)
def test_create_and_get_rule(self):
payload = {
"rule_id": "r-test",
"name": "load high",
"metric": "load_1m",
"aggregation": "avg",
"operator": "gt",
"threshold": 5.0,
"for_duration": "60s",
"severity": "warning",
"scope": {"scope_type": "all"},
"notify_channels": ["email"],
"enabled": True,
}
status, body = self._request("POST", "/api/v1/rules", payload)
self.assertEqual(status, 200)
self.assertEqual(body["data"]["rule_id"], "r-test")
status, body = self._request("GET", "/api/v1/rules/r-test")
self.assertEqual(status, 200)
self.assertEqual(body["data"]["threshold"], 5.0)
def test_ingest_triggers_firing_event(self):
samples = [
{"name": "cpu_usage", "value": 95.0, "timestamp": float(i * 15), "labels": {"service": "web"}}
for i in range(5)
]
status, body = self._request(
"POST", "/api/v1/ingest",
{"host_id": "h-001", "host_group": "web", "samples": samples},
)
self.assertEqual(status, 200)
events = body["data"]["events"]
self.assertEqual(len(events), 1)
self.assertEqual(events[0]["status"], "firing")
# 事件已落库
status, body = self._request("GET", "/api/v1/events?host_id=h-001")
self.assertEqual(status, 200)
self.assertEqual(body["data"]["total"], 1)
# 告警已生成
status, body = self._request("GET", "/api/v1/alerts")
self.assertEqual(status, 200)
self.assertEqual(body["data"]["total"], 1)
alert_id = body["data"]["items"][0]["alert_id"]
# 确认告警
status, body = self._request("POST", f"/api/v1/alerts/{alert_id}/ack", {"ack_by": "tester"})
self.assertEqual(status, 200)
self.assertEqual(body["data"]["ack_status"], "acked")
def test_batch_ack(self):
self._request(
"POST", "/api/v1/ingest",
{"host_id": "h-002", "host_group": "web",
"samples": [{"name": "cpu_usage", "value": 95.0, "timestamp": float(i * 15)} for i in range(5)]},
)
status, body = self._request("GET", "/api/v1/alerts")
alert_ids = [a["alert_id"] for a in body["data"]["items"]]
status, body = self._request("POST", "/api/v1/alerts/batch-ack", {"alert_ids": alert_ids})
self.assertEqual(status, 200)
self.assertEqual(body["data"]["acked"], len(alert_ids))
if __name__ == "__main__":
unittest.main()