Add tests for M4 workspace files and wizard
- test_settings.py: Add TestDocsModeSettings and TestWorkspaceExportImport - test_paths.py: Comprehensive PathResolver tests with proper isolation - test_wizard.py: Wizard structure and settings integration tests All 71 tests pass. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
5742b7088b
commit
b60af09922
|
|
@ -0,0 +1,308 @@
|
||||||
|
"""Tests for the PathResolver module."""
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
from pathlib import Path
|
||||||
|
from unittest.mock import patch, MagicMock
|
||||||
|
|
||||||
|
from development_hub.paths import PathResolver, paths
|
||||||
|
from development_hub.settings import Settings
|
||||||
|
|
||||||
|
|
||||||
|
class TestPathResolver:
|
||||||
|
"""Test PathResolver path resolution."""
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def isolated_settings(self, tmp_path, monkeypatch):
|
||||||
|
"""Create isolated settings for testing."""
|
||||||
|
Settings._instance = None
|
||||||
|
PathResolver._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
|
||||||
|
PathResolver._instance = None
|
||||||
|
|
||||||
|
def test_singleton_pattern(self):
|
||||||
|
"""PathResolver is a singleton."""
|
||||||
|
PathResolver._instance = None
|
||||||
|
|
||||||
|
resolver1 = PathResolver()
|
||||||
|
resolver2 = PathResolver()
|
||||||
|
|
||||||
|
assert resolver1 is resolver2
|
||||||
|
|
||||||
|
PathResolver._instance = None
|
||||||
|
|
||||||
|
def test_projects_root_from_settings(self, isolated_settings, tmp_path):
|
||||||
|
"""Projects root comes from settings."""
|
||||||
|
isolated_settings.project_search_paths = [str(tmp_path / "my-projects")]
|
||||||
|
|
||||||
|
resolver = PathResolver()
|
||||||
|
assert resolver.projects_root == tmp_path / "my-projects"
|
||||||
|
|
||||||
|
def test_projects_root_default(self, isolated_settings, tmp_path):
|
||||||
|
"""Projects root defaults to ~/Projects when not set."""
|
||||||
|
isolated_settings.project_search_paths = []
|
||||||
|
|
||||||
|
resolver = PathResolver()
|
||||||
|
assert resolver.projects_root == Path.home() / "Projects"
|
||||||
|
|
||||||
|
def test_docs_root_from_settings(self, isolated_settings, tmp_path):
|
||||||
|
"""Docs root comes from settings."""
|
||||||
|
isolated_settings.docs_mode = "standalone"
|
||||||
|
|
||||||
|
resolver = PathResolver()
|
||||||
|
docs_root = resolver.docs_root
|
||||||
|
|
||||||
|
assert "development-hub" in str(docs_root)
|
||||||
|
|
||||||
|
def test_project_docs_dir(self, isolated_settings, tmp_path):
|
||||||
|
"""Project docs dir returns docusaurus path."""
|
||||||
|
project_docs = tmp_path / "project-docs"
|
||||||
|
project_docs.mkdir()
|
||||||
|
|
||||||
|
isolated_settings.project_search_paths = [str(tmp_path)]
|
||||||
|
|
||||||
|
resolver = PathResolver()
|
||||||
|
assert resolver.project_docs_dir == project_docs
|
||||||
|
|
||||||
|
def test_project_docs_dir_none_when_not_exists(self, isolated_settings, tmp_path):
|
||||||
|
"""Project docs dir is None when not configured."""
|
||||||
|
isolated_settings.project_search_paths = [str(tmp_path)]
|
||||||
|
# No project-docs folder
|
||||||
|
|
||||||
|
resolver = PathResolver()
|
||||||
|
assert resolver.project_docs_dir is None
|
||||||
|
|
||||||
|
def test_progress_dir(self, isolated_settings):
|
||||||
|
"""Progress dir comes from settings."""
|
||||||
|
resolver = PathResolver()
|
||||||
|
progress = resolver.progress_dir
|
||||||
|
|
||||||
|
assert isinstance(progress, Path)
|
||||||
|
|
||||||
|
def test_build_script_when_exists(self, isolated_settings, tmp_path):
|
||||||
|
"""Build script returns path when it exists."""
|
||||||
|
project_docs = tmp_path / "project-docs"
|
||||||
|
scripts_dir = project_docs / "scripts"
|
||||||
|
scripts_dir.mkdir(parents=True)
|
||||||
|
|
||||||
|
build_script = scripts_dir / "build-public-docs.sh"
|
||||||
|
build_script.touch()
|
||||||
|
|
||||||
|
isolated_settings.project_search_paths = [str(tmp_path)]
|
||||||
|
|
||||||
|
resolver = PathResolver()
|
||||||
|
assert resolver.build_script == build_script
|
||||||
|
|
||||||
|
def test_build_script_none_when_not_exists(self, isolated_settings, tmp_path):
|
||||||
|
"""Build script is None when it doesn't exist."""
|
||||||
|
isolated_settings.project_search_paths = [str(tmp_path)]
|
||||||
|
|
||||||
|
resolver = PathResolver()
|
||||||
|
assert resolver.build_script is None
|
||||||
|
|
||||||
|
def test_project_docs_path(self, isolated_settings):
|
||||||
|
"""project_docs_path returns correct path for project."""
|
||||||
|
resolver = PathResolver()
|
||||||
|
|
||||||
|
path = resolver.project_docs_path("my-project")
|
||||||
|
|
||||||
|
assert path.name == "my-project"
|
||||||
|
assert "projects" in str(path)
|
||||||
|
|
||||||
|
def test_git_url_not_configured(self, isolated_settings):
|
||||||
|
"""git_url returns empty string when not configured."""
|
||||||
|
resolver = PathResolver()
|
||||||
|
|
||||||
|
assert resolver.git_url() == ""
|
||||||
|
assert resolver.git_url("owner", "repo") == ""
|
||||||
|
|
||||||
|
def test_git_url_configured(self, isolated_settings):
|
||||||
|
"""git_url returns correct URL when configured."""
|
||||||
|
isolated_settings.git_host_type = "github"
|
||||||
|
isolated_settings.git_host_url = "https://github.com"
|
||||||
|
isolated_settings.git_host_owner = "testowner"
|
||||||
|
|
||||||
|
resolver = PathResolver()
|
||||||
|
|
||||||
|
assert resolver.git_url() == "https://github.com/testowner"
|
||||||
|
assert resolver.git_url("testowner", "testrepo") == "https://github.com/testowner/testrepo"
|
||||||
|
|
||||||
|
def test_git_url_custom_owner(self, isolated_settings):
|
||||||
|
"""git_url can use custom owner."""
|
||||||
|
isolated_settings.git_host_type = "github"
|
||||||
|
isolated_settings.git_host_url = "https://github.com"
|
||||||
|
isolated_settings.git_host_owner = "default"
|
||||||
|
|
||||||
|
resolver = PathResolver()
|
||||||
|
|
||||||
|
assert resolver.git_url("custom", "repo") == "https://github.com/custom/repo"
|
||||||
|
|
||||||
|
def test_pages_url_not_configured(self, isolated_settings):
|
||||||
|
"""pages_url returns empty string when not configured."""
|
||||||
|
resolver = PathResolver()
|
||||||
|
|
||||||
|
assert resolver.pages_url() == ""
|
||||||
|
|
||||||
|
def test_pages_url_configured(self, isolated_settings):
|
||||||
|
"""pages_url returns correct URL when configured."""
|
||||||
|
isolated_settings.git_host_owner = "testowner"
|
||||||
|
isolated_settings.pages_url = "https://pages.example.com"
|
||||||
|
|
||||||
|
resolver = PathResolver()
|
||||||
|
|
||||||
|
assert "pages.example.com" in resolver.pages_url()
|
||||||
|
assert "testowner" in resolver.pages_url()
|
||||||
|
|
||||||
|
def test_is_docs_enabled(self, isolated_settings):
|
||||||
|
"""is_docs_enabled reflects settings."""
|
||||||
|
isolated_settings.docs_mode = "standalone"
|
||||||
|
|
||||||
|
resolver = PathResolver()
|
||||||
|
assert resolver.is_docs_enabled == True
|
||||||
|
|
||||||
|
def test_is_git_configured(self, isolated_settings):
|
||||||
|
"""is_git_configured reflects settings."""
|
||||||
|
resolver = PathResolver()
|
||||||
|
assert resolver.is_git_configured == False
|
||||||
|
|
||||||
|
isolated_settings.git_host_type = "github"
|
||||||
|
isolated_settings.git_host_url = "https://github.com"
|
||||||
|
isolated_settings.git_host_owner = "user"
|
||||||
|
|
||||||
|
assert resolver.is_git_configured == True
|
||||||
|
|
||||||
|
def test_effective_docs_mode(self, isolated_settings, tmp_path):
|
||||||
|
"""effective_docs_mode reflects settings."""
|
||||||
|
isolated_settings.project_search_paths = [str(tmp_path)]
|
||||||
|
isolated_settings.docs_mode = "auto"
|
||||||
|
|
||||||
|
resolver = PathResolver()
|
||||||
|
|
||||||
|
# No project-docs, so standalone
|
||||||
|
assert resolver.effective_docs_mode == "standalone"
|
||||||
|
|
||||||
|
# Create project-docs
|
||||||
|
(tmp_path / "project-docs").mkdir()
|
||||||
|
assert resolver.effective_docs_mode == "project-docs"
|
||||||
|
|
||||||
|
|
||||||
|
class TestCmdForgeAvailability:
|
||||||
|
"""Test CmdForge availability checks."""
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def isolated_settings(self, tmp_path, monkeypatch):
|
||||||
|
"""Create isolated settings for testing."""
|
||||||
|
Settings._instance = None
|
||||||
|
PathResolver._instance = None
|
||||||
|
|
||||||
|
monkeypatch.setattr(Settings, "_settings_file", tmp_path / "settings.json")
|
||||||
|
monkeypatch.setattr(Settings, "_session_file", tmp_path / "session.json")
|
||||||
|
|
||||||
|
settings = Settings()
|
||||||
|
# Use isolated paths to prevent finding real CmdForge
|
||||||
|
settings.project_search_paths = [str(tmp_path)]
|
||||||
|
yield settings
|
||||||
|
|
||||||
|
Settings._instance = None
|
||||||
|
PathResolver._instance = None
|
||||||
|
|
||||||
|
def test_cmdforge_path_explicit(self, isolated_settings, tmp_path):
|
||||||
|
"""CmdForge path from explicit setting."""
|
||||||
|
cmdforge_dir = tmp_path / "CmdForge"
|
||||||
|
cmdforge_dir.mkdir()
|
||||||
|
|
||||||
|
isolated_settings.cmdforge_path = cmdforge_dir
|
||||||
|
|
||||||
|
resolver = PathResolver()
|
||||||
|
assert resolver.cmdforge_path == cmdforge_dir
|
||||||
|
|
||||||
|
def test_cmdforge_executable_from_venv(self, isolated_settings, tmp_path):
|
||||||
|
"""CmdForge executable found in venv."""
|
||||||
|
cmdforge_dir = tmp_path / "CmdForge"
|
||||||
|
venv_bin = cmdforge_dir / ".venv" / "bin"
|
||||||
|
venv_bin.mkdir(parents=True)
|
||||||
|
|
||||||
|
cmdforge_exe = venv_bin / "cmdforge"
|
||||||
|
cmdforge_exe.touch()
|
||||||
|
|
||||||
|
isolated_settings.cmdforge_path = cmdforge_dir
|
||||||
|
|
||||||
|
resolver = PathResolver()
|
||||||
|
assert resolver.cmdforge_executable == cmdforge_exe
|
||||||
|
|
||||||
|
@patch("development_hub.paths.shutil.which")
|
||||||
|
def test_cmdforge_executable_from_path(self, mock_which, isolated_settings, tmp_path):
|
||||||
|
"""CmdForge executable found in PATH when not in explicit location."""
|
||||||
|
mock_which.return_value = "/usr/local/bin/cmdforge"
|
||||||
|
# Ensure no explicit cmdforge path is set
|
||||||
|
isolated_settings.cmdforge_path = None
|
||||||
|
|
||||||
|
resolver = PathResolver()
|
||||||
|
exe = resolver.cmdforge_executable
|
||||||
|
|
||||||
|
assert exe == Path("/usr/local/bin/cmdforge")
|
||||||
|
|
||||||
|
@patch("development_hub.paths.shutil.which")
|
||||||
|
def test_cmdforge_executable_not_found(self, mock_which, isolated_settings, tmp_path):
|
||||||
|
"""CmdForge executable None when not found."""
|
||||||
|
mock_which.return_value = None
|
||||||
|
# Ensure no explicit cmdforge path is set
|
||||||
|
isolated_settings.cmdforge_path = None
|
||||||
|
|
||||||
|
resolver = PathResolver()
|
||||||
|
assert resolver.cmdforge_executable is None
|
||||||
|
|
||||||
|
@patch("development_hub.paths.shutil.which")
|
||||||
|
def test_is_cmdforge_available_false(self, mock_which, isolated_settings, tmp_path):
|
||||||
|
"""is_cmdforge_available is False when not found."""
|
||||||
|
mock_which.return_value = None
|
||||||
|
isolated_settings.cmdforge_path = None
|
||||||
|
|
||||||
|
resolver = PathResolver()
|
||||||
|
assert resolver.is_cmdforge_available == False
|
||||||
|
|
||||||
|
@patch("development_hub.paths.shutil.which")
|
||||||
|
def test_is_cmdforge_available_true(self, mock_which, isolated_settings, tmp_path):
|
||||||
|
"""is_cmdforge_available is True when found in PATH."""
|
||||||
|
mock_which.return_value = "/usr/bin/cmdforge"
|
||||||
|
isolated_settings.cmdforge_path = None
|
||||||
|
|
||||||
|
resolver = PathResolver()
|
||||||
|
assert resolver.is_cmdforge_available == True
|
||||||
|
|
||||||
|
|
||||||
|
class TestGlobalPathsInstance:
|
||||||
|
"""Test the global paths singleton instance."""
|
||||||
|
|
||||||
|
def test_paths_is_path_resolver(self):
|
||||||
|
"""Global paths is a PathResolver instance."""
|
||||||
|
assert isinstance(paths, PathResolver)
|
||||||
|
|
||||||
|
def test_paths_has_expected_properties(self):
|
||||||
|
"""Global paths has expected properties."""
|
||||||
|
assert hasattr(paths, "projects_root")
|
||||||
|
assert hasattr(paths, "docs_root")
|
||||||
|
assert hasattr(paths, "project_docs_dir")
|
||||||
|
assert hasattr(paths, "progress_dir")
|
||||||
|
assert hasattr(paths, "build_script")
|
||||||
|
assert hasattr(paths, "cmdforge_path")
|
||||||
|
assert hasattr(paths, "cmdforge_executable")
|
||||||
|
assert hasattr(paths, "is_docs_enabled")
|
||||||
|
assert hasattr(paths, "is_cmdforge_available")
|
||||||
|
assert hasattr(paths, "is_git_configured")
|
||||||
|
|
||||||
|
def test_paths_has_expected_methods(self):
|
||||||
|
"""Global paths has expected methods."""
|
||||||
|
assert callable(paths.project_docs_path)
|
||||||
|
assert callable(paths.git_url)
|
||||||
|
assert callable(paths.pages_url)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
pytest.main([__file__, "-v"])
|
||||||
|
|
@ -245,5 +245,291 @@ class TestGitHostSettings:
|
||||||
assert settings.is_git_configured == False
|
assert settings.is_git_configured == False
|
||||||
|
|
||||||
|
|
||||||
|
class TestDocsModeSettings:
|
||||||
|
"""Test documentation mode 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_default_docs_mode(self, isolated_settings):
|
||||||
|
"""Default docs mode is auto."""
|
||||||
|
settings = isolated_settings
|
||||||
|
assert settings.docs_mode == "auto"
|
||||||
|
|
||||||
|
def test_set_docs_mode(self, isolated_settings):
|
||||||
|
"""Can set docs mode."""
|
||||||
|
settings = isolated_settings
|
||||||
|
|
||||||
|
settings.docs_mode = "standalone"
|
||||||
|
assert settings.docs_mode == "standalone"
|
||||||
|
|
||||||
|
settings.docs_mode = "project-docs"
|
||||||
|
assert settings.docs_mode == "project-docs"
|
||||||
|
|
||||||
|
def test_effective_docs_mode_standalone_when_no_project_docs(self, isolated_settings, tmp_path):
|
||||||
|
"""Effective mode is standalone when project-docs doesn't exist."""
|
||||||
|
settings = isolated_settings
|
||||||
|
settings.project_search_paths = [str(tmp_path)]
|
||||||
|
|
||||||
|
# No project-docs folder exists
|
||||||
|
assert settings.effective_docs_mode == "standalone"
|
||||||
|
|
||||||
|
def test_effective_docs_mode_project_docs_when_exists(self, isolated_settings, tmp_path):
|
||||||
|
"""Effective mode is project-docs when folder exists."""
|
||||||
|
settings = isolated_settings
|
||||||
|
settings.project_search_paths = [str(tmp_path)]
|
||||||
|
|
||||||
|
# Create project-docs folder
|
||||||
|
(tmp_path / "project-docs").mkdir()
|
||||||
|
|
||||||
|
assert settings.effective_docs_mode == "project-docs"
|
||||||
|
|
||||||
|
def test_explicit_mode_overrides_auto(self, isolated_settings, tmp_path):
|
||||||
|
"""Explicit mode setting overrides auto-detection."""
|
||||||
|
settings = isolated_settings
|
||||||
|
settings.project_search_paths = [str(tmp_path)]
|
||||||
|
|
||||||
|
# Create project-docs (would trigger project-docs mode in auto)
|
||||||
|
(tmp_path / "project-docs").mkdir()
|
||||||
|
|
||||||
|
# But explicitly set to standalone
|
||||||
|
settings.docs_mode = "standalone"
|
||||||
|
assert settings.effective_docs_mode == "standalone"
|
||||||
|
|
||||||
|
def test_docs_root_standalone_mode(self, isolated_settings):
|
||||||
|
"""Docs root in standalone mode uses local share."""
|
||||||
|
settings = isolated_settings
|
||||||
|
settings.docs_mode = "standalone"
|
||||||
|
|
||||||
|
docs_root = settings.docs_root
|
||||||
|
assert ".local/share/development-hub" in str(docs_root)
|
||||||
|
|
||||||
|
def test_docusaurus_path_property(self, isolated_settings, tmp_path):
|
||||||
|
"""Can set and get docusaurus path."""
|
||||||
|
settings = isolated_settings
|
||||||
|
|
||||||
|
settings.docusaurus_path = tmp_path / "my-docs"
|
||||||
|
assert settings.docusaurus_path == tmp_path / "my-docs"
|
||||||
|
|
||||||
|
def test_pages_url_property(self, isolated_settings):
|
||||||
|
"""Can set and get pages URL."""
|
||||||
|
settings = isolated_settings
|
||||||
|
|
||||||
|
settings.pages_url = "https://pages.example.com"
|
||||||
|
assert settings.pages_url == "https://pages.example.com"
|
||||||
|
|
||||||
|
def test_pages_url_derived_from_gitea(self, isolated_settings):
|
||||||
|
"""Pages URL can be derived from gitea URL."""
|
||||||
|
settings = isolated_settings
|
||||||
|
|
||||||
|
settings.git_host_type = "gitea"
|
||||||
|
settings.git_host_url = "https://gitea.example.com"
|
||||||
|
|
||||||
|
# When not explicitly set, should derive
|
||||||
|
assert "pages.example.com" in settings.pages_url
|
||||||
|
|
||||||
|
def test_is_docs_enabled_standalone(self, isolated_settings):
|
||||||
|
"""Docs are always enabled in standalone mode."""
|
||||||
|
settings = isolated_settings
|
||||||
|
settings.docs_mode = "standalone"
|
||||||
|
|
||||||
|
assert settings.is_docs_enabled == True
|
||||||
|
|
||||||
|
def test_cmdforge_path_property(self, isolated_settings, tmp_path):
|
||||||
|
"""Can set and get cmdforge path."""
|
||||||
|
settings = isolated_settings
|
||||||
|
|
||||||
|
settings.cmdforge_path = tmp_path / "CmdForge"
|
||||||
|
assert settings.cmdforge_path == tmp_path / "CmdForge"
|
||||||
|
|
||||||
|
def test_progress_dir_property(self, isolated_settings, tmp_path):
|
||||||
|
"""Can set and get progress directory."""
|
||||||
|
settings = isolated_settings
|
||||||
|
|
||||||
|
settings.progress_dir = tmp_path / "progress"
|
||||||
|
assert settings.progress_dir == tmp_path / "progress"
|
||||||
|
|
||||||
|
|
||||||
|
class TestWorkspaceExportImport:
|
||||||
|
"""Test workspace file export/import."""
|
||||||
|
|
||||||
|
@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_export_workspace_creates_file(self, isolated_settings, tmp_path):
|
||||||
|
"""Export creates a YAML workspace file."""
|
||||||
|
settings = isolated_settings
|
||||||
|
workspace_path = tmp_path / "workspace.yaml"
|
||||||
|
|
||||||
|
settings.export_workspace(workspace_path)
|
||||||
|
|
||||||
|
assert workspace_path.exists()
|
||||||
|
|
||||||
|
def test_export_workspace_contains_required_fields(self, isolated_settings, tmp_path):
|
||||||
|
"""Exported workspace contains required fields."""
|
||||||
|
import yaml
|
||||||
|
|
||||||
|
settings = isolated_settings
|
||||||
|
settings.project_search_paths = [str(tmp_path / "projects")]
|
||||||
|
workspace_path = tmp_path / "workspace.yaml"
|
||||||
|
|
||||||
|
settings.export_workspace(workspace_path)
|
||||||
|
|
||||||
|
with open(workspace_path) as f:
|
||||||
|
workspace = yaml.safe_load(f)
|
||||||
|
|
||||||
|
assert "name" in workspace
|
||||||
|
assert "version" in workspace
|
||||||
|
assert workspace["version"] == 1
|
||||||
|
assert "paths" in workspace
|
||||||
|
assert "projects_root" in workspace["paths"]
|
||||||
|
assert "documentation" in workspace
|
||||||
|
assert "features" in workspace
|
||||||
|
|
||||||
|
def test_export_includes_git_hosting_when_configured(self, isolated_settings, tmp_path):
|
||||||
|
"""Exported workspace includes git hosting when configured."""
|
||||||
|
import yaml
|
||||||
|
|
||||||
|
settings = isolated_settings
|
||||||
|
settings.git_host_type = "github"
|
||||||
|
settings.git_host_url = "https://github.com"
|
||||||
|
settings.git_host_owner = "testuser"
|
||||||
|
|
||||||
|
workspace_path = tmp_path / "workspace.yaml"
|
||||||
|
settings.export_workspace(workspace_path)
|
||||||
|
|
||||||
|
with open(workspace_path) as f:
|
||||||
|
workspace = yaml.safe_load(f)
|
||||||
|
|
||||||
|
assert "git_hosting" in workspace
|
||||||
|
assert workspace["git_hosting"]["type"] == "github"
|
||||||
|
assert workspace["git_hosting"]["url"] == "https://github.com"
|
||||||
|
assert workspace["git_hosting"]["owner"] == "testuser"
|
||||||
|
|
||||||
|
def test_import_workspace_sets_values(self, isolated_settings, tmp_path):
|
||||||
|
"""Import workspace sets settings values."""
|
||||||
|
import yaml
|
||||||
|
|
||||||
|
settings = isolated_settings
|
||||||
|
|
||||||
|
workspace = {
|
||||||
|
"name": "Test Workspace",
|
||||||
|
"version": 1,
|
||||||
|
"paths": {
|
||||||
|
"projects_root": str(tmp_path / "my-projects")
|
||||||
|
},
|
||||||
|
"documentation": {
|
||||||
|
"mode": "standalone"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
workspace_path = tmp_path / "workspace.yaml"
|
||||||
|
with open(workspace_path, "w") as f:
|
||||||
|
yaml.dump(workspace, f)
|
||||||
|
|
||||||
|
results = settings.import_workspace(workspace_path)
|
||||||
|
|
||||||
|
assert "projects_root" in results["imported"]
|
||||||
|
assert settings.project_search_paths == [str(tmp_path / "my-projects")]
|
||||||
|
assert settings.docs_mode == "standalone"
|
||||||
|
|
||||||
|
def test_import_workspace_sets_git_hosting(self, isolated_settings, tmp_path):
|
||||||
|
"""Import workspace sets git hosting values."""
|
||||||
|
import yaml
|
||||||
|
|
||||||
|
settings = isolated_settings
|
||||||
|
|
||||||
|
workspace = {
|
||||||
|
"name": "Test",
|
||||||
|
"version": 1,
|
||||||
|
"paths": {"projects_root": str(tmp_path)},
|
||||||
|
"git_hosting": {
|
||||||
|
"type": "gitea",
|
||||||
|
"url": "https://git.example.com",
|
||||||
|
"owner": "myorg",
|
||||||
|
"pages_url": "https://pages.example.com"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
workspace_path = tmp_path / "workspace.yaml"
|
||||||
|
with open(workspace_path, "w") as f:
|
||||||
|
yaml.dump(workspace, f)
|
||||||
|
|
||||||
|
settings.import_workspace(workspace_path)
|
||||||
|
|
||||||
|
assert settings.git_host_type == "gitea"
|
||||||
|
assert settings.git_host_url == "https://git.example.com"
|
||||||
|
assert settings.git_host_owner == "myorg"
|
||||||
|
assert settings.pages_url == "https://pages.example.com"
|
||||||
|
|
||||||
|
def test_import_marks_setup_completed(self, isolated_settings, tmp_path):
|
||||||
|
"""Import workspace marks setup as completed."""
|
||||||
|
import yaml
|
||||||
|
|
||||||
|
settings = isolated_settings
|
||||||
|
|
||||||
|
workspace = {
|
||||||
|
"name": "Test",
|
||||||
|
"version": 1,
|
||||||
|
"paths": {"projects_root": str(tmp_path)}
|
||||||
|
}
|
||||||
|
|
||||||
|
workspace_path = tmp_path / "workspace.yaml"
|
||||||
|
with open(workspace_path, "w") as f:
|
||||||
|
yaml.dump(workspace, f)
|
||||||
|
|
||||||
|
settings.import_workspace(workspace_path)
|
||||||
|
|
||||||
|
assert settings.get("setup_completed") == True
|
||||||
|
|
||||||
|
def test_export_import_round_trip(self, isolated_settings, tmp_path):
|
||||||
|
"""Export and import preserves settings."""
|
||||||
|
settings = isolated_settings
|
||||||
|
|
||||||
|
# Configure settings
|
||||||
|
settings.project_search_paths = [str(tmp_path / "projects")]
|
||||||
|
settings.docs_mode = "project-docs"
|
||||||
|
settings.git_host_type = "gitlab"
|
||||||
|
settings.git_host_url = "https://gitlab.com"
|
||||||
|
settings.git_host_owner = "myuser"
|
||||||
|
|
||||||
|
# Export
|
||||||
|
workspace_path = tmp_path / "workspace.yaml"
|
||||||
|
settings.export_workspace(workspace_path)
|
||||||
|
|
||||||
|
# Reset settings
|
||||||
|
Settings._instance = None
|
||||||
|
new_settings = Settings()
|
||||||
|
|
||||||
|
# Import
|
||||||
|
new_settings.import_workspace(workspace_path)
|
||||||
|
|
||||||
|
assert new_settings.project_search_paths == [str(tmp_path / "projects")]
|
||||||
|
assert new_settings.docs_mode == "project-docs"
|
||||||
|
assert new_settings.git_host_type == "gitlab"
|
||||||
|
assert new_settings.git_host_url == "https://gitlab.com"
|
||||||
|
assert new_settings.git_host_owner == "myuser"
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
pytest.main([__file__, "-v"])
|
pytest.main([__file__, "-v"])
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,227 @@
|
||||||
|
"""Tests for the SetupWizardDialog."""
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
from pathlib import Path
|
||||||
|
from unittest.mock import patch, MagicMock
|
||||||
|
|
||||||
|
from development_hub.settings import Settings
|
||||||
|
|
||||||
|
|
||||||
|
class TestSetupWizardStructure:
|
||||||
|
"""Test SetupWizardDialog structure without Qt app."""
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def isolated_settings(self, tmp_path, monkeypatch):
|
||||||
|
"""Create isolated settings for testing."""
|
||||||
|
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_wizard_import_succeeds(self):
|
||||||
|
"""SetupWizardDialog can be imported."""
|
||||||
|
from development_hub.dialogs import SetupWizardDialog
|
||||||
|
assert SetupWizardDialog is not None
|
||||||
|
|
||||||
|
def test_wizard_has_expected_methods(self):
|
||||||
|
"""SetupWizardDialog has expected methods."""
|
||||||
|
from development_hub.dialogs import SetupWizardDialog
|
||||||
|
|
||||||
|
# Check for key methods
|
||||||
|
assert hasattr(SetupWizardDialog, "_create_welcome_page")
|
||||||
|
assert hasattr(SetupWizardDialog, "_create_simple_mode_page")
|
||||||
|
assert hasattr(SetupWizardDialog, "_create_docs_mode_page")
|
||||||
|
assert hasattr(SetupWizardDialog, "_create_import_page")
|
||||||
|
assert hasattr(SetupWizardDialog, "_finish_simple_mode")
|
||||||
|
assert hasattr(SetupWizardDialog, "_finish_docs_mode")
|
||||||
|
assert hasattr(SetupWizardDialog, "_finish_import")
|
||||||
|
|
||||||
|
|
||||||
|
class TestWizardSettingsIntegration:
|
||||||
|
"""Test wizard settings integration logic."""
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def isolated_settings(self, tmp_path, monkeypatch):
|
||||||
|
"""Create isolated settings for testing."""
|
||||||
|
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_simple_mode_sets_standalone_docs(self, isolated_settings, tmp_path):
|
||||||
|
"""Simple mode configuration sets standalone docs mode."""
|
||||||
|
settings = isolated_settings
|
||||||
|
|
||||||
|
# Simulate what _finish_simple_mode does
|
||||||
|
projects_path = tmp_path / "Projects"
|
||||||
|
projects_path.mkdir()
|
||||||
|
|
||||||
|
settings.default_project_path = projects_path
|
||||||
|
settings.project_search_paths = [str(projects_path)]
|
||||||
|
settings.docs_mode = "standalone"
|
||||||
|
settings.set("setup_completed", True)
|
||||||
|
|
||||||
|
assert settings.docs_mode == "standalone"
|
||||||
|
assert settings.get("setup_completed") == True
|
||||||
|
|
||||||
|
def test_docs_mode_sets_project_docs(self, isolated_settings, tmp_path):
|
||||||
|
"""Documentation mode configuration sets project-docs mode."""
|
||||||
|
settings = isolated_settings
|
||||||
|
|
||||||
|
# Simulate what _finish_docs_mode does
|
||||||
|
projects_path = tmp_path / "PycharmProjects"
|
||||||
|
projects_path.mkdir()
|
||||||
|
docusaurus_path = projects_path / "project-docs"
|
||||||
|
docusaurus_path.mkdir()
|
||||||
|
|
||||||
|
settings.default_project_path = projects_path
|
||||||
|
settings.project_search_paths = [str(projects_path)]
|
||||||
|
settings.docs_mode = "project-docs"
|
||||||
|
settings.docusaurus_path = docusaurus_path
|
||||||
|
settings.auto_start_docs_server = True
|
||||||
|
settings.git_host_type = "gitea"
|
||||||
|
settings.git_host_url = "https://gitea.example.com"
|
||||||
|
settings.git_host_owner = "testuser"
|
||||||
|
settings.set("setup_completed", True)
|
||||||
|
|
||||||
|
assert settings.docs_mode == "project-docs"
|
||||||
|
assert settings.docusaurus_path == docusaurus_path
|
||||||
|
assert settings.auto_start_docs_server == True
|
||||||
|
assert settings.is_git_configured == True
|
||||||
|
assert settings.get("setup_completed") == True
|
||||||
|
|
||||||
|
def test_import_workspace_integration(self, isolated_settings, tmp_path):
|
||||||
|
"""Import workspace correctly sets all values."""
|
||||||
|
import yaml
|
||||||
|
|
||||||
|
settings = isolated_settings
|
||||||
|
|
||||||
|
# Create a workspace file
|
||||||
|
workspace = {
|
||||||
|
"name": "Test Workspace",
|
||||||
|
"version": 1,
|
||||||
|
"paths": {
|
||||||
|
"projects_root": str(tmp_path / "projects")
|
||||||
|
},
|
||||||
|
"documentation": {
|
||||||
|
"mode": "project-docs",
|
||||||
|
"docusaurus_path": str(tmp_path / "docs"),
|
||||||
|
"auto_start_server": False
|
||||||
|
},
|
||||||
|
"git_hosting": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com",
|
||||||
|
"owner": "myorg"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
workspace_path = tmp_path / "test-workspace.yaml"
|
||||||
|
with open(workspace_path, "w") as f:
|
||||||
|
yaml.dump(workspace, f)
|
||||||
|
|
||||||
|
# Import (simulates _finish_import)
|
||||||
|
results = settings.import_workspace(workspace_path)
|
||||||
|
|
||||||
|
assert settings.project_search_paths == [str(tmp_path / "projects")]
|
||||||
|
assert settings.docs_mode == "project-docs"
|
||||||
|
assert settings.docusaurus_path == tmp_path / "docs"
|
||||||
|
assert settings.auto_start_docs_server == False
|
||||||
|
assert settings.git_host_type == "github"
|
||||||
|
assert settings.git_host_owner == "myorg"
|
||||||
|
assert settings.get("setup_completed") == True
|
||||||
|
|
||||||
|
|
||||||
|
class TestWizardModeSelection:
|
||||||
|
"""Test wizard mode selection logic."""
|
||||||
|
|
||||||
|
def test_mode_values(self):
|
||||||
|
"""Test expected mode values."""
|
||||||
|
modes = ["simple", "docs", "import"]
|
||||||
|
|
||||||
|
# These are the modes used in the wizard
|
||||||
|
for mode in modes:
|
||||||
|
assert isinstance(mode, str)
|
||||||
|
|
||||||
|
def test_docs_mode_settings_values(self):
|
||||||
|
"""Test valid docs_mode setting values."""
|
||||||
|
valid_modes = ["auto", "standalone", "project-docs"]
|
||||||
|
|
||||||
|
for mode in valid_modes:
|
||||||
|
assert isinstance(mode, str)
|
||||||
|
|
||||||
|
|
||||||
|
class TestWizardGitTypeHandling:
|
||||||
|
"""Test git type handling in wizard."""
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def isolated_settings(self, tmp_path, monkeypatch):
|
||||||
|
"""Create isolated settings."""
|
||||||
|
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_github_configuration(self, isolated_settings):
|
||||||
|
"""GitHub configuration sets correct values."""
|
||||||
|
settings = isolated_settings
|
||||||
|
|
||||||
|
settings.git_host_type = "github"
|
||||||
|
settings.git_host_url = "https://github.com"
|
||||||
|
settings.git_host_owner = "testuser"
|
||||||
|
|
||||||
|
assert settings.is_git_configured == True
|
||||||
|
assert settings.git_host_type == "github"
|
||||||
|
|
||||||
|
def test_gitlab_configuration(self, isolated_settings):
|
||||||
|
"""GitLab configuration sets correct values."""
|
||||||
|
settings = isolated_settings
|
||||||
|
|
||||||
|
settings.git_host_type = "gitlab"
|
||||||
|
settings.git_host_url = "https://gitlab.com"
|
||||||
|
settings.git_host_owner = "testuser"
|
||||||
|
|
||||||
|
assert settings.is_git_configured == True
|
||||||
|
assert settings.git_host_type == "gitlab"
|
||||||
|
|
||||||
|
def test_gitea_configuration(self, isolated_settings):
|
||||||
|
"""Gitea configuration sets correct values."""
|
||||||
|
settings = isolated_settings
|
||||||
|
|
||||||
|
settings.git_host_type = "gitea"
|
||||||
|
settings.git_host_url = "https://gitea.example.com"
|
||||||
|
settings.git_host_owner = "testuser"
|
||||||
|
|
||||||
|
assert settings.is_git_configured == True
|
||||||
|
assert settings.git_host_type == "gitea"
|
||||||
|
|
||||||
|
def test_optional_git_in_simple_mode(self, isolated_settings, tmp_path):
|
||||||
|
"""Git is optional in simple mode."""
|
||||||
|
settings = isolated_settings
|
||||||
|
|
||||||
|
# Simple mode without git
|
||||||
|
settings.docs_mode = "standalone"
|
||||||
|
settings.project_search_paths = [str(tmp_path)]
|
||||||
|
settings.set("setup_completed", True)
|
||||||
|
|
||||||
|
# Git not configured is OK
|
||||||
|
assert settings.is_git_configured == False
|
||||||
|
assert settings.get("setup_completed") == True
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
pytest.main([__file__, "-v"])
|
||||||
Loading…
Reference in New Issue