Download tests/test_engine_cache.py from sitcod3/modul-x: direct link, hf CLI and curl.
- Browser
- Download file 8.56 kB
-
https://huggingface.co/spaces/sitcod3/modul-x/resolve/main/tests/test_engine_cache.py
- Command line
-
hf download hf://spaces/sitcod3/modul-x/tests/test_engine_cache.py
-
curl -L -o test_engine_cache.py https://huggingface.co/spaces/sitcod3/modul-x/resolve/main/tests/test_engine_cache.py
8.56 kB
| import unittest | |
| from unittest.mock import AsyncMock, patch | |
| from app.models import DNSResult, DisposableResult, SMTPResult, ValidationStatus | |
| from app.validator.engine import EmailValidationEngine | |
| def _dns(host="mx.example.test"): | |
| return DNSResult( | |
| domain_exists=True, | |
| has_mx_records=True, | |
| mx_records=[{"host": host, "priority": 10}], | |
| ) | |
| def _clean(): | |
| return DisposableResult( | |
| is_disposable=False, is_free_provider=False, is_role_based=False | |
| ) | |
| class RecordingCache: | |
| """In-memory stand-in for ValidationCache that records interactions.""" | |
| def __init__(self): | |
| self.dns: dict[str, DNSResult] = {} | |
| self.verdicts: dict[tuple[str, bool], tuple[SMTPResult, object]] = {} | |
| self.dns_puts: list[str] = [] | |
| self.verdict_puts: list[str] = [] | |
| async def get_dns(self, domain): | |
| return self.dns.get(domain.lower()) | |
| async def put_dns(self, domain, result): | |
| if result.error_code in {"timeout", "resolver_error"}: | |
| return | |
| self.dns_puts.append(domain) | |
| self.dns[domain.lower()] = result | |
| async def get_verdict(self, email, deep): | |
| return self.verdicts.get((email.lower(), deep)) | |
| async def put_verdict(self, email, deep, smtp, catch_all): | |
| if smtp.verified is None and catch_all is not True: | |
| return | |
| self.verdict_puts.append(email) | |
| self.verdicts[(email.lower(), deep)] = (smtp, catch_all) | |
| class CountingProvider: | |
| def __init__(self, smtp_map=None): | |
| self._smtp = smtp_map or {} | |
| self.batch_calls = 0 | |
| self.pairs_seen: list[tuple[str, str]] = [] | |
| async def verify_many(self, pairs): | |
| self.batch_calls += 1 | |
| self.pairs_seen.extend(pairs) | |
| return ( | |
| {e: self._smtp.get(e, SMTPResult(verified=True, smtp_code=250)) for e, _ in pairs}, | |
| {}, | |
| ) | |
| async def verify(self, email, mx_host): | |
| return self._smtp.get(email, SMTPResult(verified=True, smtp_code=250)) | |
| async def catch_all(self, domain, mx_host): | |
| return (False, 0.9) | |
| class EngineCacheTests(unittest.IsolatedAsyncioTestCase): | |
| """The batch pipeline must consult and populate the cache.""" | |
| async def test_dns_cache_hit_skips_resolution(self): | |
| cache = RecordingCache() | |
| cache.dns["corp.test"] = _dns() | |
| engine = EmailValidationEngine(mailbox=CountingProvider(), cache=cache) | |
| with ( | |
| patch( | |
| "app.validator.engine.check_dns", new=AsyncMock(return_value=_dns()) | |
| ) as dns_mock, | |
| patch("app.validator.engine.check_disposable", return_value=_clean()), | |
| ): | |
| results = await engine.validate_batch(["a@corp.test"], deep=True) | |
| self.assertEqual(dns_mock.await_count, 0, "cache hit should skip DNS") | |
| self.assertEqual(results[0].status, ValidationStatus.VALID) | |
| async def test_dns_miss_is_populated_for_next_run(self): | |
| cache = RecordingCache() | |
| engine = EmailValidationEngine(mailbox=CountingProvider(), cache=cache) | |
| with ( | |
| patch("app.validator.engine.check_dns", new=AsyncMock(return_value=_dns())), | |
| patch("app.validator.engine.check_disposable", return_value=_clean()), | |
| ): | |
| await engine.validate_batch(["a@corp.test"], deep=True) | |
| self.assertIn("corp.test", cache.dns_puts) | |
| async def test_dns_result_is_reused_across_addresses_in_one_run(self): | |
| cache = RecordingCache() | |
| engine = EmailValidationEngine(mailbox=CountingProvider(), cache=cache) | |
| with ( | |
| patch( | |
| "app.validator.engine.check_dns", new=AsyncMock(return_value=_dns()) | |
| ) as dns_mock, | |
| patch("app.validator.engine.check_disposable", return_value=_clean()), | |
| ): | |
| await engine.validate_batch( | |
| ["a@corp.test", "b@corp.test", "c@corp.test"], deep=True | |
| ) | |
| self.assertEqual(dns_mock.await_count, 1) | |
| async def test_cached_verdict_skips_the_mailbox_probe(self): | |
| cache = RecordingCache() | |
| cache.dns["corp.test"] = _dns() | |
| cache.verdicts[("a@corp.test", True)] = ( | |
| SMTPResult(verified=True, smtp_code=250), | |
| False, | |
| ) | |
| provider = CountingProvider() | |
| engine = EmailValidationEngine(mailbox=provider, cache=cache) | |
| with ( | |
| patch("app.validator.engine.check_dns", new=AsyncMock(return_value=_dns())), | |
| patch("app.validator.engine.check_disposable", return_value=_clean()), | |
| ): | |
| results = await engine.validate_batch(["a@corp.test"], deep=True) | |
| self.assertEqual(provider.batch_calls, 0, "cached verdict should skip the probe") | |
| self.assertEqual(results[0].status, ValidationStatus.VALID) | |
| self.assertTrue(results[0].smtp.verified) | |
| async def test_only_uncached_addresses_are_probed(self): | |
| cache = RecordingCache() | |
| cache.dns["corp.test"] = _dns() | |
| cache.verdicts[("a@corp.test", True)] = ( | |
| SMTPResult(verified=True, smtp_code=250), | |
| False, | |
| ) | |
| provider = CountingProvider() | |
| engine = EmailValidationEngine(mailbox=provider, cache=cache) | |
| with ( | |
| patch("app.validator.engine.check_dns", new=AsyncMock(return_value=_dns())), | |
| patch("app.validator.engine.check_disposable", return_value=_clean()), | |
| ): | |
| await engine.validate_batch(["a@corp.test", "b@corp.test"], deep=True) | |
| self.assertEqual(provider.pairs_seen, [("b@corp.test", "mx.example.test")]) | |
| async def test_definitive_verdict_is_cached(self): | |
| cache = RecordingCache() | |
| engine = EmailValidationEngine( | |
| mailbox=CountingProvider( | |
| {"a@corp.test": SMTPResult(verified=False, smtp_code=550, | |
| smtp_message="550 no such user")} | |
| ), | |
| cache=cache, | |
| ) | |
| with ( | |
| patch("app.validator.engine.check_dns", new=AsyncMock(return_value=_dns())), | |
| patch("app.validator.engine.check_disposable", return_value=_clean()), | |
| ): | |
| await engine.validate_batch(["a@corp.test"], deep=True) | |
| self.assertIn("a@corp.test", cache.verdict_puts) | |
| async def test_inconclusive_verdict_is_not_cached(self): | |
| cache = RecordingCache() | |
| engine = EmailValidationEngine( | |
| mailbox=CountingProvider( | |
| {"a@corp.test": SMTPResult(verified=None, error="transport_error")} | |
| ), | |
| cache=cache, | |
| ) | |
| with ( | |
| patch("app.validator.engine.check_dns", new=AsyncMock(return_value=_dns())), | |
| patch("app.validator.engine.check_disposable", return_value=_clean()), | |
| ): | |
| await engine.validate_batch(["a@corp.test"], deep=True) | |
| self.assertNotIn("a@corp.test", cache.verdict_puts) | |
| async def test_cached_row_still_reports_all_seven_layers(self): | |
| cache = RecordingCache() | |
| cache.dns["corp.test"] = _dns() | |
| cache.verdicts[("a@corp.test", True)] = ( | |
| SMTPResult(verified=True, smtp_code=250), | |
| False, | |
| ) | |
| engine = EmailValidationEngine(mailbox=CountingProvider(), cache=cache) | |
| with ( | |
| patch("app.validator.engine.check_dns", new=AsyncMock(return_value=_dns())), | |
| patch("app.validator.engine.check_disposable", return_value=_clean()), | |
| ): | |
| results = await engine.validate_batch(["a@corp.test"], deep=True) | |
| self.assertEqual( | |
| results[0].checks_run, | |
| ["syntax", "dns", "mx", "disposable", "catch_all", "smtp", "scoring"], | |
| ) | |
| async def test_fast_mode_does_not_read_deep_verdicts(self): | |
| cache = RecordingCache() | |
| cache.dns["corp.test"] = _dns() | |
| cache.verdicts[("a@corp.test", True)] = ( | |
| SMTPResult(verified=True, smtp_code=250), | |
| False, | |
| ) | |
| engine = EmailValidationEngine(mailbox=CountingProvider(), cache=cache) | |
| with ( | |
| patch("app.validator.engine.check_dns", new=AsyncMock(return_value=_dns())), | |
| patch("app.validator.engine.check_disposable", return_value=_clean()), | |
| ): | |
| results = await engine.validate_batch(["a@corp.test"], deep=False) | |
| # Fast mode skips the mailbox layers entirely, so it must not surface a | |
| # deep verdict as if it had probed. | |
| self.assertIsNone(results[0].smtp) | |
| if __name__ == "__main__": | |
| unittest.main() |