gabriel / muse public
test_domains_publish_supported_commands.py python
188 lines 7.2 KB
Raw
sha256:24d9cb81ac1dd071e5d98862c740ad101180588361b4f96a790af79a99aed901 docs: architectural plan for unified-store snapshot-content… Sonnet 5 9 days ago
1 """Tests for auto-derived `supported_commands` in `muse domains publish` —
2 muse#74 Phase 3 (SCR_08-12).
3
4 Before this fix, `run_publish`'s auto-derive path (no `--capabilities`)
5 hardcoded `supported_commands=["commit", "diff", "merge", "log", "status"]`
6 for every domain, regardless of what it actually supports. This file pins
7 the fix: the auto-derive path (and the explicit-`--capabilities`-with-
8 omitted-key path, publish only) now call
9 `domain_command_registry.get_supported_commands(active_domain_name)`
10 instead.
11
12 `run_update` is deliberately excluded from this fallback (see the plan's
13 Design section) — SCR_12 pins that its omitted-key behavior stays `[]`,
14 unchanged.
15 """
16 from __future__ import annotations
17
18 import json
19 import pathlib
20 import unittest.mock
21 import urllib.request
22 from typing import TYPE_CHECKING
23
24 from tests.cli_test_helper import CliRunner
25
26 from muse._version import __version__
27 from muse.core.paths import muse_dir
28
29 if TYPE_CHECKING:
30 from muse.core.transport import SigningIdentity
31
32 cli = None # argparse migration — CliRunner ignores this arg
33
34 runner = CliRunner()
35
36
37 def _make_signing() -> "SigningIdentity":
38 from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
39 from muse.core.transport import SigningIdentity
40 return SigningIdentity(handle="testuser", private_key=Ed25519PrivateKey.generate())
41
42
43 def _make_repo(tmp_path: pathlib.Path, monkeypatch, domain: str) -> pathlib.Path:
44 dot_muse = muse_dir(tmp_path)
45 (dot_muse / "refs" / "heads").mkdir(parents=True)
46 (dot_muse / "objects").mkdir()
47 (dot_muse / "commits").mkdir()
48 (dot_muse / "snapshots").mkdir()
49 (dot_muse / "repo.json").write_text(
50 json.dumps({"repo_id": "test-repo", "schema_version": __version__, "domain": domain})
51 )
52 (dot_muse / "HEAD").write_text("ref: refs/heads/main\n")
53 monkeypatch.chdir(tmp_path)
54 monkeypatch.setattr("muse.cli.commands.domains.get_signing_identity", lambda *a, **kw: _make_signing())
55 return tmp_path
56
57
58 def _mock_urlopen_capture() -> tuple[list[urllib.request.Request], object]:
59 captured: list[urllib.request.Request] = []
60
61 def fake_urlopen(
62 req: urllib.request.Request, timeout: float | None = None, context: object = None
63 ) -> unittest.mock.MagicMock:
64 captured.append(req)
65 resp = unittest.mock.MagicMock()
66 resp.read.return_value = json.dumps(
67 {"domain_id": "d1", "scoped_id": "@testuser/genomics", "manifest_hash": "sha256:abc"}
68 ).encode()
69 resp.__enter__ = unittest.mock.MagicMock(return_value=resp)
70 resp.__exit__ = unittest.mock.MagicMock(return_value=False)
71 return resp
72
73 return captured, fake_urlopen
74
75
76 _BASE_ARGS = [
77 "domains", "publish",
78 "--author", "testuser",
79 "--slug", "genomics",
80 "--name", "Genomics",
81 "--description", "Version DNA sequences",
82 "--viewer-type", "genome",
83 "--hub", "https://hub.test",
84 ]
85
86
87 def _sent_supported_commands(captured: list[urllib.request.Request]) -> list[str]:
88 assert len(captured) == 1, "expected exactly one HTTP request"
89 body = json.loads(captured[0].data.decode())
90 return list(body["capabilities"]["supported_commands"])
91
92
93 class TestAutoDeriveFromRealCliNamespace:
94 def test_scr08_code_domain_gets_real_commands_not_hardcoded_five(
95 self, tmp_path: pathlib.Path, monkeypatch: "pytest.MonkeyPatch" # noqa: F821
96 ) -> None:
97 _make_repo(tmp_path, monkeypatch, domain="code")
98 captured, fake_urlopen = _mock_urlopen_capture()
99
100 with unittest.mock.patch("urllib.request.urlopen", fake_urlopen):
101 result = runner.invoke(cli, _BASE_ARGS)
102
103 assert result.exit_code == 0, result.output
104 commands = _sent_supported_commands(captured)
105 assert commands != ["commit", "diff", "merge", "log", "status"], (
106 "still sending the old hardcoded universal list"
107 )
108 for anchor in ("grep", "impact", "symbols"):
109 assert anchor in commands, f"{anchor!r} missing from published code capabilities"
110 assert len(commands) > 30
111
112 def test_scr09_identity_domain_gets_empty_list(
113 self, tmp_path: pathlib.Path, monkeypatch: "pytest.MonkeyPatch" # noqa: F821
114 ) -> None:
115 _make_repo(tmp_path, monkeypatch, domain="identity")
116 captured, fake_urlopen = _mock_urlopen_capture()
117
118 with unittest.mock.patch("urllib.request.urlopen", fake_urlopen):
119 result = runner.invoke(cli, _BASE_ARGS)
120
121 assert result.exit_code == 0, result.output
122 assert _sent_supported_commands(captured) == []
123
124
125 class TestExplicitCapabilitiesOverride:
126 def test_scr10_explicit_supported_commands_key_overrides_auto_derive(
127 self, tmp_path: pathlib.Path, monkeypatch: "pytest.MonkeyPatch" # noqa: F821
128 ) -> None:
129 _make_repo(tmp_path, monkeypatch, domain="code")
130 captured, fake_urlopen = _mock_urlopen_capture()
131 caps = json.dumps({
132 "dimensions": [{"name": "x", "description": "y"}],
133 "merge_semantics": "three_way",
134 "supported_commands": ["custom-one", "custom-two"],
135 })
136
137 with unittest.mock.patch("urllib.request.urlopen", fake_urlopen):
138 result = runner.invoke(cli, [*_BASE_ARGS, "--capabilities", caps])
139
140 assert result.exit_code == 0, result.output
141 assert _sent_supported_commands(captured) == ["custom-one", "custom-two"]
142
143 def test_scr11_explicit_capabilities_without_supported_commands_key_falls_back_to_auto_derive(
144 self, tmp_path: pathlib.Path, monkeypatch: "pytest.MonkeyPatch" # noqa: F821
145 ) -> None:
146 _make_repo(tmp_path, monkeypatch, domain="code")
147 captured, fake_urlopen = _mock_urlopen_capture()
148 caps = json.dumps({
149 "dimensions": [{"name": "x", "description": "y"}],
150 "merge_semantics": "three_way",
151 })
152
153 with unittest.mock.patch("urllib.request.urlopen", fake_urlopen):
154 result = runner.invoke(cli, [*_BASE_ARGS, "--capabilities", caps])
155
156 assert result.exit_code == 0, result.output
157 commands = _sent_supported_commands(captured)
158 assert commands != []
159 assert "grep" in commands
160
161
162 class TestUpdateBehaviorDeliberatelyUnchanged:
163 def test_scr12_update_with_capabilities_and_no_supported_commands_key_stays_empty(
164 self, tmp_path: pathlib.Path, monkeypatch: "pytest.MonkeyPatch" # noqa: F821
165 ) -> None:
166 _make_repo(tmp_path, monkeypatch, domain="code")
167 captured, fake_urlopen = _mock_urlopen_capture()
168 caps = json.dumps({
169 "dimensions": [{"name": "x", "description": "y"}],
170 "merge_semantics": "three_way",
171 })
172 update_args = [
173 "domains", "update",
174 "--author", "testuser",
175 "--slug", "genomics",
176 "--capabilities", caps,
177 "--hub", "https://hub.test",
178 ]
179
180 with unittest.mock.patch("urllib.request.urlopen", fake_urlopen):
181 result = runner.invoke(cli, update_args)
182
183 assert result.exit_code == 0, result.output
184 assert len(captured) == 1
185 body = json.loads(captured[0].data.decode())
186 # `update` sends the whole capabilities object only when provided;
187 # this pins that it is NOT auto-derived — omitted key stays [].
188 assert body["capabilities"]["supported_commands"] == []
File History 1 commit
sha256:24d9cb81ac1dd071e5d98862c740ad101180588361b4f96a790af79a99aed901 docs: architectural plan for unified-store snapshot-content… Sonnet 5 9 days ago