"""Unit tests for napalm_proxmox.utils.""" from __future__ import annotations import pytest from napalm_proxmox import utils class TestNormalizeMAC: def test_colon_upper(self): assert utils.normalize_mac("AA:BB:CC:DD:EE:FF") == "aa:bb:cc:dd:ee:ff" def test_dash_separated(self): assert utils.normalize_mac("AA-BB-CC-DD-EE-FF") == "aa:bb:cc:dd:ee:ff" def test_lowercase_passthrough(self): assert utils.normalize_mac("aa:bb:cc:dd:ee:ff") == "aa:bb:cc:dd:ee:ff" def test_empty(self): assert utils.normalize_mac("") == "" class TestParseCIDR: def test_ipv4(self): ip, plen = utils.parse_cidr("192.168.1.1/24") assert ip == "192.168.1.1" assert plen == 24 def test_ipv6(self): ip, plen = utils.parse_cidr("2001:db8::1/64") assert ip == "2001:db8::1" assert plen == 64 def test_no_prefix(self): ip, plen = utils.parse_cidr("10.0.0.1") assert ip == "10.0.0.1" assert plen == 32 def test_host_route(self): ip, plen = utils.parse_cidr("10.0.0.1/32") assert plen == 32 class TestPrefixLength: def test_slash24(self): assert utils.prefix_length("10.0.0.0/24") == 24 def test_slash_16(self): assert utils.prefix_length("192.168.0.0/16") == 16 def test_no_slash(self): assert utils.prefix_length("10.0.0.1") == 32 class TestAddressesFromNodeNetwork: def test_address_and_netmask(self): data = {"address": "10.0.0.1", "netmask": "255.255.255.0"} result = utils.addresses_from_node_network(data) assert "ipv4" in result assert "10.0.0.1" in result["ipv4"] assert result["ipv4"]["10.0.0.1"]["prefix_length"] == 24 def test_cidr_ipv4(self): data = {"cidr": "10.0.0.1/24"} result = utils.addresses_from_node_network(data) assert "ipv4" in result assert "10.0.0.1" in result["ipv4"] def test_cidr_ipv6(self): data = {"cidr6": "2001:db8::1/64"} result = utils.addresses_from_node_network(data) assert "ipv6" in result assert "2001:db8::1" in result["ipv6"] assert result["ipv6"]["2001:db8::1"]["prefix_length"] == 64 def test_both_ipv4_and_ipv6(self): data = {"cidr": "10.0.0.1/24", "cidr6": "2001:db8::1/64"} result = utils.addresses_from_node_network(data) assert "ipv4" in result assert "ipv6" in result def test_empty_input(self): result = utils.addresses_from_node_network({}) assert result == {} class TestIsOVSInterface: def test_ovs_bridge(self): assert utils.is_ovs_interface({"type": "OVSBridge"}) is True def test_ovs_bond(self): assert utils.is_ovs_interface({"type": "OVSBond"}) is True def test_ovs_int_port(self): assert utils.is_ovs_interface({"type": "OVSIntPort"}) is True def test_linux_bridge(self): assert utils.is_ovs_interface({"type": "bridge"}) is False def test_eth(self): assert utils.is_ovs_interface({"type": "eth"}) is False def test_no_type_key(self): assert utils.is_ovs_interface({}) is False class TestIsSDNVNet: def test_vnet_pattern(self): assert utils.is_sdn_vnet("vnet0") is True assert utils.is_sdn_vnet("vnet99") is True def test_non_vnet(self): assert utils.is_sdn_vnet("eth0") is False assert utils.is_sdn_vnet("vmbr0") is False def test_vnet_with_suffix(self): assert utils.is_sdn_vnet("vnet0sub") is False class TestSpeedMbps: def test_speed_present(self): assert utils.speed_mbps({"speed": "1000"}) == 1000.0 def test_speed_mbit_suffix(self): # Some entries may have "Mbit" unit in the string — handled by parsing assert utils.speed_mbps({"speed": "10000"}) == 10000.0 def test_no_speed(self): assert utils.speed_mbps({}) == -1.0 def test_speed_non_numeric(self): assert utils.speed_mbps({"speed": "unknown"}) == -1.0 class TestSDNZoneToNetworkInstance: def test_vlan_zone(self): zone = {"zone": "z1", "type": "vlan", "mtu": 1500} result = utils.sdn_zone_to_network_instance(zone) assert result["type"] == "L2VPN" assert result["name"] == "z1" def test_evpn_zone(self): zone = {"zone": "z2", "type": "evpn", "peers": "10.0.0.1", "rt": 10001} result = utils.sdn_zone_to_network_instance(zone) assert result["type"] == "L3VRF" def test_vxlan_zone(self): zone = {"zone": "z3", "type": "vxlan", "mtu": 9000} result = utils.sdn_zone_to_network_instance(zone) assert result["type"] == "L2VPN" def test_simple_zone(self): zone = {"zone": "z4", "type": "simple"} result = utils.sdn_zone_to_network_instance(zone) assert result["type"] == "L3VRF" def test_unknown_zone_type(self): zone = {"zone": "z5", "type": "qinq"} result = utils.sdn_zone_to_network_instance(zone) assert result["type"] == "L2VPN" def test_result_has_interfaces(self): zone = {"zone": "z1", "type": "vlan"} result = utils.sdn_zone_to_network_instance(zone) assert "interfaces" in result class TestParseARPOutput: ARP_N = ( "Address HWtype HWaddress Flags Iface\n" "10.0.0.254 ether aa:bb:cc:dd:ee:ff C eth0\n" "10.0.0.10 ether 11:22:33:44:55:66 C vmbr0\n" "? (10.0.0.20) at on eth0\n" ) def test_parses_two_entries(self): result = utils.parse_arp_output(self.ARP_N) assert len(result) == 2 def test_entry_fields(self): result = utils.parse_arp_output(self.ARP_N) assert result[0]["ip"] == "10.0.0.254" assert result[0]["mac"] == "aa:bb:cc:dd:ee:ff" assert result[0]["interface"] == "eth0" def test_skips_incomplete(self): result = utils.parse_arp_output(self.ARP_N) ips = {e["ip"] for e in result} assert "10.0.0.20" not in ips def test_empty_input(self): result = utils.parse_arp_output("") assert result == [] class TestParseOVSVsctlShow: RAW = ( " Bridge vmbr1\n" " Port eth1\n" " Port vnet0\n" " Port vmbr1\n" " Bridge vmbr2\n" " Port eth2\n" " Port vmbr2\n" ) def test_finds_both_bridges(self): result = utils.parse_ovs_vsctl_show(self.RAW) assert "vmbr1" in result assert "vmbr2" in result def test_ports_exclude_self(self): result = utils.parse_ovs_vsctl_show(self.RAW) assert "vmbr1" not in result["vmbr1"]["ports"] assert "vmbr2" not in result["vmbr2"]["ports"] def test_ports_included(self): result = utils.parse_ovs_vsctl_show(self.RAW) assert "eth1" in result["vmbr1"]["ports"] assert "eth2" in result["vmbr2"]["ports"] def test_empty_input(self): result = utils.parse_ovs_vsctl_show("") assert result == {}