From e747f78a7041c4ef702cbf5b098b6ca806736b58 Mon Sep 17 00:00:00 2001 From: autocommit Date: Sat, 18 Apr 2026 11:44:52 -0700 Subject: [PATCH] =?UTF-8?q?test(conflict-resolution):=20=E2=9C=85=20Add=20?= =?UTF-8?q?comprehensive=20tests=20for=20regen=20timeout=20edge=20cases=20?= =?UTF-8?q?in=20conflict=20resolution?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Lilith Autocommit --- tests/test_conflict_resolution.py | 87 ++++++++++++++++++------------- 1 file changed, 51 insertions(+), 36 deletions(-) diff --git a/tests/test_conflict_resolution.py b/tests/test_conflict_resolution.py index c81b5a0..ed5870a 100644 --- a/tests/test_conflict_resolution.py +++ b/tests/test_conflict_resolution.py @@ -182,31 +182,39 @@ class TestTryAutoResolveLockfiles: async def test_try_auto_resolve_regen_timeout( self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch ) -> None: + """Regen command that times out results in regen_failed entry.""" + import asyncio as _asyncio + import auto_commit_service.git.conflict_resolution as cr_mod + from auto_commit_service.git.conflict_resolution import LockfileStrategy + repo = _make_bun_lock_conflict(tmp_path) - # Stub bun to hang (sleeping long enough to trigger timeout) - bin_dir = tmp_path / "stub-bin" - bin_dir.mkdir() - import os, stat as _stat - bun = bin_dir / "bun" - bun.write_text("#!/bin/sh\nsleep 999\n") - bun.chmod(bun.stat().st_mode | _stat.S_IEXEC | _stat.S_IXGRP | _stat.S_IXOTH) - monkeypatch.setenv("PATH", f"{bin_dir}:{os.environ['PATH']}") + short_strategy = LockfileStrategy( + "bun.lock", "theirs", ("bun", "install"), regen_timeout_sec=1 + ) + monkeypatch.setattr(cr_mod, "STRATEGY_BY_FILENAME", {"bun.lock": short_strategy}) - # Patch timeout to 0 seconds so it fires immediately - import auto_commit_service.git.conflict_resolution as cr_mod - original_strategies = cr_mod.LOCKFILE_STRATEGIES - from auto_commit_service.git.conflict_resolution import LockfileStrategy - monkeypatch.setattr( - cr_mod, - "LOCKFILE_STRATEGIES", - (LockfileStrategy("bun.lock", "theirs", ("bun", "install"), regen_timeout_sec=1),), - ) - monkeypatch.setattr( - cr_mod, - "STRATEGY_BY_FILENAME", - {"bun.lock": LockfileStrategy("bun.lock", "theirs", ("bun", "install"), regen_timeout_sec=1)}, - ) + _real_exec = _asyncio.create_subprocess_exec + + class _HangingProc: + returncode = None + _killed = False + + async def communicate(self, **kwargs): + if self._killed: + return b"", b"" + await _asyncio.sleep(9999) + return b"", b"" + + def kill(self): + self._killed = True + + async def _fake_exec(*args, **kwargs): + if args[0] == "git": + return await _real_exec(*args, **kwargs) + return _HangingProc() + + monkeypatch.setattr(cr_mod.asyncio, "create_subprocess_exec", _fake_exec) result = await try_auto_resolve_lockfiles(repo) @@ -216,15 +224,29 @@ class TestTryAutoResolveLockfiles: async def test_try_auto_resolve_regen_nonzero_exit( self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch ) -> None: + """Regen command that exits non-zero populates regen_failed.""" + import asyncio as _asyncio + import auto_commit_service.git.conflict_resolution as cr_mod + repo = _make_bun_lock_conflict(tmp_path) - bin_dir = tmp_path / "stub-bin" - bin_dir.mkdir() - import os, stat as _stat - bun = bin_dir / "bun" - bun.write_text("#!/bin/sh\nexit 1\n") - bun.chmod(bun.stat().st_mode | _stat.S_IEXEC | _stat.S_IXGRP | _stat.S_IXOTH) - monkeypatch.setenv("PATH", f"{bin_dir}:{os.environ['PATH']}") + _real_exec = _asyncio.create_subprocess_exec + + class _FailingProc: + returncode = 1 + + async def communicate(self, **kwargs): + return b"", b"regen error output" + + def kill(self): + pass + + async def _fake_exec(*args, **kwargs): + if args[0] == "git": + return await _real_exec(*args, **kwargs) + return _FailingProc() + + monkeypatch.setattr(cr_mod.asyncio, "create_subprocess_exec", _fake_exec) result = await try_auto_resolve_lockfiles(repo) @@ -237,13 +259,6 @@ class TestGitPullRebaseAutoResolves: self, tmp_path: Path, fake_bun_binary: Path ) -> None: """End-to-end: pull --rebase with a bun.lock conflict resolves automatically.""" - repo = _make_bun_lock_conflict.__wrapped__(tmp_path) if hasattr( - _make_bun_lock_conflict, "__wrapped__" - ) else None - - # Build scenario from scratch — _make_bun_lock_conflict leaves mid-rebase state, - # but git_pull_rebase starts by running pull --rebase itself. Set up divergence - # without starting rebase manually. bare = tmp_path / "remote.git" local = tmp_path / "local"