From 7acd7d2eda18c815ae8ea72de8e91aa6ec97ae67 Mon Sep 17 00:00:00 2001 From: Pascal Date: Wed, 24 Jun 2026 09:26:34 +0200 Subject: [PATCH 1/3] test: isolate integration test home Assisted-by: Codex (model: GPT-5, autonomous) --- tests/integrations/conftest.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tests/integrations/conftest.py b/tests/integrations/conftest.py index 54f59e23a7..42135402a9 100644 --- a/tests/integrations/conftest.py +++ b/tests/integrations/conftest.py @@ -1,8 +1,24 @@ """Shared test helpers for integration tests.""" +import pytest + from specify_cli.integrations.base import MarkdownIntegration +@pytest.fixture(autouse=True) +def _isolate_integration_home(monkeypatch: pytest.MonkeyPatch, tmp_path): + """Keep integration tests from reading or writing the real user home.""" + home = tmp_path / "home" + for path in (home, home / ".cache", home / ".config", home / ".local" / "share"): + path.mkdir(parents=True, exist_ok=True) + + monkeypatch.setenv("HOME", str(home)) + monkeypatch.setenv("USERPROFILE", str(home)) + monkeypatch.setenv("XDG_CACHE_HOME", str(home / ".cache")) + monkeypatch.setenv("XDG_CONFIG_HOME", str(home / ".config")) + monkeypatch.setenv("XDG_DATA_HOME", str(home / ".local" / "share")) + + class StubIntegration(MarkdownIntegration): """Minimal concrete integration for testing.""" From e1aaf479d12e6af0b5c2b7bd076cd5600f0543a8 Mon Sep 17 00:00:00 2001 From: Pascal Date: Fri, 3 Jul 2026 00:03:05 +0200 Subject: [PATCH 2/3] test: assert integration home isolation Assisted-by: Codex (model: GPT-5, autonomous) --- tests/integrations/test_home_isolation.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 tests/integrations/test_home_isolation.py diff --git a/tests/integrations/test_home_isolation.py b/tests/integrations/test_home_isolation.py new file mode 100644 index 0000000000..e79ff5dff6 --- /dev/null +++ b/tests/integrations/test_home_isolation.py @@ -0,0 +1,21 @@ +"""Regression tests for integration-test environment isolation.""" + +from __future__ import annotations + +import os +from pathlib import Path + + +def test_integration_tests_use_tmp_home(tmp_path: Path) -> None: + home = tmp_path / "home" + + assert Path(os.environ["HOME"]) == home + assert Path(os.environ["USERPROFILE"]) == home + assert Path(os.environ["XDG_CACHE_HOME"]) == home / ".cache" + assert Path(os.environ["XDG_CONFIG_HOME"]) == home / ".config" + assert Path(os.environ["XDG_DATA_HOME"]) == home / ".local" / "share" + + assert home.is_dir() + assert (home / ".cache").is_dir() + assert (home / ".config").is_dir() + assert (home / ".local" / "share").is_dir() From ce065dbdae8b0cd49f62d10d5b4cfcd5ccfb2f83 Mon Sep 17 00:00:00 2001 From: Pascal Date: Tue, 7 Jul 2026 00:17:09 +0200 Subject: [PATCH 3/3] test: extend integration home isolation to module-scoped setup Address Copilot review on #3144. Add a session-scoped autouse fixture so HOME/USERPROFILE/XDG are redirected for setup that runs outside a test function (e.g. the module-scoped status_* fixtures in test_integration_subcommand.py that run `specify init` before any per-test isolation applies). The function-scoped fixture still overrides HOME per test. Also assert Path.home() resolves to the isolated home, since most integrations (Hermes, catalog) read the home via that API rather than the env vars directly. --- tests/integrations/conftest.py | 28 +++++++++++++++++++---- tests/integrations/test_home_isolation.py | 4 ++++ 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/tests/integrations/conftest.py b/tests/integrations/conftest.py index 4671872356..4b7806c4a5 100644 --- a/tests/integrations/conftest.py +++ b/tests/integrations/conftest.py @@ -5,10 +5,8 @@ from specify_cli.integrations.base import MarkdownIntegration -@pytest.fixture(autouse=True) -def _isolate_integration_home(monkeypatch: pytest.MonkeyPatch, tmp_path): - """Keep integration tests from reading or writing the real user home.""" - home = tmp_path / "home" +def _redirect_home(monkeypatch: pytest.MonkeyPatch, home) -> None: + """Point HOME/USERPROFILE/XDG env vars at an isolated *home* directory.""" for path in (home, home / ".cache", home / ".config", home / ".local" / "share"): path.mkdir(parents=True, exist_ok=True) @@ -19,6 +17,28 @@ def _isolate_integration_home(monkeypatch: pytest.MonkeyPatch, tmp_path): monkeypatch.setenv("XDG_DATA_HOME", str(home / ".local" / "share")) +@pytest.fixture(scope="session", autouse=True) +def _isolate_integration_home_session(tmp_path_factory): + """Isolate the user home for setup that runs outside a test function. + + The per-test fixture below re-points HOME for each test, but function-scoped + fixtures do not apply to module-/session-scoped fixtures. Some of those (e.g. + the ``status_*_template`` fixtures in ``test_integration_subcommand.py``) run + ``specify init`` during setup, before any per-test isolation takes effect. + A standalone ``MonkeyPatch`` gives them an isolated home too. + """ + monkeypatch = pytest.MonkeyPatch() + _redirect_home(monkeypatch, tmp_path_factory.mktemp("session-home")) + yield + monkeypatch.undo() + + +@pytest.fixture(autouse=True) +def _isolate_integration_home(monkeypatch: pytest.MonkeyPatch, tmp_path): + """Keep integration tests from reading or writing the real user home.""" + _redirect_home(monkeypatch, tmp_path / "home") + + class StubIntegration(MarkdownIntegration): """Minimal concrete integration for testing.""" diff --git a/tests/integrations/test_home_isolation.py b/tests/integrations/test_home_isolation.py index e79ff5dff6..c862b16869 100644 --- a/tests/integrations/test_home_isolation.py +++ b/tests/integrations/test_home_isolation.py @@ -15,6 +15,10 @@ def test_integration_tests_use_tmp_home(tmp_path: Path) -> None: assert Path(os.environ["XDG_CONFIG_HOME"]) == home / ".config" assert Path(os.environ["XDG_DATA_HOME"]) == home / ".local" / "share" + # Most integrations resolve the user home via Path.home() (e.g. Hermes, + # catalog), so the isolation has to reach that API, not just the env vars. + assert Path.home() == home + assert home.is_dir() assert (home / ".cache").is_dir() assert (home / ".config").is_dir()