"""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()