import sys import unittest from pathlib import Path from unittest.mock import MagicMock, patch PROJECT_ROOT = Path(__file__).resolve().parents[2] DOMAINCHECK_ROOT = PROJECT_ROOT / "domainCheck" if str(DOMAINCHECK_ROOT) not in sys.path: sys.path.insert(0, str(DOMAINCHECK_ROOT)) from app.core.detect_engine import DetectEngine from app.detectors.rdap_detector import RDAPDetector class DetectEngineOutcomeTests(unittest.TestCase): def _build_engine(self): patchers = [ patch("app.core.detect_engine.Database"), patch("app.core.detect_engine.RDAPDetector"), patch("app.core.detect_engine.WaybackDetector"), patch("app.core.detect_engine.BaiduDetector"), patch("app.core.detect_engine.Qihu360Detector"), patch("app.core.detect_engine.GoogleDetector"), patch("app.core.detect_engine.ChinazDetector"), patch("app.core.detect_engine.AizhanDetector"), patch("app.core.detect_engine.JuziseoDetector"), patch("app.core.detect_engine.JuchaDetector"), ] started = [patcher.start() for patcher in patchers] self.addCleanup(lambda: [patcher.stop() for patcher in reversed(patchers)]) engine = DetectEngine() for started_mock in started: started_mock.return_value = MagicMock() return engine def test_process_task_blacklisted_is_completed_without_retry(self): engine = self._build_engine() engine.db.get_task_by_id.return_value = {"id": 7, "domain_id": 42, "retry_count": 1} engine._detect_domain_with_outcome = MagicMock(return_value=engine.OUTCOME_BLACKLISTED) success = engine.process_task(7) self.assertTrue(success) self.assertEqual( [(7, 1), (7, 2)], [call.args for call in engine.db.update_task_status.call_args_list], ) engine.db.update_task_retry_count.assert_not_called() def test_process_task_failed_requeues_for_retry(self): engine = self._build_engine() engine.db.get_task_by_id.return_value = {"id": 9, "domain_id": 99, "retry_count": 0} engine._detect_domain_with_outcome = MagicMock(return_value=engine.OUTCOME_FAILED) success = engine.process_task(9) self.assertFalse(success) self.assertEqual( [(9, 1), (9, 0)], [call.args for call in engine.db.update_task_status.call_args_list], ) engine.db.update_task_retry_count.assert_called_once_with(9, 1) def test_deep_detect_fails_when_any_detector_returns_error(self): engine = self._build_engine() engine.baidu_detector.check_history.return_value = {"error": "timeout"} engine.baidu_detector.check_site.return_value = {"has_收录": False, "subdomains": []} engine.qihu360_detector.check_site.return_value = {"has_收录": False, "subdomains": []} engine.google_detector.check_site.return_value = {"has_收录": False} engine.chinaz_detector.check_domain.return_value = {"title": "", "category": "", "has_sensitive": False} engine.aizhan_detector.check_domain.return_value = {"title": "", "risk": "", "has_sensitive": False} engine.juziseo_detector.check_domain.return_value = {"history": {}, "backlink": {}} engine.jucha_detector.check_domain.return_value = {"whois": {}, "beian": {}, "intercept": {"normal": True}} engine.db.add_detection_result.return_value = True outcome = engine._deep_detect(1, "example.com") self.assertEqual(engine.OUTCOME_FAILED, outcome) engine.db.add_detection_result.assert_not_called() def test_deep_detect_fails_when_result_persistence_fails(self): engine = self._build_engine() engine.baidu_detector.check_history.return_value = {"has_history": False, "has_gray": False} engine.baidu_detector.check_site.return_value = {"has_收录": False, "subdomains": []} engine.qihu360_detector.check_site.return_value = {"has_收录": False, "subdomains": []} engine.google_detector.check_site.return_value = {"has_收录": False} engine.chinaz_detector.check_domain.return_value = {"title": "", "category": "", "has_sensitive": False} engine.aizhan_detector.check_domain.return_value = {"title": "", "risk": "", "has_sensitive": False} engine.juziseo_detector.check_domain.return_value = {"history": {}, "backlink": {}} engine.jucha_detector.check_domain.return_value = {"whois": {}, "beian": {}, "intercept": {"normal": True}} engine.db.add_detection_result.return_value = False outcome = engine._deep_detect(1, "example.com") self.assertEqual(engine.OUTCOME_FAILED, outcome) engine.db.add_detection_result.assert_called_once() def test_deep_detect_normalizes_results_before_persisting(self): engine = self._build_engine() engine.baidu_detector.check_history.return_value = {"has_history": True, "has_gray": False} engine.baidu_detector.check_site.return_value = {"has_收录": True, "subdomains": ["www"]} engine.qihu360_detector.check_site.return_value = {"has_收录": False, "subdomains": []} engine.google_detector.check_site.return_value = {"has_收录": False} engine.chinaz_detector.check_domain.return_value = {"title": "Example", "category": "", "has_sensitive": False} engine.aizhan_detector.check_domain.return_value = {"title": "Example", "risk": "", "has_sensitive": False} engine.juziseo_detector.check_domain.return_value = { "history": {"has_sensitive": False, "has_baidu_history": True, "has_subdomains": False, "is_simplified": True}, "backlink": {"has_sensitive": False, "has_subdomains": False}, } engine.jucha_detector.check_domain.return_value = { "whois": {"status": ""}, "beian": {"has_beian": True, "beian_year": "2024", "is_enterprise": True, "beian_match": True}, "intercept": {"normal": True}, } engine.db.add_detection_result.return_value = True outcome = engine._deep_detect(1, "example.com") self.assertEqual(engine.OUTCOME_SUCCESS, outcome) persisted_args = engine.db.add_detection_result.call_args.args self.assertTrue(persisted_args[1]["status"]) self.assertTrue(persisted_args[1]["has_history"]) self.assertTrue(persisted_args[2]["status"]) self.assertTrue(persisted_args[2]["has_收录"]) self.assertIn("state", persisted_args[7]["history"]) self.assertTrue(persisted_args[8]["beian"]["status"]) def test_deep_detect_stops_early_after_blacklist_hit(self): engine = self._build_engine() engine.baidu_detector.check_history.return_value = {"has_history": True, "has_gray": True} engine.baidu_detector.check_site.return_value = {"has_收录": False, "subdomains": []} engine.db.add_detection_result.return_value = True outcome = engine._deep_detect(1, "example.com") self.assertEqual(engine.OUTCOME_BLACKLISTED, outcome) engine.qihu360_detector.check_site.assert_not_called() engine.google_detector.check_site.assert_not_called() engine.db.add_detection_result.assert_called_once() def test_deep_detect_fails_when_nested_detector_returns_error(self): engine = self._build_engine() engine.baidu_detector.check_history.return_value = {"has_history": False, "has_gray": False} engine.baidu_detector.check_site.return_value = {"has_收录": False, "subdomains": []} engine.qihu360_detector.check_site.return_value = {"has_收录": False, "subdomains": []} engine.google_detector.check_site.return_value = {"has_收录": False} engine.chinaz_detector.check_domain.return_value = {"title": "", "category": "", "has_sensitive": False} engine.aizhan_detector.check_domain.return_value = {"title": "", "risk": "", "has_sensitive": False} engine.juziseo_detector.check_domain.return_value = { "history": {"error": "HTTP 429"}, "backlink": {"has_sensitive": False, "has_subdomains": False}, } engine.jucha_detector.check_domain.return_value = {"whois": {}, "beian": {}, "intercept": {"normal": True}} outcome = engine._deep_detect(1, "example.com") self.assertEqual(engine.OUTCOME_FAILED, outcome) engine.db.add_detection_result.assert_not_called() class RDAPDetectorStatusMappingTests(unittest.TestCase): def test_check_register_status_uses_statuses_field(self): detector = RDAPDetector() with patch.object(detector, "check_domain", return_value={"statuses": ["clientHold"]}): status = detector.check_register_status("example.com") self.assertEqual(7, status) if __name__ == "__main__": unittest.main()