Download tests/test_ml_mx_gate.py from sitcod3/modul-x: direct link, hf CLI and curl.
- Browser
- Download file 3.37 kB
-
https://huggingface.co/spaces/sitcod3/modul-x/resolve/main/tests/test_ml_mx_gate.py
- Command line
-
hf download hf://spaces/sitcod3/modul-x/tests/test_ml_mx_gate.py
-
curl -L -o test_ml_mx_gate.py https://huggingface.co/spaces/sitcod3/modul-x/resolve/main/tests/test_ml_mx_gate.py
3.37 kB
| import unittest | |
| from unittest.mock import AsyncMock, MagicMock, patch | |
| from app.models import DNSResult, ValidationStatus | |
| from app.validator.engine import EmailValidationEngine | |
| def _dns_with_mx(host): | |
| return DNSResult( | |
| domain_exists=True, | |
| has_mx_records=True, | |
| mx_records=[{"host": host, "priority": 10}], | |
| ) | |
| def _clean(): | |
| from app.models import DisposableResult | |
| return DisposableResult( | |
| is_disposable=False, is_free_provider=False, is_role_based=False | |
| ) | |
| class NullCache: | |
| async def get_dns(self, domain): | |
| return None | |
| async def put_dns(self, domain, result): | |
| return None | |
| async def get_verdict(self, email, deep): | |
| return None | |
| async def put_verdict(self, email, deep, smtp, catch_all): | |
| return None | |
| class FakeProvider: | |
| async def verify_many(self, pairs): | |
| return ({e: SMTPResult(verified=True, smtp_code=250) for e, _ in pairs}, {}) | |
| async def verify(self, email, mx_host): | |
| return SMTPResult(verified=True, smtp_code=250) | |
| async def catch_all(self, domain, mx_host): | |
| return (False, 0.9) | |
| def _fake_model(answer): | |
| m = MagicMock() | |
| m.predict = MagicMock(return_value=[("DISPOSABLE", 0.99)]) | |
| return m | |
| class TrustedMxGateTests(unittest.IsolatedAsyncioTestCase): | |
| """A domain running on a trusted enterprise mail platform (Google Workspace, | |
| M365, ...) is almost never disposable — disposable domains live on cheap | |
| hosts. The ML model learned 'unfamiliar domain string = disposable' from an | |
| imbalanced dataset, so for trusted-MX domains the model's opinion must be | |
| ignored: the deterministic MX intelligence is the better signal.""" | |
| def setUp(self): | |
| self.patchers = [ | |
| patch("app.validator.engine.get_model_async", | |
| new=AsyncMock(return_value=_fake_model("DISPOSABLE"))), | |
| ] | |
| for p in self.patchers: | |
| p.start() | |
| def tearDown(self): | |
| for p in self.patchers: | |
| p.stop() | |
| def _engine(self): | |
| return EmailValidationEngine(mailbox=FakeProvider(), cache=NullCache()) | |
| async def test_google_workspace_mx_skips_ml_single(self): | |
| engine = self._engine() | |
| with patch("app.validator.engine.check_dns", | |
| new=AsyncMock(return_value=_dns_with_mx("aspmx.l.google.com"))): | |
| result = await engine.validate("info@thefreshfactory.co", deep=True) | |
| self.assertIsNone(result.disposable.ml_label) | |
| self.assertNotIn( | |
| "ML_DISPOSABLE_SUSPECT", | |
| {k for k in result.scoring.breakdown}, | |
| ) | |
| async def test_unknown_mx_still_gets_ml_signal(self): | |
| engine = self._engine() | |
| with patch("app.validator.engine.check_dns", | |
| new=AsyncMock(return_value=_dns_with_mx("mx.sketchy-host.test"))): | |
| result = await engine.validate("a@sketchy.test", deep=True) | |
| self.assertEqual(result.disposable.ml_label, "DISPOSABLE") | |
| async def test_m365_mx_skips_ml_bulk(self): | |
| engine = self._engine() | |
| with patch( | |
| "app.validator.engine.check_dns", | |
| new=AsyncMock(return_value=_dns_with_mx("x.mail.protection.outlook.com")), | |
| ): | |
| results = await engine.validate_batch(["a@contoso.com"], deep=True) | |
| self.assertIsNone(results[0].disposable.ml_label) |