"""Regression tests for muse mist read #44. ``run_read`` built the wrong API path for the ``owner/ID`` input form — ``/api/{owner}/mists/{id}`` instead of the real route ``/api/mists/{id}`` (owner is not part of the path; every other mist subcommand already discards it correctly). The wrong path hits no matching route, and the resulting non-JSON response surfaced as a misleading "Server returned invalid JSON" error instead of the actual HTTP status. """ from __future__ import annotations import argparse import pathlib import unittest.mock as mock import pytest from muse.core.identity import IdentityEntry from muse.core.transport import TransportError def _args(mist_id: str, *, json_output: bool = True) -> argparse.Namespace: return argparse.Namespace(mist_id=mist_id, json_output=json_output, hub="https://musehub.ai") def _fake_identity() -> IdentityEntry: return {"type": "human", "handle": "alice"} class TestRunReadApiPath: def test_owner_qualified_id_uses_mists_path_not_owner_path( self, capsys: pytest.CaptureFixture[str] ) -> None: """owner/ID form must call /api/mists/{id} — MR_03.""" from muse.cli.commands.mist import run_read with ( mock.patch( "muse.cli.commands.mist._require_hub", return_value=("https://musehub.ai", _fake_identity()), ), mock.patch( "muse.cli.commands.mist._hub_api", return_value={"mist_id": "H7XFVEzeDfqk"} ) as mocked, ): run_read(_args("gabriel/H7XFVEzeDfqk")) called_path = mocked.call_args.args[3] assert called_path == "/api/mists/H7XFVEzeDfqk" def test_bare_id_uses_mists_path(self, capsys: pytest.CaptureFixture[str]) -> None: """Bare ID form must call /api/mists/{id} — MR_04.""" from muse.cli.commands.mist import run_read with ( mock.patch( "muse.cli.commands.mist._require_hub", return_value=("https://musehub.ai", _fake_identity()), ), mock.patch( "muse.cli.commands.mist._hub_api", return_value={"mist_id": "H7XFVEzeDfqk"} ) as mocked, ): run_read(_args("H7XFVEzeDfqk")) called_path = mocked.call_args.args[3] assert called_path == "/api/mists/H7XFVEzeDfqk" def test_owner_is_discarded_not_embedded_in_path( self, capsys: pytest.CaptureFixture[str] ) -> None: """The owner segment is advisory only — never present in the API path — MR_05.""" from muse.cli.commands.mist import run_read with ( mock.patch( "muse.cli.commands.mist._require_hub", return_value=("https://musehub.ai", _fake_identity()), ), mock.patch( "muse.cli.commands.mist._hub_api", return_value={"mist_id": "abc123"} ) as mocked, ): run_read(_args("someOwner/abc123")) called_path = mocked.call_args.args[3] assert "someOwner" not in called_path assert called_path == "/api/mists/abc123" def test_bare_id_and_owner_qualified_id_produce_identical_output( self, capsys: pytest.CaptureFixture[str] ) -> None: """Both input forms must be equivalent — acceptance criterion 1.""" from muse.cli.commands.mist import run_read with ( mock.patch( "muse.cli.commands.mist._require_hub", return_value=("https://musehub.ai", _fake_identity()), ), mock.patch( "muse.cli.commands.mist._hub_api", return_value={"mist_id": "H7XFVEzeDfqk"} ), ): run_read(_args("gabriel/H7XFVEzeDfqk")) out_qualified = capsys.readouterr().out run_read(_args("H7XFVEzeDfqk")) out_bare = capsys.readouterr().out assert out_qualified == out_bare class TestHubApi404Message: def test_404_surfaces_http_status_not_json_parse_error( self, capsys: pytest.CaptureFixture[str] ) -> None: """A 404 with a valid JSON body must report the HTTP status — MR_02/MR_06.""" from muse.cli.commands.mist import _hub_api err = TransportError('{"detail": "Not Found"}', 404) with ( mock.patch("muse.cli.config.get_signing_identity", return_value=None), mock.patch( "muse.core.transport.HttpTransport.hub_json", side_effect=err ), ): with pytest.raises(SystemExit): _hub_api("https://musehub.ai", _fake_identity(), "GET", "/api/mists/missing") captured = capsys.readouterr() assert "404" in captured.err assert "invalid JSON" not in captured.err