import _bootstrap # noqa: F401 import unittest from datetime import datetime, timezone from fault_log_analyzer.models import ( Event, FaultFilterRule, FaultLog, FaultType, RootCause, ) from fault_log_analyzer.storage import ( InMemoryDedupCache, InMemoryEventRepository, InMemoryFaultLogRepository, InMemoryFaultTypeRepository, InMemoryFilterRuleRepository, InMemoryLogSink, InMemoryRootCauseRepository, ) def make_log(fid, host="h-1", fault_type="disk_full", level="ERROR", message="disk full", minute=0): return FaultLog( fault_log_id=fid, host_id=host, fingerprint=f"fp-{fid}", level=level, message=message, occurred_at=datetime(2025, 1, 1, 0, minute, 0, tzinfo=timezone.utc), fault_type=fault_type, ) class TestInMemoryFaultLogRepository(unittest.TestCase): def setUp(self): self.repo = InMemoryFaultLogRepository() self.repo.save(make_log("fl1", host="h-1", fault_type="disk_full", level="ERROR", message="disk full", minute=1)) self.repo.save(make_log("fl2", host="h-2", fault_type="oom", level="FATAL", message="oom killed", minute=2)) def test_get(self): self.assertIsNotNone(self.repo.get("fl1")) self.assertIsNone(self.repo.get("nope")) def test_list_filter_by_host(self): total, items = self.repo.list(host_id="h-1") self.assertEqual(total, 1) self.assertEqual(items[0].fault_log_id, "fl1") def test_list_filter_by_fault_type(self): total, items = self.repo.list(fault_type="oom") self.assertEqual(total, 1) self.assertEqual(items[0].fault_log_id, "fl2") def test_list_filter_by_level_and_keyword(self): total, _ = self.repo.list(level="fatal") self.assertEqual(total, 1) total, items = self.repo.list(keyword="disk") self.assertEqual(total, 1) self.assertEqual(items[0].fault_log_id, "fl1") def test_pagination(self): total, items = self.repo.list(page=1, page_size=1) self.assertEqual(total, 2) self.assertEqual(len(items), 1) class TestInMemoryRootCauseRepository(unittest.TestCase): def test_save_and_get(self): repo = InMemoryRootCauseRepository() repo.save(RootCause(fault_log_id="fl1", cause_type="disk_full", confidence=0.9)) self.assertEqual(repo.get("fl1").cause_type, "disk_full") self.assertIsNone(repo.get("nope")) class TestInMemoryFaultTypeRepository(unittest.TestCase): def test_add_list_get(self): repo = InMemoryFaultTypeRepository() repo.add(FaultType(fault_type="disk_full", name="磁盘满")) self.assertEqual(repo.get("disk_full").name, "磁盘满") self.assertEqual(len(repo.list()), 1) def test_add_empty_raises(self): repo = InMemoryFaultTypeRepository() with self.assertRaises(ValueError): repo.add(FaultType(fault_type="", name="x")) class TestInMemoryFilterRuleRepository(unittest.TestCase): def test_add_assigns_id(self): repo = InMemoryFilterRuleRepository() rule = repo.add(FaultFilterRule(name="disk", pattern="disk")) self.assertIsNotNone(rule.id) self.assertEqual(len(repo.list()), 1) class TestInMemoryEventRepository(unittest.TestCase): def test_list_by_host_window(self): e1 = Event(event_id="e1", host_id="h-1", metric="cpu", value=1.0, fired_at=datetime(2025, 1, 1, 0, 0, 0, tzinfo=timezone.utc)) e2 = Event(event_id="e2", host_id="h-2", metric="cpu", value=1.0, fired_at=datetime(2025, 1, 1, 0, 0, 0, tzinfo=timezone.utc)) repo = InMemoryEventRepository([e1, e2]) start = datetime(2024, 12, 31, tzinfo=timezone.utc) end = datetime(2025, 1, 2, tzinfo=timezone.utc) self.assertEqual(len(repo.list_by_host("h-1", start, end)), 1) self.assertEqual(repo.list_by_host("h-1", start, end)[0].event_id, "e1") class TestInMemoryLogSink(unittest.TestCase): def test_write_and_search(self): sink = InMemoryLogSink() sink.write(make_log("fl1", host="h-1", level="ERROR", message="disk full")) sink.write(make_log("fl2", host="h-2", level="FATAL", message="oom")) self.assertEqual(len(sink.search()), 2) self.assertEqual(len(sink.search(host_id="h-1")), 1) self.assertEqual(len(sink.search(level="fatal")), 1) self.assertEqual(len(sink.search(keyword="disk")), 1) class TestInMemoryDedupCache(unittest.TestCase): def test_seen_before(self): cache = InMemoryDedupCache() self.assertFalse(cache.seen_before("fp1")) self.assertTrue(cache.seen_before("fp1")) self.assertFalse(cache.seen_before("fp2")) if __name__ == "__main__": unittest.main()