test_fetch_mpack_command_wiring.py
file-level
1
files
1
commits
0
hotspots
0
π§ dead
0
π₯ blast risk
| 1 | """TDD β Phase 4: fetch_mpack wired into clone and pull commands (issue #47). |
| 2 | |
| 3 | All clone and pull paths must call transport.fetch_mpack exclusively. |
| 4 | |
| 5 | Tests: |
| 6 | CW0 clone (depth=None) calls fetch_mpack. |
| 7 | CW1 clone --depth 1 also calls fetch_mpack. |
| 8 | CW2 pull calls fetch_mpack. |
| 9 | """ |
| 10 | from __future__ import annotations |
| 11 | |
| 12 | import argparse |
| 13 | import contextlib |
| 14 | import pathlib |
| 15 | from typing import TYPE_CHECKING, Callable |
| 16 | from unittest.mock import MagicMock, patch |
| 17 | |
| 18 | import pytest |
| 19 | |
| 20 | from muse.core.types import blob_id, fake_id |
| 21 | |
| 22 | if TYPE_CHECKING: |
| 23 | from muse.core.transport import SigningIdentity |
| 24 | |
| 25 | _FetchResult = dict[str, str | list[str] | int] # transport fetch response |
| 26 | _KwVal = str | int | bool | None |
| 27 | |
| 28 | # ββ helpers βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ |
| 29 | |
| 30 | _COMMIT_ID = fake_id("commit-1") |
| 31 | _SNAP_ID = fake_id("snap-1") |
| 32 | _REPO_ID = fake_id("repo-1") |
| 33 | |
| 34 | _FETCH_RESULT = { |
| 35 | "repo_id": _REPO_ID, |
| 36 | "domain": "code", |
| 37 | "default_branch": "main", |
| 38 | "branch_heads": {"main": _COMMIT_ID}, |
| 39 | "commits": [], |
| 40 | "snapshots": [], |
| 41 | "blobs_received": 0, |
| 42 | "shallow_commits": [], |
| 43 | } |
| 44 | |
| 45 | _APPLY_RESULT = { |
| 46 | "commits_written": 1, |
| 47 | "snapshots_written": 1, |
| 48 | "blobs_written": 0, |
| 49 | "blobs_skipped": 0, |
| 50 | } |
| 51 | |
| 52 | _REMOTE_INFO = { |
| 53 | "repo_id": _REPO_ID, |
| 54 | "domain": "code", |
| 55 | "default_branch": "main", |
| 56 | "branch_heads": {"main": _COMMIT_ID}, |
| 57 | "object_count": 1, |
| 58 | "size_bytes": 100, |
| 59 | } |
| 60 | |
| 61 | |
| 62 | def _make_transport() -> MagicMock: |
| 63 | """Transport mock: fetch_mpack returns _FETCH_RESULT.""" |
| 64 | t = MagicMock() |
| 65 | t.fetch_remote_info.return_value = _REMOTE_INFO |
| 66 | t.fetch_mpack.return_value = _FETCH_RESULT |
| 67 | return t |
| 68 | |
| 69 | |
| 70 | def _clone_patches(t: MagicMock) -> list[contextlib.AbstractContextManager[MagicMock]]: |
| 71 | return [ |
| 72 | patch("muse.cli.commands.clone.get_signing_identity", return_value=None), |
| 73 | patch("muse.cli.commands.clone.make_transport", return_value=t), |
| 74 | patch("muse.cli.commands.clone.apply_mpack", return_value=_APPLY_RESULT), |
| 75 | patch("muse.cli.commands.clone.set_remote"), |
| 76 | patch("muse.cli.commands.clone.set_remote_head"), |
| 77 | patch("muse.cli.commands.clone.set_upstream"), |
| 78 | patch("muse.cli.commands.clone.apply_manifest"), |
| 79 | patch("muse.cli.commands.clone.read_commit", return_value=MagicMock(snapshot_id=_SNAP_ID)), |
| 80 | patch("muse.cli.commands.clone.read_snapshot", return_value=MagicMock(manifest={})), |
| 81 | ] |
| 82 | |
| 83 | |
| 84 | # ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ |
| 85 | # CW0 β clone (depth=None) calls fetch_mpack |
| 86 | # ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ |
| 87 | |
| 88 | def test_cw0_clone_full_calls_fetch_mpack(tmp_path: pathlib.Path) -> None: |
| 89 | """Full clone (no --depth) must call transport.fetch_mpack, not fetch_presign_or_stream.""" |
| 90 | from muse.cli.commands.clone import run |
| 91 | |
| 92 | t = _make_transport() |
| 93 | args = argparse.Namespace( |
| 94 | url="https://hub.example.com/gabriel/repo", |
| 95 | directory=str(tmp_path / "cloned"), |
| 96 | branch=None, |
| 97 | depth=None, |
| 98 | dry_run=False, |
| 99 | no_checkout=False, |
| 100 | json_out=False, |
| 101 | ) |
| 102 | |
| 103 | patches = _clone_patches(t) |
| 104 | with patches[0], patches[1], patches[2], patches[3], patches[4], patches[5], patches[6], patches[7], patches[8]: |
| 105 | run(args) |
| 106 | |
| 107 | t.fetch_mpack.assert_called_once() |
| 108 | |
| 109 | |
| 110 | # ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ |
| 111 | # CW1 β clone --depth 1 calls fetch_mpack (presign-only policy) |
| 112 | # ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ |
| 113 | |
| 114 | def test_cw1_clone_shallow_calls_fetch_mpack(tmp_path: pathlib.Path) -> None: |
| 115 | """--depth clone must call transport.fetch_mpack β presign-only policy, no stream path.""" |
| 116 | from muse.cli.commands.clone import run |
| 117 | |
| 118 | t = _make_transport() |
| 119 | args = argparse.Namespace( |
| 120 | url="https://hub.example.com/gabriel/repo", |
| 121 | directory=str(tmp_path / "cloned"), |
| 122 | branch=None, |
| 123 | depth=1, |
| 124 | dry_run=False, |
| 125 | no_checkout=False, |
| 126 | json_out=False, |
| 127 | ) |
| 128 | |
| 129 | patches = _clone_patches(t) |
| 130 | with patches[0], patches[1], patches[2], patches[3], patches[4], patches[5], patches[6], patches[7], patches[8]: |
| 131 | run(args) |
| 132 | |
| 133 | t.fetch_mpack.assert_called_once() |
| 134 | |
| 135 | |
| 136 | # ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ |
| 137 | # CW2 β pull calls fetch_mpack |
| 138 | # ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ |
| 139 | |
| 140 | def test_cw2_pull_calls_fetch_mpack(tmp_path: pathlib.Path) -> None: |
| 141 | """pull must call transport.fetch_mpack, not fetch_presign_or_stream.""" |
| 142 | import argparse |
| 143 | from muse.cli.commands.pull import run |
| 144 | |
| 145 | def _fetch_mpack( |
| 146 | url: str, |
| 147 | signing: "SigningIdentity | None", |
| 148 | *, |
| 149 | want: str, |
| 150 | have: list[str], |
| 151 | on_object: Callable[[str, bytes], None] | None = None, |
| 152 | **kw: _KwVal, |
| 153 | ) -> _FetchResult: |
| 154 | return _FETCH_RESULT |
| 155 | |
| 156 | t = MagicMock() |
| 157 | t.fetch_remote_info.return_value = _REMOTE_INFO |
| 158 | t.fetch_mpack.side_effect = _fetch_mpack |
| 159 | |
| 160 | args = argparse.Namespace( |
| 161 | remote="origin", |
| 162 | branch_flag=None, |
| 163 | branch_pos=None, |
| 164 | no_merge=False, |
| 165 | ff_only=False, |
| 166 | dry_run=False, |
| 167 | message=None, |
| 168 | json_out=False, |
| 169 | ) |
| 170 | |
| 171 | with ( |
| 172 | patch("muse.cli.commands.pull.require_repo", return_value=tmp_path), |
| 173 | patch("muse.cli.commands.pull.get_remote", return_value="https://hub.example.com"), |
| 174 | patch("muse.cli.commands.pull.get_signing_identity", return_value=None), |
| 175 | patch("muse.cli.commands.pull.read_current_branch", return_value="main"), |
| 176 | patch("muse.cli.commands.pull.get_upstream", return_value=None), |
| 177 | patch("muse.cli.commands.pull.make_transport", return_value=t), |
| 178 | patch("muse.cli.commands.pull.get_remote_head", return_value=None), # not up-to-date |
| 179 | patch("muse.cli.commands.pull.get_head_commit_id", return_value=None), |
| 180 | patch("muse.cli.commands.pull.get_all_commits", return_value=[]), |
| 181 | patch("muse.cli.commands.pull.apply_mpack", return_value=_APPLY_RESULT), |
| 182 | patch("muse.cli.commands.pull.set_remote_head"), |
| 183 | patch("muse.cli.commands.pull.apply_manifest"), |
| 184 | patch("muse.cli.commands.pull.read_commit", return_value=MagicMock(snapshot_id=_SNAP_ID)), |
| 185 | patch("muse.cli.commands.pull.read_snapshot", return_value=MagicMock(manifest={})), |
| 186 | ): |
| 187 | try: |
| 188 | run(args) |
| 189 | except SystemExit: |
| 190 | pass # exit after pull is OK β we only care which transport method was called |
| 191 | |
| 192 | t.fetch_mpack.assert_called_once() |