test(sandbox): update v3 paths-aware tests for FR1+FR3 invariants
- test_paths.py: explicit initialize_paths(<empty_config>) instead of
SLOP_CONFIG env var (v3 design); add restore_paths fixture so other
tests keep their conftest workspace init.
- test_summary_cache.py: use tmp_path (under ./tests/) instead of
hardcoded Path('.test_cache') that FR1 blocks.
- test_orchestrator_pm_history.py: use tempfile.mkdtemp() instead of
writing to project-root 'test_conductor/' that FR1 blocks.
- test_gui_paths.py::test_save_paths: mock src.paths.initialize_paths
instead of src.paths.reset_paths (v3 entry point).
All 12 tests pass in the Tier 2 clone after these fixes.
This commit is contained in:
@@ -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()
|
||||
@@ -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)
|
||||
|
||||
+32
-16
@@ -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(<tmp_config>) 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
|
||||
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user