import json import os from pathlib import Path import pytest @pytest.fixture def tmp_pyra_home(tmp_path, monkeypatch): """Redirect pyra_home() to a temporary directory for isolation.""" fake_home = tmp_path / ".pyra" # Patch Path.home() so pyra_home() returns our tmp dir monkeypatch.setattr( "pyra.utils.paths.Path", type("FakePath", (Path,), {"home": staticmethod(lambda: tmp_path)}), ) # Also patch the module-level constants already computed import pyra.security.boundaries as b import pyra.memory.index as mi import pyra.memory.reader as mr import pyra.memory.writer as mw import pyra.vault.reader as vr import pyra.vault.writer as vw import pyra.security.injection as si import pyra.config.manager as cm b.VAULT_PATH = fake_home / "vault" b.BLOCKED_PREFIXES = [b.VAULT_PATH] mi._MEMORY_ROOT = fake_home / "memory" mi._INDEX_FILE = fake_home / "memory" / "MEMORY_INDEX.md" mr._MEMORY_ROOT = fake_home / "memory" mw._MEMORY_ROOT = fake_home / "memory" vr._KEYS_FILE = fake_home / "vault" / "secrets" / "api_keys.json" vw._KEYS_FILE = fake_home / "vault" / "secrets" / "api_keys.json" si._LOG_FILE = fake_home / "security.log" cm._CONFIG_PATH = fake_home / "config.yaml" # Bootstrap the directory structure from pyra.config.dirs import bootstrap (fake_home / "vault").mkdir(parents=True) (fake_home / "vault" / "secrets").mkdir() (fake_home / "vault" / ".vault_lock").touch(mode=0o400) (fake_home / "memory" / "user").mkdir(parents=True) (fake_home / "memory" / "context").mkdir() (fake_home / "memory" / "knowledge").mkdir() return fake_home @pytest.fixture def lmstudio_available(): """Skip test if LM Studio is not reachable.""" import httpx try: r = httpx.get("http://localhost:1234/v1/models", timeout=2.0) r.raise_for_status() return True except Exception: pytest.skip("LM Studio not reachable at localhost:1234")