test_transport_push_mpack_presign.py
python
sha256:79ffe87f5fe2ec146e35f05521218bbf54dffdb0440c07f970bad05f16efb89f
chore: merge main — carry all urllib/typing/test fixes from dev
Sonnet 4.6
minor
⚠ breaking
20 days ago
| 1 | """TDD — HttpTransport.push_mpack_presign: client-side Step 1 of the mpack push protocol. |
| 2 | |
| 3 | PP-1 Happy path: POST returns {upload_url, mpack_key} — both echoed back correctly. |
| 4 | PP-2 The POST body contains mpack_key == blob_id(mpack_bytes) and correct size_bytes. |
| 5 | PP-3 The POST is sent to /{owner}/{slug}/push/mpack-presign. |
| 6 | PP-4 Non-200 response raises TransportError. |
| 7 | PP-5 Response missing upload_url raises TransportError. |
| 8 | """ |
| 9 | from __future__ import annotations |
| 10 | |
| 11 | from unittest.mock import MagicMock, patch |
| 12 | |
| 13 | import msgpack |
| 14 | import pytest |
| 15 | |
| 16 | from muse.core.transport import HttpTransport, TransportError |
| 17 | from muse.core.types import blob_id |
| 18 | |
| 19 | _URL = "https://staging.musehub.ai/gabriel/muse" |
| 20 | _UPLOAD_URL = "https://minio.example.com/mpacks/sha256:abc?sig=fake" |
| 21 | |
| 22 | |
| 23 | def _presign_response(mpack_key: str, upload_url: str = _UPLOAD_URL) -> bytes: |
| 24 | return msgpack.packb( |
| 25 | {"upload_url": upload_url, "mpack_key": mpack_key}, |
| 26 | use_bin_type=True, |
| 27 | ) |
| 28 | |
| 29 | |
| 30 | class _Resp: |
| 31 | def __init__(self, body: bytes, status: int = 200) -> None: |
| 32 | self.status_code = status |
| 33 | self.content = body |
| 34 | |
| 35 | |
| 36 | def _mock_client(resp: _Resp) -> MagicMock: |
| 37 | mock = MagicMock() |
| 38 | mock.__enter__ = MagicMock(return_value=mock) |
| 39 | mock.__exit__ = MagicMock(return_value=False) |
| 40 | mock.post = MagicMock(return_value=resp) |
| 41 | return mock |
| 42 | |
| 43 | |
| 44 | # ── PP-1 ────────────────────────────────────────────────────────────────────── |
| 45 | |
| 46 | def test_pp1_happy_path_returns_upload_url_and_mpack_key() -> None: |
| 47 | """Server 200 → dict with upload_url and mpack_key returned to caller.""" |
| 48 | mpack_bytes = b"fake mpack content" |
| 49 | mpack_key = blob_id(mpack_bytes) |
| 50 | mock_client = _mock_client(_Resp(_presign_response(mpack_key))) |
| 51 | |
| 52 | transport = HttpTransport() |
| 53 | with patch("muse.core.transport._httpx_mod.Client", return_value=mock_client): |
| 54 | result = transport.push_mpack_presign(_URL, None, mpack_bytes) |
| 55 | |
| 56 | assert result["upload_url"] == _UPLOAD_URL |
| 57 | assert result["mpack_key"] == mpack_key |
| 58 | |
| 59 | |
| 60 | # ── PP-2 ────────────────────────────────────────────────────────────────────── |
| 61 | |
| 62 | def test_pp2_post_body_contains_correct_key_and_size() -> None: |
| 63 | """The POST body must encode mpack_key = blob_id(bytes) and size_bytes = len(bytes).""" |
| 64 | mpack_bytes = b"x" * 512 |
| 65 | mpack_key = blob_id(mpack_bytes) |
| 66 | mock_client = _mock_client(_Resp(_presign_response(mpack_key))) |
| 67 | |
| 68 | transport = HttpTransport() |
| 69 | with patch("muse.core.transport._httpx_mod.Client", return_value=mock_client): |
| 70 | transport.push_mpack_presign(_URL, None, mpack_bytes) |
| 71 | |
| 72 | call_kwargs = mock_client.post.call_args |
| 73 | sent_body: bytes = call_kwargs[1].get("content") or call_kwargs[0][1] |
| 74 | payload = msgpack.unpackb(sent_body, raw=False) |
| 75 | assert payload["mpack_key"] == mpack_key |
| 76 | assert payload["size_bytes"] == 512 |
| 77 | |
| 78 | |
| 79 | # ── PP-3 ────────────────────────────────────────────────────────────────────── |
| 80 | |
| 81 | def test_pp3_posts_to_correct_endpoint() -> None: |
| 82 | """POST must go to /{owner}/{slug}/push/mpack-presign.""" |
| 83 | mpack_bytes = b"data" |
| 84 | mpack_key = blob_id(mpack_bytes) |
| 85 | mock_client = _mock_client(_Resp(_presign_response(mpack_key))) |
| 86 | |
| 87 | transport = HttpTransport() |
| 88 | with patch("muse.core.transport._httpx_mod.Client", return_value=mock_client): |
| 89 | transport.push_mpack_presign(_URL, None, mpack_bytes) |
| 90 | |
| 91 | posted_url: str = mock_client.post.call_args[0][0] |
| 92 | assert posted_url.endswith("/push/mpack-presign") |
| 93 | |
| 94 | |
| 95 | # ── PP-4 ────────────────────────────────────────────────────────────────────── |
| 96 | |
| 97 | def test_pp4_non_200_raises_transport_error() -> None: |
| 98 | """A 4xx/5xx response must raise TransportError.""" |
| 99 | mock_client = _mock_client(_Resp(b"quota exceeded", status=429)) |
| 100 | |
| 101 | transport = HttpTransport() |
| 102 | with patch("muse.core.transport._httpx_mod.Client", return_value=mock_client): |
| 103 | with pytest.raises(TransportError): |
| 104 | transport.push_mpack_presign(_URL, None, b"bytes") |
| 105 | |
| 106 | |
| 107 | # ── PP-5 ────────────────────────────────────────────────────────────────────── |
| 108 | |
| 109 | def test_pp5_missing_upload_url_raises_transport_error() -> None: |
| 110 | """If the server omits upload_url, TransportError must be raised.""" |
| 111 | mpack_bytes = b"data" |
| 112 | mpack_key = blob_id(mpack_bytes) |
| 113 | bad_resp = msgpack.packb({"mpack_key": mpack_key}, use_bin_type=True) |
| 114 | mock_client = _mock_client(_Resp(bad_resp)) |
| 115 | |
| 116 | transport = HttpTransport() |
| 117 | with patch("muse.core.transport._httpx_mod.Client", return_value=mock_client): |
| 118 | with pytest.raises(TransportError, match="upload_url"): |
| 119 | transport.push_mpack_presign(_URL, None, mpack_bytes) |
File History
2 commits
sha256:79ffe87f5fe2ec146e35f05521218bbf54dffdb0440c07f970bad05f16efb89f
chore: merge main — carry all urllib/typing/test fixes from dev
Sonnet 4.6
minor
⚠
20 days ago
sha256:0bea7600d1eee83e87950be49933b1006fa9dc2c71e7c4ee748d324f61138156
chore: bump version to 0.2.0rc11; fix typing audit violatio…
Sonnet 4.6
minor
⚠
20 days ago