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:
parent
43fa8c871a
commit
1fddff91c8
18 changed files with 1364 additions and 1038 deletions
|
@ -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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue