From 3af1c29a16ecbfc8198f3eced3453cde84c06a1c Mon Sep 17 00:00:00 2001 From: Christoph Date: Tue, 20 Oct 2026 12:36:45 +0200 Subject: [PATCH] Add offline SPF regression tests for SRS DNS preflight --- tests/test_srs_dns.py | 106 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 tests/test_srs_dns.py diff --git a/tests/test_srs_dns.py b/tests/test_srs_dns.py new file mode 100644 index 0000000..ed59611 --- /dev/null +++ b/tests/test_srs_dns.py @@ -0,0 +1,106 @@ +#!/usr/bin/env python3 +"""Offline regression tests for the selective SRS DNS preflight helper. + +All DNS responses are mocked. No live DNS queries, Postfix changes, +or SRS installation operations are performed. +""" + +import contextlib +import io +import runpy +import unittest +from pathlib import Path +from unittest.mock import patch + + +ROOT = Path(__file__).resolve().parents[1] +HELPER = ROOT / "helpers/srs-dns-check.py" + +module = runpy.run_path(str(HELPER)) +check_server = module["check_server"] + + +class TestSrsDnsSpf(unittest.TestCase): + + DOMAIN = "srs.example.org" + HOSTNAME = "mail.example.org" + IPV4 = "192.0.2.25" + IPV6 = "2001:db8::25" + + def check_with_spf(self, txt_records): + answers = { + (self.DOMAIN, "TXT"): txt_records, + (self.DOMAIN, "MX"): [ + f"10 {self.HOSTNAME}." + ], + (self.HOSTNAME, "A"): [ + self.IPV4 + ], + (self.HOSTNAME, "AAAA"): [ + self.IPV6 + ], + } + + def fake_dig(server, name, kind, authoritative=False): + self.assertEqual(server, "192.0.2.53") + self.assertTrue( + authoritative, + "DNS checks must query authoritative nameservers" + ) + return answers.get((name, kind), []) + + output = io.StringIO() + + with patch.dict( + check_server.__globals__, + {"dig": fake_dig} + ): + with contextlib.redirect_stdout(output): + result = check_server( + "ns.example.org", + "192.0.2.53", + self.DOMAIN, + self.HOSTNAME, + self.IPV4, + self.IPV6, + ) + + return result, output.getvalue() + + def test_valid_spf(self): + result, output = self.check_with_spf( + ['"v=spf1 mx -all"'] + ) + + self.assertEqual(result, (True, True)) + self.assertIn("[ ok ]", output) + + def test_missing_spf(self): + result, output = self.check_with_spf([]) + + self.assertEqual(result, (False, False)) + self.assertIn("SPF is not the approved", output) + self.assertIn("[ failed ]", output) + + def test_invalid_spf(self): + result, output = self.check_with_spf( + ['"v=spf1 -all"'] + ) + + self.assertEqual(result, (False, False)) + self.assertIn("SPF is not the approved", output) + self.assertIn("[ failed ]", output) + + def test_multiple_spf_records(self): + result, output = self.check_with_spf([ + '"v=spf1 mx -all"', + '"v=spf1 ip4:192.0.2.1 -all"', + ]) + + self.assertEqual(result, (False, False)) + self.assertIn("SPF is not the approved", output) + self.assertIn("[ failed ]", output) + + +if __name__ == "__main__": + unittest.main(verbosity=2)