gabriel / muse public
conftest.py python
135 lines 5.4 KB
Raw
sha256:81ae324db5ad375fbfe4834c6fcb378312cafad3cc92dec5d3e5c427306621a2 fix: remove commit_exists filter from have anchors — server… Sonnet 4.6 patch 20 days ago
1 """Pytest configuration and shared fixtures for the muse test suite.
2
3 All fixtures defined here are automatically available to every test module
4 in the ``tests/`` directory without explicit import.
5 """
6
7 from __future__ import annotations
8
9 import os
10 import pathlib
11 from typing import Generator
12 from unittest.mock import patch
13
14 import pytest
15
16
17 @pytest.fixture(autouse=True)
18 def _isolate_muse_home(tmp_path: pathlib.Path) -> Generator[None, None, None]:
19 """Redirect every module-level constant that caches ``~/.muse/`` to a
20 throwaway temp directory for the duration of each test, and isolate the
21 OS keychain so tests never read or write gabriel's real mnemonic.
22
23 Without this, any test that exercises auth, identity, keychain, hub-trust,
24 or global config would read from *and write to* the real ``~/.muse/``,
25 silently clobbering the developer's Ed25519 identity and invalidating
26 push authentication against the local hub.
27
28 Constants patched because they are computed once at import time:
29
30 muse.core.keypair _KEYS_DIR
31 muse.core.identity _IDENTITY_DIR, _IDENTITY_FILE
32 muse.core.hub_trust _HUB_TRUST_FILE
33 muse.cli.config _GLOBAL_MUSE_DIR, _GLOBAL_CONFIG_FILE
34 muse.core.agent_slots _SLOTS_FILE, _SLOTS_DIR
35
36 Keychain functions patched to an in-memory store so no test can read or
37 overwrite the real OS keychain mnemonic:
38
39 muse.core.keychain is_available, store, load, delete
40
41 All tests that previously called a per-file ``_patch_home()`` helper
42 continue to work — their monkeypatches win over these because they run
43 *after* this fixture sets up the initial redirect.
44 """
45 from muse.core.paths import muse_dir
46
47 fake_home = tmp_path / "fake_home"
48 fake_home.mkdir(parents=True, exist_ok=True)
49 fake_muse = muse_dir(fake_home)
50 fake_muse.mkdir(parents=True, exist_ok=True)
51
52 import muse.cli.config as _cfg_mod
53 import muse.core.agent_slots as _slots_mod
54 import muse.core.hub_trust as _ht_mod
55 import muse.core.identity as _id_mod
56 import muse.core.keypair as _kp_mod
57 import muse.core.keychain as _kc_mod
58
59 # Fresh in-memory keychain for every test — never touches the OS keychain.
60 _kc_store: dict[str, str] = {}
61
62 with (
63 patch.object(pathlib.Path, "home", staticmethod(lambda: fake_home)),
64 patch.object(_kp_mod, "_KEYS_DIR", fake_muse / "keys"),
65 patch.object(_id_mod, "_IDENTITY_DIR", fake_muse),
66 patch.object(_id_mod, "_IDENTITY_FILE", fake_muse / "identity.toml"),
67 patch.object(_ht_mod, "_HUB_TRUST_FILE", fake_muse / "hub_trust.toml"),
68 patch.object(_cfg_mod, "_GLOBAL_MUSE_DIR", fake_muse),
69 patch.object(_cfg_mod, "_GLOBAL_CONFIG_FILE", fake_muse / "config.toml"),
70 patch.object(_slots_mod, "_SLOTS_FILE", fake_muse / "agent-slots.toml"),
71 patch.object(_slots_mod, "_SLOTS_DIR", fake_muse),
72 patch.object(_kc_mod, "is_available", lambda: True),
73 patch.object(_kc_mod, "store", lambda m: _kc_store.__setitem__("mnemonic", m) or True),
74 patch.object(_kc_mod, "load", lambda: _kc_store.get("mnemonic")),
75 patch.object(_kc_mod, "delete", lambda: _kc_store.pop("mnemonic", None) is not None),
76 ):
77 yield
78
79
80 @pytest.fixture(autouse=True)
81 def _restore_cwd() -> Generator[None, None, None]:
82 """Restore the working directory and MUSE_REPO_ROOT after every test.
83
84 Several tests call ``os.chdir()`` to enter a temporary repository, and
85 some directly mutate ``os.environ["MUSE_REPO_ROOT"]`` without cleanup.
86 Without this fixture those side-effects leak into subsequent tests,
87 causing ``find_repo_root()`` to resolve against a stale path and
88 producing spurious failures across the entire suite.
89
90 Note: module-scoped fixtures that set these values run *before* this
91 function-scoped fixture captures them, so they must manage their own
92 cleanup (yield + restore in the fixture body).
93 """
94 original_cwd = os.getcwd()
95 original_root = os.environ.get("MUSE_REPO_ROOT")
96 yield
97 os.chdir(original_cwd)
98 if original_root is None:
99 os.environ.pop("MUSE_REPO_ROOT", None)
100 else:
101 os.environ["MUSE_REPO_ROOT"] = original_root
102
103
104 @pytest.fixture()
105 def muse_repo(tmp_path: pathlib.Path) -> pathlib.Path:
106 """Fully initialised Muse repo in an isolated temp directory.
107
108 Runs ``muse init`` via the CLI so HEAD, repo.json, .museignore, and
109 .museattributes are all present — identical to what a real user sees after
110 ``muse init``. Use this fixture whenever you need a complete repo
111 (config, commit, checkout, push dry-runs, etc.).
112
113 The repo root is returned. To run CLI commands against it, pass
114 ``cwd=muse_repo`` to ``runner.invoke`` **or** set
115 ``env={"MUSE_REPO_ROOT": str(muse_repo)}``.
116 """
117 from tests.cli_test_helper import CliRunner
118 CliRunner().invoke(None, ["init"], cwd=tmp_path)
119 return tmp_path
120
121
122 @pytest.fixture()
123 def bare_muse_repo(tmp_path: pathlib.Path) -> pathlib.Path:
124 """Minimal ``.muse/`` directory tree in an isolated temp directory.
125
126 Calls :func:`muse.core.paths.init_repo_dirs` — creates the canonical
127 subdirectory layout without writing HEAD, repo.json, or any other file.
128 Use this for low-level unit tests that construct objects directly (commit
129 records, snapshots, bridge state) and don't need a full CLI-initialised
130 repo.
131
132 The repo root is returned.
133 """
134 from muse.core.paths import init_repo_dirs
135 return init_repo_dirs(tmp_path)
File History 4 commits
sha256:81ae324db5ad375fbfe4834c6fcb378312cafad3cc92dec5d3e5c427306621a2 fix: remove commit_exists filter from have anchors — server… Sonnet 4.6 patch 20 days ago
sha256:36c3cb3e76619d4c30a6d9bf81b5ec4ff148e30dcfed913e3114ca7b43b81c7e fix: rename objects→blobs in push client and all stale test… Sonnet 4.6 patch 22 days ago
sha256:c06a9b9b9fee26c68ea725b44d54b2c0a171301ce9de746d5b656617b4463a9a fix: repair four test failures from post-migration audit Sonnet 4.6 patch 28 days ago
sha256:1900655993c83c4107067375548a7be823e471d2515830842f1a12cba4bd3cdf fix: unified object store migration — idempotent writes, JS… Sonnet 4.6 minor 28 days ago