250 lines
7.9 KiB
Python
250 lines
7.9 KiB
Python
"""Tests for settings and session serialization."""
|
|
|
|
import json
|
|
import pytest
|
|
from pathlib import Path
|
|
|
|
from development_hub.settings import Settings
|
|
|
|
|
|
class TestSettingsPersistence:
|
|
"""Test settings persistence and round-trips."""
|
|
|
|
@pytest.fixture
|
|
def isolated_settings(self, tmp_path, monkeypatch):
|
|
"""Create an isolated Settings instance with temp files."""
|
|
# Reset singleton
|
|
Settings._instance = None
|
|
|
|
# Point to temp directory
|
|
settings_file = tmp_path / "settings.json"
|
|
session_file = tmp_path / "session.json"
|
|
|
|
monkeypatch.setattr(Settings, "_settings_file", settings_file)
|
|
monkeypatch.setattr(Settings, "_session_file", session_file)
|
|
|
|
settings = Settings()
|
|
yield settings
|
|
|
|
# Clean up singleton
|
|
Settings._instance = None
|
|
|
|
def test_default_values(self, isolated_settings):
|
|
"""Settings have sensible defaults."""
|
|
settings = isolated_settings
|
|
|
|
assert settings.deploy_docs_after_creation == True
|
|
assert settings.preferred_editor == "auto"
|
|
assert settings.auto_start_docs_server == True
|
|
assert isinstance(settings.project_search_paths, list)
|
|
assert isinstance(settings.project_ignore_folders, list)
|
|
|
|
def test_set_and_get(self, isolated_settings):
|
|
"""Values can be set and retrieved."""
|
|
settings = isolated_settings
|
|
|
|
settings.preferred_editor = "pycharm"
|
|
assert settings.preferred_editor == "pycharm"
|
|
|
|
settings.deploy_docs_after_creation = False
|
|
assert settings.deploy_docs_after_creation == False
|
|
|
|
def test_persistence_round_trip(self, isolated_settings, tmp_path):
|
|
"""Settings persist across instances."""
|
|
settings = isolated_settings
|
|
|
|
# Modify settings
|
|
settings.preferred_editor = "code"
|
|
settings.auto_start_docs_server = False
|
|
settings.set("custom_key", "custom_value")
|
|
|
|
# Reset singleton and create new instance
|
|
Settings._instance = None
|
|
new_settings = Settings()
|
|
|
|
assert new_settings.preferred_editor == "code"
|
|
assert new_settings.auto_start_docs_server == False
|
|
assert new_settings.get("custom_key") == "custom_value"
|
|
|
|
def test_file_created_on_save(self, isolated_settings, tmp_path):
|
|
"""Settings file is created when saving."""
|
|
settings = isolated_settings
|
|
|
|
settings.preferred_editor = "xed"
|
|
|
|
# Check file exists
|
|
assert Settings._settings_file.exists()
|
|
|
|
def test_invalid_json_handled(self, isolated_settings, tmp_path):
|
|
"""Invalid JSON in settings file doesn't crash."""
|
|
# Write invalid JSON
|
|
Settings._settings_file.parent.mkdir(parents=True, exist_ok=True)
|
|
Settings._settings_file.write_text("{ invalid json }")
|
|
|
|
# Reset and reload
|
|
Settings._instance = None
|
|
settings = Settings()
|
|
|
|
# Should fall back to defaults
|
|
assert settings.preferred_editor == "auto"
|
|
|
|
|
|
class TestSessionSerialization:
|
|
"""Test session state serialization."""
|
|
|
|
@pytest.fixture
|
|
def isolated_settings(self, tmp_path, monkeypatch):
|
|
"""Create an isolated Settings instance with temp files."""
|
|
Settings._instance = None
|
|
|
|
settings_file = tmp_path / "settings.json"
|
|
session_file = tmp_path / "session.json"
|
|
|
|
monkeypatch.setattr(Settings, "_settings_file", settings_file)
|
|
monkeypatch.setattr(Settings, "_session_file", session_file)
|
|
|
|
settings = Settings()
|
|
yield settings
|
|
|
|
Settings._instance = None
|
|
|
|
def test_save_load_session_round_trip(self, isolated_settings):
|
|
"""Session state round-trips correctly."""
|
|
settings = isolated_settings
|
|
|
|
state = {
|
|
"window_geometry": {"x": 100, "y": 200, "width": 800, "height": 600},
|
|
"splitter_sizes": [300, 500],
|
|
"panes": [
|
|
{
|
|
"tabs": [
|
|
{"type": "terminal", "cwd": "/home/user/project", "title": "Terminal 1"},
|
|
{"type": "dashboard", "project_key": "my-project"},
|
|
],
|
|
"active_tab": 0,
|
|
}
|
|
],
|
|
"active_pane": 0,
|
|
}
|
|
|
|
settings.save_session(state)
|
|
loaded = settings.load_session()
|
|
|
|
assert loaded == state
|
|
|
|
def test_save_session_creates_directory(self, isolated_settings, tmp_path):
|
|
"""Session save creates config directory if needed."""
|
|
settings = isolated_settings
|
|
|
|
# Ensure parent doesn't exist
|
|
if Settings._session_file.parent.exists():
|
|
import shutil
|
|
shutil.rmtree(Settings._session_file.parent)
|
|
|
|
state = {"test": True}
|
|
settings.save_session(state)
|
|
|
|
assert Settings._session_file.exists()
|
|
|
|
def test_load_empty_session(self, isolated_settings):
|
|
"""Loading non-existent session returns empty dict."""
|
|
settings = isolated_settings
|
|
loaded = settings.load_session()
|
|
assert loaded == {}
|
|
|
|
def test_load_invalid_session(self, isolated_settings):
|
|
"""Invalid JSON in session returns empty dict."""
|
|
settings = isolated_settings
|
|
|
|
Settings._session_file.parent.mkdir(parents=True, exist_ok=True)
|
|
Settings._session_file.write_text("not valid json")
|
|
|
|
loaded = settings.load_session()
|
|
assert loaded == {}
|
|
|
|
def test_session_preserves_types(self, isolated_settings):
|
|
"""Session preserves various data types."""
|
|
settings = isolated_settings
|
|
|
|
state = {
|
|
"string": "hello",
|
|
"number": 42,
|
|
"float": 3.14,
|
|
"bool": True,
|
|
"none": None,
|
|
"list": [1, 2, 3],
|
|
"nested": {"a": {"b": "c"}},
|
|
}
|
|
|
|
settings.save_session(state)
|
|
loaded = settings.load_session()
|
|
|
|
assert loaded["string"] == "hello"
|
|
assert loaded["number"] == 42
|
|
assert loaded["float"] == 3.14
|
|
assert loaded["bool"] == True
|
|
assert loaded["none"] is None
|
|
assert loaded["list"] == [1, 2, 3]
|
|
assert loaded["nested"]["a"]["b"] == "c"
|
|
|
|
def test_session_handles_unicode(self, isolated_settings):
|
|
"""Session handles unicode characters."""
|
|
settings = isolated_settings
|
|
|
|
state = {
|
|
"emoji": "Test",
|
|
"japanese": "Hello",
|
|
"symbols": "angle bracket < > quotes \" '",
|
|
}
|
|
|
|
settings.save_session(state)
|
|
loaded = settings.load_session()
|
|
|
|
assert loaded == state
|
|
|
|
|
|
class TestGitHostSettings:
|
|
"""Test git hosting configuration."""
|
|
|
|
@pytest.fixture
|
|
def isolated_settings(self, tmp_path, monkeypatch):
|
|
"""Create an isolated Settings instance."""
|
|
Settings._instance = None
|
|
|
|
monkeypatch.setattr(Settings, "_settings_file", tmp_path / "settings.json")
|
|
monkeypatch.setattr(Settings, "_session_file", tmp_path / "session.json")
|
|
|
|
settings = Settings()
|
|
yield settings
|
|
|
|
Settings._instance = None
|
|
|
|
def test_git_not_configured_by_default(self, isolated_settings):
|
|
"""Git is not configured initially."""
|
|
settings = isolated_settings
|
|
assert settings.is_git_configured == False
|
|
|
|
def test_git_configured_when_all_set(self, isolated_settings):
|
|
"""Git is configured when all required fields are set."""
|
|
settings = isolated_settings
|
|
|
|
settings.git_host_type = "gitea"
|
|
settings.git_host_url = "https://gitea.example.com"
|
|
settings.git_host_owner = "myuser"
|
|
|
|
assert settings.is_git_configured == True
|
|
|
|
def test_git_not_configured_if_missing_field(self, isolated_settings):
|
|
"""Git not configured if any required field is missing."""
|
|
settings = isolated_settings
|
|
|
|
settings.git_host_type = "github"
|
|
settings.git_host_url = "https://github.com"
|
|
# Missing: git_host_owner
|
|
|
|
assert settings.is_git_configured == False
|
|
|
|
|
|
if __name__ == "__main__":
|
|
pytest.main([__file__, "-v"])
|