gabriel / muse public
test_cli_remote.py python
191 lines 8.0 KB
0841b933 Show fetch and push lines in muse remote -v, matching git remote -v Gabriel Cardona <gabriel@tellurstori.com> 17h ago
1 """Tests for muse remote — add, remove, rename, list, get-url, set-url."""
2
3 from __future__ import annotations
4
5 import json
6 import os
7 import pathlib
8
9 import pytest
10 from tests.cli_test_helper import CliRunner
11
12 from muse._version import __version__
13 cli = None # argparse migration — CliRunner ignores this arg
14 from muse.cli.config import get_remote, list_remotes
15
16
17 # ---------------------------------------------------------------------------
18 # Fixture — initialised repo
19 # ---------------------------------------------------------------------------
20
21
22 @pytest.fixture
23 def repo(tmp_path: pathlib.Path, monkeypatch: pytest.MonkeyPatch) -> pathlib.Path:
24 """Minimal .muse/ repo; cwd and MUSE_REPO_ROOT set to tmp_path."""
25 muse_dir = tmp_path / ".muse"
26 (muse_dir / "refs" / "heads").mkdir(parents=True)
27 (muse_dir / "objects").mkdir()
28 (muse_dir / "commits").mkdir()
29 (muse_dir / "snapshots").mkdir()
30 (muse_dir / "repo.json").write_text(
31 json.dumps({"repo_id": "test-repo", "schema_version": __version__, "domain": "midi"})
32 )
33 (muse_dir / "HEAD").write_text("ref: refs/heads/main\n")
34 (muse_dir / "refs" / "heads" / "main").write_text("")
35 (muse_dir / "config.toml").write_text("")
36 monkeypatch.setenv("MUSE_REPO_ROOT", str(tmp_path))
37 monkeypatch.chdir(tmp_path)
38 return tmp_path
39
40
41 runner = CliRunner()
42
43
44 # ---------------------------------------------------------------------------
45 # remote add
46 # ---------------------------------------------------------------------------
47
48
49 class TestRemoteAdd:
50 def test_add_new_remote(self, repo: pathlib.Path) -> None:
51 result = runner.invoke(cli, ["remote", "add", "origin", "https://hub.muse.io/repos/r1"])
52 assert result.exit_code == 0
53 assert "origin" in result.output
54 assert get_remote("origin", repo) == "https://hub.muse.io/repos/r1"
55
56 def test_add_duplicate_remote_fails(self, repo: pathlib.Path) -> None:
57 runner.invoke(cli, ["remote", "add", "origin", "https://hub.muse.io/repos/r1"])
58 result = runner.invoke(cli, ["remote", "add", "origin", "https://other.com/r"])
59 assert result.exit_code != 0
60 assert "already exists" in result.output
61
62 def test_add_multiple_remotes(self, repo: pathlib.Path) -> None:
63 runner.invoke(cli, ["remote", "add", "origin", "https://hub.muse.io/repos/r1"])
64 runner.invoke(cli, ["remote", "add", "upstream", "https://hub.muse.io/repos/r2"])
65 remotes = {r["name"] for r in list_remotes(repo)}
66 assert remotes == {"origin", "upstream"}
67
68
69 # ---------------------------------------------------------------------------
70 # remote remove
71 # ---------------------------------------------------------------------------
72
73
74 class TestRemoteRemove:
75 def test_remove_existing_remote(self, repo: pathlib.Path) -> None:
76 runner.invoke(cli, ["remote", "add", "origin", "https://hub.muse.io/repos/r1"])
77 result = runner.invoke(cli, ["remote", "remove", "origin"])
78 assert result.exit_code == 0
79 assert get_remote("origin", repo) is None
80
81 def test_remove_nonexistent_remote_fails(self, repo: pathlib.Path) -> None:
82 result = runner.invoke(cli, ["remote", "remove", "ghost"])
83 assert result.exit_code != 0
84 assert "does not exist" in result.output
85
86 def test_remove_cleans_tracking_refs(self, repo: pathlib.Path) -> None:
87 runner.invoke(cli, ["remote", "add", "origin", "https://hub.muse.io/repos/r1"])
88 refs_dir = repo / ".muse" / "remotes" / "origin"
89 refs_dir.mkdir(parents=True)
90 (refs_dir / "main").write_text("abc123")
91 runner.invoke(cli, ["remote", "remove", "origin"])
92 assert not refs_dir.exists()
93
94
95 # ---------------------------------------------------------------------------
96 # remote rename
97 # ---------------------------------------------------------------------------
98
99
100 class TestRemoteRename:
101 def test_rename_existing_remote(self, repo: pathlib.Path) -> None:
102 runner.invoke(cli, ["remote", "add", "origin", "https://hub.muse.io/repos/r1"])
103 result = runner.invoke(cli, ["remote", "rename", "origin", "upstream"])
104 assert result.exit_code == 0
105 assert get_remote("upstream", repo) == "https://hub.muse.io/repos/r1"
106 assert get_remote("origin", repo) is None
107
108 def test_rename_nonexistent_fails(self, repo: pathlib.Path) -> None:
109 result = runner.invoke(cli, ["remote", "rename", "ghost", "new"])
110 assert result.exit_code != 0
111 assert "does not exist" in result.output
112
113 def test_rename_to_existing_name_fails(self, repo: pathlib.Path) -> None:
114 runner.invoke(cli, ["remote", "add", "origin", "https://hub.muse.io/repos/r1"])
115 runner.invoke(cli, ["remote", "add", "upstream", "https://hub.muse.io/repos/r2"])
116 result = runner.invoke(cli, ["remote", "rename", "origin", "upstream"])
117 assert result.exit_code != 0
118 assert "already exists" in result.output
119
120
121 # ---------------------------------------------------------------------------
122 # muse remote (implied list)
123 # ---------------------------------------------------------------------------
124
125
126 class TestRemoteList:
127 def test_list_empty(self, repo: pathlib.Path) -> None:
128 result = runner.invoke(cli, ["remote"])
129 assert result.exit_code == 0
130 assert "No remotes" in result.output
131
132 def test_list_shows_names(self, repo: pathlib.Path) -> None:
133 runner.invoke(cli, ["remote", "add", "origin", "https://hub.muse.io/repos/r1"])
134 runner.invoke(cli, ["remote", "add", "upstream", "https://hub.muse.io/repos/r2"])
135 result = runner.invoke(cli, ["remote"])
136 assert result.exit_code == 0
137 assert "origin" in result.output
138 assert "upstream" in result.output
139
140 def test_list_verbose_shows_url(self, repo: pathlib.Path) -> None:
141 runner.invoke(cli, ["remote", "add", "origin", "https://hub.muse.io/repos/r1"])
142 result = runner.invoke(cli, ["remote", "-v"])
143 assert result.exit_code == 0
144 assert "https://hub.muse.io/repos/r1" in result.output
145
146 def test_list_verbose_shows_fetch_and_push(self, repo: pathlib.Path) -> None:
147 runner.invoke(cli, ["remote", "add", "origin", "https://hub.muse.io/repos/r1"])
148 result = runner.invoke(cli, ["remote", "-v"])
149 assert result.exit_code == 0
150 assert "(fetch)" in result.output
151 assert "(push)" in result.output
152 lines = [l for l in result.output.splitlines() if "hub.muse.io" in l]
153 assert len(lines) == 2, "verbose should print one fetch line and one push line"
154
155
156 # ---------------------------------------------------------------------------
157 # remote get-url
158 # ---------------------------------------------------------------------------
159
160
161 class TestRemoteGetUrl:
162 def test_get_url_existing(self, repo: pathlib.Path) -> None:
163 runner.invoke(cli, ["remote", "add", "origin", "https://hub.muse.io/repos/r1"])
164 result = runner.invoke(cli, ["remote", "get-url", "origin"])
165 assert result.exit_code == 0
166 assert "https://hub.muse.io/repos/r1" in result.output
167
168 def test_get_url_nonexistent_fails(self, repo: pathlib.Path) -> None:
169 result = runner.invoke(cli, ["remote", "get-url", "ghost"])
170 assert result.exit_code != 0
171 assert "does not exist" in result.output
172
173
174 # ---------------------------------------------------------------------------
175 # remote set-url
176 # ---------------------------------------------------------------------------
177
178
179 class TestRemoteSetUrl:
180 def test_set_url_updates_existing(self, repo: pathlib.Path) -> None:
181 runner.invoke(cli, ["remote", "add", "origin", "https://hub.muse.io/repos/r1"])
182 result = runner.invoke(
183 cli, ["remote", "set-url", "origin", "https://hub.muse.io/repos/r2"]
184 )
185 assert result.exit_code == 0
186 assert get_remote("origin", repo) == "https://hub.muse.io/repos/r2"
187
188 def test_set_url_nonexistent_fails(self, repo: pathlib.Path) -> None:
189 result = runner.invoke(cli, ["remote", "set-url", "ghost", "https://example.com"])
190 assert result.exit_code != 0
191 assert "does not exist" in result.output