modul-x / tests /test_mx_batch.py
sitcod3's picture
Upload folder using huggingface_hub
03c7b2c verified
Raw History Blame Contribute Delete
10.2 kB
import unittest
from app.validator.mx_batch import CatchAllCache, MxBatchVerifier
from app.validator.smtp_client import RcptOutcome
class FakeConnection:
"""Records probes and answers from a scripted map.
Keyed by lowercased address; a "*" entry is the default answer, which is how
a catch-all domain behaves (accepts everything).
"""
registry: dict[str, dict] = {}
opened: list[str] = []
closed: list[int] = []
def __init__(self, host, **kwargs):
self.host = host
self.answers = FakeConnection.registry.get(host, {})
self.ready = True
self.opened.append(host)
def open(self):
self.ready = True
return True
def probe(self, email):
# Catch-all probe uses a gibberish local part -> "*" rule applies.
answer = self.answers.get(email.lower(), self.answers.get("*"))
if answer is None:
return RcptOutcome(code=550, message="550 5.1.1 no such user")
if isinstance(answer, RcptOutcome):
return answer
return RcptOutcome(code=answer, message=f"{answer} response")
def quit(self):
FakeConnection.closed.append(1)
def close(self):
pass
class MxBatchVerifierTests(unittest.TestCase):
def setUp(self):
FakeConnection.registry = {}
FakeConnection.opened = []
FakeConnection.closed = []
def verifier(self, **kw):
return MxBatchVerifier(
connection_factory=FakeConnection,
max_connections_per_host=kw.pop("max_connections_per_host", 2),
**kw,
)
def test_groups_by_mx_and_opens_one_session_per_host(self):
# google host: two addresses; corp host: one address
FakeConnection.registry = {
"gmail-smtp-in.l.google.com": {"a@gmail.com": 250, "b@gmail.com": 550, "*": 550},
"mx.corp.test": {"c@corp.test": 250, "*": 550},
}
v = self.verifier()
out = v.verify_batch(
[
("a@gmail.com", "gmail-smtp-in.l.google.com"),
("b@gmail.com", "gmail-smtp-in.l.google.com"),
("c@corp.test", "mx.corp.test"),
]
)
self.assertEqual(out["a@gmail.com"].verified, True)
self.assertEqual(out["b@gmail.com"].verified, False)
self.assertEqual(out["c@corp.test"].verified, True)
# One connection opened per MX host (not per address).
self.assertEqual(sorted(FakeConnection.opened), ["gmail-smtp-in.l.google.com", "mx.corp.test"])
def test_catch_all_domain_does_not_certify_mailbox(self):
# Microsoft 365 behaviour: accepts everything at RCPT.
FakeConnection.registry = {
"x.mail.protection.outlook.com": {"*": 250},
}
v = self.verifier()
out = v.verify_batch([("anyone@contoso.com", "x.mail.protection.outlook.com")])
result = out["anyone@contoso.com"]
# Accepted by an accept-all host, so NOT verified.
self.assertIsNone(result.verified)
self.assertIn("catch_all", (result.error or "").lower())
def test_catch_all_probe_is_cached_per_domain(self):
FakeConnection.registry = {"mx.corp.test": {"*": 250}}
v = self.verifier()
v.verify_batch([("a@corp.test", "mx.corp.test")])
probes_after_first = len(FakeConnection.opened)
# Second address in the same domain must reuse the cached catch-all flag.
v.verify_batch([("b@corp.test", "mx.corp.test")])
self.assertEqual(len(FakeConnection.opened), probes_after_first)
def test_transport_error_marks_unknown_not_invalid(self):
class DeadConn(FakeConnection):
def open(self):
self.ready = False
return False
FakeConnection.registry = {"mx.dead.test": {"*": 250}}
v = MxBatchVerifier(connection_factory=DeadConn)
out = v.verify_batch([("a@dead.test", "mx.dead.test")])
r = out["a@dead.test"]
self.assertIsNone(r.verified)
self.assertIsNotNone(r.error)
def test_greylisted_address_is_reported_retryable(self):
FakeConnection.registry = {
"mx.corp.test": {"a@corp.test": RcptOutcome(code=451, message="451 greylisted"), "*": 550}
}
v = self.verifier()
out = v.verify_batch([("a@corp.test", "mx.corp.test")])
self.assertIsNone(out["a@corp.test"].verified)
def test_empty_batch_returns_empty(self):
v = self.verifier()
self.assertEqual(v.verify_batch([]), {})
class CatchAllCacheTests(unittest.TestCase):
def test_stores_and_returns_within_ttl(self):
c = CatchAllCache(ttl_seconds=60)
c.put("corp.test", True)
self.assertIs(c.get("corp.test"), True)
def test_expires_after_ttl(self):
c = CatchAllCache(ttl_seconds=0)
c.put("corp.test", True)
self.assertIsNone(c.get("corp.test"))
def test_unknown_is_not_cached_as_false(self):
c = CatchAllCache(ttl_seconds=60)
c.put("corp.test", None)
# None (probe failed) must not be cached as a decision.
self.assertIsNone(c.get("corp.test"))
def test_distinguishes_false_from_missing(self):
c = CatchAllCache(ttl_seconds=60)
c.put("strict.test", False)
self.assertIs(c.get("strict.test"), False)
self.assertIsNone(c.get("never-seen.test"))
class EgressCircuitBreakerTests(unittest.TestCase):
"""When outbound port 25 is blocked (HF Spaces, most PaaS) every connect
burns the full timeout. A bulk job must detect that once and fail fast
instead of spending timeout × N — 10k rows at 10s/row is ~27 hours."""
def setUp(self):
FakeConnection.registry = {}
FakeConnection.opened = []
def test_circuit_opens_after_threshold_and_stops_connecting(self):
class DeadConn(FakeConnection):
def open(self):
self.ready = False
return False
FakeConnection.registry = {"mx.a.test": {"*": 250}}
v = MxBatchVerifier(connection_factory=DeadConn, fail_threshold=2, cooldown_seconds=60)
v.verify_batch([("a@a.test", "mx.a.test")])
v.verify_batch([("b@b.test", "mx.b.test")])
self.assertTrue(v.circuit_open)
before = len(FakeConnection.opened)
out = v.verify_batch([("c@c.test", "mx.c.test")])
# No new connection attempted while the circuit is open.
self.assertEqual(len(FakeConnection.opened), before)
self.assertIsNone(out["c@c.test"].verified)
self.assertIn("unreachable", (out["c@c.test"].error or "").lower())
def test_successful_connection_resets_failure_count(self):
class FlakyConn(FakeConnection):
fail_next = True
def open(self):
if FlakyConn.fail_next:
self.ready = False
return False
self.ready = True
return True
FakeConnection.registry = {"mx.a.test": {"*": 250}}
v = MxBatchVerifier(connection_factory=FlakyConn, fail_threshold=2, cooldown_seconds=60)
v.verify_batch([("a@a.test", "mx.a.test")])
FlakyConn.fail_next = False
v.verify_batch([("b@b.test", "mx.b.test")])
self.assertFalse(v.circuit_open)
def test_default_threshold_is_conservative(self):
v = MxBatchVerifier(connection_factory=FakeConnection)
self.assertGreaterEqual(v.fail_threshold, 2)
class ParallelDomainTests(unittest.IsolatedAsyncioTestCase):
"""Domains must be probed CONCURRENTLY (with a cap).
Sequential probing means wall-clock = sum of every domain's latency; on a
blocked host that is timeout x domains. Commercial validators get bulk
throughput from parallelism across domains, not speed per domain.
"""
async def test_domains_are_probed_in_parallel(self):
import asyncio
import time
active = {"now": 0, "peak": 0}
class SlowConn(FakeConnection):
def open(self):
active["now"] += 1
active["peak"] = max(active["peak"], active["now"])
time.sleep(0.25)
active["now"] -= 1
self.ready = True
return True
FakeConnection.registry = {f"mx{i}.test": {"*": 550} for i in range(8)}
v = MxBatchVerifier(
connection_factory=SlowConn, bulk_concurrency=8, max_connections_per_host=1
)
pairs = [(f"u{i}@dom{i}.test", f"mx{i}.test") for i in range(8)]
t0 = time.time()
out = await v.verify_batch_async(pairs)
elapsed = time.time() - t0
self.assertEqual(len(out), 8)
# 8 x 0.25s sequential = 2.0s; parallel should be well under 1s.
self.assertLess(elapsed, 1.2, f"domains ran sequentially ({elapsed:.2f}s)")
self.assertGreater(active["peak"], 2, "no real concurrency observed")
async def test_concurrency_cap_is_respected(self):
import time
active = {"now": 0, "peak": 0}
class SlowConn(FakeConnection):
def open(self):
active["now"] += 1
active["peak"] = max(active["peak"], active["now"])
time.sleep(0.15)
active["now"] -= 1
self.ready = True
return True
FakeConnection.registry = {f"mx{i}.test": {"*": 550} for i in range(10)}
v = MxBatchVerifier(
connection_factory=SlowConn, bulk_concurrency=3, max_connections_per_host=1
)
pairs = [(f"u{i}@dom{i}.test", f"mx{i}.test") for i in range(10)]
out = await v.verify_batch_async(pairs)
self.assertEqual(len(out), 10)
self.assertLessEqual(active["peak"], 3, "exceeded the concurrency cap")
async def test_sync_verify_batch_still_works(self):
FakeConnection.registry = {"mx.corp.test": {"a@corp.test": 250, "*": 550}}
v = MxBatchVerifier(connection_factory=FakeConnection)
out = v.verify_batch([("a@corp.test", "mx.corp.test")])
self.assertTrue(out["a@corp.test"].verified)
if __name__ == "__main__":
unittest.main()