Refactor tests for PermissionManager, HostsManager, HostEntry, HostsFile, and HostsParser

- Updated test cases in test_manager.py to improve readability and consistency.
- Simplified assertions and mock setups in tests for PermissionManager.
- Enhanced test coverage for HostsManager, including edit mode and entry manipulation tests.
- Improved test structure in test_models.py for HostEntry and HostsFile, ensuring clarity in test cases.
- Refined test cases in test_parser.py for better organization and readability.
- Adjusted test_save_confirmation_modal.py to maintain consistency in mocking and assertions.
This commit is contained in:
Philip Henning 2025-08-14 17:32:02 +02:00
parent 43fa8c871a
commit 1fddff91c8
18 changed files with 1364 additions and 1038 deletions

View file

@ -15,276 +15,291 @@ from hosts.core.config import Config
class TestConfig:
"""Test cases for the Config class."""
def test_config_initialization(self):
"""Test basic config initialization with defaults."""
with patch.object(Config, 'load'):
with patch.object(Config, "load"):
config = Config()
# Check default settings
assert config.get("show_default_entries") is False
assert len(config.get("default_entries", [])) == 3
assert config.get("window_settings", {}).get("last_sort_column") == ""
assert config.get("window_settings", {}).get("last_sort_ascending") is True
def test_default_settings_structure(self):
"""Test that default settings have the expected structure."""
with patch.object(Config, 'load'):
with patch.object(Config, "load"):
config = Config()
default_entries = config.get("default_entries", [])
assert len(default_entries) == 3
# Check localhost entries
localhost_entries = [e for e in default_entries if e["hostname"] == "localhost"]
localhost_entries = [
e for e in default_entries if e["hostname"] == "localhost"
]
assert len(localhost_entries) == 2 # IPv4 and IPv6
# Check broadcasthost entry
broadcast_entries = [e for e in default_entries if e["hostname"] == "broadcasthost"]
broadcast_entries = [
e for e in default_entries if e["hostname"] == "broadcasthost"
]
assert len(broadcast_entries) == 1
assert broadcast_entries[0]["ip"] == "255.255.255.255"
def test_config_paths(self):
"""Test that config paths are set correctly."""
with patch.object(Config, 'load'):
with patch.object(Config, "load"):
config = Config()
expected_dir = Path.home() / ".config" / "hosts-manager"
expected_file = expected_dir / "config.json"
assert config.config_dir == expected_dir
assert config.config_file == expected_file
def test_get_existing_key(self):
"""Test getting an existing configuration key."""
with patch.object(Config, 'load'):
with patch.object(Config, "load"):
config = Config()
result = config.get("show_default_entries")
assert result is False
def test_get_nonexistent_key_with_default(self):
"""Test getting a nonexistent key with default value."""
with patch.object(Config, 'load'):
with patch.object(Config, "load"):
config = Config()
result = config.get("nonexistent_key", "default_value")
assert result == "default_value"
def test_get_nonexistent_key_without_default(self):
"""Test getting a nonexistent key without default value."""
with patch.object(Config, 'load'):
with patch.object(Config, "load"):
config = Config()
result = config.get("nonexistent_key")
assert result is None
def test_set_configuration_value(self):
"""Test setting a configuration value."""
with patch.object(Config, 'load'):
with patch.object(Config, "load"):
config = Config()
config.set("test_key", "test_value")
assert config.get("test_key") == "test_value"
def test_set_overwrites_existing_value(self):
"""Test that setting overwrites existing values."""
with patch.object(Config, 'load'):
with patch.object(Config, "load"):
config = Config()
# Set initial value
config.set("show_default_entries", True)
assert config.get("show_default_entries") is True
# Overwrite with new value
config.set("show_default_entries", False)
assert config.get("show_default_entries") is False
def test_is_default_entry_true(self):
"""Test identifying default entries correctly."""
with patch.object(Config, 'load'):
with patch.object(Config, "load"):
config = Config()
# Test localhost IPv4
assert config.is_default_entry("127.0.0.1", "localhost") is True
# Test localhost IPv6
assert config.is_default_entry("::1", "localhost") is True
# Test broadcasthost
assert config.is_default_entry("255.255.255.255", "broadcasthost") is True
def test_is_default_entry_false(self):
"""Test that non-default entries are not identified as default."""
with patch.object(Config, 'load'):
with patch.object(Config, "load"):
config = Config()
# Test custom entries
assert config.is_default_entry("192.168.1.1", "router") is False
assert config.is_default_entry("10.0.0.1", "test.local") is False
assert config.is_default_entry("127.0.0.1", "custom") is False
def test_should_show_default_entries_default(self):
"""Test default value for show_default_entries."""
with patch.object(Config, 'load'):
with patch.object(Config, "load"):
config = Config()
assert config.should_show_default_entries() is False
def test_should_show_default_entries_configured(self):
"""Test configured value for show_default_entries."""
with patch.object(Config, 'load'):
with patch.object(Config, "load"):
config = Config()
config.set("show_default_entries", True)
assert config.should_show_default_entries() is True
def test_toggle_show_default_entries(self):
"""Test toggling the show_default_entries setting."""
with patch.object(Config, 'load'), patch.object(Config, 'save') as mock_save:
with patch.object(Config, "load"), patch.object(Config, "save") as mock_save:
config = Config()
# Initial state should be False
assert config.should_show_default_entries() is False
# Toggle to True
config.toggle_show_default_entries()
assert config.should_show_default_entries() is True
mock_save.assert_called_once()
# Toggle back to False
mock_save.reset_mock()
config.toggle_show_default_entries()
assert config.should_show_default_entries() is False
mock_save.assert_called_once()
def test_load_nonexistent_file(self):
"""Test loading config when file doesn't exist."""
with patch('pathlib.Path.exists', return_value=False):
with patch("pathlib.Path.exists", return_value=False):
config = Config()
# Should use defaults when file doesn't exist
assert config.get("show_default_entries") is False
def test_load_existing_file(self):
"""Test loading config from existing file."""
test_config = {
"show_default_entries": True,
"custom_setting": "custom_value"
}
with patch('pathlib.Path.exists', return_value=True), \
patch('builtins.open', mock_open(read_data=json.dumps(test_config))):
test_config = {"show_default_entries": True, "custom_setting": "custom_value"}
with (
patch("pathlib.Path.exists", return_value=True),
patch("builtins.open", mock_open(read_data=json.dumps(test_config))),
):
config = Config()
# Should load values from file
assert config.get("show_default_entries") is True
assert config.get("custom_setting") == "custom_value"
# Should still have defaults for missing keys
assert len(config.get("default_entries", [])) == 3
def test_load_invalid_json(self):
"""Test loading config with invalid JSON falls back to defaults."""
with patch('pathlib.Path.exists', return_value=True), \
patch('builtins.open', mock_open(read_data="invalid json")):
with (
patch("pathlib.Path.exists", return_value=True),
patch("builtins.open", mock_open(read_data="invalid json")),
):
config = Config()
# Should use defaults when JSON is invalid
assert config.get("show_default_entries") is False
def test_load_file_io_error(self):
"""Test loading config with file I/O error falls back to defaults."""
with patch('pathlib.Path.exists', return_value=True), \
patch('builtins.open', side_effect=IOError("File error")):
with (
patch("pathlib.Path.exists", return_value=True),
patch("builtins.open", side_effect=IOError("File error")),
):
config = Config()
# Should use defaults when file can't be read
assert config.get("show_default_entries") is False
def test_save_creates_directory(self):
"""Test that save creates config directory if it doesn't exist."""
with patch.object(Config, 'load'), \
patch('pathlib.Path.mkdir') as mock_mkdir, \
patch('builtins.open', mock_open()) as mock_file:
with (
patch.object(Config, "load"),
patch("pathlib.Path.mkdir") as mock_mkdir,
patch("builtins.open", mock_open()) as mock_file,
):
config = Config()
config.save()
# Should create directory with parents=True, exist_ok=True
mock_mkdir.assert_called_once_with(parents=True, exist_ok=True)
mock_file.assert_called_once()
def test_save_writes_json(self):
"""Test that save writes configuration as JSON."""
with patch.object(Config, 'load'), \
patch('pathlib.Path.mkdir'), \
patch('builtins.open', mock_open()) as mock_file:
with (
patch.object(Config, "load"),
patch("pathlib.Path.mkdir"),
patch("builtins.open", mock_open()) as mock_file,
):
config = Config()
config.set("test_key", "test_value")
config.save()
# Check that file was opened for writing
mock_file.assert_called_once_with(config.config_file, 'w')
mock_file.assert_called_once_with(config.config_file, "w")
# Check that JSON was written
handle = mock_file()
written_data = ''.join(call.args[0] for call in handle.write.call_args_list)
written_data = "".join(call.args[0] for call in handle.write.call_args_list)
# Should be valid JSON containing our test data
parsed_data = json.loads(written_data)
assert parsed_data["test_key"] == "test_value"
def test_save_io_error_silent_fail(self):
"""Test that save silently fails on I/O error."""
with patch.object(Config, 'load'), \
patch('pathlib.Path.mkdir'), \
patch('builtins.open', side_effect=IOError("Write error")):
with (
patch.object(Config, "load"),
patch("pathlib.Path.mkdir"),
patch("builtins.open", side_effect=IOError("Write error")),
):
config = Config()
# Should not raise exception
config.save()
def test_save_directory_creation_error_silent_fail(self):
"""Test that save silently fails on directory creation error."""
with patch.object(Config, 'load'), \
patch('pathlib.Path.mkdir', side_effect=OSError("Permission denied")):
with (
patch.object(Config, "load"),
patch("pathlib.Path.mkdir", side_effect=OSError("Permission denied")),
):
config = Config()
# Should not raise exception
config.save()
def test_integration_load_save_roundtrip(self):
"""Test complete load/save cycle with temporary file."""
with tempfile.TemporaryDirectory() as temp_dir:
config_dir = Path(temp_dir) / "hosts-manager"
config_file = config_dir / "config.json"
with patch.object(Config, '__init__', lambda self: None):
with patch.object(Config, "__init__", lambda self: None):
config = Config()
config.config_dir = config_dir
config.config_file = config_file
config._settings = config._load_default_settings()
# Modify some settings
config.set("show_default_entries", True)
config.set("custom_setting", "test_value")
# Save configuration
config.save()
# Verify file was created
assert config_file.exists()
# Create new config instance and load
config2 = Config()
config2.config_dir = config_dir
config2.config_file = config_file
config2._settings = config2._load_default_settings()
config2.load()
# Verify settings were loaded correctly
assert config2.get("show_default_entries") is True
assert config2.get("custom_setting") == "test_value"
# Verify defaults are still present
assert len(config2.get("default_entries", [])) == 3