import os import unittest from unittest.mock import Mock, patch from dns_sync.adapters import ( CERTIFICATES, PROXY_HOSTS, Capability, CapabilityOperation, ) from dns_sync.config import AppConfig from dns_sync.registry import load_adapter class NpmAdapterTests(unittest.TestCase): def config(self): with patch.dict( os.environ, { "NPM_ENDPOINTS": "192.168.1.3,192.168.1.4", "DNS_TARGET_IP": "192.168.1.101", }, clear=True, ): return AppConfig.from_env() def adapter(self, client): config = self.config() with patch( "dns_sync.plugins.adapters.npm.NpmClient", return_value=client, ): return load_adapter( "npm", config, config.npm_endpoints[0], ) def test_registry_creates_one_adapter_per_endpoint(self): config = self.config() first_client = Mock() second_client = Mock() with patch( "dns_sync.plugins.adapters.npm.NpmClient", side_effect=[first_client, second_client], ) as client_class: adapters = [ load_adapter("npm", config, endpoint) for endpoint in config.npm_endpoints ] self.assertEqual( tuple(adapter.endpoint.identity for adapter in adapters), ( "npm://192.168.1.3:81", "npm://192.168.1.4:81", ), ) self.assertEqual(client_class.call_count, 2) self.assertIs(adapters[0].client, first_client) self.assertIs(adapters[1].client, second_client) def test_capabilities_are_discovery_only(self): adapter = self.adapter(Mock()) self.assertEqual( adapter.capabilities, frozenset( { Capability( PROXY_HOSTS, CapabilityOperation.DISCOVER, ), Capability( CERTIFICATES, CapabilityOperation.DISCOVER, ), } ), ) def test_discovers_proxy_hosts_with_endpoint_local_key(self): client = Mock() client.proxy_hosts.return_value = [ { "id": 12, "domain_names": ["app.example.com"], "forward_scheme": "http", "forward_host": "192.168.1.20", "forward_port": 8080, "certificate_id": 67, "enabled": 1, "unexpected_secret": "must-not-be-copied", } ] adapter = self.adapter(client) discovered = adapter.discover(PROXY_HOSTS) self.assertEqual(len(discovered), 1) self.assertEqual(discovered[0].object_type, PROXY_HOSTS) self.assertEqual(discovered[0].local_key, "12") self.assertEqual( discovered[0].payload["domain_names"], ["app.example.com"], ) self.assertNotIn( "unexpected_secret", discovered[0].payload, ) def test_certificate_discovery_excludes_secret_material(self): client = Mock() client.certificates.return_value = [ { "id": 67, "domain_names": ["app.example.com"], "provider": "other", "nice_name": "cluster copy", "valid_from": "2026-09-01T00:00:00Z", "expires_on": "2026-12-01T00:00:00Z", "private_key": "must-not-be-copied", "certificate": "must-not-be-copied", "meta": { "cluster_managed": True, "acme_renew": True, "dns_challenge": True, "dns_provider_credentials": "must-not-be-copied", }, } ] adapter = self.adapter(client) discovered = adapter.discover(CERTIFICATES) self.assertEqual(len(discovered), 1) certificate = discovered[0] self.assertEqual(certificate.local_key, "67") self.assertTrue(certificate.payload["cluster_managed"]) self.assertTrue(certificate.payload["acme_renew"]) self.assertNotIn("private_key", certificate.payload) self.assertNotIn("certificate", certificate.payload) self.assertNotIn( "dns_provider_credentials", certificate.payload, ) def test_unsupported_operations_fail_explicitly(self): adapter = self.adapter(Mock()) with self.assertRaisesRegex(RuntimeError, "cannot discover"): adapter.discover("unsupported_objects") with self.assertRaisesRegex(RuntimeError, "does not support apply"): adapter.apply(Mock()) with self.assertRaisesRegex(RuntimeError, "does not support verify"): adapter.verify(Mock()) if __name__ == "__main__": unittest.main()