Add unit tests for HostEntry and HostsFile models, and implement HostsParser tests
- Created comprehensive unit tests for HostEntry class, covering creation, validation, and conversion to/from hosts file lines. - Developed unit tests for HostsFile class, including entry management, sorting, and retrieval of active/inactive entries. - Implemented tests for HostsParser class, validating parsing and serialization of hosts files, handling comments, and file operations. - Ensured coverage for edge cases such as empty files, invalid entries, and file permission checks.
This commit is contained in:
parent
40a1e67949
commit
2decad8047
21 changed files with 1691 additions and 75 deletions
298
tests/test_models.py
Normal file
298
tests/test_models.py
Normal file
|
@ -0,0 +1,298 @@
|
|||
"""
|
||||
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 == []
|
Loading…
Add table
Add a link
Reference in a new issue