import unittest from app.validator.mx_intel import ( MxPolicy, classify_mx, parse_rcpt_code, is_greylist_code, is_permanent_reject, should_retry, ) class ClassifyMxTests(unittest.TestCase): """Recognising the receiving provider from its MX host lets the engine apply the right policy: Google rejects unknown mailboxes at RCPT (trust it), Microsoft 365 accepts everything and bounces later (never trust a 250).""" def test_google_mx_is_strict(self): for host in ( "gmail-smtp-in.l.google.com", "alt1.aspmx.l.google.com", "aspmx.l.google.com", ): p = classify_mx(host) self.assertEqual(p.provider, "google", host) self.assertTrue(p.trusts_rcpt, host) self.assertFalse(p.accepts_all, host) def test_microsoft365_is_accept_all(self): for host in ( "example-com.mail.protection.outlook.com", "mail.protection.outlook.com", ): p = classify_mx(host) self.assertEqual(p.provider, "microsoft365", host) self.assertTrue(p.accepts_all, host) self.assertFalse(p.trusts_rcpt, host) def test_mimecast_and_proofpoint_are_accept_all_gateways(self): for host in ( "us-smtp-inbound-1.mimecast.com", "mx0a-001b2d01.pphosted.com", ): p = classify_mx(host) self.assertTrue(p.accepts_all, host) self.assertFalse(p.trusts_rcpt, host) def test_yahoo_is_strict(self): p = classify_mx("mta5.am0.yahoodns.net") self.assertEqual(p.provider, "yahoo") self.assertTrue(p.trusts_rcpt) def test_unknown_host_defaults_to_neutral(self): p = classify_mx("mx.example-corp.test") self.assertEqual(p.provider, "unknown") self.assertFalse(p.accepts_all) # Unknown providers are not assumed untrustworthy, but a 250 alone does # not prove the mailbox exists — the catch-all probe decides that. self.assertFalse(p.trusts_rcpt) def test_case_and_trailing_dot_insensitive(self): self.assertEqual(classify_mx("ASPMX.L.GOOGLE.COM.").provider, "google") class RcptCodeTests(unittest.TestCase): def test_250_is_accept(self): self.assertEqual(parse_rcpt_code("250 2.1.5 Ok"), 250) def test_550_is_reject(self): self.assertEqual(parse_rcpt_code("550 5.1.1 The email account does not exist"), 550) def test_non_numeric_returns_none(self): self.assertIsNone(parse_rcpt_code("garbage")) def test_4xx_is_greylist(self): self.assertTrue(is_greylist_code(421)) self.assertTrue(is_greylist_code(450)) self.assertTrue(is_greylist_code(451)) self.assertFalse(is_greylist_code(250)) self.assertFalse(is_greylist_code(550)) def test_5xx_is_permanent(self): self.assertTrue(is_permanent_reject(550)) self.assertTrue(is_permanent_reject(551)) self.assertFalse(is_permanent_reject(450)) self.assertFalse(is_permanent_reject(None)) def test_retry_policy_only_for_greylist(self): self.assertTrue(should_retry(450)) self.assertTrue(should_retry(451)) self.assertFalse(should_retry(550)) self.assertFalse(should_retry(250)) self.assertFalse(should_retry(None)) def test_connection_error_retries(self): # A transport failure is transient and worth one retry. self.assertTrue(should_retry(None, transport_error=True)) class MxPolicyTests(unittest.TestCase): def test_policy_is_immutable_and_descriptive(self): p = MxPolicy(provider="google", trusts_rcpt=True, accepts_all=False) self.assertEqual(p.provider, "google") with self.assertRaises(Exception): p.provider = "other" # type: ignore[misc] if __name__ == "__main__": unittest.main()