[PATCH b4 v2 44/44] tests: cover the branch delete leaving the worktree on a branch

Christian Brauner <[email protected]> Fri, 31 Jul 2026 23:59:25 +0200
Newsgroups org.kernel.linux.tools
Message-ID <20260731-work-b4-editor-branch-guard-v2-44-243fd19d322d@kernel.org>
Abandoning and archiving both get a case. A session that started
detached gets its own commit back, and deleting a branch HEAD was
never on moves nothing.

Signed-off-by: Christian Brauner (Amutable) <[email protected]>
---
 src/tests/test_tui_tracking.py | 136 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 136 insertions(+)

diff --git a/src/tests/test_tui_tracking.py b/src/tests/test_tui_tracking.py
index 5a8b914..37f0e85 100644
--- a/src/tests/test_tui_tracking.py
+++ b/src/tests/test_tui_tracking.py
@@ -1649,6 +1649,142 @@ class TestTrackingAbandon:
             assert cursor.fetchone() is None
             conn.close()
 
+    @pytest.mark.asyncio
+    async def test_abandon_from_the_branch_lands_on_a_branch(self, gitdir: str) -> None:
+        """Deleting the branch HEAD is on detaches it to the parent commit
+        first, because that is all delete_review_branch() can name.  Left
+        that way the session ends on a commit with no branch on it, and the
+        restore in run_tracking_tui() reads a detached HEAD as no branch of
+        ours and declines to help."""
+        identifier = 'test-abandon-onbranch'
+        change_id = 'abandon-onbranch-1'
+        branch_name = _create_review_branch(gitdir, change_id, identifier=identifier)
+        _seed_db(
+            identifier,
+            [
+                {
+                    'change_id': change_id,
+                    'subject': '[PATCH] abandon from the branch',
+                    'status': 'reviewing',
+                    'message_id': '[email protected]',
+                }
+            ],
+        )
+        ecode, _out = b4.git_run_command(gitdir, ['checkout', '-q', branch_name])
+        assert ecode == 0
+
+        app = TrackingApp(identifier, 'master')
+        async with app.run_test(size=(120, 30)) as pilot:
+            await pilot.pause()
+            app._on_abandon_confirmed(True, change_id, branch_name, True)
+            await pilot.pause()
+
+        assert not b4.git_branch_exists(gitdir, branch_name)
+        assert b4.git_get_current_branch(gitdir) == 'master'
+
+    @pytest.mark.asyncio
+    async def test_archive_from_the_branch_lands_on_a_branch(self, gitdir: str) -> None:
+        """Archiving deletes the branch at the end, so it detaches HEAD the
+        same way abandoning does."""
+        identifier = 'test-archive-onbranch'
+        change_id = 'archive-onbranch-1'
+        branch_name = _create_review_branch(
+            gitdir, change_id, identifier=identifier, with_patch=True
+        )
+        _seed_db(
+            identifier,
+            [
+                {
+                    'change_id': change_id,
+                    'subject': '[PATCH] archive from the branch',
+                    'status': 'accepted',
+                    'message_id': '[email protected]',
+                }
+            ],
+        )
+        ecode, _out = b4.git_run_command(gitdir, ['checkout', '-q', branch_name])
+        assert ecode == 0
+
+        app = TrackingApp(identifier, 'master')
+        async with app.run_test(size=(120, 30)) as pilot:
+            await pilot.pause()
+            assert app._archive_branch(change_id, 1, branch_name)
+            await pilot.pause()
+
+        assert not b4.git_branch_exists(gitdir, branch_name)
+        assert b4.git_get_current_branch(gitdir) == 'master'
+
+    @pytest.mark.asyncio
+    async def test_abandon_lands_back_on_a_detached_head(self, gitdir: str) -> None:
+        """A session that started detached came from a commit, not a branch.
+
+        delete_review_branch() can only name the deleted branch's parent, so
+        without the session's own starting point the user is left parked on
+        whatever that happens to be."""
+        identifier = 'test-abandon-detached'
+        change_id = 'abandon-detached-1'
+        branch_name = _create_review_branch(gitdir, change_id, identifier=identifier)
+        _seed_db(
+            identifier,
+            [
+                {
+                    'change_id': change_id,
+                    'subject': '[PATCH] abandon detached',
+                    'status': 'reviewing',
+                    'message_id': '[email protected]',
+                }
+            ],
+        )
+        ecode, out = b4.git_run_command(gitdir, ['rev-parse', 'master'])
+        assert ecode == 0
+        start_sha = out.strip()
+        ecode, _out = b4.git_run_command(gitdir, ['checkout', '-q', branch_name])
+        assert ecode == 0
+
+        app = TrackingApp(
+            identifier, None, original_head=['checkout', '--detach', start_sha]
+        )
+        async with app.run_test(size=(120, 30)) as pilot:
+            await pilot.pause()
+            app._on_abandon_confirmed(True, change_id, branch_name, True)
+            await pilot.pause()
+
+        assert not b4.git_branch_exists(gitdir, branch_name)
+        assert b4.git_get_current_branch(gitdir) is None
+        ecode, out = b4.git_run_command(gitdir, ['rev-parse', 'HEAD'])
+        assert ecode == 0
+        assert out.strip() == start_sha
+
+    @pytest.mark.asyncio
+    async def test_abandon_from_elsewhere_leaves_head_alone(self, gitdir: str) -> None:
+        """HEAD was never on the deleted branch, so nothing moved it and
+        nothing should move it back."""
+        identifier = 'test-abandon-elsewhere'
+        change_id = 'abandon-elsewhere-1'
+        branch_name = _create_review_branch(gitdir, change_id, identifier=identifier)
+        _seed_db(
+            identifier,
+            [
+                {
+                    'change_id': change_id,
+                    'subject': '[PATCH] abandon from elsewhere',
+                    'status': 'reviewing',
+                    'message_id': '[email protected]',
+                }
+            ],
+        )
+        ecode, _out = b4.git_run_command(gitdir, ['checkout', '-q', '-b', 'mine'])
+        assert ecode == 0
+
+        app = TrackingApp(identifier, 'master')
+        async with app.run_test(size=(120, 30)) as pilot:
+            await pilot.pause()
+            app._on_abandon_confirmed(True, change_id, branch_name, True)
+            await pilot.pause()
+
+        assert not b4.git_branch_exists(gitdir, branch_name)
+        assert b4.git_get_current_branch(gitdir) == 'mine'
+
 
 class TestTrackingWaiting:
     """Tests for the 'mark as waiting' workflow."""

-- 
2.53.0