gabriel / muse public
test_mist_read_api_path.py python
135 lines 4.7 KB
Raw
sha256:c287f599c5429903a139eadf3c5db5d930520e57cb0c3c575d9570e953c3b2d6 chore: bump version to 0.2.0.dev2 — nightly.2 Sonnet 4.6 patch 12 days ago
1 """Regression tests for muse mist read #44.
2
3 ``run_read`` built the wrong API path for the ``owner/ID`` input form —
4 ``/api/{owner}/mists/{id}`` instead of the real route ``/api/mists/{id}``
5 (owner is not part of the path; every other mist subcommand already
6 discards it correctly). The wrong path hits no matching route, and the
7 resulting non-JSON response surfaced as a misleading
8 "Server returned invalid JSON" error instead of the actual HTTP status.
9 """
10
11 from __future__ import annotations
12
13 import argparse
14 import pathlib
15 import unittest.mock as mock
16
17 import pytest
18
19 from muse.core.identity import IdentityEntry
20 from muse.core.transport import TransportError
21
22
23 def _args(mist_id: str, *, json_output: bool = True) -> argparse.Namespace:
24 return argparse.Namespace(mist_id=mist_id, json_output=json_output, hub="https://musehub.ai")
25
26
27 def _fake_identity() -> IdentityEntry:
28 return {"type": "human", "handle": "alice"}
29
30
31 class TestRunReadApiPath:
32 def test_owner_qualified_id_uses_mists_path_not_owner_path(
33 self, capsys: pytest.CaptureFixture[str]
34 ) -> None:
35 """owner/ID form must call /api/mists/{id} — MR_03."""
36 from muse.cli.commands.mist import run_read
37
38 with (
39 mock.patch(
40 "muse.cli.commands.mist._require_hub",
41 return_value=("https://musehub.ai", _fake_identity()),
42 ),
43 mock.patch(
44 "muse.cli.commands.mist._hub_api", return_value={"mist_id": "H7XFVEzeDfqk"}
45 ) as mocked,
46 ):
47 run_read(_args("gabriel/H7XFVEzeDfqk"))
48
49 called_path = mocked.call_args.args[3]
50 assert called_path == "/api/mists/H7XFVEzeDfqk"
51
52 def test_bare_id_uses_mists_path(self, capsys: pytest.CaptureFixture[str]) -> None:
53 """Bare ID form must call /api/mists/{id} — MR_04."""
54 from muse.cli.commands.mist import run_read
55
56 with (
57 mock.patch(
58 "muse.cli.commands.mist._require_hub",
59 return_value=("https://musehub.ai", _fake_identity()),
60 ),
61 mock.patch(
62 "muse.cli.commands.mist._hub_api", return_value={"mist_id": "H7XFVEzeDfqk"}
63 ) as mocked,
64 ):
65 run_read(_args("H7XFVEzeDfqk"))
66
67 called_path = mocked.call_args.args[3]
68 assert called_path == "/api/mists/H7XFVEzeDfqk"
69
70 def test_owner_is_discarded_not_embedded_in_path(
71 self, capsys: pytest.CaptureFixture[str]
72 ) -> None:
73 """The owner segment is advisory only — never present in the API path — MR_05."""
74 from muse.cli.commands.mist import run_read
75
76 with (
77 mock.patch(
78 "muse.cli.commands.mist._require_hub",
79 return_value=("https://musehub.ai", _fake_identity()),
80 ),
81 mock.patch(
82 "muse.cli.commands.mist._hub_api", return_value={"mist_id": "abc123"}
83 ) as mocked,
84 ):
85 run_read(_args("someOwner/abc123"))
86
87 called_path = mocked.call_args.args[3]
88 assert "someOwner" not in called_path
89 assert called_path == "/api/mists/abc123"
90
91 def test_bare_id_and_owner_qualified_id_produce_identical_output(
92 self, capsys: pytest.CaptureFixture[str]
93 ) -> None:
94 """Both input forms must be equivalent — acceptance criterion 1."""
95 from muse.cli.commands.mist import run_read
96
97 with (
98 mock.patch(
99 "muse.cli.commands.mist._require_hub",
100 return_value=("https://musehub.ai", _fake_identity()),
101 ),
102 mock.patch(
103 "muse.cli.commands.mist._hub_api", return_value={"mist_id": "H7XFVEzeDfqk"}
104 ),
105 ):
106 run_read(_args("gabriel/H7XFVEzeDfqk"))
107 out_qualified = capsys.readouterr().out
108
109 run_read(_args("H7XFVEzeDfqk"))
110 out_bare = capsys.readouterr().out
111
112 assert out_qualified == out_bare
113
114
115 class TestHubApi404Message:
116 def test_404_surfaces_http_status_not_json_parse_error(
117 self, capsys: pytest.CaptureFixture[str]
118 ) -> None:
119 """A 404 with a valid JSON body must report the HTTP status — MR_02/MR_06."""
120 from muse.cli.commands.mist import _hub_api
121
122 err = TransportError('{"detail": "Not Found"}', 404)
123
124 with (
125 mock.patch("muse.cli.config.get_signing_identity", return_value=None),
126 mock.patch(
127 "muse.core.transport.HttpTransport.hub_json", side_effect=err
128 ),
129 ):
130 with pytest.raises(SystemExit):
131 _hub_api("https://musehub.ai", _fake_identity(), "GET", "/api/mists/missing")
132
133 captured = capsys.readouterr()
134 assert "404" in captured.err
135 assert "invalid JSON" not in captured.err
File History 1 commit
sha256:c287f599c5429903a139eadf3c5db5d930520e57cb0c3c575d9570e953c3b2d6 chore: bump version to 0.2.0.dev2 — nightly.2 Sonnet 4.6 patch 12 days ago