Files
Gitea-Tools/tests/test_webui_worktree_hygiene.py
T
sysadminandClaude Opus 4.8 276a71bd1a feat: add worktree hygiene dashboard to web UI (Closes #432)
Adds read-only /worktrees and /api/worktrees routes that scan branches/
directories, classify worktree state, flag missing preserved worktrees from
the issue lock, and surface the canonical cleanup prompt without deletion.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-07-07 15:26:22 -04:00

114 lines
4.0 KiB
Python

"""Tests for web UI worktree hygiene dashboard (#432)."""
import json
import sys
import tempfile
import unittest
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
from starlette.testclient import TestClient
from webui.app import create_app
from webui.worktree_scanner import (
classify_entry,
load_hygiene_snapshot,
parse_worktree_list_porcelain,
snapshot_to_dict,
)
class TestWorktreeScanner(unittest.TestCase):
def test_parse_worktree_porcelain(self):
porcelain = (
"worktree /repo/branches/feat-issue-1-foo\n"
"HEAD abcdef0123456789\n"
"branch refs/heads/feat/issue-1-foo\n"
"\n"
)
entries = parse_worktree_list_porcelain(porcelain)
self.assertEqual(len(entries), 1)
self.assertEqual(entries[0]["branch"], "feat/issue-1-foo")
def test_classify_active_pr(self):
classification, _ = classify_entry(
rel_path="branches/feat-issue-99-demo",
worktree_record={"branch": "feat/issue-99-demo"},
state={"exists": True, "clean": True, "dirty_files": []},
open_pr_branches={"feat/issue-99-demo"},
active_lock_branch=None,
)
self.assertEqual(classification, "active-pr")
def test_classify_dirty(self):
classification, _ = classify_entry(
rel_path="branches/feat-issue-2-bar",
worktree_record={"branch": "feat/issue-2-bar"},
state={"exists": True, "clean": False, "dirty_files": ["webui/app.py"]},
open_pr_branches=set(),
active_lock_branch=None,
)
self.assertEqual(classification, "dirty")
def test_missing_lock_worktree_anomaly(self):
with tempfile.TemporaryDirectory() as tmp:
branches = Path(tmp) / "branches" / "other-worktree"
branches.mkdir(parents=True)
lock_path = Path(tmp) / "lock.json"
lock_path.write_text(
json.dumps({
"issue_number": 432,
"branch_name": "feat/issue-432-worktree-hygiene",
"worktree_path": str(Path(tmp) / "branches" / "missing-tree"),
}),
encoding="utf-8",
)
snapshot = load_hygiene_snapshot(
project_root=tmp,
worktree_porcelain="",
open_pr_branches=set(),
issue_lock_path=str(lock_path),
)
self.assertTrue(snapshot.anomalies)
self.assertIn("Missing preserved worktree", snapshot.anomalies[0])
def test_snapshot_to_dict(self):
with tempfile.TemporaryDirectory() as tmp:
(Path(tmp) / "branches").mkdir()
snapshot = load_hygiene_snapshot(
project_root=tmp,
worktree_porcelain="",
open_pr_branches=set(),
issue_lock_path=str(Path(tmp) / "no-lock.json"),
)
data = snapshot_to_dict(snapshot)
self.assertIn("entries", data)
self.assertIn("cleanup_prompt", data)
class TestWorktreeRoutes(unittest.TestCase):
def setUp(self):
self.client = TestClient(create_app())
def test_worktrees_page_renders(self):
response = self.client.get("/worktrees")
self.assertEqual(response.status_code, 200)
self.assertIn("Worktree hygiene", response.text)
self.assertIn("Canonical cleanup prompt", response.text)
self.assertIn("Copy cleanup prompt", response.text)
self.assertNotIn("child issue", response.text.lower())
def test_api_worktrees_json(self):
response = self.client.get("/api/worktrees")
self.assertEqual(response.status_code, 200)
data = response.json()
self.assertIn("entries", data)
self.assertIn("cleanup_prompt", data)
self.assertIn("anomalies", data)
def test_nav_includes_worktrees(self):
self.assertIn('href="/worktrees"', self.client.get("/").text)
if __name__ == "__main__":
unittest.main()