import _bootstrap # noqa: F401 import unittest from datetime import datetime, timezone from fault_log_analyzer.cluster import ( ClusterEngine, TfidfVectorizer, cosine_distance, cosine_similarity, dbscan, ) from fault_log_analyzer.models import FaultLog def make_log(fid, message, host="h-1"): return FaultLog( fault_log_id=fid, host_id=host, fingerprint="fp", level="ERROR", message=message, occurred_at=datetime(2025, 1, 1, tzinfo=timezone.utc), ) class TestTfidfVectorizer(unittest.TestCase): def test_fit_transform(self): vec = TfidfVectorizer().fit([["a", "b"], ["b", "c"]]) out = vec.transform([["a", "b"], ["b", "c"]]) self.assertEqual(len(out), 2) self.assertTrue(all(isinstance(v, dict) for v in out)) def test_empty_documents(self): vec = TfidfVectorizer().fit([]) self.assertEqual(vec.transform([]), []) class TestCosineSimilarity(unittest.TestCase): def test_identical(self): self.assertEqual(cosine_similarity({"a": 1.0}, {"a": 1.0}), 1.0) def test_disjoint(self): self.assertEqual(cosine_similarity({"a": 1.0}, {"b": 1.0}), 0.0) def test_distance(self): self.assertAlmostEqual(cosine_distance({"a": 1.0}, {"a": 1.0}), 0.0) class TestDbscan(unittest.TestCase): def test_empty(self): result = dbscan([]) self.assertEqual(result.labels, []) self.assertEqual(result.cluster_ids, {}) def test_single_noise(self): result = dbscan([{"a": 1.0}], eps=0.75, min_samples=2) self.assertEqual(result.labels, [-1]) def test_two_similar_cluster(self): result = dbscan([{"a": 1.0}, {"a": 1.0}], eps=0.75, min_samples=1) self.assertEqual(result.labels[0], result.labels[1]) self.assertNotEqual(result.labels[0], -1) def test_two_dissimilar_noise(self): result = dbscan([{"a": 1.0}, {"b": 1.0}], eps=0.75, min_samples=2) self.assertEqual(result.labels, [-1, -1]) class TestClusterEngine(unittest.TestCase): def test_cluster_batch_empty(self): ce = ClusterEngine() self.assertEqual(ce.cluster_batch([]), {}) def test_cluster_batch_groups_identical(self): ce = ClusterEngine(eps=0.75, min_samples=1) logs = [make_log("fl1", "No space left on device"), make_log("fl2", "No space left on device")] assignment = ce.cluster_batch(logs) self.assertEqual(assignment["fl1"], assignment["fl2"]) def test_incremental_match_existing_cluster(self): ce = ClusterEngine(eps=0.75, min_samples=1) first = ce.cluster_batch([make_log("fl1", "No space left on device")]) second = ce.cluster_batch([make_log("fl2", "No space left on device")]) self.assertEqual(second["fl2"], first["fl1"]) if __name__ == "__main__": unittest.main()