|
6 | 6 | import nbformat |
7 | 7 | import pytest |
8 | 8 |
|
| 9 | +from jupyter_jcli._enums import MergeMode |
9 | 10 | from jupyter_jcli.drift import DriftResult, check_drift, three_way_merge |
10 | 11 | from jupyter_jcli.parser import Cell |
11 | 12 |
|
@@ -206,12 +207,15 @@ def test_no_git_base_drift_only_equal(self, tmp_path): |
206 | 207 | assert result.status == "in_sync" |
207 | 208 |
|
208 | 209 | def test_no_git_base_drift_only_unequal(self, tmp_path): |
209 | | - """No git base + unequal cells -> drift_only.""" |
| 210 | + """No git base + same count but different sources -> merged (py wins).""" |
210 | 211 | py, ipynb = _write_pair(tmp_path, ["x = 1"], ["x = 99"]) |
211 | 212 | with self._patch_git(None, None): |
212 | 213 | result = check_drift(py, ipynb) |
213 | | - assert result.status == "drift_only" |
214 | | - assert len(result.conflict_indices) >= 1 |
| 214 | + assert result.status == "merged" |
| 215 | + assert result.merge_mode == MergeMode.PY_WINS_NO_BASE |
| 216 | + assert result.ipynb_needs_update is True |
| 217 | + assert result.py_needs_update is False |
| 218 | + assert result.merged_cells[0].source == "x = 1" |
215 | 219 |
|
216 | 220 | def test_both_changed_different_cells_merged(self, tmp_path): |
217 | 221 | """Both sides changed different cells -> merged, both files need update.""" |
@@ -245,17 +249,43 @@ def _side_effect(path: Path) -> str | None: |
245 | 249 | assert calls_by_suffix[".py"] >= 1 |
246 | 250 |
|
247 | 251 | def test_py_untracked_ipynb_only_exists(self, tmp_path): |
248 | | - """With py untracked, any source difference is DRIFT_ONLY regardless of ipynb state.""" |
| 252 | + """With py untracked and same cell count, different sources -> py wins (MERGED).""" |
249 | 253 | py, ipynb = _write_pair(tmp_path, ["x = 1"], ["x = 99"]) |
250 | | - # Only py has no HEAD; we don't even check ipynb HEAD |
251 | 254 | with self._patch_git(None): |
252 | 255 | result = check_drift(py, ipynb) |
253 | | - assert result.status == "drift_only" |
254 | | - assert len(result.conflict_indices) >= 1 |
| 256 | + assert result.status == "merged" |
| 257 | + assert result.merge_mode == MergeMode.PY_WINS_NO_BASE |
255 | 258 |
|
256 | 259 | def test_py_untracked_sources_equal_is_in_sync(self, tmp_path): |
257 | 260 | """With py untracked and equal sources -> IN_SYNC.""" |
258 | 261 | py, ipynb = _write_pair(tmp_path, ["x = 1", "y = 2"], ["x = 1", "y = 2"]) |
259 | 262 | with self._patch_git(None): |
260 | 263 | result = check_drift(py, ipynb) |
261 | 264 | assert result.status == "in_sync" |
| 265 | + |
| 266 | + def test_no_git_base_py_wins_py_canonical(self, tmp_path): |
| 267 | + """No git base + 2 non-empty cells each, different sources -> MERGED, py wins.""" |
| 268 | + py, ipynb = _write_pair(tmp_path, ["x = 1", "y = 2"], ["x = 99", "y = 88"]) |
| 269 | + with self._patch_git(None): |
| 270 | + result = check_drift(py, ipynb) |
| 271 | + assert result.status == "merged" |
| 272 | + assert result.merge_mode == MergeMode.PY_WINS_NO_BASE |
| 273 | + assert result.py_needs_update is False |
| 274 | + assert result.ipynb_needs_update is True |
| 275 | + assert [c.source for c in result.merged_cells] == ["x = 1", "y = 2"] |
| 276 | + |
| 277 | + def test_no_git_base_structural_mismatch_stays_drift_only(self, tmp_path): |
| 278 | + """No git base + cell count mismatch -> DRIFT_ONLY (structural divergence).""" |
| 279 | + py, ipynb = _write_pair(tmp_path, ["x = 1", "y = 2", "z = 3"], ["x = 99"]) |
| 280 | + with self._patch_git(None): |
| 281 | + result = check_drift(py, ipynb) |
| 282 | + assert result.status == "drift_only" |
| 283 | + assert len(result.conflict_indices) >= 1 |
| 284 | + |
| 285 | + def test_no_git_base_py_trailing_empty_cell_is_in_sync(self, tmp_path): |
| 286 | + """No git base + py has trailing empty cell -> filtered out, still IN_SYNC.""" |
| 287 | + # _write_pair writes cells as-is; empty string -> empty cell in py |
| 288 | + py, ipynb = _write_pair(tmp_path, ["x = 1", ""], ["x = 1"]) |
| 289 | + with self._patch_git(None): |
| 290 | + result = check_drift(py, ipynb) |
| 291 | + assert result.status == "in_sync" |
0 commit comments