import asyncio import unittest from unittest.mock import patch, MagicMock import hbd.server.dns as dns class TestDNS(unittest.TestCase): def test_create_nsupdate_payload_ipv4(self): p = dns.create_nsupdate_payload("host", "1.2.3.4", "example") self.assertIn("update add host.dy.example 5 A 1.2.3.4", p) self.assertNotIn("AAAA", p) def test_create_nsupdate_payload_ipv6(self): p = dns.create_nsupdate_payload("host", "2001:db8::1", "example") self.assertIn("update add host.dy.example 5 AAAA 2001:db8::1", p) @patch("hbd.server.dns.Popen") def test_nsupdate_success(self, mock_popen): proc = MagicMock() proc.communicate.return_value = (b"status: NOERROR", None) mock_popen.return_value = proc err = dns.nsupdate( "host", "1.2.3.4", "example", nsupdate_bin="/usr/bin/nsupdate", rndc_key="/etc/rndc.key", ) self.assertIsNone(err) mock_popen.assert_called_once_with( ["/usr/bin/nsupdate", "-k", "/etc/rndc.key", "-v"], shell=False, bufsize=0, stdin=dns.PIPE, stdout=dns.PIPE, stderr=dns.STDOUT, ) @patch("hbd.server.dns.Popen") def test_nsupdate_failure(self, mock_popen): proc = MagicMock() proc.communicate.return_value = (b"some error", None) mock_popen.return_value = proc err = dns.nsupdate( "host", "1.2.3.4", "example", nsupdate_bin="/usr/bin/nsupdate", rndc_key="/etc/rndc.key", ) self.assertIsNotNone(err) self.assertIn("some error", err) @patch("hbd.server.dns.Popen") def test_nsupdate_raises_oserror(self, mock_popen): mock_popen.side_effect = OSError("noexec") err = dns.nsupdate( "h", "1.2.3.4", "example", nsupdate_bin="/usr/bin/nsupdate", rndc_key="/etc/rndc.key", ) self.assertIsNotNone(err) self.assertIn("execution failed", err) class TestDNSWorker(unittest.IsolatedAsyncioTestCase): async def _run_worker(self, nsupdate_result): """Feed one update through dns_update_worker; returns the (name, level, message) log calls.""" logs = [] queue = asyncio.Queue() await queue.put(("testhost", "1.2.3.4")) await queue.put(None) with patch("hbd.server.dns.nsupdate", return_value=nsupdate_result): await dns.dns_update_worker( None, {"dyndomains": ["example"]}, async_queue=queue, log=lambda name, level, msg: logs.append((name, level, msg)), ) return logs async def test_worker_logs_successful_update(self): logs = await self._run_worker(None) self.assertEqual(len(logs), 1) name, level, msg = logs[0] self.assertEqual((name, level), ("testhost", "INFO")) self.assertIn("DNS updated testhost.dy.example", msg) self.assertIn("1.2.3.4", msg) async def test_worker_logs_error_on_failed_update(self): logs = await self._run_worker("error: failed") self.assertEqual(len(logs), 1) name, level, msg = logs[0] self.assertEqual((name, level), ("testhost", "ERROR")) self.assertIn("DNS update failed", msg) self.assertIn("error: failed", msg) if __name__ == "__main__": unittest.main()