diff --git a/tests/test_test_sandbox.py b/tests/test_test_sandbox.py index c3442bfa..7f4a475d 100644 --- a/tests/test_test_sandbox.py +++ b/tests/test_test_sandbox.py @@ -246,6 +246,62 @@ def test_appcontroller_init_does_not_load_config() -> None: raise AssertionError("AppController.__init__ not found") +def test_config_overrides_toml_has_paths_section() -> None: + """The auto-generated config_overrides.toml must include a [paths] section + that overrides every path getter to point inside _ISOLATION_WORKSPACE. + This is the v2 design (FR2 + per-path routing via config.toml, no env vars). + [C: tests/conftest.py:isolate_workspace]""" + import tomllib + runs = sorted(Path("tests/artifacts").glob("_isolation_workspace_*")) + assert runs, "no isolation workspaces found — did a test run yet?" + latest = runs[-1] + config_file = latest / "config_overrides.toml" + assert config_file.exists(), f"missing {config_file}" + with open(config_file, "rb") as f: + cfg = tomllib.load(f) + assert "paths" in cfg, ( + f"config_overrides.toml must contain a [paths] section; " + f"got sections: {list(cfg.keys())}" + ) + paths = cfg["paths"] + expected_keys = { + "presets", "tool_presets", "personas", "themes", + "workspace_profiles", "credentials", "logs_dir", "scripts_dir", + } + missing = expected_keys - set(paths.keys()) + assert not missing, f"missing [paths] keys: {missing}" + for key, value in paths.items(): + assert str(latest) in str(value), ( + f"[paths].{key} = '{value}' does not point inside {latest}" + ) + + +def test_path_getters_read_from_config_paths_section() -> None: + """Every global path getter in src/paths.py reads from the [paths] section + of the active config when no env var is set. Verifies via AST that each + get_*_path() function calls _resolve_path (not raw os.environ.get).""" + import ast + paths_py = Path(__file__).resolve().parent.parent / "src" / "paths.py" + tree = ast.parse(paths_py.read_text(encoding="utf-8")) + getters = { + "get_global_presets_path": "presets", + "get_global_tool_presets_path": "tool_presets", + "get_global_personas_path": "personas", + "get_global_themes_path": "themes", + "get_global_workspace_profiles_path": "workspace_profiles", + "get_credentials_path": "credentials", + "get_logs_dir": "logs_dir", + "get_scripts_dir": "scripts_dir", + } + for node in ast.walk(tree): + if isinstance(node, ast.FunctionDef) and node.name in getters: + src = ast.unparse(node) + assert "_resolve_path(" in src, ( + f"{node.name} must call _resolve_path() to read from config.toml [paths]; " + f"got direct os.environ.get() instead." + ) + + @pytest.mark.skipif(os.name != "nt", reason="Windows-only sandbox wrapper") def test_run_tests_sandboxed_whatif() -> None: """pwsh -File scripts/run_tests_sandboxed.ps1 -WhatIf exits 0 without