gabriel / muse public
test_transport_push_mpack_put.py python
100 lines 4.6 KB
Raw
sha256:79ffe87f5fe2ec146e35f05521218bbf54dffdb0440c07f970bad05f16efb89f chore: merge main — carry all urllib/typing/test fixes from dev Sonnet 4.6 minor ⚠ breaking 19 days ago
1 """TDD — HttpTransport.push_mpack_put: client-side Step 2 of the mpack push protocol.
2
3 PUT mpack_bytes directly to a presigned MinIO URL. No MuseHub API. No auth
4 header. No encoding. The presigned URL is the credential.
5
6 MP-1 PUT is sent to upload_url, not any MuseHub endpoint.
7 MP-2 Request body is raw mpack_bytes — no msgpack encoding, no wrapping.
8 MP-3 No Authorization header is added.
9 MP-4 2xx response → returns None (no error).
10 MP-5 Non-2xx response → raises TransportError.
11 MP-6 MinIO 204 (no content) is also treated as success.
12 """
13 from __future__ import annotations
14
15 from typing import Protocol
16 from unittest.mock import patch
17
18 import pytest
19
20 from muse.core.transport import HttpTransport, TransportError
21
22
23 class _SideEffectFn(Protocol):
24 calls: list[tuple[str, str, "dict[str, str]", "bytes | None"]]
25 def __call__(self, method: str, url: str, headers: "dict[str, str]", data: "bytes | None" = ..., **kwargs: "str | int | bool") -> bytes: ...
26
27 _UPLOAD_URL = "https://minio.example.com/mpacks/sha256:abc?X-Amz-Signature=fake"
28 _MPACK_BYTES = b"fake-mpack-content" * 50
29 _Headers = dict[str, str]
30
31
32 def _mock_urllib_do(status: int = 200, body: bytes = b"") -> "_SideEffectFn":
33 calls: list[tuple[str, str, dict[str, str], bytes | None]] = []
34
35 def _side_effect(method: str, url: str, headers: "dict[str, str]", data: "bytes | None" = None, **kwargs: "str | int | bool") -> bytes:
36 calls.append((method, url, headers, data))
37 if status >= 400:
38 raise TransportError(f"HTTP {status}", status)
39 return body
40
41 _side_effect.calls = calls
42 return _side_effect
43
44
45 # ── MP-1 ──────────────────────────────────────────────────────────────────────
46
47 def test_mp1_put_sent_to_upload_url() -> None:
48 do = _mock_urllib_do(200)
49 with patch("muse.core.transport._urllib_do", side_effect=do):
50 HttpTransport().push_mpack_put(_UPLOAD_URL, _MPACK_BYTES)
51 _, url, _, _ = do.calls[0]
52 assert url == _UPLOAD_URL
53
54
55 # ── MP-2 ──────────────────────────────────────────────────────────────────────
56
57 def test_mp2_body_is_raw_bytes() -> None:
58 do = _mock_urllib_do(200)
59 with patch("muse.core.transport._urllib_do", side_effect=do):
60 HttpTransport().push_mpack_put(_UPLOAD_URL, _MPACK_BYTES)
61 _, _, _, sent_data = do.calls[0]
62 assert sent_data == _MPACK_BYTES
63
64
65 # ── MP-3 ──────────────────────────────────────────────────────────────────────
66
67 def test_mp3_no_authorization_header() -> None:
68 do = _mock_urllib_do(200)
69 with patch("muse.core.transport._urllib_do", side_effect=do):
70 HttpTransport().push_mpack_put(_UPLOAD_URL, _MPACK_BYTES)
71 _, _, headers, _ = do.calls[0]
72 assert "Authorization" not in headers
73 assert "authorization" not in {k.lower() for k in headers}
74
75
76 # ── MP-4 ──────────────────────────────────────────────────────────────────────
77
78 def test_mp4_200_returns_none() -> None:
79 do = _mock_urllib_do(200)
80 with patch("muse.core.transport._urllib_do", side_effect=do):
81 result = HttpTransport().push_mpack_put(_UPLOAD_URL, _MPACK_BYTES)
82 assert result is None
83
84
85 # ── MP-5 ──────────────────────────────────────────────────────────────────────
86
87 def test_mp5_non_2xx_raises_transport_error() -> None:
88 do = _mock_urllib_do(403, b"Request has expired")
89 with patch("muse.core.transport._urllib_do", side_effect=do):
90 with pytest.raises(TransportError):
91 HttpTransport().push_mpack_put(_UPLOAD_URL, _MPACK_BYTES)
92
93
94 # ── MP-6 ──────────────────────────────────────────────────────────────────────
95
96 def test_mp6_204_no_content_is_success() -> None:
97 do = _mock_urllib_do(204)
98 with patch("muse.core.transport._urllib_do", side_effect=do):
99 result = HttpTransport().push_mpack_put(_UPLOAD_URL, _MPACK_BYTES)
100 assert result is None
File History 2 commits
sha256:79ffe87f5fe2ec146e35f05521218bbf54dffdb0440c07f970bad05f16efb89f chore: merge main — carry all urllib/typing/test fixes from dev Sonnet 4.6 minor 19 days ago
sha256:0bea7600d1eee83e87950be49933b1006fa9dc2c71e7c4ee748d324f61138156 chore: bump version to 0.2.0rc11; fix typing audit violatio… Sonnet 4.6 minor 19 days ago