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
6
tests/__init__.py
Normal file
6
tests/__init__.py
Normal file
|
@ -0,0 +1,6 @@
|
|||
"""
|
||||
Test suite for the hosts TUI application.
|
||||
|
||||
This module contains unit tests, integration tests, and TUI tests
|
||||
for validating the functionality of the hosts manager.
|
||||
"""
|
BIN
tests/__pycache__/__init__.cpython-313.pyc
Normal file
BIN
tests/__pycache__/__init__.cpython-313.pyc
Normal file
Binary file not shown.
BIN
tests/__pycache__/test_models.cpython-313-pytest-8.4.1.pyc
Normal file
BIN
tests/__pycache__/test_models.cpython-313-pytest-8.4.1.pyc
Normal file
Binary file not shown.
BIN
tests/__pycache__/test_parser.cpython-313-pytest-8.4.1.pyc
Normal file
BIN
tests/__pycache__/test_parser.cpython-313-pytest-8.4.1.pyc
Normal file
Binary file not shown.
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 == []
|
353
tests/test_parser.py
Normal file
353
tests/test_parser.py
Normal file
|
@ -0,0 +1,353 @@
|
|||
"""
|
||||
Tests for the hosts file parser.
|
||||
|
||||
This module contains unit tests for the HostsParser class,
|
||||
validating file parsing and serialization functionality.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
import tempfile
|
||||
import os
|
||||
from pathlib import Path
|
||||
from hosts.core.parser import HostsParser
|
||||
from hosts.core.models import HostEntry, HostsFile
|
||||
|
||||
|
||||
class TestHostsParser:
|
||||
"""Test cases for the HostsParser class."""
|
||||
|
||||
def test_parser_initialization(self):
|
||||
"""Test parser initialization with default and custom paths."""
|
||||
# Default path
|
||||
parser = HostsParser()
|
||||
assert str(parser.file_path) == "/etc/hosts"
|
||||
|
||||
# Custom path
|
||||
custom_path = "/tmp/test_hosts"
|
||||
parser = HostsParser(custom_path)
|
||||
assert str(parser.file_path) == custom_path
|
||||
|
||||
def test_parse_simple_hosts_file(self):
|
||||
"""Test parsing a simple hosts file."""
|
||||
content = """127.0.0.1 localhost
|
||||
192.168.1.1 router
|
||||
"""
|
||||
|
||||
with tempfile.NamedTemporaryFile(mode='w', delete=False) as f:
|
||||
f.write(content)
|
||||
f.flush()
|
||||
|
||||
parser = HostsParser(f.name)
|
||||
hosts_file = parser.parse()
|
||||
|
||||
assert len(hosts_file.entries) == 2
|
||||
|
||||
# Check first entry
|
||||
entry1 = hosts_file.entries[0]
|
||||
assert entry1.ip_address == "127.0.0.1"
|
||||
assert entry1.hostnames == ["localhost"]
|
||||
assert entry1.is_active is True
|
||||
assert entry1.comment is None
|
||||
|
||||
# Check second entry
|
||||
entry2 = hosts_file.entries[1]
|
||||
assert entry2.ip_address == "192.168.1.1"
|
||||
assert entry2.hostnames == ["router"]
|
||||
assert entry2.is_active is True
|
||||
assert entry2.comment is None
|
||||
|
||||
os.unlink(f.name)
|
||||
|
||||
def test_parse_hosts_file_with_comments(self):
|
||||
"""Test parsing hosts file with comments and inactive entries."""
|
||||
content = """# This is a header comment
|
||||
# Another header comment
|
||||
|
||||
127.0.0.1 localhost loopback # Loopback address
|
||||
192.168.1.1 router gateway # Local router
|
||||
# 10.0.0.1 test.local # Disabled test entry
|
||||
|
||||
# Footer comment
|
||||
"""
|
||||
|
||||
with tempfile.NamedTemporaryFile(mode='w', delete=False) as f:
|
||||
f.write(content)
|
||||
f.flush()
|
||||
|
||||
parser = HostsParser(f.name)
|
||||
hosts_file = parser.parse()
|
||||
|
||||
# Check header comments
|
||||
assert len(hosts_file.header_comments) == 2
|
||||
assert hosts_file.header_comments[0] == "This is a header comment"
|
||||
assert hosts_file.header_comments[1] == "Another header comment"
|
||||
|
||||
# Check entries
|
||||
assert len(hosts_file.entries) == 3
|
||||
|
||||
# Active entry with comment
|
||||
entry1 = hosts_file.entries[0]
|
||||
assert entry1.ip_address == "127.0.0.1"
|
||||
assert entry1.hostnames == ["localhost", "loopback"]
|
||||
assert entry1.comment == "Loopback address"
|
||||
assert entry1.is_active is True
|
||||
|
||||
# Another active entry
|
||||
entry2 = hosts_file.entries[1]
|
||||
assert entry2.ip_address == "192.168.1.1"
|
||||
assert entry2.hostnames == ["router", "gateway"]
|
||||
assert entry2.comment == "Local router"
|
||||
assert entry2.is_active is True
|
||||
|
||||
# Inactive entry
|
||||
entry3 = hosts_file.entries[2]
|
||||
assert entry3.ip_address == "10.0.0.1"
|
||||
assert entry3.hostnames == ["test.local"]
|
||||
assert entry3.comment == "Disabled test entry"
|
||||
assert entry3.is_active is False
|
||||
|
||||
# Check footer comments
|
||||
assert len(hosts_file.footer_comments) == 1
|
||||
assert hosts_file.footer_comments[0] == "Footer comment"
|
||||
|
||||
os.unlink(f.name)
|
||||
|
||||
def test_parse_empty_file(self):
|
||||
"""Test parsing an empty hosts file."""
|
||||
with tempfile.NamedTemporaryFile(mode='w', delete=False) as f:
|
||||
f.write("")
|
||||
f.flush()
|
||||
|
||||
parser = HostsParser(f.name)
|
||||
hosts_file = parser.parse()
|
||||
|
||||
assert len(hosts_file.entries) == 0
|
||||
assert len(hosts_file.header_comments) == 0
|
||||
assert len(hosts_file.footer_comments) == 0
|
||||
|
||||
os.unlink(f.name)
|
||||
|
||||
def test_parse_comments_only_file(self):
|
||||
"""Test parsing a file with only comments."""
|
||||
content = """# This is a comment
|
||||
# Another comment
|
||||
# Yet another comment
|
||||
"""
|
||||
|
||||
with tempfile.NamedTemporaryFile(mode='w', delete=False) as f:
|
||||
f.write(content)
|
||||
f.flush()
|
||||
|
||||
parser = HostsParser(f.name)
|
||||
hosts_file = parser.parse()
|
||||
|
||||
assert len(hosts_file.entries) == 0
|
||||
assert len(hosts_file.header_comments) == 3
|
||||
assert hosts_file.header_comments[0] == "This is a comment"
|
||||
assert hosts_file.header_comments[1] == "Another comment"
|
||||
assert hosts_file.header_comments[2] == "Yet another comment"
|
||||
|
||||
os.unlink(f.name)
|
||||
|
||||
def test_parse_nonexistent_file(self):
|
||||
"""Test parsing a nonexistent file raises FileNotFoundError."""
|
||||
parser = HostsParser("/nonexistent/path/hosts")
|
||||
|
||||
with pytest.raises(FileNotFoundError):
|
||||
parser.parse()
|
||||
|
||||
def test_serialize_simple_hosts_file(self):
|
||||
"""Test serializing a simple 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)
|
||||
|
||||
parser = HostsParser()
|
||||
content = parser.serialize(hosts_file)
|
||||
|
||||
expected = """127.0.0.1 localhost
|
||||
192.168.1.1 router
|
||||
"""
|
||||
assert content == expected
|
||||
|
||||
def test_serialize_hosts_file_with_comments(self):
|
||||
"""Test serializing hosts file with comments."""
|
||||
hosts_file = HostsFile()
|
||||
hosts_file.header_comments = ["Header comment 1", "Header comment 2"]
|
||||
hosts_file.footer_comments = ["Footer comment"]
|
||||
|
||||
entry1 = HostEntry(
|
||||
ip_address="127.0.0.1",
|
||||
hostnames=["localhost"],
|
||||
comment="Loopback"
|
||||
)
|
||||
entry2 = HostEntry(
|
||||
ip_address="10.0.0.1",
|
||||
hostnames=["test"],
|
||||
is_active=False
|
||||
)
|
||||
|
||||
hosts_file.add_entry(entry1)
|
||||
hosts_file.add_entry(entry2)
|
||||
|
||||
parser = HostsParser()
|
||||
content = parser.serialize(hosts_file)
|
||||
|
||||
expected = """# Header comment 1
|
||||
# Header comment 2
|
||||
|
||||
127.0.0.1 localhost # Loopback
|
||||
# 10.0.0.1 test
|
||||
|
||||
# Footer comment
|
||||
"""
|
||||
assert content == expected
|
||||
|
||||
def test_serialize_empty_hosts_file(self):
|
||||
"""Test serializing an empty hosts file."""
|
||||
hosts_file = HostsFile()
|
||||
parser = HostsParser()
|
||||
content = parser.serialize(hosts_file)
|
||||
|
||||
assert content == "\n"
|
||||
|
||||
def test_write_hosts_file(self):
|
||||
"""Test writing hosts file to disk."""
|
||||
hosts_file = HostsFile()
|
||||
entry = HostEntry(ip_address="127.0.0.1", hostnames=["localhost"])
|
||||
hosts_file.add_entry(entry)
|
||||
|
||||
with tempfile.NamedTemporaryFile(delete=False) as f:
|
||||
parser = HostsParser(f.name)
|
||||
parser.write(hosts_file, backup=False)
|
||||
|
||||
# Read back and verify
|
||||
with open(f.name, 'r') as read_file:
|
||||
content = read_file.read()
|
||||
assert content == "127.0.0.1 localhost\n"
|
||||
|
||||
os.unlink(f.name)
|
||||
|
||||
def test_write_hosts_file_with_backup(self):
|
||||
"""Test writing hosts file with backup creation."""
|
||||
# Create initial file
|
||||
initial_content = "192.168.1.1 router\n"
|
||||
|
||||
with tempfile.NamedTemporaryFile(mode='w', delete=False) as f:
|
||||
f.write(initial_content)
|
||||
f.flush()
|
||||
|
||||
# Create new hosts file to write
|
||||
hosts_file = HostsFile()
|
||||
entry = HostEntry(ip_address="127.0.0.1", hostnames=["localhost"])
|
||||
hosts_file.add_entry(entry)
|
||||
|
||||
parser = HostsParser(f.name)
|
||||
parser.write(hosts_file, backup=True)
|
||||
|
||||
# Check that backup was created
|
||||
backup_path = Path(f.name).with_suffix('.bak')
|
||||
assert backup_path.exists()
|
||||
|
||||
# Check backup content
|
||||
with open(backup_path, 'r') as backup_file:
|
||||
backup_content = backup_file.read()
|
||||
assert backup_content == initial_content
|
||||
|
||||
# Check new content
|
||||
with open(f.name, 'r') as new_file:
|
||||
new_content = new_file.read()
|
||||
assert new_content == "127.0.0.1 localhost\n"
|
||||
|
||||
# Cleanup
|
||||
os.unlink(backup_path)
|
||||
|
||||
os.unlink(f.name)
|
||||
|
||||
def test_validate_write_permissions(self):
|
||||
"""Test write permission validation."""
|
||||
# Test with a temporary file (should be writable)
|
||||
with tempfile.NamedTemporaryFile() as f:
|
||||
parser = HostsParser(f.name)
|
||||
assert parser.validate_write_permissions() is True
|
||||
|
||||
# Test with a nonexistent file in /tmp (should be writable)
|
||||
parser = HostsParser("/tmp/test_hosts_nonexistent")
|
||||
assert parser.validate_write_permissions() is True
|
||||
|
||||
# Test with a path that likely doesn't have write permissions
|
||||
parser = HostsParser("/root/test_hosts")
|
||||
# This might be True if running as root, so we can't assert False
|
||||
result = parser.validate_write_permissions()
|
||||
assert isinstance(result, bool)
|
||||
|
||||
def test_get_file_info(self):
|
||||
"""Test getting file information."""
|
||||
content = "127.0.0.1 localhost\n"
|
||||
|
||||
with tempfile.NamedTemporaryFile(mode='w', delete=False) as f:
|
||||
f.write(content)
|
||||
f.flush()
|
||||
|
||||
parser = HostsParser(f.name)
|
||||
info = parser.get_file_info()
|
||||
|
||||
assert info['path'] == f.name
|
||||
assert info['exists'] is True
|
||||
assert info['readable'] is True
|
||||
assert info['size'] == len(content)
|
||||
assert info['modified'] is not None
|
||||
assert isinstance(info['modified'], float)
|
||||
|
||||
os.unlink(f.name)
|
||||
|
||||
def test_get_file_info_nonexistent(self):
|
||||
"""Test getting file information for nonexistent file."""
|
||||
parser = HostsParser("/nonexistent/path")
|
||||
info = parser.get_file_info()
|
||||
|
||||
assert info['path'] == "/nonexistent/path"
|
||||
assert info['exists'] is False
|
||||
assert info['readable'] is False
|
||||
assert info['writable'] is False
|
||||
assert info['size'] == 0
|
||||
assert info['modified'] is None
|
||||
|
||||
def test_round_trip_parsing(self):
|
||||
"""Test that parsing and serializing preserves content."""
|
||||
original_content = """# System hosts file
|
||||
# Do not edit manually
|
||||
|
||||
127.0.0.1 localhost loopback # Local loopback
|
||||
::1 localhost # IPv6 loopback
|
||||
192.168.1.1 router gateway # Local router
|
||||
# 10.0.0.1 test.local # Test entry (disabled)
|
||||
|
||||
# End of file
|
||||
"""
|
||||
|
||||
with tempfile.NamedTemporaryFile(mode='w', delete=False) as f:
|
||||
f.write(original_content)
|
||||
f.flush()
|
||||
|
||||
# Parse and serialize
|
||||
parser = HostsParser(f.name)
|
||||
hosts_file = parser.parse()
|
||||
|
||||
# Write back and read
|
||||
parser.write(hosts_file, backup=False)
|
||||
|
||||
with open(f.name, 'r') as read_file:
|
||||
final_content = read_file.read()
|
||||
|
||||
# The content should be functionally equivalent
|
||||
# (though formatting might differ slightly)
|
||||
assert "127.0.0.1 localhost loopback # Local loopback" in final_content
|
||||
assert "::1 localhost # IPv6 loopback" in final_content
|
||||
assert "192.168.1.1 router gateway # Local router" in final_content
|
||||
assert "# 10.0.0.1 test.local # Test entry (disabled)" in final_content
|
||||
|
||||
os.unlink(f.name)
|
Loading…
Add table
Add a link
Reference in a new issue