"""TDD — HttpTransport.push_mpack_presign: client-side Step 1 of the mpack push protocol. PP-1 Happy path: POST returns {upload_url, mpack_key} — both echoed back correctly. PP-2 The POST body contains mpack_key == blob_id(mpack_bytes) and correct size_bytes. PP-3 The POST is sent to /{owner}/{slug}/push/mpack-presign. PP-4 Non-200 response raises TransportError. PP-5 Response missing upload_url raises TransportError. """ from __future__ import annotations from unittest.mock import MagicMock, patch import msgpack import pytest from muse.core.transport import HttpTransport, TransportError from muse.core.types import blob_id _URL = "https://staging.musehub.ai/gabriel/muse" _UPLOAD_URL = "https://minio.example.com/mpacks/sha256:abc?sig=fake" def _presign_response(mpack_key: str, upload_url: str = _UPLOAD_URL) -> bytes: return msgpack.packb( {"upload_url": upload_url, "mpack_key": mpack_key}, use_bin_type=True, ) class _Resp: def __init__(self, body: bytes, status: int = 200) -> None: self.status_code = status self.content = body def _mock_client(resp: _Resp) -> MagicMock: mock = MagicMock() mock.__enter__ = MagicMock(return_value=mock) mock.__exit__ = MagicMock(return_value=False) mock.post = MagicMock(return_value=resp) return mock # ── PP-1 ────────────────────────────────────────────────────────────────────── def test_pp1_happy_path_returns_upload_url_and_mpack_key() -> None: """Server 200 → dict with upload_url and mpack_key returned to caller.""" mpack_bytes = b"fake mpack content" mpack_key = blob_id(mpack_bytes) mock_client = _mock_client(_Resp(_presign_response(mpack_key))) transport = HttpTransport() with patch("muse.core.transport._httpx_mod.Client", return_value=mock_client): result = transport.push_mpack_presign(_URL, None, mpack_bytes) assert result["upload_url"] == _UPLOAD_URL assert result["mpack_key"] == mpack_key # ── PP-2 ────────────────────────────────────────────────────────────────────── def test_pp2_post_body_contains_correct_key_and_size() -> None: """The POST body must encode mpack_key = blob_id(bytes) and size_bytes = len(bytes).""" mpack_bytes = b"x" * 512 mpack_key = blob_id(mpack_bytes) mock_client = _mock_client(_Resp(_presign_response(mpack_key))) transport = HttpTransport() with patch("muse.core.transport._httpx_mod.Client", return_value=mock_client): transport.push_mpack_presign(_URL, None, mpack_bytes) call_kwargs = mock_client.post.call_args sent_body: bytes = call_kwargs[1].get("content") or call_kwargs[0][1] payload = msgpack.unpackb(sent_body, raw=False) assert payload["mpack_key"] == mpack_key assert payload["size_bytes"] == 512 # ── PP-3 ────────────────────────────────────────────────────────────────────── def test_pp3_posts_to_correct_endpoint() -> None: """POST must go to /{owner}/{slug}/push/mpack-presign.""" mpack_bytes = b"data" mpack_key = blob_id(mpack_bytes) mock_client = _mock_client(_Resp(_presign_response(mpack_key))) transport = HttpTransport() with patch("muse.core.transport._httpx_mod.Client", return_value=mock_client): transport.push_mpack_presign(_URL, None, mpack_bytes) posted_url: str = mock_client.post.call_args[0][0] assert posted_url.endswith("/push/mpack-presign") # ── PP-4 ────────────────────────────────────────────────────────────────────── def test_pp4_non_200_raises_transport_error() -> None: """A 4xx/5xx response must raise TransportError.""" mock_client = _mock_client(_Resp(b"quota exceeded", status=429)) transport = HttpTransport() with patch("muse.core.transport._httpx_mod.Client", return_value=mock_client): with pytest.raises(TransportError): transport.push_mpack_presign(_URL, None, b"bytes") # ── PP-5 ────────────────────────────────────────────────────────────────────── def test_pp5_missing_upload_url_raises_transport_error() -> None: """If the server omits upload_url, TransportError must be raised.""" mpack_bytes = b"data" mpack_key = blob_id(mpack_bytes) bad_resp = msgpack.packb({"mpack_key": mpack_key}, use_bin_type=True) mock_client = _mock_client(_Resp(bad_resp)) transport = HttpTransport() with patch("muse.core.transport._httpx_mod.Client", return_value=mock_client): with pytest.raises(TransportError, match="upload_url"): transport.push_mpack_presign(_URL, None, mpack_bytes)