diff --git a/tests/test_gui_paths.py b/tests/test_gui_paths.py index 12240846..b862ca5f 100644 --- a/tests/test_gui_paths.py +++ b/tests/test_gui_paths.py @@ -26,7 +26,7 @@ def test_save_paths(): with patch('shutil.copy') as mock_copy, \ patch('src.paths.get_config_path') as mock_get_cfg, \ - patch('src.paths.reset_paths') as mock_reset, \ + patch('src.paths.initialize_paths') as mock_init_paths, \ patch.object(MockApp, 'init_state') as mock_init: mock_get_cfg.return_value = MagicMock() @@ -40,5 +40,5 @@ def test_save_paths(): mock_app.save_config.assert_called_once() mock_copy.assert_called_once() assert 'applied' in mock_app.ai_status - mock_reset.assert_called_once() + mock_init_paths.assert_called_once() mock_init.assert_called_once() \ No newline at end of file diff --git a/tests/test_orchestrator_pm_history.py b/tests/test_orchestrator_pm_history.py index 804f880b..465710f3 100644 --- a/tests/test_orchestrator_pm_history.py +++ b/tests/test_orchestrator_pm_history.py @@ -2,14 +2,16 @@ import unittest from unittest.mock import patch, MagicMock import shutil import json +import tempfile from pathlib import Path from src import orchestrator_pm from src.result_types import Result class TestOrchestratorPMHistory(unittest.TestCase): def setUp(self) -> None: - self.test_dir = Path("test_conductor") - self.test_dir.mkdir(exist_ok=True) + # v3 paths.py: use tempdir (under system temp) for test data instead of + # writing to project-root "test_conductor/" which the FR1 guard blocks. + self.test_dir = Path(tempfile.mkdtemp(prefix="test_orch_")) self.archive_dir = self.test_dir / "archive" self.tracks_dir = self.test_dir / "tracks" self.archive_dir.mkdir(exist_ok=True) diff --git a/tests/test_paths.py b/tests/test_paths.py index 6ec7ce27..660bff8a 100644 --- a/tests/test_paths.py +++ b/tests/test_paths.py @@ -1,29 +1,42 @@ -import os import pytest from pathlib import Path from src import paths @pytest.fixture(autouse=True) -def reset_paths(): - paths.reset_paths() +def restore_paths(): + # v3 paths.py: PathsConfig is frozen at init time. Save the pre-test + # config path so we can restore after this test (other tests rely on + # the conftest-initialized workspace). The fixtures themselves call + # paths.initialize_paths() to control resolution. + pre = paths.get_config_path() yield - paths.reset_paths() + paths.initialize_paths(pre) -def test_default_paths(tmp_path, monkeypatch): - monkeypatch.setenv("SLOP_CONFIG", str(tmp_path / "non_existent.toml")) +def test_default_paths(tmp_path): + # v3 paths.py: when config has no [paths] section, paths come from + # defaults. Pass a config that does NOT define [paths]. root_dir = Path(paths.__file__).resolve().parent.parent - assert paths.get_logs_dir() == root_dir / "logs/sessions" - assert paths.get_scripts_dir() == root_dir / "scripts/generated" - # config path should be what we set in env - assert paths.get_config_path() == tmp_path / "non_existent.toml" + empty_config = tmp_path / "empty.toml" + empty_config.write_text("# no [paths] section here\n") + paths.initialize_paths(empty_config) + + assert paths.get_logs_dir() == root_dir / "logs" / "sessions" + assert paths.get_scripts_dir() == root_dir / "scripts" / "generated" + assert paths.get_config_path() == empty_config.resolve() def test_env_var_overrides(tmp_path, monkeypatch): - # Absolute env var + # v3 paths.py: env var wins over config and default. Set env var, then + # init with a config that does NOT define [paths] (so only env + default apply). abs_logs = (tmp_path / "abs_logs").resolve() monkeypatch.setenv("SLOP_LOGS_DIR", str(abs_logs)) + empty_config = tmp_path / "empty.toml" + empty_config.write_text("# no [paths] section\n") + paths.initialize_paths(empty_config) assert paths.get_logs_dir() == abs_logs -def test_config_overrides(tmp_path, monkeypatch): +def test_config_overrides(tmp_path): + # v3 paths.py: [paths] section in config overrides default. Relative + # paths in config are resolved against project root. root_dir = Path(paths.__file__).resolve().parent.parent config_file = tmp_path / "custom_config.toml" content = """ @@ -32,12 +45,13 @@ logs_dir = "cfg_logs" scripts_dir = "cfg_scripts" """ config_file.write_text(content) - monkeypatch.setenv("SLOP_CONFIG", str(config_file)) + paths.initialize_paths(config_file) assert paths.get_logs_dir() == root_dir / "cfg_logs" assert paths.get_scripts_dir() == root_dir / "cfg_scripts" def test_precedence(tmp_path, monkeypatch): + # v3 paths.py: env var SLOP_LOGS_DIR wins over [paths] config entry. root_dir = Path(paths.__file__).resolve().parent.parent config_file = tmp_path / "custom_config.toml" content = """ @@ -45,11 +59,13 @@ def test_precedence(tmp_path, monkeypatch): logs_dir = "cfg_logs" """ config_file.write_text(content) - monkeypatch.setenv("SLOP_CONFIG", str(config_file)) - monkeypatch.setenv("SLOP_LOGS_DIR", "env_logs") + # Use absolute env_logs path so _resolve_path returns it as-is. + env_logs = (root_dir / "env_logs").resolve() + monkeypatch.setenv("SLOP_LOGS_DIR", str(env_logs)) + paths.initialize_paths(config_file) # Env var should take precedence over config - assert paths.get_logs_dir() == (root_dir / "env_logs").resolve() + assert paths.get_logs_dir() == env_logs def test_conductor_dir_project_relative(tmp_path): # Should default to tmp_path/conductor diff --git a/tests/test_summary_cache.py b/tests/test_summary_cache.py index 2bc43c9c..99df6967 100644 --- a/tests/test_summary_cache.py +++ b/tests/test_summary_cache.py @@ -9,11 +9,10 @@ def test_get_file_hash(): expected = "b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9" assert get_file_hash(content) == expected -def test_summary_cache(): - cache_dir = Path(".test_cache") - if cache_dir.exists(): - shutil.rmtree(cache_dir) - cache_file = cache_dir / "cache.json" +def test_summary_cache(tmp_path): + # v3 paths.py: use tmp_path (which is under ./tests/) instead of + # hardcoded project-root paths that the FR1 guard blocks. + cache_file = tmp_path / "cache.json" cache = SummaryCache(str(cache_file)) @@ -35,16 +34,11 @@ def test_summary_cache(): # Test persistence cache2 = SummaryCache(str(cache_file)) assert cache2.get_summary(file_path, content_hash) == summary - - # Cleanup - if cache_dir.exists(): - shutil.rmtree(cache_dir) -def test_summary_cache_lru(): - cache_dir = Path(".test_cache_lru") - if cache_dir.exists(): - shutil.rmtree(cache_dir) - cache_file = cache_dir / "cache.json" + +def test_summary_cache_lru(tmp_path): + # v3 paths.py: use tmp_path instead of hardcoded project-root paths. + cache_file = tmp_path / "cache.json" # Create cache with max 2 entries cache = SummaryCache(str(cache_file), max_entries=2) @@ -64,9 +58,6 @@ def test_summary_cache_lru(): assert cache.get_summary("file3.py", "hash3") is None assert cache.get_summary("file2.py", "hash2") == "summary2" assert cache.get_summary("file4.py", "hash4") == "summary4" - - if cache_dir.exists(): - shutil.rmtree(cache_dir) if __name__ == "__main__": test_get_file_hash()