feat(conductor): Implement configurable paths and mark track 'Conductor Path Configuration' as complete
This commit is contained in:
@@ -54,6 +54,16 @@ class VerificationLogger:
|
||||
f.write(f"{status} {self.test_name} ({result_msg})\n\n")
|
||||
print(f"[FINAL] {self.test_name}: {status} - {result_msg}")
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def reset_paths() -> Generator[None, None, None]:
|
||||
"""
|
||||
Autouse fixture that resets the paths global state before each test.
|
||||
"""
|
||||
from src import paths
|
||||
paths.reset_resolved()
|
||||
yield
|
||||
paths.reset_resolved()
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def reset_ai_client() -> Generator[None, None, None]:
|
||||
"""
|
||||
|
||||
@@ -15,6 +15,7 @@ def test_mcp_tool_call_is_dispatched(app_instance: App) -> None:
|
||||
mock_fc.args = {"file_path": "test.txt"}
|
||||
# 2. Construct the mock AI response (Gemini format)
|
||||
mock_response_with_tool = MagicMock()
|
||||
mock_response_with_tool.text = ""
|
||||
mock_part = MagicMock()
|
||||
mock_part.text = ""
|
||||
mock_part.function_call = mock_fc
|
||||
|
||||
@@ -48,6 +48,8 @@ def app_instance(mock_config: Path, mock_project: Path, monkeypatch: pytest.Monk
|
||||
app.ui_state = MagicMock()
|
||||
app.ui_files_base_dir = "."
|
||||
app.files = []
|
||||
app.controller = MagicMock()
|
||||
app.controller.event_queue = MagicMock()
|
||||
# Since we bypassed __init__, we need to bind the method manually
|
||||
# but python allows calling it directly.
|
||||
return app
|
||||
|
||||
@@ -11,20 +11,19 @@ def e2e_setup(tmp_path: Path, monkeypatch: Any) -> Any:
|
||||
# Ensure closed before starting
|
||||
session_logger.close_session()
|
||||
monkeypatch.setattr(session_logger, "_comms_fh", None)
|
||||
# Mock _LOG_DIR and _SCRIPTS_DIR in session_logger
|
||||
original_log_dir = session_logger._LOG_DIR
|
||||
session_logger._LOG_DIR = tmp_path / "logs"
|
||||
monkeypatch.setattr(session_logger, "_LOG_DIR", tmp_path / "logs")
|
||||
session_logger._LOG_DIR.mkdir(parents=True, exist_ok=True)
|
||||
original_scripts_dir = session_logger._SCRIPTS_DIR
|
||||
session_logger._SCRIPTS_DIR = tmp_path / "scripts" / "generated"
|
||||
monkeypatch.setattr(session_logger, "_SCRIPTS_DIR", tmp_path / "scripts" / "generated")
|
||||
session_logger._SCRIPTS_DIR.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
logs_dir = tmp_path / "logs"
|
||||
scripts_dir = tmp_path / "scripts" / "generated"
|
||||
logs_dir.mkdir(parents=True, exist_ok=True)
|
||||
scripts_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
from src import paths
|
||||
monkeypatch.setattr(paths, "get_logs_dir", lambda: logs_dir)
|
||||
monkeypatch.setattr(paths, "get_scripts_dir", lambda: scripts_dir)
|
||||
|
||||
yield tmp_path
|
||||
# Cleanup
|
||||
session_logger.close_session()
|
||||
session_logger._LOG_DIR = original_log_dir
|
||||
session_logger._SCRIPTS_DIR = original_scripts_dir
|
||||
|
||||
def test_logging_e2e(e2e_setup: Any) -> None:
|
||||
tmp_path = e2e_setup
|
||||
|
||||
@@ -28,8 +28,11 @@ class TestOrchestratorPMHistory(unittest.TestCase):
|
||||
with open(track_path / "spec.md", "w") as f:
|
||||
f.write(spec_content)
|
||||
|
||||
@patch('src.orchestrator_pm.CONDUCTOR_PATH', Path("test_conductor"))
|
||||
def test_get_track_history_summary(self) -> None:
|
||||
@patch('src.paths.get_archive_dir')
|
||||
@patch('src.paths.get_tracks_dir')
|
||||
def test_get_track_history_summary(self, mock_get_tracks: MagicMock, mock_get_archive: MagicMock) -> None:
|
||||
mock_get_archive.return_value = self.archive_dir
|
||||
mock_get_tracks.return_value = self.tracks_dir
|
||||
self.create_track(self.archive_dir, "track_001", "Initial Setup", "completed", "Setting up the project structure.")
|
||||
self.create_track(self.tracks_dir, "track_002", "Feature A", "in_progress", "Implementing Feature A.")
|
||||
summary = orchestrator_pm.get_track_history_summary()
|
||||
@@ -40,8 +43,11 @@ class TestOrchestratorPMHistory(unittest.TestCase):
|
||||
self.assertIn("in_progress", summary)
|
||||
self.assertIn("Implementing Feature A.", summary)
|
||||
|
||||
@patch('src.orchestrator_pm.CONDUCTOR_PATH', Path("test_conductor"))
|
||||
def test_get_track_history_summary_missing_files(self) -> None:
|
||||
@patch('src.paths.get_archive_dir')
|
||||
@patch('src.paths.get_tracks_dir')
|
||||
def test_get_track_history_summary_missing_files(self, mock_get_tracks: MagicMock, mock_get_archive: MagicMock) -> None:
|
||||
mock_get_archive.return_value = self.archive_dir
|
||||
mock_get_tracks.return_value = self.tracks_dir
|
||||
track_path = self.tracks_dir / "track_003"
|
||||
track_path.mkdir(exist_ok=True)
|
||||
with open(track_path / "metadata.json", "w") as f:
|
||||
|
||||
67
tests/test_paths.py
Normal file
67
tests/test_paths.py
Normal file
@@ -0,0 +1,67 @@
|
||||
import os
|
||||
import pytest
|
||||
from pathlib import Path
|
||||
from src import paths
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def reset_paths():
|
||||
paths.reset_resolved()
|
||||
yield
|
||||
paths.reset_resolved()
|
||||
|
||||
def test_default_paths():
|
||||
assert paths.get_conductor_dir() == Path("conductor")
|
||||
assert paths.get_logs_dir() == Path("logs/sessions")
|
||||
assert paths.get_scripts_dir() == Path("scripts/generated")
|
||||
assert paths.get_config_path() == Path("config.toml")
|
||||
assert paths.get_tracks_dir() == Path("conductor/tracks")
|
||||
assert paths.get_archive_dir() == Path("conductor/archive")
|
||||
|
||||
def test_env_var_overrides(monkeypatch):
|
||||
monkeypatch.setenv("SLOP_CONDUCTOR_DIR", "custom_conductor")
|
||||
monkeypatch.setenv("SLOP_LOGS_DIR", "custom_logs")
|
||||
monkeypatch.setenv("SLOP_SCRIPTS_DIR", "custom_scripts")
|
||||
|
||||
assert paths.get_conductor_dir() == Path("custom_conductor")
|
||||
assert paths.get_logs_dir() == Path("custom_logs")
|
||||
assert paths.get_scripts_dir() == Path("custom_scripts")
|
||||
assert paths.get_tracks_dir() == Path("custom_conductor/tracks")
|
||||
|
||||
def test_config_overrides(tmp_path, monkeypatch):
|
||||
config_file = tmp_path / "custom_config.toml"
|
||||
content = """
|
||||
[paths]
|
||||
conductor_dir = "cfg_conductor"
|
||||
logs_dir = "cfg_logs"
|
||||
scripts_dir = "cfg_scripts"
|
||||
"""
|
||||
config_file.write_text(content)
|
||||
monkeypatch.setenv("SLOP_CONFIG", str(config_file))
|
||||
|
||||
# Need to update the _CONFIG_PATH in paths.py since it's set at import
|
||||
# Actually, the get_config_path() uses _CONFIG_PATH which is Path(os.environ.get("SLOP_CONFIG", "config.toml"))
|
||||
# But it's defined at module level. Let's see if we can reload it or if monkeypatching early enough works.
|
||||
# In src/paths.py: _CONFIG_PATH: Path = Path(os.environ.get("SLOP_CONFIG", "config.toml"))
|
||||
# This is set when src.paths is first imported.
|
||||
|
||||
# For the test to work, we might need to manually set paths._CONFIG_PATH or reload the module.
|
||||
# paths._CONFIG_PATH = config_file # No longer needed
|
||||
|
||||
assert paths.get_conductor_dir() == Path("cfg_conductor")
|
||||
assert paths.get_logs_dir() == Path("cfg_logs")
|
||||
assert paths.get_scripts_dir() == Path("cfg_scripts")
|
||||
|
||||
def test_precedence(tmp_path, monkeypatch):
|
||||
config_file = tmp_path / "custom_config.toml"
|
||||
content = """
|
||||
[paths]
|
||||
conductor_dir = "cfg_conductor"
|
||||
"""
|
||||
config_file.write_text(content)
|
||||
monkeypatch.setenv("SLOP_CONFIG", str(config_file))
|
||||
monkeypatch.setenv("SLOP_CONDUCTOR_DIR", "env_conductor")
|
||||
|
||||
# paths._CONFIG_PATH = config_file # No longer needed
|
||||
|
||||
# Env var should take precedence over config
|
||||
assert paths.get_conductor_dir() == Path("env_conductor")
|
||||
@@ -9,21 +9,19 @@ def temp_logs(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> Generator[Path
|
||||
# Ensure closed before starting
|
||||
session_logger.close_session()
|
||||
monkeypatch.setattr(session_logger, "_comms_fh", None)
|
||||
# Mock _LOG_DIR in session_logger
|
||||
original_log_dir = session_logger._LOG_DIR
|
||||
session_logger._LOG_DIR = tmp_path / "logs"
|
||||
monkeypatch.setattr(session_logger, "_LOG_DIR", tmp_path / "logs")
|
||||
session_logger._LOG_DIR.mkdir(parents=True, exist_ok=True)
|
||||
# Mock _SCRIPTS_DIR
|
||||
original_scripts_dir = session_logger._SCRIPTS_DIR
|
||||
session_logger._SCRIPTS_DIR = tmp_path / "scripts" / "generated"
|
||||
monkeypatch.setattr(session_logger, "_SCRIPTS_DIR", tmp_path / "scripts" / "generated")
|
||||
session_logger._SCRIPTS_DIR.mkdir(parents=True, exist_ok=True)
|
||||
yield tmp_path / "logs"
|
||||
|
||||
log_dir = tmp_path / "logs"
|
||||
scripts_dir = tmp_path / "scripts" / "generated"
|
||||
log_dir.mkdir(parents=True, exist_ok=True)
|
||||
scripts_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
from src import paths
|
||||
monkeypatch.setattr(paths, "get_logs_dir", lambda: log_dir)
|
||||
monkeypatch.setattr(paths, "get_scripts_dir", lambda: scripts_dir)
|
||||
|
||||
yield log_dir
|
||||
# Cleanup: Close handles if open
|
||||
session_logger.close_session()
|
||||
session_logger._LOG_DIR = original_log_dir
|
||||
session_logger._SCRIPTS_DIR = original_scripts_dir
|
||||
|
||||
def test_open_session_creates_subdir_and_registry(temp_logs: Path) -> None:
|
||||
|
||||
|
||||
Reference in New Issue
Block a user