132 lines
4.7 KiB
Python
132 lines
4.7 KiB
Python
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()
|