import _bootstrap # noqa: F401 import unittest from datetime import datetime, timezone from fault_log_analyzer.models import Event, FaultLog, FaultType from fault_log_analyzer.storage import ( InMemoryEventRepository, InMemoryFaultLogRepository, InMemoryFaultTypeRepository, InMemoryRootCauseRepository, ) from fault_log_analyzer.workers import ClusteringWorker, RootCauseWorker def make_log(fid, message, host="h-1"): return FaultLog( fault_log_id=fid, host_id=host, fingerprint=f"fp-{fid}", level="ERROR", message=message, occurred_at=datetime(2025, 1, 1, 10, 0, 0, tzinfo=timezone.utc), ) class TestClusteringWorker(unittest.TestCase): def test_run_classifies_and_persists(self): fl_repo = InMemoryFaultLogRepository() ft_repo = InMemoryFaultTypeRepository([FaultType(fault_type="disk_full", name="磁盘满", pattern=r"no space")]) worker = ClusteringWorker(fl_repo, ft_repo, eps=0.75, min_samples=1) logs = [make_log("fl1", "No space left on device")] out = worker.run(logs) self.assertEqual(out[0].fault_type, "disk_full") self.assertNotEqual(out[0].cluster_id, "") persisted = fl_repo.get("fl1") self.assertEqual(persisted.fault_type, "disk_full") self.assertEqual(persisted.cluster_id, out[0].cluster_id) def test_run_empty(self): worker = ClusteringWorker(InMemoryFaultLogRepository(), InMemoryFaultTypeRepository()) self.assertEqual(worker.run([]), []) def test_guess_candidate_when_no_type_matches(self): fl_repo = InMemoryFaultLogRepository() worker = ClusteringWorker(fl_repo, InMemoryFaultTypeRepository(), eps=0.75, min_samples=1) out = worker.run([make_log("fl1", "No space left on device")]) self.assertEqual(out[0].fault_type, "disk_full") class TestRootCauseWorker(unittest.TestCase): def test_run_persists_root_cause(self): rc_repo = InMemoryRootCauseRepository() event_repo = InMemoryEventRepository( [ Event( event_id="e1", host_id="h-1", metric="disk_used_percent", value=97.0, fired_at=datetime(2025, 1, 1, 10, 1, 0, tzinfo=timezone.utc), ) ] ) worker = RootCauseWorker(rc_repo, event_repo, window_minutes=5) results = worker.run([make_log("fl1", "No space left on device")]) self.assertEqual(results[0].cause_type, "disk_full") self.assertEqual(rc_repo.get("fl1").cause_type, "disk_full") if __name__ == "__main__": unittest.main()