160 lines
5.6 KiB
Python
160 lines
5.6 KiB
Python
"""Tests for the branch-worktree helper scripts (#38/#39).
|
|
|
|
Exercises path generation and refuse-to-overwrite via the scripts' ``--dry-run``
|
|
mode, so no real worktrees, branches, network, or deletions are involved.
|
|
"""
|
|
import os
|
|
import subprocess
|
|
import unittest
|
|
from pathlib import Path
|
|
|
|
REPO = Path(__file__).resolve().parent.parent
|
|
SCRIPTS = REPO / "scripts"
|
|
BRANCHES = REPO / "branches"
|
|
|
|
|
|
def run(script, *args):
|
|
branch = None
|
|
for arg in args:
|
|
if not arg.startswith("-"):
|
|
branch = arg
|
|
break
|
|
|
|
lock_file = Path("/tmp/gitea_issue_lock.json")
|
|
created_lock = False
|
|
if script == "worktree-start" and branch:
|
|
import re
|
|
import json
|
|
m = re.search(r"issue-(\d+)", branch)
|
|
if not m:
|
|
m = re.search(r"pr-(\d+)", branch)
|
|
issue_num = int(m.group(1)) if m else 999
|
|
lock_file.write_text(json.dumps({
|
|
"issue_number": issue_num,
|
|
"branch_name": branch,
|
|
"remote": "prgs",
|
|
"org": "Scaled-Tech-Consulting",
|
|
"repo": "Gitea-Tools"
|
|
}), encoding="utf-8")
|
|
created_lock = True
|
|
|
|
try:
|
|
proc = subprocess.run(
|
|
["bash", str(SCRIPTS / script), *args],
|
|
capture_output=True, text=True, cwd=str(REPO),
|
|
)
|
|
return proc.returncode, proc.stdout, proc.stderr
|
|
finally:
|
|
if created_lock and lock_file.exists():
|
|
lock_file.unlink()
|
|
|
|
|
|
class TestWorktreeStart(unittest.TestCase):
|
|
|
|
def test_dry_run_path_generation(self):
|
|
rc, out, _ = run("worktree-start", "--dry-run", "fix/issue-123-example")
|
|
self.assertEqual(rc, 0)
|
|
self.assertIn("branches/fix-issue-123-example", out)
|
|
self.assertIn("fix/issue-123-example", out)
|
|
self.assertIn("prgs/master", out) # default start-ref
|
|
|
|
def test_bad_args_exit_2(self):
|
|
rc, _, _ = run("worktree-start")
|
|
self.assertEqual(rc, 2)
|
|
|
|
def test_refuses_existing_worktree(self):
|
|
branch = f"fix/issue-999-refuse-{os.getpid()}"
|
|
slug = branch.replace("/", "-")
|
|
target = BRANCHES / slug
|
|
target.mkdir(parents=True, exist_ok=True)
|
|
try:
|
|
rc, _, err = run("worktree-start", "--dry-run", branch)
|
|
self.assertEqual(rc, 1)
|
|
self.assertIn("Refusing to reuse", err)
|
|
finally:
|
|
target.rmdir()
|
|
|
|
# -- issue-linked branch validation (#48) --------------------------------
|
|
|
|
def test_accepts_issue_linked_impl_branches(self):
|
|
for branch in ("fix/issue-123-example", "feat/issue-123-example",
|
|
"docs/issue-123-example", "chore/issue-123-example"):
|
|
rc, out, err = run("worktree-start", "--dry-run", branch)
|
|
self.assertEqual(rc, 0, f"{branch}: {err}")
|
|
self.assertIn(f"branches/{branch.replace('/', '-')}", out)
|
|
|
|
def test_accepts_review_branch(self):
|
|
rc, out, _ = run("worktree-start", "--dry-run", "review/pr-456-example")
|
|
self.assertEqual(rc, 0)
|
|
self.assertIn("branches/review-pr-456-example", out)
|
|
|
|
def test_rejects_untraceable_branches(self):
|
|
for branch in ("fix/random-name", "my-branch", "feat/no-issue-here",
|
|
"fix/issue-abc-x"):
|
|
rc, _, err = run("worktree-start", "--dry-run", branch)
|
|
self.assertEqual(rc, 2, branch)
|
|
self.assertIn("Untraceable branch name", err)
|
|
|
|
def test_allow_unlinked_override(self):
|
|
rc, out, _ = run("worktree-start", "--dry-run", "--allow-unlinked", "my-branch")
|
|
self.assertEqual(rc, 0)
|
|
self.assertIn("branches/my-branch", out)
|
|
|
|
|
|
class TestWorktreeReview(unittest.TestCase):
|
|
|
|
def test_dry_run_detached_review_path(self):
|
|
rc, out, _ = run("worktree-review", "--dry-run", "fix/issue-9-x")
|
|
self.assertEqual(rc, 0)
|
|
self.assertIn("branches/review-fix-issue-9-x", out)
|
|
self.assertIn("--detach", out)
|
|
self.assertIn("prgs/fix/issue-9-x", out) # default start-ref
|
|
|
|
def test_refuses_existing_review_worktree(self):
|
|
slug = f"review-zz-refuse-rev-{os.getpid()}"
|
|
target = BRANCHES / slug
|
|
target.mkdir(parents=True, exist_ok=True)
|
|
try:
|
|
# branch name maps to the same slug: review-<branch>
|
|
rc, _, err = run("worktree-review", "--dry-run",
|
|
f"zz-refuse-rev-{os.getpid()}")
|
|
self.assertEqual(rc, 1)
|
|
self.assertIn("Refusing to reuse", err)
|
|
finally:
|
|
target.rmdir()
|
|
|
|
|
|
class TestWorktreeClean(unittest.TestCase):
|
|
|
|
def test_missing_worktree_errors(self):
|
|
rc, _, err = run("worktree-clean", "--dry-run", "does-not-exist-xyz")
|
|
self.assertEqual(rc, 1)
|
|
self.assertIn("No such worktree", err)
|
|
|
|
def test_dry_run_does_not_delete(self):
|
|
slug = f"zz-clean-{os.getpid()}"
|
|
target = BRANCHES / slug
|
|
target.mkdir(parents=True, exist_ok=True)
|
|
try:
|
|
rc, out, _ = run("worktree-clean", "--dry-run", slug)
|
|
self.assertEqual(rc, 0)
|
|
self.assertIn("worktree remove", out)
|
|
self.assertTrue(target.is_dir()) # nothing removed in dry-run
|
|
finally:
|
|
target.rmdir()
|
|
|
|
def test_dry_run_delete_branch_lists_branch_command(self):
|
|
slug = f"zz-clean-b-{os.getpid()}"
|
|
target = BRANCHES / slug
|
|
target.mkdir(parents=True, exist_ok=True)
|
|
try:
|
|
rc, out, _ = run("worktree-clean", "--dry-run", "--delete-branch", slug)
|
|
self.assertEqual(rc, 0)
|
|
self.assertIn("branch -d", out)
|
|
finally:
|
|
target.rmdir()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|