diff --git a/backend/tests/test_incident_service.py b/backend/tests/test_incident_service.py new file mode 100644 index 00000000..ff4c3bca --- /dev/null +++ b/backend/tests/test_incident_service.py @@ -0,0 +1,84 @@ +import pytest +from unittest.mock import MagicMock +import sys +import os + +sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..')) + +from backend.services.incident_service import IncidentService, CORRELATION_THRESHOLD, WINDOW_SECONDS + + +class MockDuplicateService: + def __init__(self): + self.model = None + self._loaded = False + + def load(self): + self._loaded = True + + +class TestIncidentServicePrune: + def test_prune_removes_old_tickets(self): + mock_dup = MockDuplicateService() + service = IncidentService(mock_dup) + now = 1000.0 + service._recent = [ + {"ticket_id": "1", "ts": now - 100}, + {"ticket_id": "2", "ts": now - WINDOW_SECONDS - 10}, + {"ticket_id": "3", "ts": now -50}, + ] + service._prune(now) + assert len(service._recent) == 2 + assert all(t["ts"] >= now - WINDOW_SECONDS for t in service._recent) + + def test_prune_keeps_recent_tickets(self): + mock_dup = MockDuplicateService() + service = IncidentService(mock_dup) + now = 1000.0 + service._recent = [ + {"ticket_id": "1", "ts": now - 100}, + {"ticket_id": "2", "ts": now - 200}, + ] + service._prune(now) + assert len(service._recent) == 2 + + +class TestIncidentServiceCritical: + def test_is_critical_priority_critical(self): + mock_dup = MockDuplicateService() + service = IncidentService(mock_dup) + assert service._is_critical("critical", None) is True + assert service._is_critical("CRITICAL", None) is True + + def test_is_critical_category_email(self): + mock_dup = MockDuplicateService() + service = IncidentService(mock_dup) + assert service._is_critical(None, "email") is True + assert service._is_critical(None, "network") is True + assert service._is_critical(None, "authentication") is True + assert service._is_critical(None, "exchange") is True + + def test_is_critical_not_critical(self): + mock_dup = MockDuplicateService() + service = IncidentService(mock_dup) + assert service._is_critical("high", "general") is False + assert service._is_critical(None, None) is False + + +class TestIncidentServiceCorrelate: + def test_correlate_returns_defaults_when_no_model(self): + mock_dup = MockDuplicateService() + service = IncidentService(mock_dup) + result = service.correlate("Test ticket text") + assert result["incident_id"] is None + assert result["is_major_incident"] is False + assert result["ticket_count"] == 0 + assert result["affected_users"] == 0 + assert result["similarity"] == 0.0 + + +class TestIncidentServiceListActive: + def test_list_active_returns_empty_initially(self): + mock_dup = MockDuplicateService() + service = IncidentService(mock_dup) + assert service.list_active() == [] diff --git a/backend/tests/test_ner_service.py b/backend/tests/test_ner_service.py new file mode 100644 index 00000000..d180059f --- /dev/null +++ b/backend/tests/test_ner_service.py @@ -0,0 +1,111 @@ +import pytest +from unittest.mock import MagicMock, patch +import sys +import os + +sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..')) + +import importlib.util + +spec = importlib.util.spec_from_file_location( + "ner_service", + "/home/itsmaestro/gssoc/HELPDESK.AI/backend/services/ner_service.py" +) +ner_module = importlib.util.module_from_spec(spec) +sys.modules['backend.services.ner_service'] = ner_module +spec.loader.exec_module(ner_module) + + +class TestNERServiceInit: + def test_init_sets_defaults(self): + service = ner_module.NERService() + assert service.model is None + assert service.tokenizer is None + assert service.id2label is None + assert service.label2id is None + assert service._loaded is False + + +class TestNERServiceCleanLabel: + def test_clean_label_o_tag(self): + service = ner_module.NERService() + bio, entity = service._clean_label("O") + assert bio == "O" + assert entity == "" + + def test_clean_label_b_b_entity(self): + service = ner_module.NERService() + bio, entity = service._clean_label("B-B-APP_NAME") + assert bio == "B" + assert entity == "APP_NAME" + + def test_clean_label_i_b_entity(self): + service = ner_module.NERService() + bio, entity = service._clean_label("I-B-APP_NAME") + assert bio == "I" + assert entity == "APP_NAME" + + def test_clean_label_b_entity(self): + service = ner_module.NERService() + bio, entity = service._clean_label("B-ENTITY") + assert bio == "B" + assert entity == "ENTITY" + + def test_clean_label_i_entity(self): + service = ner_module.NERService() + bio, entity = service._clean_label("I-ENTITY") + assert bio == "I" + assert entity == "ENTITY" + + def test_clean_label_unknown_returns_o(self): + service = ner_module.NERService() + bio, entity = service._clean_label("UNKNOWN") + assert bio == "O" + assert entity == "" + + +class TestNERServiceLoad: + def test_load_without_torch(self): + original_has_torch = ner_module._HAS_TORCH + ner_module._HAS_TORCH = False + try: + service = ner_module.NERService() + service.load() + assert service._loaded is False + finally: + ner_module._HAS_TORCH = original_has_torch + + def test_load_idempotent(self): + service = ner_module.NERService() + service._loaded = True + service.load() + assert service._loaded is True + + +class TestNERServiceExtractEntities: + def test_extract_entities_empty_text(self): + service = ner_module.NERService() + service._loaded = True + service._load_failed = True + result = service.extract_entities("") + assert result == [] + + +class TestRegexPatterns: + def test_regex_patterns_exist(self): + assert hasattr(ner_module, 'REGEX_PATTERNS') + patterns = ner_module.REGEX_PATTERNS + assert "IP_ADDRESS" in patterns + assert "HOSTNAME" in patterns + assert "NETWORK_ERROR" in patterns + assert "LOGIN_ISSUE" in patterns + assert "VLAN" in patterns + assert "DATABASE" in patterns + assert "SYSTEM" in patterns + assert "BROWSER" in patterns + + +class TestNERServiceMaxLen: + def test_max_len_constant(self): + assert hasattr(ner_module, 'MAX_LEN') + assert ner_module.MAX_LEN == 128