171 lines
8.7 KiB
Python
171 lines
8.7 KiB
Python
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()
|