"""Tests for app.utils.ip_utils.""" import pytest from app.utils.ip_utils import ( ip_version, is_valid_ip, is_valid_ip_or_network, is_valid_network, normalise_ip, normalise_network, ) class TestIsValidIp: """Tests for :func:`is_valid_ip`.""" def test_is_valid_ip_with_valid_ipv4_returns_true(self) -> None: assert is_valid_ip("192.168.1.1") is True def test_is_valid_ip_with_valid_ipv6_returns_true(self) -> None: assert is_valid_ip("2001:db8::1") is True def test_is_valid_ip_with_cidr_returns_false(self) -> None: assert is_valid_ip("10.0.0.0/8") is False def test_is_valid_ip_with_empty_string_returns_false(self) -> None: assert is_valid_ip("") is False def test_is_valid_ip_with_hostname_returns_false(self) -> None: assert is_valid_ip("example.com") is False def test_is_valid_ip_with_loopback_returns_true(self) -> None: assert is_valid_ip("127.0.0.1") is True class TestIsValidNetwork: """Tests for :func:`is_valid_network`.""" def test_is_valid_network_with_valid_cidr_returns_true(self) -> None: assert is_valid_network("192.168.0.0/24") is True def test_is_valid_network_with_host_bits_set_returns_true(self) -> None: # strict=False means host bits being set is allowed. assert is_valid_network("192.168.0.1/24") is True def test_is_valid_network_with_plain_ip_returns_true(self) -> None: # A bare IP is treated as a host-only /32 network — this is valid. assert is_valid_network("192.168.0.1") is True def test_is_valid_network_with_hostname_returns_false(self) -> None: assert is_valid_network("example.com") is False def test_is_valid_network_with_invalid_prefix_returns_false(self) -> None: assert is_valid_network("10.0.0.0/99") is False class TestIsValidIpOrNetwork: """Tests for :func:`is_valid_ip_or_network`.""" def test_accepts_plain_ip(self) -> None: assert is_valid_ip_or_network("1.2.3.4") is True def test_accepts_cidr(self) -> None: assert is_valid_ip_or_network("10.0.0.0/8") is True def test_rejects_garbage(self) -> None: assert is_valid_ip_or_network("not-an-ip") is False class TestNormaliseIp: """Tests for :func:`normalise_ip`.""" def test_normalise_ip_ipv4_unchanged(self) -> None: assert normalise_ip("10.20.30.40") == "10.20.30.40" def test_normalise_ip_ipv6_compressed(self) -> None: assert normalise_ip("2001:0db8:0000:0000:0000:0000:0000:0001") == "2001:db8::1" def test_normalise_ip_invalid_raises_value_error(self) -> None: with pytest.raises(ValueError): normalise_ip("not-an-ip") class TestNormaliseNetwork: """Tests for :func:`normalise_network`.""" def test_normalise_network_masks_host_bits(self) -> None: assert normalise_network("192.168.1.5/24") == "192.168.1.0/24" def test_normalise_network_already_canonical(self) -> None: assert normalise_network("10.0.0.0/8") == "10.0.0.0/8" class TestIpVersion: """Tests for :func:`ip_version`.""" def test_ip_version_ipv4_returns_4(self) -> None: assert ip_version("8.8.8.8") == 4 def test_ip_version_ipv6_returns_6(self) -> None: assert ip_version("::1") == 6 def test_ip_version_invalid_raises_value_error(self) -> None: with pytest.raises(ValueError): ip_version("garbage")