test_domains_signing_identity.py
python
sha256:c5d88af19bcd6c1e60fd829dea85a4fdd5a74f74c5617031ba5b2955ba6a5b95
fix: domains.py signing-identity resolution uses resolved h…
Sonnet 5
patch
15 hours ago
| 1 | """muse#20 Phase 1 — domains.py must resolve signing identity against the |
| 2 | target hub URL, not the repo's default [hub] url. |
| 3 | |
| 4 | Regression for the same bug class as ls-remote's HTTP 401 (muse#20 |
| 5 | background): `run_publish`/`run_update`/`run_delete` all computed |
| 6 | `resolved_hub` (from `--hub`, or the repo's `[hub] url`, or the |
| 7 | musehub.ai default) but then called `get_signing_identity(repo_root)` |
| 8 | without forwarding it — so the identity lookup used the repo's default |
| 9 | hub URL instead of the actual target, producing a wrong-hostname lookup |
| 10 | whenever `--hub` pointed somewhere else (e.g. staging while the repo's |
| 11 | default config points at localhost). |
| 12 | |
| 13 | Mirrors the `TestSigningIdentityForwarding` pattern from |
| 14 | tests/test_cmd_ls_remote.py. |
| 15 | """ |
| 16 | from __future__ import annotations |
| 17 | |
| 18 | import pathlib |
| 19 | from unittest.mock import patch |
| 20 | |
| 21 | from tests.cli_test_helper import CliRunner |
| 22 | |
| 23 | cli = None |
| 24 | runner = CliRunner() |
| 25 | |
| 26 | _OTHER_HUB = "https://staging.musehub.ai" |
| 27 | |
| 28 | |
| 29 | def _env(root: pathlib.Path) -> dict[str, str]: |
| 30 | return {"MUSE_REPO_ROOT": str(root)} |
| 31 | |
| 32 | |
| 33 | def _init_repo(tmp_path: pathlib.Path) -> pathlib.Path: |
| 34 | """A minimal repo whose default [hub] url deliberately differs from |
| 35 | the --hub target used in each test, so a wrong-hostname lookup would |
| 36 | be caught.""" |
| 37 | import json |
| 38 | from muse.core.paths import muse_dir |
| 39 | |
| 40 | dot_muse = muse_dir(tmp_path) |
| 41 | dot_muse.mkdir(parents=True, exist_ok=True) |
| 42 | (dot_muse / "repo.json").write_text(json.dumps({"repo_id": "test-repo", "domain": "code"})) |
| 43 | (dot_muse / "config.toml").write_text('[hub]\nurl = "https://localhost:1337"\n') |
| 44 | return tmp_path |
| 45 | |
| 46 | |
| 47 | class TestPublishSigningIdentityForwarding: |
| 48 | def test_resolved_hub_forwarded_to_signing_identity(self, tmp_path: pathlib.Path) -> None: |
| 49 | repo = _init_repo(tmp_path) |
| 50 | with patch("muse.cli.commands.domains.get_signing_identity") as mock_gsi: |
| 51 | mock_gsi.return_value = None |
| 52 | runner.invoke( |
| 53 | cli, |
| 54 | [ |
| 55 | "domains", "publish", |
| 56 | "--author", "gabriel", "--slug", "genomics", |
| 57 | "--name", "Genomics", "--description", "desc", |
| 58 | "--viewer-type", "generic", "--hub", _OTHER_HUB, |
| 59 | ], |
| 60 | env=_env(repo), |
| 61 | ) |
| 62 | mock_gsi.assert_called_once() |
| 63 | _, kwargs = mock_gsi.call_args |
| 64 | assert kwargs.get("remote_url") == _OTHER_HUB, ( |
| 65 | f"get_signing_identity must be called with remote_url={_OTHER_HUB!r}; " |
| 66 | f"got {kwargs.get('remote_url')!r}. Without this, publish uses the " |
| 67 | "wrong signing key whenever --hub differs from the repo's default." |
| 68 | ) |
| 69 | |
| 70 | |
| 71 | class TestUpdateSigningIdentityForwarding: |
| 72 | def test_resolved_hub_forwarded_to_signing_identity(self, tmp_path: pathlib.Path) -> None: |
| 73 | repo = _init_repo(tmp_path) |
| 74 | with patch("muse.cli.commands.domains.get_signing_identity") as mock_gsi: |
| 75 | mock_gsi.return_value = None |
| 76 | runner.invoke( |
| 77 | cli, |
| 78 | [ |
| 79 | "domains", "update", |
| 80 | "--author", "gabriel", "--slug", "genomics", |
| 81 | "--name", "Genomics v2", "--hub", _OTHER_HUB, |
| 82 | ], |
| 83 | env=_env(repo), |
| 84 | ) |
| 85 | mock_gsi.assert_called_once() |
| 86 | _, kwargs = mock_gsi.call_args |
| 87 | assert kwargs.get("remote_url") == _OTHER_HUB, ( |
| 88 | f"get_signing_identity must be called with remote_url={_OTHER_HUB!r}; " |
| 89 | f"got {kwargs.get('remote_url')!r}." |
| 90 | ) |
| 91 | |
| 92 | |
| 93 | class TestDeleteSigningIdentityForwarding: |
| 94 | def test_resolved_hub_forwarded_to_signing_identity(self, tmp_path: pathlib.Path) -> None: |
| 95 | repo = _init_repo(tmp_path) |
| 96 | with patch("muse.cli.commands.domains.get_signing_identity") as mock_gsi: |
| 97 | mock_gsi.return_value = None |
| 98 | runner.invoke( |
| 99 | cli, |
| 100 | [ |
| 101 | "domains", "delete", |
| 102 | "--author", "gabriel", "--slug", "genomics", |
| 103 | "--hub", _OTHER_HUB, |
| 104 | ], |
| 105 | env=_env(repo), |
| 106 | ) |
| 107 | mock_gsi.assert_called_once() |
| 108 | _, kwargs = mock_gsi.call_args |
| 109 | assert kwargs.get("remote_url") == _OTHER_HUB, ( |
| 110 | f"get_signing_identity must be called with remote_url={_OTHER_HUB!r}; " |
| 111 | f"got {kwargs.get('remote_url')!r}." |
| 112 | ) |
| 113 | |
| 114 | |
| 115 | class TestReleaseSigningIdentityForwarding: |
| 116 | """muse#20 Phase 2 — release.py's get_signing_identity calls, already |
| 117 | behaviorally correct (positional remote_url), are now explicit keyword |
| 118 | style. These tests pin behavior so a future positional-arg drift (e.g. |
| 119 | an accidental new positional parameter inserted before remote_url in |
| 120 | get_signing_identity's signature) cannot silently break identity |
| 121 | resolution without a loud test failure. |
| 122 | """ |
| 123 | |
| 124 | def test_release_list_forwards_resolved_remote_url(self, tmp_path: pathlib.Path) -> None: |
| 125 | import json |
| 126 | from muse.core.paths import muse_dir |
| 127 | |
| 128 | repo = tmp_path |
| 129 | dot_muse = muse_dir(repo) |
| 130 | dot_muse.mkdir(parents=True, exist_ok=True) |
| 131 | (dot_muse / "repo.json").write_text(json.dumps({"repo_id": "test-repo", "domain": "code"})) |
| 132 | (dot_muse / "config.toml").write_text( |
| 133 | f'[hub]\nurl = "https://localhost:1337"\n' |
| 134 | f'[remotes.staging]\nurl = "{_OTHER_HUB}"\n' |
| 135 | ) |
| 136 | |
| 137 | with patch("muse.cli.commands.release.get_signing_identity") as mock_gsi: |
| 138 | mock_gsi.return_value = None |
| 139 | with patch("muse.cli.commands.release.make_transport") as mt: |
| 140 | mt.return_value.list_releases_remote.return_value = [] |
| 141 | runner.invoke( |
| 142 | cli, |
| 143 | ["release", "list", "--remote", "staging", "--json"], |
| 144 | env=_env(repo), |
| 145 | ) |
| 146 | mock_gsi.assert_called_once() |
| 147 | _, kwargs = mock_gsi.call_args |
| 148 | assert kwargs.get("remote_url") == _OTHER_HUB, ( |
| 149 | f"get_signing_identity must be called with remote_url={_OTHER_HUB!r}; " |
| 150 | f"got {kwargs.get('remote_url')!r}." |
| 151 | ) |
File History
1 commit
sha256:c5d88af19bcd6c1e60fd829dea85a4fdd5a74f74c5617031ba5b2955ba6a5b95
fix: domains.py signing-identity resolution uses resolved h…
Sonnet 5
patch
15 hours ago