Download tests/test_engine_metadata.py from sitcod3/modul-x: direct link, hf CLI and curl.
- Browser
- Download file 10.4 kB
-
https://huggingface.co/spaces/sitcod3/modul-x/resolve/main/tests/test_engine_metadata.py
- Command line
-
hf download hf://spaces/sitcod3/modul-x/tests/test_engine_metadata.py
-
curl -L -o test_engine_metadata.py https://huggingface.co/spaces/sitcod3/modul-x/resolve/main/tests/test_engine_metadata.py
10.4 kB
| import unittest | |
| from unittest.mock import AsyncMock, patch | |
| from app.models import ( | |
| CatchAllResult, | |
| DNSResult, | |
| DisposableResult, | |
| ScoringResult, | |
| SMTPResult, | |
| SyntaxResult, | |
| ValidationStatus, | |
| ) | |
| from app.config import settings | |
| from app.validator.engine import EmailValidationEngine | |
| def _syntax(**kw): | |
| base = dict( | |
| passed=True, | |
| normalized_email="person@example.test", | |
| domain="example.test", | |
| local_part="person", | |
| ) | |
| base.update(kw) | |
| return SyntaxResult(**base) | |
| def _dns(**kw): | |
| base = dict( | |
| domain_exists=True, | |
| has_mx_records=True, | |
| mx_records=[{"host": "mx.example.test", "priority": 10}], | |
| ) | |
| base.update(kw) | |
| return DNSResult(**base) | |
| def _disposable(**kw): | |
| base = dict(is_disposable=False, is_free_provider=False, is_role_based=False) | |
| base.update(kw) | |
| return DisposableResult(**base) | |
| class FakeMailbox: | |
| """Injectable mailbox provider — controls the Layer 5/6 verdicts exactly. | |
| Injecting the provider (instead of patching module internals) keeps these | |
| tests independent of which concrete provider the engine resolves by default. | |
| """ | |
| def __init__(self, smtp=None, catch_all=(False, 0.9), raises=None): | |
| self._smtp = smtp if smtp is not None else SMTPResult(verified=True, smtp_code=250) | |
| self._catch_all = catch_all | |
| self._raises = raises | |
| async def verify(self, email, mx_host): | |
| if self._raises: | |
| raise self._raises | |
| return self._smtp | |
| async def catch_all(self, domain, mx_host): | |
| if self._raises: | |
| raise self._raises | |
| return self._catch_all | |
| def _engine_with(smtp=None, catch_all=(False, 0.9)): | |
| return EmailValidationEngine( | |
| mailbox=FakeMailbox(smtp=smtp, catch_all=catch_all) | |
| ) | |
| class EngineMetadataTests(unittest.IsolatedAsyncioTestCase): | |
| """The engine must emit the provenance fields the frontend contract expects: | |
| checks_run, reason_code, validator_version, run_id (RC5).""" | |
| async def test_deep_result_emits_provenance_fields(self): | |
| engine = _engine_with() | |
| with ( | |
| patch("app.validator.engine.validate_syntax", return_value=_syntax()), | |
| patch("app.validator.engine.check_dns", new=AsyncMock(return_value=_dns())), | |
| patch("app.validator.engine.check_disposable", return_value=_disposable()), | |
| ): | |
| result = await engine.validate("person@example.test", deep=True) | |
| self.assertEqual( | |
| result.checks_run, | |
| ["syntax", "dns", "mx", "disposable", "catch_all", "smtp", "scoring"], | |
| ) | |
| self.assertEqual(result.validator_version, settings.app_version) | |
| self.assertTrue(result.run_id) | |
| self.assertGreater(len(result.run_id), 5) | |
| async def test_fast_mode_checks_run_excludes_mailbox_layers(self): | |
| engine = _engine_with() | |
| with ( | |
| patch("app.validator.engine.validate_syntax", return_value=_syntax()), | |
| patch("app.validator.engine.check_dns", new=AsyncMock(return_value=_dns())), | |
| patch("app.validator.engine.check_disposable", return_value=_disposable()), | |
| ): | |
| result = await engine.validate("person@example.test", deep=False) | |
| self.assertEqual( | |
| result.checks_run, ["syntax", "dns", "mx", "disposable", "scoring"] | |
| ) | |
| self.assertIsNone(result.smtp) | |
| self.assertIsNone(result.catch_all) | |
| async def test_reason_code_dns_timeout(self): | |
| engine = _engine_with() | |
| with ( | |
| patch("app.validator.engine.validate_syntax", return_value=_syntax()), | |
| patch( | |
| "app.validator.engine.check_dns", | |
| new=AsyncMock( | |
| return_value=_dns( | |
| domain_exists=False, | |
| has_mx_records=False, | |
| error="DNS timeout", | |
| error_code="timeout", | |
| ) | |
| ), | |
| ), | |
| ): | |
| result = await engine.validate("person@example.test", deep=True) | |
| self.assertEqual(result.reason_code, "dns_timeout") | |
| async def test_reason_code_nxdomain(self): | |
| engine = _engine_with() | |
| with ( | |
| patch("app.validator.engine.validate_syntax", return_value=_syntax()), | |
| patch( | |
| "app.validator.engine.check_dns", | |
| new=AsyncMock( | |
| return_value=_dns( | |
| domain_exists=False, | |
| has_mx_records=False, | |
| error="Domain not found", | |
| error_code="nxdomain", | |
| ) | |
| ), | |
| ), | |
| ): | |
| result = await engine.validate("person@example.test", deep=True) | |
| self.assertEqual(result.reason_code, "nxdomain") | |
| async def test_reason_code_catch_all(self): | |
| engine = _engine_with( | |
| smtp=SMTPResult(verified=True, smtp_code=250), catch_all=(True, 0.95) | |
| ) | |
| with ( | |
| patch("app.validator.engine.validate_syntax", return_value=_syntax()), | |
| patch("app.validator.engine.check_dns", new=AsyncMock(return_value=_dns())), | |
| patch("app.validator.engine.check_disposable", return_value=_disposable()), | |
| ): | |
| result = await engine.validate("person@example.test", deep=True) | |
| self.assertEqual(result.reason_code, "catch_all_detected") | |
| self.assertEqual(result.status, ValidationStatus.RISKY) | |
| async def test_explicit_5xx_rejection_outranks_catch_all(self): | |
| # A definitive "mailbox does not exist" (5xx) is stronger and more | |
| # actionable than the domain-level catch-all flag. | |
| engine = _engine_with( | |
| smtp=SMTPResult( | |
| verified=False, | |
| smtp_code=550, | |
| smtp_message="550 mailbox does not exist (provider confirmed)", | |
| ), | |
| catch_all=(True, 0.95), | |
| ) | |
| with ( | |
| patch("app.validator.engine.validate_syntax", return_value=_syntax()), | |
| patch("app.validator.engine.check_dns", new=AsyncMock(return_value=_dns())), | |
| patch("app.validator.engine.check_disposable", return_value=_disposable()), | |
| ): | |
| result = await engine.validate("person@example.test", deep=True) | |
| self.assertEqual(result.reason_code, "rcpt_5xx") | |
| self.assertEqual(result.scoring.score, 0) | |
| self.assertEqual(result.status, ValidationStatus.INVALID) | |
| async def test_reason_code_smtp_timeout(self): | |
| engine = _engine_with( | |
| smtp=SMTPResult(verified=None, error="SMTP verification timed out"), | |
| catch_all=(None, 0.0), | |
| ) | |
| with ( | |
| patch("app.validator.engine.validate_syntax", return_value=_syntax()), | |
| patch("app.validator.engine.check_dns", new=AsyncMock(return_value=_dns())), | |
| patch("app.validator.engine.check_disposable", return_value=_disposable()), | |
| ): | |
| result = await engine.validate("person@example.test", deep=True) | |
| self.assertEqual(result.reason_code, "smtp_timeout") | |
| class CatchAllUnknownTests(unittest.IsolatedAsyncioTestCase): | |
| """An unreachable catch-all probe must read as UNKNOWN (None), never as | |
| False ("domain rejects unknown recipients"). RC6.""" | |
| async def test_catch_all_probe_unavailable_is_unknown_not_false(self): | |
| engine = _engine_with( | |
| smtp=SMTPResult(verified=None, error="SMTP verification timed out"), | |
| catch_all=(None, 0.0), | |
| ) | |
| with ( | |
| patch("app.validator.engine.validate_syntax", return_value=_syntax()), | |
| patch("app.validator.engine.check_dns", new=AsyncMock(return_value=_dns())), | |
| patch("app.validator.engine.check_disposable", return_value=_disposable()), | |
| ): | |
| result = await engine.validate("person@example.test", deep=True) | |
| self.assertIsNone(result.catch_all.is_catch_all) | |
| self.assertEqual(result.catch_all.confidence, 0.0) | |
| async def test_catch_all_exception_is_unknown_not_false(self): | |
| engine = EmailValidationEngine( | |
| mailbox=FakeMailbox(raises=RuntimeError("port 25 unreachable")) | |
| ) | |
| with ( | |
| patch("app.validator.engine.validate_syntax", return_value=_syntax()), | |
| patch("app.validator.engine.check_dns", new=AsyncMock(return_value=_dns())), | |
| patch("app.validator.engine.check_disposable", return_value=_disposable()), | |
| ): | |
| result = await engine.validate("person@example.test", deep=True) | |
| self.assertIsNone(result.catch_all.is_catch_all) | |
| class ScoreCoherenceTests(unittest.IsolatedAsyncioTestCase): | |
| """A perfect score (100/low) must only appear when a mailbox verdict exists.""" | |
| async def test_smtp_verified_can_reach_high_score(self): | |
| engine = _engine_with( | |
| smtp=SMTPResult(verified=True, smtp_code=250), catch_all=(False, 0.9) | |
| ) | |
| with ( | |
| patch("app.validator.engine.validate_syntax", return_value=_syntax()), | |
| patch("app.validator.engine.check_dns", new=AsyncMock(return_value=_dns())), | |
| patch( | |
| "app.validator.engine.check_disposable", | |
| return_value=_disposable(is_free_provider=False), | |
| ), | |
| ): | |
| result = await engine.validate("person@example.test", deep=True) | |
| self.assertGreaterEqual(result.scoring.score, 75) | |
| self.assertEqual(result.scoring.risk_level.value, "low") | |
| self.assertEqual(result.status, ValidationStatus.VALID) | |
| async def test_unverified_mailbox_never_scores_100(self): | |
| engine = _engine_with( | |
| smtp=SMTPResult(verified=None, error="SMTP verification timed out"), | |
| catch_all=(None, 0.0), | |
| ) | |
| with ( | |
| patch("app.validator.engine.validate_syntax", return_value=_syntax()), | |
| patch("app.validator.engine.check_dns", new=AsyncMock(return_value=_dns())), | |
| patch("app.validator.engine.check_disposable", return_value=_disposable()), | |
| ): | |
| result = await engine.validate("person@example.test", deep=True) | |
| self.assertLess(result.scoring.score, 75) | |
| self.assertNotEqual(result.scoring.risk_level.value, "low") | |
| if __name__ == "__main__": | |
| unittest.main() |