test_wire_oc_frames.py
python
sha256:b636f72dcba9e190afb980bece906fa5b717fbde014b76ef023df8cb96e01eb9
docs: expand cache plan with all seven testing tiers and do…
Sonnet 4.6
132 days ago
| 1 | """TDD — Phase 14B: OC (Object Chunk) frame protocol. |
| 2 | |
| 3 | Large objects (> OC_CHUNK_SIZE) are split into OC frames so they can be |
| 4 | streamed through MWP without hitting proxy body limits. |
| 5 | |
| 6 | Rules encoded here: |
| 7 | |
| 8 | P14B-0 OC_CHUNK_SIZE exported from muse.core.transport, equals 290 * 1024. |
| 9 | |
| 10 | P14B-1 Object of exactly OC_CHUNK_SIZE bytes → 1 plain O-frame (no chunking). |
| 11 | |
| 12 | P14B-2 Object of OC_CHUNK_SIZE + 1 bytes → 2 OC-frames; each ≤ OC_CHUNK_SIZE. |
| 13 | |
| 14 | P14B-3 Object of 3 × OC_CHUNK_SIZE bytes → 3 OC-frames; bytes sum to original. |
| 15 | |
| 16 | P14B-4 chunk_index=0 has `encoding`, `path`, `sz`; subsequent chunks do not. |
| 17 | |
| 18 | P14B-5 `split_object_into_oc_frames` is exported from muse.core.transport and |
| 19 | returns a list of frame dicts (not ObjectPayload — raw dicts for wire). |
| 20 | |
| 21 | P14B-6 All returned frames have t="OC", object_id, chunk_index, total_chunks, |
| 22 | content. Last frame has chunk_index == total_chunks - 1. |
| 23 | """ |
| 24 | from __future__ import annotations |
| 25 | |
| 26 | import math |
| 27 | from typing import Any |
| 28 | |
| 29 | import pytest |
| 30 | |
| 31 | |
| 32 | # --------------------------------------------------------------------------- |
| 33 | # P14B-0 — OC_CHUNK_SIZE exported from muse.core.transport |
| 34 | # --------------------------------------------------------------------------- |
| 35 | |
| 36 | class TestP14B0ChunkSizeExported: |
| 37 | def test_oc_chunk_size_is_positive_int(self) -> None: |
| 38 | from muse.core.transport import OC_CHUNK_SIZE # type: ignore[attr-defined] |
| 39 | assert isinstance(OC_CHUNK_SIZE, int) |
| 40 | assert OC_CHUNK_SIZE > 0 |
| 41 | |
| 42 | def test_oc_chunk_size_equals_290kb(self) -> None: |
| 43 | from muse.core.transport import OC_CHUNK_SIZE # type: ignore[attr-defined] |
| 44 | assert OC_CHUNK_SIZE == 290 * 1024 |
| 45 | |
| 46 | |
| 47 | # --------------------------------------------------------------------------- |
| 48 | # P14B-1 — Object exactly OC_CHUNK_SIZE → 1 plain O-frame (no chunking) |
| 49 | # --------------------------------------------------------------------------- |
| 50 | |
| 51 | class TestP14B1ExactSizeNoChunking: |
| 52 | def test_object_at_chunk_size_is_single_o_frame(self) -> None: |
| 53 | from muse.core.transport import OC_CHUNK_SIZE, split_object_into_oc_frames # type: ignore[attr-defined] |
| 54 | |
| 55 | obj = { |
| 56 | "object_id": "sha256:" + "b" * 64, |
| 57 | "content": bytes(OC_CHUNK_SIZE), |
| 58 | "encoding": "raw", |
| 59 | "path": "big.bin", |
| 60 | } |
| 61 | frames = split_object_into_oc_frames(obj) |
| 62 | # Exactly one frame, and it's a regular O-frame |
| 63 | assert len(frames) == 1 |
| 64 | assert frames[0]["t"] == "O" |
| 65 | assert frames[0]["content"] == obj["content"] |
| 66 | |
| 67 | def test_object_below_chunk_size_is_single_o_frame(self) -> None: |
| 68 | from muse.core.transport import OC_CHUNK_SIZE, split_object_into_oc_frames # type: ignore[attr-defined] |
| 69 | |
| 70 | obj = { |
| 71 | "object_id": "sha256:" + "c" * 64, |
| 72 | "content": bytes(OC_CHUNK_SIZE - 1), |
| 73 | "encoding": "raw", |
| 74 | "path": "medium.bin", |
| 75 | } |
| 76 | frames = split_object_into_oc_frames(obj) |
| 77 | assert len(frames) == 1 |
| 78 | assert frames[0]["t"] == "O" |
| 79 | |
| 80 | |
| 81 | # --------------------------------------------------------------------------- |
| 82 | # P14B-2 — Object of OC_CHUNK_SIZE + 1 bytes → 2 OC-frames |
| 83 | # --------------------------------------------------------------------------- |
| 84 | |
| 85 | class TestP14B2TwoChunks: |
| 86 | def _get_frames(self, size: int) -> list[dict]: |
| 87 | from muse.core.transport import OC_CHUNK_SIZE, split_object_into_oc_frames # type: ignore[attr-defined] |
| 88 | pattern = bytes(range(256)) |
| 89 | content = (pattern * ((size // 256) + 1))[:size] |
| 90 | obj = { |
| 91 | "object_id": "sha256:" + "d" * 64, |
| 92 | "content": content, |
| 93 | "encoding": "raw", |
| 94 | "path": "large.bin", |
| 95 | } |
| 96 | return split_object_into_oc_frames(obj) |
| 97 | |
| 98 | def test_one_byte_over_chunk_size_produces_two_frames(self) -> None: |
| 99 | from muse.core.transport import OC_CHUNK_SIZE # type: ignore[attr-defined] |
| 100 | frames = self._get_frames(OC_CHUNK_SIZE + 1) |
| 101 | assert len(frames) == 2 |
| 102 | |
| 103 | def test_all_frames_are_oc_type(self) -> None: |
| 104 | from muse.core.transport import OC_CHUNK_SIZE # type: ignore[attr-defined] |
| 105 | frames = self._get_frames(OC_CHUNK_SIZE + 1) |
| 106 | for f in frames: |
| 107 | assert f["t"] == "OC" |
| 108 | |
| 109 | def test_each_frame_content_le_chunk_size(self) -> None: |
| 110 | from muse.core.transport import OC_CHUNK_SIZE # type: ignore[attr-defined] |
| 111 | frames = self._get_frames(OC_CHUNK_SIZE + 1) |
| 112 | for f in frames: |
| 113 | assert len(f["content"]) <= OC_CHUNK_SIZE |
| 114 | |
| 115 | def test_frame_content_sums_to_original(self) -> None: |
| 116 | from muse.core.transport import OC_CHUNK_SIZE # type: ignore[attr-defined] |
| 117 | size = OC_CHUNK_SIZE + 1 |
| 118 | frames = self._get_frames(size) |
| 119 | reassembled = b"".join(f["content"] for f in frames) |
| 120 | assert len(reassembled) == size |
| 121 | |
| 122 | |
| 123 | # --------------------------------------------------------------------------- |
| 124 | # P14B-3 — Object of 3 × OC_CHUNK_SIZE bytes → 3 OC-frames |
| 125 | # --------------------------------------------------------------------------- |
| 126 | |
| 127 | class TestP14B3ThreeChunks: |
| 128 | def test_three_chunk_size_object_produces_three_frames(self) -> None: |
| 129 | from muse.core.transport import OC_CHUNK_SIZE, split_object_into_oc_frames # type: ignore[attr-defined] |
| 130 | |
| 131 | size = OC_CHUNK_SIZE * 3 |
| 132 | content = bytes([i % 256 for i in range(size)]) |
| 133 | obj = { |
| 134 | "object_id": "sha256:" + "e" * 64, |
| 135 | "content": content, |
| 136 | "encoding": "raw", |
| 137 | "path": "triple.bin", |
| 138 | } |
| 139 | frames = split_object_into_oc_frames(obj) |
| 140 | assert len(frames) == 3 |
| 141 | |
| 142 | def test_three_chunks_reassemble_correctly(self) -> None: |
| 143 | from muse.core.transport import OC_CHUNK_SIZE, split_object_into_oc_frames # type: ignore[attr-defined] |
| 144 | |
| 145 | size = OC_CHUNK_SIZE * 3 |
| 146 | content = bytes([i % 256 for i in range(size)]) |
| 147 | obj = { |
| 148 | "object_id": "sha256:" + "f" * 64, |
| 149 | "content": content, |
| 150 | "encoding": "raw", |
| 151 | "path": "triple.bin", |
| 152 | } |
| 153 | frames = split_object_into_oc_frames(obj) |
| 154 | reassembled = b"".join(f["content"] for f in frames) |
| 155 | assert reassembled == content |
| 156 | |
| 157 | def test_total_chunks_field_is_correct(self) -> None: |
| 158 | from muse.core.transport import OC_CHUNK_SIZE, split_object_into_oc_frames # type: ignore[attr-defined] |
| 159 | |
| 160 | size = OC_CHUNK_SIZE * 3 |
| 161 | content = bytes(size) |
| 162 | obj = { |
| 163 | "object_id": "sha256:" + "e" * 64, |
| 164 | "content": content, |
| 165 | "encoding": "raw", |
| 166 | "path": "triple.bin", |
| 167 | } |
| 168 | frames = split_object_into_oc_frames(obj) |
| 169 | for f in frames: |
| 170 | assert f["total_chunks"] == 3 |
| 171 | |
| 172 | def test_chunk_indices_are_sequential(self) -> None: |
| 173 | from muse.core.transport import OC_CHUNK_SIZE, split_object_into_oc_frames # type: ignore[attr-defined] |
| 174 | |
| 175 | size = OC_CHUNK_SIZE * 3 |
| 176 | content = bytes(size) |
| 177 | obj = { |
| 178 | "object_id": "sha256:" + "e" * 64, |
| 179 | "content": content, |
| 180 | "encoding": "raw", |
| 181 | "path": "triple.bin", |
| 182 | } |
| 183 | frames = split_object_into_oc_frames(obj) |
| 184 | indices = [f["chunk_index"] for f in frames] |
| 185 | assert indices == [0, 1, 2] |
| 186 | |
| 187 | |
| 188 | # --------------------------------------------------------------------------- |
| 189 | # P14B-4 — Only chunk_index=0 carries encoding, path, sz |
| 190 | # --------------------------------------------------------------------------- |
| 191 | |
| 192 | class TestP14B4MetadataOnFirstChunkOnly: |
| 193 | def _frames_for_large_object(self) -> list[dict]: |
| 194 | from muse.core.transport import OC_CHUNK_SIZE, split_object_into_oc_frames # type: ignore[attr-defined] |
| 195 | size = OC_CHUNK_SIZE * 2 + 1 |
| 196 | content = bytes(size) |
| 197 | obj = { |
| 198 | "object_id": "sha256:" + "a" * 64, |
| 199 | "content": content, |
| 200 | "encoding": "raw", |
| 201 | "path": "large.bin", |
| 202 | } |
| 203 | return split_object_into_oc_frames(obj) |
| 204 | |
| 205 | def test_first_chunk_has_encoding(self) -> None: |
| 206 | frames = self._frames_for_large_object() |
| 207 | assert "encoding" in frames[0] |
| 208 | |
| 209 | def test_first_chunk_has_path(self) -> None: |
| 210 | frames = self._frames_for_large_object() |
| 211 | assert "path" in frames[0] |
| 212 | |
| 213 | def test_first_chunk_has_sz(self) -> None: |
| 214 | frames = self._frames_for_large_object() |
| 215 | assert "sz" in frames[0] |
| 216 | from muse.core.transport import OC_CHUNK_SIZE # type: ignore[attr-defined] |
| 217 | assert frames[0]["sz"] == OC_CHUNK_SIZE * 2 + 1 |
| 218 | |
| 219 | def test_subsequent_chunks_have_no_encoding(self) -> None: |
| 220 | frames = self._frames_for_large_object() |
| 221 | for f in frames[1:]: |
| 222 | assert "encoding" not in f |
| 223 | |
| 224 | def test_subsequent_chunks_have_no_path(self) -> None: |
| 225 | frames = self._frames_for_large_object() |
| 226 | for f in frames[1:]: |
| 227 | assert "path" not in f |
| 228 | |
| 229 | def test_subsequent_chunks_have_no_sz(self) -> None: |
| 230 | frames = self._frames_for_large_object() |
| 231 | for f in frames[1:]: |
| 232 | assert "sz" not in f |
| 233 | |
| 234 | |
| 235 | # --------------------------------------------------------------------------- |
| 236 | # P14B-5 — split_object_into_oc_frames exported, returns list of dicts |
| 237 | # --------------------------------------------------------------------------- |
| 238 | |
| 239 | class TestP14B5FunctionExported: |
| 240 | def test_function_is_callable(self) -> None: |
| 241 | from muse.core.transport import split_object_into_oc_frames # type: ignore[attr-defined] |
| 242 | assert callable(split_object_into_oc_frames) |
| 243 | |
| 244 | def test_returns_list_of_dicts(self) -> None: |
| 245 | from muse.core.transport import OC_CHUNK_SIZE, split_object_into_oc_frames # type: ignore[attr-defined] |
| 246 | obj = { |
| 247 | "object_id": "sha256:" + "a" * 64, |
| 248 | "content": bytes(10), |
| 249 | "encoding": "raw", |
| 250 | "path": "tiny.bin", |
| 251 | } |
| 252 | result = split_object_into_oc_frames(obj) |
| 253 | assert isinstance(result, list) |
| 254 | for item in result: |
| 255 | assert isinstance(item, dict) |
| 256 | |
| 257 | |
| 258 | # --------------------------------------------------------------------------- |
| 259 | # P14B-6 — All OC frames have required fields; last frame has correct index |
| 260 | # --------------------------------------------------------------------------- |
| 261 | |
| 262 | class TestP14B6FrameFields: |
| 263 | def test_all_oc_frames_have_required_fields(self) -> None: |
| 264 | from muse.core.transport import OC_CHUNK_SIZE, split_object_into_oc_frames # type: ignore[attr-defined] |
| 265 | |
| 266 | size = OC_CHUNK_SIZE * 2 + 512 |
| 267 | content = bytes(size) |
| 268 | obj = { |
| 269 | "object_id": "sha256:" + "b" * 64, |
| 270 | "content": content, |
| 271 | "encoding": "raw", |
| 272 | "path": "multi.bin", |
| 273 | } |
| 274 | frames = split_object_into_oc_frames(obj) |
| 275 | for f in frames: |
| 276 | assert "t" in f |
| 277 | assert "object_id" in f |
| 278 | assert "chunk_index" in f |
| 279 | assert "total_chunks" in f |
| 280 | assert "content" in f |
| 281 | assert f["object_id"] == obj["object_id"] |
| 282 | |
| 283 | def test_last_frame_chunk_index_equals_total_minus_one(self) -> None: |
| 284 | from muse.core.transport import OC_CHUNK_SIZE, split_object_into_oc_frames # type: ignore[attr-defined] |
| 285 | |
| 286 | size = OC_CHUNK_SIZE * 4 |
| 287 | content = bytes(size) |
| 288 | obj = { |
| 289 | "object_id": "sha256:" + "c" * 64, |
| 290 | "content": content, |
| 291 | "encoding": "raw", |
| 292 | "path": "quad.bin", |
| 293 | } |
| 294 | frames = split_object_into_oc_frames(obj) |
| 295 | last = frames[-1] |
| 296 | assert last["chunk_index"] == last["total_chunks"] - 1 |
File History
1 commit
sha256:b636f72dcba9e190afb980bece906fa5b717fbde014b76ef023df8cb96e01eb9
docs: expand cache plan with all seven testing tiers and do…
Sonnet 4.6
132 days ago