""" Tests for the hosts data models. This module contains unit tests for the HostEntry and HostsFile classes, validating their functionality and data integrity. """ import pytest from hosts.core.models import HostEntry, HostsFile class TestHostEntry: """Test cases for the HostEntry class.""" def test_host_entry_creation(self): """Test basic host entry creation.""" entry = HostEntry(ip_address="127.0.0.1", hostnames=["localhost"]) assert entry.ip_address == "127.0.0.1" assert entry.hostnames == ["localhost"] assert entry.is_active is True assert entry.comment is None assert entry.dns_name is None def test_host_entry_with_comment(self): """Test host entry creation with comment.""" entry = HostEntry( ip_address="192.168.1.1", hostnames=["router", "gateway"], comment="Local router" ) assert entry.comment == "Local router" def test_host_entry_inactive(self): """Test inactive host entry creation.""" entry = HostEntry( ip_address="10.0.0.1", hostnames=["test.local"], is_active=False ) assert entry.is_active is False def test_invalid_ip_address(self): """Test that invalid IP addresses raise ValueError.""" with pytest.raises(ValueError, match="Invalid IP address"): HostEntry(ip_address="invalid.ip", hostnames=["test"]) def test_empty_hostnames(self): """Test that empty hostnames list raises ValueError.""" with pytest.raises(ValueError, match="At least one hostname is required"): HostEntry(ip_address="127.0.0.1", hostnames=[]) def test_invalid_hostname(self): """Test that invalid hostnames raise ValueError.""" with pytest.raises(ValueError, match="Invalid hostname"): HostEntry(ip_address="127.0.0.1", hostnames=["invalid..hostname"]) def test_ipv6_address(self): """Test IPv6 address support.""" entry = HostEntry(ip_address="::1", hostnames=["localhost"]) assert entry.ip_address == "::1" def test_to_hosts_line_active(self): """Test conversion to hosts file line format for active entry.""" entry = HostEntry( ip_address="127.0.0.1", hostnames=["localhost", "local"], comment="Loopback" ) line = entry.to_hosts_line() assert line == "127.0.0.1 localhost local # Loopback" def test_to_hosts_line_inactive(self): """Test conversion to hosts file line format for inactive entry.""" entry = HostEntry( ip_address="192.168.1.1", hostnames=["router"], is_active=False ) line = entry.to_hosts_line() assert line == "# 192.168.1.1 router" def test_from_hosts_line_simple(self): """Test parsing simple hosts file line.""" line = "127.0.0.1 localhost" entry = HostEntry.from_hosts_line(line) assert entry is not None assert entry.ip_address == "127.0.0.1" assert entry.hostnames == ["localhost"] assert entry.is_active is True assert entry.comment is None def test_from_hosts_line_with_comment(self): """Test parsing hosts file line with comment.""" line = "192.168.1.1 router gateway # Local network" entry = HostEntry.from_hosts_line(line) assert entry is not None assert entry.ip_address == "192.168.1.1" assert entry.hostnames == ["router", "gateway"] assert entry.comment == "Local network" def test_from_hosts_line_inactive(self): """Test parsing inactive hosts file line.""" line = "# 10.0.0.1 test.local" entry = HostEntry.from_hosts_line(line) assert entry is not None assert entry.ip_address == "10.0.0.1" assert entry.hostnames == ["test.local"] assert entry.is_active is False def test_from_hosts_line_empty(self): """Test parsing empty line returns None.""" assert HostEntry.from_hosts_line("") is None assert HostEntry.from_hosts_line(" ") is None def test_from_hosts_line_comment_only(self): """Test parsing comment-only line returns None.""" assert HostEntry.from_hosts_line("# This is just a comment") is None def test_from_hosts_line_invalid(self): """Test parsing invalid line returns None.""" assert HostEntry.from_hosts_line("invalid line") is None assert HostEntry.from_hosts_line("192.168.1.1") is None # No hostname class TestHostsFile: """Test cases for the HostsFile class.""" def test_hosts_file_creation(self): """Test basic hosts file creation.""" hosts_file = HostsFile() assert len(hosts_file.entries) == 0 assert len(hosts_file.header_comments) == 0 assert len(hosts_file.footer_comments) == 0 def test_add_entry(self): """Test adding entries to hosts file.""" hosts_file = HostsFile() entry = HostEntry(ip_address="127.0.0.1", hostnames=["localhost"]) hosts_file.add_entry(entry) assert len(hosts_file.entries) == 1 assert hosts_file.entries[0] == entry def test_add_invalid_entry(self): """Test that adding invalid entry raises ValueError.""" hosts_file = HostsFile() with pytest.raises(ValueError): # This will fail validation in add_entry invalid_entry = HostEntry.__new__(HostEntry) # Bypass __init__ invalid_entry.ip_address = "invalid" invalid_entry.hostnames = ["test"] hosts_file.add_entry(invalid_entry) def test_remove_entry(self): """Test removing entries from hosts file.""" hosts_file = HostsFile() entry1 = HostEntry(ip_address="127.0.0.1", hostnames=["localhost"]) entry2 = HostEntry(ip_address="192.168.1.1", hostnames=["router"]) hosts_file.add_entry(entry1) hosts_file.add_entry(entry2) hosts_file.remove_entry(0) assert len(hosts_file.entries) == 1 assert hosts_file.entries[0] == entry2 def test_remove_entry_invalid_index(self): """Test removing entry with invalid index does nothing.""" hosts_file = HostsFile() entry = HostEntry(ip_address="127.0.0.1", hostnames=["localhost"]) hosts_file.add_entry(entry) hosts_file.remove_entry(10) # Invalid index assert len(hosts_file.entries) == 1 def test_toggle_entry(self): """Test toggling entry active state.""" hosts_file = HostsFile() entry = HostEntry(ip_address="127.0.0.1", hostnames=["localhost"]) hosts_file.add_entry(entry) assert entry.is_active is True hosts_file.toggle_entry(0) assert entry.is_active is False hosts_file.toggle_entry(0) assert entry.is_active is True def test_get_active_entries(self): """Test getting only active entries.""" hosts_file = HostsFile() active_entry = HostEntry(ip_address="127.0.0.1", hostnames=["localhost"]) inactive_entry = HostEntry( ip_address="192.168.1.1", hostnames=["router"], is_active=False ) hosts_file.add_entry(active_entry) hosts_file.add_entry(inactive_entry) active_entries = hosts_file.get_active_entries() assert len(active_entries) == 1 assert active_entries[0] == active_entry def test_get_inactive_entries(self): """Test getting only inactive entries.""" hosts_file = HostsFile() active_entry = HostEntry(ip_address="127.0.0.1", hostnames=["localhost"]) inactive_entry = HostEntry( ip_address="192.168.1.1", hostnames=["router"], is_active=False ) hosts_file.add_entry(active_entry) hosts_file.add_entry(inactive_entry) inactive_entries = hosts_file.get_inactive_entries() assert len(inactive_entries) == 1 assert inactive_entries[0] == inactive_entry def test_sort_by_ip(self): """Test sorting entries by IP address.""" hosts_file = HostsFile() entry1 = HostEntry(ip_address="192.168.1.1", hostnames=["router"]) entry2 = HostEntry(ip_address="127.0.0.1", hostnames=["localhost"]) entry3 = HostEntry(ip_address="10.0.0.1", hostnames=["test"]) hosts_file.add_entry(entry1) hosts_file.add_entry(entry2) hosts_file.add_entry(entry3) hosts_file.sort_by_ip() assert hosts_file.entries[0].ip_address == "10.0.0.1" assert hosts_file.entries[1].ip_address == "127.0.0.1" assert hosts_file.entries[2].ip_address == "192.168.1.1" def test_sort_by_hostname(self): """Test sorting entries by hostname.""" hosts_file = HostsFile() entry1 = HostEntry(ip_address="127.0.0.1", hostnames=["zebra"]) entry2 = HostEntry(ip_address="192.168.1.1", hostnames=["alpha"]) entry3 = HostEntry(ip_address="10.0.0.1", hostnames=["beta"]) hosts_file.add_entry(entry1) hosts_file.add_entry(entry2) hosts_file.add_entry(entry3) hosts_file.sort_by_hostname() assert hosts_file.entries[0].hostnames[0] == "alpha" assert hosts_file.entries[1].hostnames[0] == "beta" assert hosts_file.entries[2].hostnames[0] == "zebra" def test_find_entries_by_hostname(self): """Test finding entries by hostname.""" hosts_file = HostsFile() entry1 = HostEntry(ip_address="127.0.0.1", hostnames=["localhost", "local"]) entry2 = HostEntry(ip_address="192.168.1.1", hostnames=["router"]) entry3 = HostEntry(ip_address="10.0.0.1", hostnames=["test", "localhost"]) hosts_file.add_entry(entry1) hosts_file.add_entry(entry2) hosts_file.add_entry(entry3) indices = hosts_file.find_entries_by_hostname("localhost") assert indices == [0, 2] indices = hosts_file.find_entries_by_hostname("router") assert indices == [1] indices = hosts_file.find_entries_by_hostname("nonexistent") assert indices == [] def test_find_entries_by_ip(self): """Test finding entries by IP address.""" hosts_file = HostsFile() entry1 = HostEntry(ip_address="127.0.0.1", hostnames=["localhost"]) entry2 = HostEntry(ip_address="192.168.1.1", hostnames=["router"]) entry3 = HostEntry(ip_address="127.0.0.1", hostnames=["local"]) hosts_file.add_entry(entry1) hosts_file.add_entry(entry2) hosts_file.add_entry(entry3) indices = hosts_file.find_entries_by_ip("127.0.0.1") assert indices == [0, 2] indices = hosts_file.find_entries_by_ip("192.168.1.1") assert indices == [1] indices = hosts_file.find_entries_by_ip("10.0.0.1") assert indices == []