gabriel / musehub public

test_merge_strategy_cherry_pick.py file-level

at sha256:3 · View file ↗ · Intel ↗

History
1 files
1 commits
0 hotspots
0 🧊 dead
0 💥 blast risk
sha256:0 fix: fall back to any indexed mpack in read_object_bytes when push mpac… · gabriel · Jun 17, 2026
1 """TDD tests for the cherry_pick merge strategy.
2
3 Cherry-pick applies the delta of each specified commit (parent→snapshot) in
4 order onto the to_branch manifest. Only the changes introduced by those commits
5 are applied — the rest of to_branch is untouched.
6 """
7
8 from __future__ import annotations
9
10 import pytest
11
12 from musehub.services.proposal_merge_strategies import (
13 MergeResult,
14 execute_merge_strategy,
15 merge_cherry_pick,
16 )
17
18
19 # ---------------------------------------------------------------------------
20 # Helpers
21 # ---------------------------------------------------------------------------
22
23 def _oid(name: str) -> str:
24 """Stable fake object ID for test manifests."""
25 return f"sha256:{name:0<64}"
26
27
28 # ---------------------------------------------------------------------------
29 # TestMergeCherryPick
30 # ---------------------------------------------------------------------------
31
32
33 class TestMergeCherryPick:
34
35 def test_single_commit_adds_file(self) -> None:
36 to_manifest = {"src/a.py": _oid("a")}
37 parent = {"src/a.py": _oid("a")}
38 commit = {"src/a.py": _oid("a"), "src/b.py": _oid("b")}
39 result = merge_cherry_pick(to_manifest, cherry_pick_commits=[(parent, commit)])
40 assert "src/b.py" in result.manifest
41 assert result.files_added == 1
42
43 def test_single_commit_modifies_file(self) -> None:
44 to_manifest = {"src/a.py": _oid("a_old")}
45 parent = {"src/a.py": _oid("a_old")}
46 commit = {"src/a.py": _oid("a_new")}
47 result = merge_cherry_pick(to_manifest, cherry_pick_commits=[(parent, commit)])
48 assert result.manifest["src/a.py"] == _oid("a_new")
49 assert result.files_modified == 1
50
51 def test_single_commit_removes_file(self) -> None:
52 to_manifest = {"src/a.py": _oid("a"), "src/b.py": _oid("b")}
53 parent = {"src/a.py": _oid("a"), "src/b.py": _oid("b")}
54 commit = {"src/a.py": _oid("a")} # b removed
55 result = merge_cherry_pick(to_manifest, cherry_pick_commits=[(parent, commit)])
56 assert "src/b.py" not in result.manifest
57 assert result.files_removed == 1
58
59 def test_to_branch_files_untouched_when_not_in_delta(self) -> None:
60 to_manifest = {"src/a.py": _oid("a"), "src/z.py": _oid("z")}
61 parent = {"src/a.py": _oid("a")}
62 commit = {"src/a.py": _oid("a2")} # only a changed
63 result = merge_cherry_pick(to_manifest, cherry_pick_commits=[(parent, commit)])
64 assert result.manifest["src/z.py"] == _oid("z")
65
66 def test_multiple_commits_applied_in_order(self) -> None:
67 to_manifest = {"src/a.py": _oid("a")}
68 # commit 1: add b.py
69 parent1 = {"src/a.py": _oid("a")}
70 commit1 = {"src/a.py": _oid("a"), "src/b.py": _oid("b1")}
71 # commit 2: update b.py
72 parent2 = {"src/a.py": _oid("a"), "src/b.py": _oid("b1")}
73 commit2 = {"src/a.py": _oid("a"), "src/b.py": _oid("b2")}
74 result = merge_cherry_pick(
75 to_manifest,
76 cherry_pick_commits=[(parent1, commit1), (parent2, commit2)],
77 )
78 assert result.manifest["src/b.py"] == _oid("b2")
79
80 def test_second_commit_sees_state_after_first(self) -> None:
81 to_manifest = {}
82 parent1 = {}
83 commit1 = {"src/a.py": _oid("a")}
84 parent2 = {"src/a.py": _oid("a")}
85 commit2 = {"src/a.py": _oid("a"), "src/b.py": _oid("b")}
86 result = merge_cherry_pick(
87 to_manifest,
88 cherry_pick_commits=[(parent1, commit1), (parent2, commit2)],
89 )
90 assert "src/a.py" in result.manifest
91 assert "src/b.py" in result.manifest
92
93 def test_conflict_recorded_when_to_branch_modified_same_file(self) -> None:
94 to_manifest = {"src/a.py": _oid("a_to")} # to_branch has its own version
95 parent = {"src/a.py": _oid("a_base")} # commit diverged from a different base
96 commit = {"src/a.py": _oid("a_pick")}
97 result = merge_cherry_pick(to_manifest, cherry_pick_commits=[(parent, commit)])
98 assert len(result.conflicts) == 1
99 assert result.conflicts[0].path == "src/a.py"
100 assert result.conflicts[0].resolution == "from_wins"
101
102 def test_no_conflict_when_to_branch_has_same_content(self) -> None:
103 to_manifest = {"src/a.py": _oid("a_pick")} # already at target state
104 parent = {"src/a.py": _oid("a_base")}
105 commit = {"src/a.py": _oid("a_pick")}
106 result = merge_cherry_pick(to_manifest, cherry_pick_commits=[(parent, commit)])
107 assert result.conflicts == []
108
109 def test_strategy_name(self) -> None:
110 to_manifest = {"src/a.py": _oid("a")}
111 parent = {"src/a.py": _oid("a")}
112 commit = {"src/a.py": _oid("a2")}
113 result = merge_cherry_pick(to_manifest, cherry_pick_commits=[(parent, commit)])
114 assert result.strategy == "cherry_pick"
115
116 def test_empty_commits_raises(self) -> None:
117 with pytest.raises(ValueError, match="cherry_pick"):
118 merge_cherry_pick({"src/a.py": _oid("a")}, cherry_pick_commits=[])
119
120 def test_domains_merged_populated(self) -> None:
121 to_manifest = {}
122 parent = {}
123 commit = {"src/main.py": _oid("m"), "tracks/beat.mid": _oid("b")}
124 result = merge_cherry_pick(to_manifest, cherry_pick_commits=[(parent, commit)])
125 assert "code" in result.domains_merged
126 assert "midi" in result.domains_merged
127
128 def test_files_added_count_across_multiple_commits(self) -> None:
129 to_manifest = {}
130 parent1, commit1 = {}, {"a.py": _oid("a")}
131 parent2, commit2 = {"a.py": _oid("a")}, {"a.py": _oid("a"), "b.py": _oid("b")}
132 result = merge_cherry_pick(
133 to_manifest,
134 cherry_pick_commits=[(parent1, commit1), (parent2, commit2)],
135 )
136 assert result.files_added == 2
137
138
139 # ---------------------------------------------------------------------------
140 # TestStrategyRouterCherryPick
141 # ---------------------------------------------------------------------------
142
143
144 class TestStrategyRouterCherryPick:
145
146 def test_routes_cherry_pick(self) -> None:
147 to_manifest = {"src/a.py": _oid("a")}
148 parent = {"src/a.py": _oid("a")}
149 commit = {"src/a.py": _oid("a2")}
150 result = execute_merge_strategy(
151 "cherry_pick",
152 to_manifest,
153 {},
154 cherry_pick_commits=[(parent, commit)],
155 )
156 assert result.strategy == "cherry_pick"
157 assert result.manifest["src/a.py"] == _oid("a2")
158
159 def test_cherry_pick_missing_commits_raises(self) -> None:
160 with pytest.raises(ValueError, match="cherry_pick"):
161 execute_merge_strategy("cherry_pick", {"a.py": _oid("a")}, {})
162
163 def test_cherry_pick_empty_commits_raises(self) -> None:
164 with pytest.raises(ValueError, match="cherry_pick"):
165 execute_merge_strategy(
166 "cherry_pick", {"a.py": _oid("a")}, {}, cherry_pick_commits=[]
167 )