"""TDD — --force flag must appear in the unpack-mpack request body. Gap 2: _run_mpack_path() never includes "force" in the unpack body. The server receives the unpack request with no force field and has no way to distinguish a force push from a normal push. Test plan --------- F1 (RED) _push_mpack with force=True sends {"force": True} in the unpack-mpack POST body. F2 (RED) _push_mpack with force=False sends {"force": False} (or omits it and server defaults to False). F3 _push_mpack without --force does NOT set force=True by accident. F4 The "force" key is present in the unpack body regardless of the force value (protocol stability). """ from __future__ import annotations import asyncio import datetime import json import pathlib from unittest.mock import MagicMock, patch import msgpack import pytest from muse._version import __version__ from muse.core.mpack import PushResult, RemoteInfo from muse.core.object_store import write_object from muse.core.paths import heads_dir, muse_dir _Headers = dict[str, str] # HTTP header map _JsonDict = dict[str, str | int | float | bool | None | list[str]] # JSON object from muse.core.ids import hash_commit as compute_commit_id, hash_snapshot as compute_snapshot_id from muse.core.commits import ( CommitRecord, write_commit, ) from muse.core.snapshots import ( SnapshotRecord, write_snapshot, ) from muse.core.types import Manifest, blob_id # --------------------------------------------------------------------------- # Helpers # --------------------------------------------------------------------------- def _bare_repo(tmp_path: pathlib.Path) -> pathlib.Path: muse = muse_dir(tmp_path) for d in ("commits", "snapshots", "objects", "refs/heads", "remotes"): (muse / d).mkdir(parents=True, exist_ok=True) (muse / "HEAD").write_text("ref: refs/heads/main\n") (muse / "repo.json").write_text( json.dumps({"repo_id": "test-repo", "schema_version": __version__, "domain": "code"}) ) (muse / "config.toml").write_text('[remotes.origin]\nurl = "https://hub.example.com/r"\n') return tmp_path def _make_commit( root: pathlib.Path, label: str, parent_id: str | None = None, content: bytes | None = None, ) -> CommitRecord: raw = content if content is not None else f"content-{label}".encode() oid = blob_id(raw) write_object(root, oid, raw) manifest: Manifest = {"file.txt": oid} snap_id = compute_snapshot_id(manifest) write_snapshot(root, SnapshotRecord(snapshot_id=snap_id, manifest=manifest)) committed_at = datetime.datetime(2026, 1, 1, tzinfo=datetime.timezone.utc) parent_ids = [parent_id] if parent_id else [] cid = compute_commit_id( parent_ids=parent_ids, snapshot_id=snap_id, message=f"commit {label}", committed_at_iso=committed_at.isoformat(), ) write_commit(root, CommitRecord( commit_id=cid, branch="main", snapshot_id=snap_id, message=f"commit {label}", committed_at=committed_at, parent_commit_id=parent_id, )) return CommitRecord( commit_id=cid, branch="main", snapshot_id=snap_id, message=f"commit {label}", committed_at=committed_at, parent_commit_id=parent_id, ) def _fake_resp(body: bytes, status: int = 200) -> MagicMock: r = MagicMock() r.status_code = status r.content = body r.headers = {"content-type": "application/x-msgpack"} r.text = "" return r class _FakeHttpxClient: """Captures all HTTP requests made by _run_mpack_path.""" def __init__(self, upload_url: str = "https://minio.example.com/put?sig=x") -> None: self.posts: list[tuple[str, bytes]] = [] # (url, body) self.puts: list[tuple[str, bytes]] = [] # (url, body) self._upload_url = upload_url async def __aenter__(self) -> "_FakeHttpxClient": return self async def __aexit__(self, *_: object) -> None: pass async def post(self, url: str, *, content: bytes, headers: _Headers) -> MagicMock: self.posts.append((url, content)) if "mpack-presign" in url: return _fake_resp(msgpack.packb( {"upload_url": self._upload_url}, use_bin_type=True, )) # unpack-mpack return _fake_resp(msgpack.packb( {"job_id": "job-test", "head": "", "branch": "main", "objects_in_mpack": 0, "commits_in_mpack": 0}, use_bin_type=True, )) async def put(self, url: str, *, content: bytes) -> MagicMock: self.puts.append((url, content)) return _fake_resp(b"", 200) def _run_push_mpack( root: pathlib.Path, local_head: str, remote_head: str | None, force: bool, ) -> "_FakeHttpxClient": """Run _push_mpack with a fake transport and capture the HTTP requests.""" from muse.cli.commands.push import _push_mpack transport = MagicMock() transport.fetch_remote_info.return_value = RemoteInfo( domain="code", default_branch="main", branch_heads={"main": remote_head} if remote_head else {}, ) # _build_request is called to build auth headers — return a minimal mock mock_req = MagicMock() mock_req.headers = {"Authorization": "MSign stub", "Content-Type": "application/x-msgpack"} transport._build_request.return_value = mock_req fake_client = _FakeHttpxClient() with ( patch("muse.cli.commands.push._httpx.AsyncClient", return_value=fake_client), patch("muse.cli.commands.push._make_r2_client", return_value=fake_client), ): _push_mpack( transport, "https://hub.example.com/repos/r1", None, root, local_head, [], "main", force, ) return fake_client def _unpack_body(body: bytes) -> _JsonDict: """Decode the unpack-mpack POST body.""" import json return json.loads(body) # --------------------------------------------------------------------------- # F1 — force=True appears in unpack body (currently RED) # --------------------------------------------------------------------------- def test_f1_force_true_sent_in_unpack_body( tmp_path: pathlib.Path, monkeypatch: pytest.MonkeyPatch ) -> None: """_push_mpack(force=True) must send {"force": True} in the unpack body. This is currently RED: _run_mpack_path does not include "force" in the unpack payload. """ monkeypatch.setenv("MUSE_REPO_ROOT", str(tmp_path)) monkeypatch.chdir(tmp_path) root = _bare_repo(tmp_path) commit = _make_commit(root, "f1", content=b"force flag test") (heads_dir(root) / "main").write_text(commit.commit_id) fake = _run_push_mpack(root, commit.commit_id, remote_head=None, force=True) unpack_posts = [(url, body) for url, body in fake.posts if "unpack-mpack" in url] assert unpack_posts, "No POST to unpack-mpack was made" body = _unpack_body(unpack_posts[0][1]) assert "force" in body, ( f"'force' key missing from unpack body. Got keys: {list(body.keys())}. " "Fix: add 'force': force to the unpack_body dict in _run_mpack_path." ) assert body["force"] is True, ( f"Expected force=True in unpack body, got {body['force']!r}" ) # --------------------------------------------------------------------------- # F2 — force=False is also sent explicitly (currently RED) # --------------------------------------------------------------------------- def test_f2_force_false_sent_in_unpack_body( tmp_path: pathlib.Path, monkeypatch: pytest.MonkeyPatch ) -> None: """_push_mpack(force=False) must send {"force": False} in the unpack body.""" monkeypatch.setenv("MUSE_REPO_ROOT", str(tmp_path)) monkeypatch.chdir(tmp_path) root = _bare_repo(tmp_path) commit = _make_commit(root, "f2", content=b"no force test") (heads_dir(root) / "main").write_text(commit.commit_id) fake = _run_push_mpack(root, commit.commit_id, remote_head=None, force=False) unpack_posts = [(url, body) for url, body in fake.posts if "unpack-mpack" in url] assert unpack_posts, "No POST to unpack-mpack was made" body = _unpack_body(unpack_posts[0][1]) assert "force" in body, ( f"'force' key missing from unpack body. Got keys: {list(body.keys())}." ) assert body["force"] is False, ( f"Expected force=False in unpack body, got {body['force']!r}" ) # --------------------------------------------------------------------------- # F3 — "force" key is always present regardless of value # --------------------------------------------------------------------------- def test_f3_force_key_present_in_all_pushes( tmp_path: pathlib.Path, monkeypatch: pytest.MonkeyPatch ) -> None: """The 'force' key must always be in the unpack body (protocol stability).""" monkeypatch.setenv("MUSE_REPO_ROOT", str(tmp_path)) monkeypatch.chdir(tmp_path) root = _bare_repo(tmp_path) commit = _make_commit(root, "f3", content=b"always force key") (heads_dir(root) / "main").write_text(commit.commit_id) for force_value in (True, False): fake = _run_push_mpack(root, commit.commit_id, remote_head=None, force=force_value) unpack_posts = [(url, body) for url, body in fake.posts if "unpack-mpack" in url] assert unpack_posts body = _unpack_body(unpack_posts[0][1]) assert "force" in body, f"force={force_value}: 'force' missing from unpack body" assert isinstance(body["force"], bool), ( f"force={force_value}: body['force'] must be bool, got {type(body['force'])}" )