Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions src/modeldock/common/platform.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,22 +19,22 @@ def app_name() -> str:

def user_config_dir() -> Path:
"""Return the per-user config directory for ModelDock."""
return Path(platformdirs.user_config_dir(app_name(), roaming=True))
return Path(platformdirs.user_config_dir(app_name(), appauthor=False, roaming=True))


def user_cache_dir() -> Path:
"""Return the per-user cache directory for ModelDock."""
return Path(platformdirs.user_cache_dir(app_name()))
return Path(platformdirs.user_cache_dir(app_name(), appauthor=False))


def user_data_dir() -> Path:
"""Return the per-user data directory for ModelDock."""
return Path(platformdirs.user_data_dir(app_name()))
return Path(platformdirs.user_data_dir(app_name(), appauthor=False))


def system_config_dir() -> Path:
"""Return the system-wide config directory (may not exist)."""
return Path(platformdirs.site_config_dir(app_name()))
return Path(platformdirs.site_config_dir(app_name(), appauthor=False))


def default_cache_dir() -> Path:
Expand Down
130 changes: 130 additions & 0 deletions tests/unit/test_platform.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
"""Unit tests for common/platform.py, including Windows-specific path handling."""

from __future__ import annotations

import os
import sys
from unittest.mock import MagicMock
from pathlib import Path

import pytest

from modeldock.common import platform as md_platform

# ---------------------------------------------------------------------------
# Mocked tests — run on every OS, force Windows-style platformdirs output.
# ---------------------------------------------------------------------------


class TestWindowsPathsMocked:
"""Force platformdirs to return Windows-style paths and check our wrappers."""

def test_user_config_dir_windows(self, monkeypatch: pytest.MonkeyPatch) -> None:
mock_fn = MagicMock(return_value=r"C:\Users\test\AppData\Roaming\modeldock")
monkeypatch.setattr(md_platform.platformdirs, "user_config_dir", mock_fn)
result = md_platform.user_config_dir()
mock_fn.assert_called_once_with("modeldock", appauthor=False, roaming=True)
assert isinstance(result, Path)
assert result == Path(r"C:\Users\test\AppData\Roaming\modeldock")

def test_user_cache_dir_windows(self, monkeypatch: pytest.MonkeyPatch) -> None:
mock_fn = MagicMock(return_value=r"C:\Users\test\AppData\Local\modeldock\Cache")
monkeypatch.setattr(md_platform.platformdirs, "user_cache_dir", mock_fn)
result = md_platform.user_cache_dir()
mock_fn.assert_called_once_with("modeldock", appauthor=False)
assert isinstance(result, Path)
assert result == Path(r"C:\Users\test\AppData\Local\modeldock\Cache")

def test_user_data_dir_windows(self, monkeypatch: pytest.MonkeyPatch) -> None:
mock_fn = MagicMock(return_value=r"C:\Users\test\AppData\Local\modeldock")
monkeypatch.setattr(md_platform.platformdirs, "user_data_dir", mock_fn)
result = md_platform.user_data_dir()
mock_fn.assert_called_once_with("modeldock", appauthor=False)
assert isinstance(result, Path)
assert result == Path(r"C:\Users\test\AppData\Local\modeldock")

def test_system_config_dir_windows(self, monkeypatch: pytest.MonkeyPatch) -> None:
mock_fn = MagicMock(return_value=r"C:\ProgramData\modeldock")
monkeypatch.setattr(md_platform.platformdirs, "site_config_dir", mock_fn)
result = md_platform.system_config_dir()
mock_fn.assert_called_once_with("modeldock", appauthor=False)
assert isinstance(result, Path)
assert result == Path(r"C:\ProgramData\modeldock")

def test_default_cache_dir_windows_no_override(
self, monkeypatch: pytest.MonkeyPatch
) -> None:
monkeypatch.delenv("MODELDOCK_CACHE_DIR", raising=False)
mock_fn = MagicMock(return_value=r"C:\Users\test\AppData\Local\modeldock")
monkeypatch.setattr(md_platform.platformdirs, "user_cache_dir", mock_fn)
result = md_platform.default_cache_dir()
assert result == Path(r"C:\Users\test\AppData\Local\modeldock") / "models"

def test_default_cache_dir_windows_env_override(
self, monkeypatch: pytest.MonkeyPatch
) -> None:
monkeypatch.setenv("MODELDOCK_CACHE_DIR", r"D:\ModelDockCache")
result = md_platform.default_cache_dir()
assert result == Path(r"D:\ModelDockCache")

def test_is_windows_true(self, monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr(md_platform.os, "name", "nt")
assert md_platform.is_windows() is True

def test_is_windows_false_on_posix(self, monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr(md_platform.os, "name", "posix")
assert md_platform.is_windows() is False


# ---------------------------------------------------------------------------
# Real tests — only meaningful (and only run) on an actual Windows host,
# e.g. the Windows job in the CI matrix. Exercises real platformdirs
# behavior rather than mocks.
# ---------------------------------------------------------------------------


@pytest.mark.skipif(sys.platform != "win32", reason="Windows-only path behavior")
class TestWindowsPathsReal:
"""Exercise real (unmocked) platformdirs output on an actual Windows host."""

def test_is_windows_true_on_real_windows(self) -> None:
assert md_platform.is_windows() is True

def test_user_config_dir_under_real_appdata(self) -> None:
result = md_platform.user_config_dir()
appdata = os.environ.get("APPDATA", "")
assert appdata and str(result).lower().startswith(appdata.lower())

def test_user_config_dir_not_doubled(self) -> None:
"""Regression test: appauthor must not default to appname
(previously produced AppData\\Roaming\\modeldock\\modeldock)."""
result = md_platform.user_config_dir()
assert str(result).lower().count("modeldock") == 1

def test_user_cache_dir_under_real_localappdata(self) -> None:
result = md_platform.user_cache_dir()
localappdata = os.environ["LOCALAPPDATA"]
assert str(result).lower().startswith(localappdata.lower())

def test_user_cache_dir_not_doubled(self) -> None:
result = md_platform.user_cache_dir()
assert str(result).lower().count("modeldock") == 1

def test_user_data_dir_not_doubled(self) -> None:
"""Regression test: appauthor must not default to appname for the
data dir either (mirrors the config/cache doubling bug)."""
result = md_platform.user_data_dir()
assert str(result).lower().count("modeldock") == 1

def test_paths_use_backslash_separators(self) -> None:
# Real Windows paths render with backslashes via str(Path).
assert "\\" in str(md_platform.user_config_dir())
assert "\\" in str(md_platform.user_cache_dir())

def test_default_cache_dir_env_override_with_windows_drive_letter(
self, monkeypatch: pytest.MonkeyPatch
) -> None:
monkeypatch.setenv("MODELDOCK_CACHE_DIR", r"E:\Custom\Cache")
result = md_platform.default_cache_dir()
assert result == Path(r"E:\Custom\Cache")
assert result.drive == "E:"
Loading