"""Unit tests for FritzBoxDriver — no real device required.""" from unittest.mock import MagicMock, patch import pytest from fritzconnection.core.exceptions import ( FritzActionError, FritzArrayIndexError, FritzServiceError, ) from napalm.base.exceptions import ConnectionClosedException, ConnectionException from napalm_fritzbox.fritzbox import FritzBoxDriver, _to_float # --------------------------------------------------------------------------- # Sample TR-064 responses # --------------------------------------------------------------------------- DEVICE_INFO = { "NewModelName": "FRITZ!Box 7590", "NewSoftwareVersion": "7.57", "NewSerialNumber": "1234567890ABC", "NewUpTime": 123456, } LAN_INFO = { "NewEnable": True, "NewStatus": "Up", "NewMACAddress": "AA:BB:CC:DD:EE:01", "NewMaxBitRate": 1000, } WAN_LINK_PROPERTIES = { "NewWANAccessType": "DSL", "NewPhysicalLinkStatus": "Up", "NewLayer1UpstreamMaxBitRate": 40000000, "NewLayer1DownstreamMaxBitRate": 250000000, } WAN_ADDON_INFOS = { "NewTotalBytesSent": 123456789, "NewTotalBytesReceived": 987654321, } WAN_STATUS_INFO = { "NewConnectionStatus": "Connected", "NewUptime": 345600, } WAN_EXTERNAL_IP = {"NewExternalIPAddress": "203.0.113.7"} HOSTS_COUNT = {"NewHostNumberOfEntries": 2} HOST_ENTRIES = [ { "NewMACAddress": "11:22:33:44:55:66", "NewIPAddress": "192.168.1.50", "NewHostName": "laptop", "NewInterfaceType": "WLAN", "NewActive": True, "NewLeaseTimeRemaining": 3600, }, { "NewMACAddress": "AA:11:22:33:44:55", "NewIPAddress": "192.168.1.51", "NewHostName": "desktop", "NewInterfaceType": "LAN", "NewActive": False, }, ] PORT_FORWARDS = [ { "NewPortMappingDescription": "Web", "NewProtocol": "TCP", "NewExternalPort": 443, "NewInternalClient": "192.168.1.10", "NewInternalPort": 443, "NewPortMappingEnabled": True, "NewRemoteHost": "", } ] WLAN1_INFO = { "NewEnable": True, "NewStatus": "Up", "NewSSID": "MyWifi", "NewBSSID": "AA:BB:CC:DD:EE:02", "NewMaxBitRate": 866, "NewChannel": 36, "NewBeaconType": "11i", } WLAN1_TOTAL_ASSOCIATIONS = {"NewTotalAssociations": 1} WLAN1_ASSOCIATED_DEVICE = { "NewAssociatedDeviceMACAddress": "11:22:33:44:55:66", "NewAssociatedDeviceIPAddress": "192.168.1.50", "NewX_AVM-DE_SignalStrength": 80, "NewX_AVM-DE_Speed": 866, } WLAN2_INFO = { "NewEnable": False, "NewStatus": "Down", "NewSSID": "MyWifi-Guest", "NewBSSID": "AA:BB:CC:DD:EE:03", "NewMaxBitRate": 0, "NewChannel": 1, "NewBeaconType": "11i", } WLAN2_TOTAL_ASSOCIATIONS = {"NewTotalAssociations": 0} LAN_INFO_AUTO_RATE = { "NewEnable": True, "NewStatus": "Up", "NewMACAddress": "AA:BB:CC:DD:EE:01", "NewMaxBitRate": "Auto", } PPP_STATUS_INFO = { "NewConnectionStatus": "Connected", "NewLastConnectionError": "ERROR_NONE", "NewUptime": 7200, } PPP_EXTERNAL_IP = {"NewExternalIPAddress": "203.0.113.99"} DSL_INFO = { "NewEnable": True, "NewStatus": "Up", "NewDownstreamCurrRate": 100000, "NewUpstreamCurrRate": 40000, "NewDownstreamMaxRate": 120000, "NewUpstreamMaxRate": 50000, "NewDownstreamNoiseMargin": 120, # 12.0 dB "NewUpstreamNoiseMargin": 80, # 8.0 dB "NewDownstreamAttenuation": 150, # 15.0 dB "NewUpstreamAttenuation": 90, # 9.0 dB } DSL_STATS = { "NewCRCErrors": 5, "NewFECErrors": 12, "NewHECErrors": 0, } UI_INFO = { "NewX_AVM-DE_UpdateState": "Available", "NewX_AVM-DE_Version": "08.20", } UI_INFO_NO_UPDATE = { "NewX_AVM-DE_UpdateState": "None", "NewX_AVM-DE_Version": "08.20", } DNS_SERVERS = {"NewDNSServers": "1.1.1.1,8.8.8.8"} TIME_INFO = { "NewNTPServer1": "ntp.example.com", "NewNTPServer2": "", "NewCurrentLocalTime": "2026-06-24T16:00:00+02:00", } IPV6_FIREWALL_STATUS = {"NewFirewallEnabled": True, "NewInboundPinholeAllowed": True} IPV6_PINHOLES = [ { "NewPinholeDescription": "SSH6", "NewProtocol": 6, "NewRemotePort": 0, "NewInternalIPAddress": "2001:db8::10", "NewInternalPort": 22, "NewLeaseTime": 0, } ] # --------------------------------------------------------------------------- # call_action dispatch # --------------------------------------------------------------------------- _SIMPLE_RESPONSES = { ("DeviceInfo1", "GetInfo"): DEVICE_INFO, ("LANEthernetInterfaceConfig1", "GetInfo"): LAN_INFO, ("WANCommonInterfaceConfig1", "GetCommonLinkProperties"): WAN_LINK_PROPERTIES, ("WANCommonInterfaceConfig1", "GetAddonInfos"): WAN_ADDON_INFOS, ("WANIPConnection1", "GetStatusInfo"): WAN_STATUS_INFO, ("WANIPConnection1", "GetExternalIPAddress"): WAN_EXTERNAL_IP, ("WANPPPConnection1", "GetStatusInfo"): PPP_STATUS_INFO, ("WANPPPConnection1", "GetExternalIPAddress"): PPP_EXTERNAL_IP, ("Hosts1", "GetHostNumberOfEntries"): HOSTS_COUNT, ("WLANConfiguration1", "GetInfo"): WLAN1_INFO, ("WLANConfiguration1", "GetTotalAssociations"): WLAN1_TOTAL_ASSOCIATIONS, ("WLANConfiguration2", "GetInfo"): WLAN2_INFO, ("WLANConfiguration2", "GetTotalAssociations"): WLAN2_TOTAL_ASSOCIATIONS, ("WANDSLInterfaceConfig1", "GetInfo"): DSL_INFO, ("WANDSLInterfaceConfig1", "GetStatisticsTotal"): DSL_STATS, ("UserInterface1", "X_AVM-DE_GetInfo"): UI_INFO, ("LANHostConfigManagement1", "GetDNSServers"): DNS_SERVERS, ("Time1", "GetInfo"): TIME_INFO, ("WANIPv6Firewall1", "GetFirewallStatus"): IPV6_FIREWALL_STATUS, } def _call_action(service, action, **kwargs): if (service, action) == ("Hosts1", "GetGenericHostEntry"): index = kwargs["NewIndex"] if index >= len(HOST_ENTRIES): raise FritzArrayIndexError("index out of range") return HOST_ENTRIES[index] if (service, action) == ("WANIPConnection1", "GetGenericPortMappingEntry"): index = kwargs["NewPortMappingIndex"] if index >= len(PORT_FORWARDS): raise FritzArrayIndexError("index out of range") return PORT_FORWARDS[index] if (service, action) == ("WANIPv6Firewall1", "GetGenericPinholeEntry"): index = kwargs["NewPinholeIndex"] if index >= len(IPV6_PINHOLES): raise FritzArrayIndexError("index out of range") return IPV6_PINHOLES[index] if (service, action) == ("WLANConfiguration1", "GetGenericAssociatedDeviceInfo"): index = kwargs["NewAssociatedDeviceIndex"] if index >= 1: raise FritzArrayIndexError("index out of range") return WLAN1_ASSOCIATED_DEVICE if (service, action) == ("WLANConfiguration2", "GetGenericAssociatedDeviceInfo"): raise FritzArrayIndexError("index out of range") if action == "X_AVM-DE_GetExternalIPv6Address": raise FritzActionError("action not supported") return _SIMPLE_RESPONSES[(service, action)] # --------------------------------------------------------------------------- # Fixtures # --------------------------------------------------------------------------- @pytest.fixture def driver(): """Return a driver instance with a mocked FritzConnection (WANIPConnection1 / cable).""" mock_fc = MagicMock() mock_fc.services = { "DeviceInfo1": MagicMock(), "LANEthernetInterfaceConfig1": MagicMock(), "WANCommonInterfaceConfig1": MagicMock(), "WANIPConnection1": MagicMock(), "Hosts1": MagicMock(), "WLANConfiguration1": MagicMock(), "WLANConfiguration2": MagicMock(), "WANDSLInterfaceConfig1": MagicMock(), "UserInterface1": MagicMock(), "LANHostConfigManagement1": MagicMock(), "Time1": MagicMock(), "WANIPv6Firewall1": MagicMock(), } mock_fc.call_action.side_effect = _call_action drv = FritzBoxDriver(hostname="fritz.box", username="user", password="pass") drv.fc = mock_fc yield drv @pytest.fixture def pppoe_driver(): """Return a driver instance simulating a PPPoE FritzBox (WANPPPConnection1).""" mock_fc = MagicMock() mock_fc.services = { "DeviceInfo1": MagicMock(), "LANEthernetInterfaceConfig1": MagicMock(), "WANCommonInterfaceConfig1": MagicMock(), "WANPPPConnection1": MagicMock(), "Hosts1": MagicMock(), "WLANConfiguration1": MagicMock(), "WLANConfiguration2": MagicMock(), } mock_fc.call_action.side_effect = _call_action drv = FritzBoxDriver(hostname="fritz.box", username="user", password="pass") drv.fc = mock_fc yield drv # --------------------------------------------------------------------------- # open() / close() / is_alive() # --------------------------------------------------------------------------- class TestOpenClose: def test_open_raises_connection_exception_on_error(self): drv = FritzBoxDriver("unreachable.invalid", "u", "p") with patch("napalm_fritzbox.fritzbox.FritzConnection", side_effect=OSError("boom")): with pytest.raises(ConnectionException): drv.open() def test_open_sets_fc_on_success(self): drv = FritzBoxDriver("fritz.box", "u", "p") mock_fc = MagicMock() mock_fc.call_action.return_value = DEVICE_INFO with patch("napalm_fritzbox.fritzbox.FritzConnection", return_value=mock_fc): drv.open() assert drv.fc is mock_fc def test_close_clears_fc(self, driver): driver.close() assert driver.fc is None def test_close_is_idempotent(self, driver): driver.close() driver.close() class TestIsAlive: def test_returns_false_when_no_fc(self): drv = FritzBoxDriver("fritz.box", "u", "p") assert drv.is_alive() == {"is_alive": False} def test_returns_true_on_successful_connection(self, driver): with patch("napalm_fritzbox.fritzbox.socket.create_connection") as mock_conn: mock_conn.return_value.__enter__ = MagicMock(return_value=None) mock_conn.return_value.__exit__ = MagicMock(return_value=False) result = driver.is_alive() assert result == {"is_alive": True} def test_returns_false_on_socket_error(self, driver): with patch( "napalm_fritzbox.fritzbox.socket.create_connection", side_effect=OSError("refused"), ): result = driver.is_alive() assert result == {"is_alive": False} # --------------------------------------------------------------------------- # _call() # --------------------------------------------------------------------------- class TestCall: def test_raises_when_not_connected(self): drv = FritzBoxDriver("fritz.box", "u", "p") with pytest.raises(ConnectionClosedException): drv._call("DeviceInfo1", "GetInfo") # --------------------------------------------------------------------------- # get_facts() # --------------------------------------------------------------------------- class TestGetFacts: def test_vendor_is_avm(self, driver): assert driver.get_facts()["vendor"] == "AVM" def test_model_parsed(self, driver): assert driver.get_facts()["model"] == "FRITZ!Box 7590" def test_os_version_parsed(self, driver): assert driver.get_facts()["os_version"] == "7.57" def test_serial_number_parsed(self, driver): assert driver.get_facts()["serial_number"] == "1234567890ABC" def test_uptime_parsed(self, driver): assert driver.get_facts()["uptime"] == 123456 def test_interface_list_populated(self, driver): facts = driver.get_facts() assert "lan" in facts["interface_list"] assert "wan" in facts["interface_list"] # --------------------------------------------------------------------------- # get_interfaces() / get_interfaces_ip() # --------------------------------------------------------------------------- class TestGetInterfaces: def test_lan_interface_present(self, driver): interfaces = driver.get_interfaces() assert interfaces["lan"]["is_up"] is True assert interfaces["lan"]["mac_address"] == "aa:bb:cc:dd:ee:01" def test_wan_interface_present(self, driver): interfaces = driver.get_interfaces() assert interfaces["wan"]["is_up"] is True assert interfaces["wan"]["description"] == "DSL" def test_wlan_interfaces_present(self, driver): interfaces = driver.get_interfaces() assert interfaces["wlanconfiguration1"]["description"] == "MyWifi" assert interfaces["wlanconfiguration2"]["is_enabled"] is False class TestGetInterfacesIp: def test_wan_external_ip(self, driver): result = driver.get_interfaces_ip() assert result["wan"]["ipv4"] == {"203.0.113.7": {"prefix_length": 32}} def test_lan_address_falls_back_to_hostname(self, driver): result = driver.get_interfaces_ip() assert "fritz.box" in result["lan"]["ipv4"] # --------------------------------------------------------------------------- # get_hosts() / get_arp_table() # --------------------------------------------------------------------------- class TestGetHosts: def test_returns_all_hosts(self, driver): assert len(driver.get_hosts()) == 2 def test_active_host_fields(self, driver): host = driver.get_hosts()[0] assert host["mac"] == "11:22:33:44:55:66" assert host["ip"] == "192.168.1.50" assert host["hostname"] == "laptop" assert host["interface_type"] == "WLAN" assert host["is_active"] is True assert host["lease_time_remaining"] == 3600 def test_inactive_host_has_no_lease(self, driver): host = driver.get_hosts()[1] assert host["is_active"] is False assert "lease_time_remaining" not in host class TestGetArpTable: def test_only_active_hosts_with_mac_and_ip(self, driver): arp_table = driver.get_arp_table() assert len(arp_table) == 1 assert arp_table[0]["mac"] == "11:22:33:44:55:66" assert arp_table[0]["ip"] == "192.168.1.50" # --------------------------------------------------------------------------- # get_wan_status() # --------------------------------------------------------------------------- class TestGetWanStatus: def test_connection_type(self, driver): assert driver.get_wan_status()["connection_type"] == "DSL" def test_is_connected(self, driver): assert driver.get_wan_status()["is_connected"] is True def test_external_ip(self, driver): assert driver.get_wan_status()["external_ip"] == "203.0.113.7" def test_uptime(self, driver): assert driver.get_wan_status()["uptime"] == 345600 def test_traffic_counters(self, driver): status = driver.get_wan_status() assert status["bytes_sent"] == 123456789 assert status["bytes_received"] == 987654321 def test_max_bitrates_in_kbit(self, driver): status = driver.get_wan_status() assert status["max_bitrate_up"] == 40000 assert status["max_bitrate_down"] == 250000 def test_no_ipv6_when_unsupported(self, driver): assert "external_ipv6" not in driver.get_wan_status() # --------------------------------------------------------------------------- # get_port_forwards() / get_nat_translations() # --------------------------------------------------------------------------- class TestGetPortForwards: def test_returns_ipv4_mappings_and_ipv6_pinholes(self, driver): """One IPv4 mapping plus one IPv6 pinhole. This asserted 1 from before pinhole support existed, which put it in direct contradiction with TestIPv6Pinholes further down the same file. """ forwards = driver.get_port_forwards() assert len(forwards) == 2 assert {f["protocol"] for f in forwards} == {"TCP", "TCP6"} def test_rule_fields(self, driver): forward = driver.get_port_forwards()[0] assert forward["name"] == "Web" assert forward["protocol"] == "TCP" assert forward["external_port"] == 443 assert forward["internal_ip"] == "192.168.1.10" assert forward["internal_port"] == 443 assert forward["enabled"] is True def test_empty_remote_host_not_included(self, driver): assert "remote_host" not in driver.get_port_forwards()[0] class TestGetNatTranslations: def test_derived_from_enabled_ipv4_port_forwards(self, driver): """Only the IPv4 mapping. IPv6 pinholes are firewall holes, not translations, and IPv6 does not do NAT — including them paired an IPv6 host with the IPv4 WAN address.""" translations = driver.get_nat_translations() assert len(translations) == 1 assert translations[0]["protocol"] == "tcp" assert translations[0]["inside_local"] == "192.168.1.10:443" assert translations[0]["inside_global"] == "203.0.113.7:443" # --------------------------------------------------------------------------- # get_vpn_tunnels() # --------------------------------------------------------------------------- class TestGetVpnTunnels: def test_returns_empty_dict(self, driver): assert driver.get_vpn_tunnels() == {} # --------------------------------------------------------------------------- # get_wireless_clients() / get_ssids() / get_radio_status() # --------------------------------------------------------------------------- class TestGetWirelessClients: def test_returns_associated_clients(self, driver): clients = driver.get_wireless_clients() assert len(clients) == 1 client = clients[0] assert client["mac"] == "11:22:33:44:55:66" assert client["ssid"] == "MyWifi" assert client["radio"] == "WLANConfiguration1" assert client["signal"] == 80 assert client["ip"] == "192.168.1.50" class TestGetSsids: def test_returns_ssid_for_each_radio(self, driver): ssids = driver.get_ssids() assert "MyWifi" in ssids assert "MyWifi-Guest" in ssids def test_ssid_fields(self, driver): ssid = driver.get_ssids()["MyWifi"] assert ssid["enabled"] is True assert ssid["radio"] == "WLANConfiguration1" assert ssid["bssid"] == "aa:bb:cc:dd:ee:02" assert ssid["clients"] == 1 def test_disabled_ssid_has_zero_clients(self, driver): assert driver.get_ssids()["MyWifi-Guest"]["clients"] == 0 class TestGetRadioStatus: def test_returns_entry_per_radio(self, driver): radios = driver.get_radio_status() assert "WLANConfiguration1" in radios assert "WLANConfiguration2" in radios def test_5ghz_radio_enabled(self, driver): radio = driver.get_radio_status()["WLANConfiguration1"] assert radio["enabled"] is True assert radio["band"] == "5GHz" assert radio["channel"] == 36 def test_2_4ghz_radio_disabled(self, driver): radio = driver.get_radio_status()["WLANConfiguration2"] assert radio["enabled"] is False assert radio["band"] == "2.4GHz" assert radio["channel"] == 1 # --------------------------------------------------------------------------- # _to_float helper # --------------------------------------------------------------------------- class TestToFloat: def test_numeric_string(self): assert _to_float("1000") == 1000.0 def test_auto_string_returns_default(self): assert _to_float("Auto") == 0.0 def test_none_returns_default(self): assert _to_float(None) == 0.0 def test_int(self): assert _to_float(250000000) == 250000000.0 def test_custom_default(self): assert _to_float("bogus", default=-1.0) == -1.0 # --------------------------------------------------------------------------- # PPPoE interface (WANPPPConnection1) # --------------------------------------------------------------------------- class TestPPPoEInterface: def test_pppoe0_present_when_ppp_service_available(self, pppoe_driver): interfaces = pppoe_driver.get_interfaces() assert "pppoe0" in interfaces def test_pppoe0_is_up(self, pppoe_driver): assert pppoe_driver.get_interfaces()["pppoe0"]["is_up"] is True def test_pppoe0_mtu(self, pppoe_driver): assert pppoe_driver.get_interfaces()["pppoe0"]["mtu"] == 1492 def test_pppoe0_not_present_without_ppp_service(self, driver): # driver fixture uses WANIPConnection1 only — no PPPoE assert "pppoe0" not in driver.get_interfaces() def test_pppoe_ip_on_pppoe0_key(self, pppoe_driver): result = pppoe_driver.get_interfaces_ip() assert "pppoe0" in result assert "203.0.113.99" in result["pppoe0"]["ipv4"] def test_cable_ip_on_wan_key(self, driver): result = driver.get_interfaces_ip() assert "wan" in result assert "203.0.113.7" in result["wan"]["ipv4"] # --------------------------------------------------------------------------- # Auto/non-numeric MaxBitRate in get_interfaces # --------------------------------------------------------------------------- class TestAutoMaxBitRate: def test_lan_speed_zero_when_auto(self, driver): driver.fc.call_action.side_effect = lambda svc, act, **kw: ( LAN_INFO_AUTO_RATE if (svc, act) == ("LANEthernetInterfaceConfig1", "GetInfo") else _call_action(svc, act, **kw) ) interfaces = driver.get_interfaces() assert interfaces["lan"]["speed"] == 0.0 # --------------------------------------------------------------------------- # get_device_warnings # --------------------------------------------------------------------------- class TestGetDeviceWarnings: def test_returns_empty_list(self, driver): # UI_INFO has UpdateState=Available → one warning expected warnings = driver.get_device_warnings() codes = [w["code"] for w in warnings] assert "firmware_update_available" in codes def test_no_firmware_warning_when_no_update(self, driver): driver.fc.call_action.side_effect = lambda svc, act, **kw: ( UI_INFO_NO_UPDATE if (svc, act) == ("UserInterface1", "X_AVM-DE_GetInfo") else _call_action(svc, act, **kw) ) warnings = driver.get_device_warnings() assert not any(w["code"] == "firmware_update_available" for w in warnings) def test_dsl_snr_low_warning(self, driver): low_snr = dict(DSL_INFO, NewDownstreamNoiseMargin=40) # 4.0 dB < 6 dB threshold driver.fc.call_action.side_effect = lambda svc, act, **kw: ( low_snr if (svc, act) == ("WANDSLInterfaceConfig1", "GetInfo") else _call_action(svc, act, **kw) ) warnings = driver.get_device_warnings() assert any(w["code"] == "dsl_snr_low" for w in warnings) def test_no_dsl_warning_when_snr_ok(self, driver): warnings = driver.get_device_warnings() assert not any(w["code"] == "dsl_snr_low" for w in warnings) # --------------------------------------------------------------------------- # get_system_config (DSL stats, firmware, DNS, NTP) # --------------------------------------------------------------------------- class TestGetSystemConfig: def test_dsl_downstream_rate(self, driver): cfg = driver.get_system_config() assert cfg["dsl_downstream_rate"] == 100000 def test_dsl_upstream_rate(self, driver): assert driver.get_system_config()["dsl_upstream_rate"] == 40000 def test_dsl_snr_margin(self, driver): cfg = driver.get_system_config() assert cfg["dsl_downstream_noise_margin"] == 120 assert cfg["dsl_upstream_noise_margin"] == 80 def test_dsl_attenuation(self, driver): cfg = driver.get_system_config() assert cfg["dsl_downstream_attenuation"] == 150 def test_dsl_crc_errors(self, driver): assert driver.get_system_config()["dsl_crc_errors"] == 5 def test_dsl_fec_errors(self, driver): assert driver.get_system_config()["dsl_fec_errors"] == 12 def test_firmware_update_available(self, driver): assert driver.get_system_config()["firmware_update_available"] is True def test_firmware_not_available_when_none(self, driver): driver.fc.call_action.side_effect = lambda svc, act, **kw: ( UI_INFO_NO_UPDATE if (svc, act) == ("UserInterface1", "X_AVM-DE_GetInfo") else _call_action(svc, act, **kw) ) assert driver.get_system_config()["firmware_update_available"] is False def test_dns_servers_parsed(self, driver): cfg = driver.get_system_config() assert cfg["dns_servers"] == ["1.1.1.1", "8.8.8.8"] def test_ntp_server(self, driver): assert driver.get_system_config()["ntp_server"] == "ntp.example.com" def test_empty_when_no_dsl_service(self, driver): del driver.fc.services["WANDSLInterfaceConfig1"] cfg = driver.get_system_config() assert "dsl_downstream_rate" not in cfg # --------------------------------------------------------------------------- # get_port_forwards — IPv6 pinholes # --------------------------------------------------------------------------- class TestIPv6Pinholes: def test_ipv6_pinhole_appended(self, driver): forwards = driver.get_port_forwards() protocols = [f["protocol"] for f in forwards] assert "TCP6" in protocols def test_ipv6_pinhole_fields(self, driver): forwards = driver.get_port_forwards() ssh6 = next(f for f in forwards if f["protocol"] == "TCP6") assert ssh6["name"] == "SSH6" assert ssh6["internal_ip"] == "2001:db8::10" assert ssh6["internal_port"] == 22 assert ssh6["enabled"] is True def test_pinholes_are_not_reported_as_nat_translations(self, driver): """A pinhole opens the firewall for a globally routable address; there is nothing to translate.""" assert all(":" not in t["inside_local"].rsplit(":", 1)[0] for t in driver.get_nat_translations()) def test_no_pinholes_without_ipv6_service(self, driver): del driver.fc.services["WANIPv6Firewall1"] forwards = driver.get_port_forwards() assert all(f["protocol"] not in ("TCP6", "UDP6") for f in forwards)