1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
|
import sys
import unittest
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
from test_claims import git
from test_cli import WF
from test_setup import GitCli
NONE = {"CLAUDE_CODE_MESSAGING_SOCKET": ""}
class StartTest(GitCli):
def setUp(self):
super().setUp()
self.wt = self.root / ".worktrees" / "slow"
def start(self, *extra, code=0):
got, out, err = self.wf("start", "t-three", "--worktree", ".worktrees/slow", "--branch", "slow/t-three",
*extra, project=False, env=NONE)
self.assertEqual(got, code, out + err)
self.assertNotIn("Traceback", err)
return out, err
def branch(self):
return (self.wt / ".git").is_file() and \
subprocess_out(self.wt, "branch", "--show-current")
def test_new_worktree_setup_progress_ctx_finish_line(self):
out, _ = self.start()
self.assertEqual(self.branch(), "slow/t-three")
self.assertEqual((self.wt / "log.txt").read_text(), "ran\n") # worktree_setup ran there
self.assertIn("**t-three** [P2] (<1h) (in progress: slow/t-three): Three.", self.tasks())
self.assertIn("worktree: .worktrees/slow (new, branch slow/t-three from master)\n", out)
self.assertEqual(out.count("- **t-three**"), 1, out)
self.assertIn("worktree_setup: 2 commands ok in .worktrees/slow\n", out)
self.assertIn("- **t-three** [P2] (<1h) (in progress: slow/t-three): Three.\n\nSection: Pending\n", out)
self.assertTrue(out.endswith(
f"Finish (after the work; fill in the quoted parts and the paths):\n"
f" cd {self.wt} && (make test) && python3 {WF} finish t-three -m \"<entry>\" --commit \"<msg + footer>\" <paths>\n"),
out)
def test_existing_branch_new_worktree_reports_wip(self):
git(self.root, "branch", "slow/t-three")
out, _ = self.start()
self.assertEqual(self.branch(), "slow/t-three")
self.assertIn("worktree: .worktrees/slow (new, existing branch slow/t-three: earlier WIP, read the notes)\n",
out)
def test_existing_clean_worktree_switches(self):
git(self.root, "worktree", "add", "-q", "--detach", str(self.wt), "master")
out, _ = self.start()
self.assertEqual(self.branch(), "slow/t-three")
self.assertIn("worktree: .worktrees/slow (switched to new branch slow/t-three from master)\n", out)
(self.wt / "log.txt").unlink() # setup output, not ignored here
out, _ = self.start() # rerun: already there
self.assertIn("worktree: .worktrees/slow (on slow/t-three)\n", out)
def test_dirty_worktree_refused_without_recovery(self):
git(self.root, "worktree", "add", "-q", "--detach", str(self.wt), "master")
(self.wt / "DESIGN.md").write_text("changed\n")
_, err = self.start(code=1)
self.assertEqual(err, "wf: worktree .worktrees/slow has uncommitted changes: M DESIGN.md "
"(hand back, or --recovery when a dead worker left them)\n")
self.assertNotIn("in progress", self.tasks())
self.assertFalse((self.wt / "log.txt").exists())
def test_recovery_keeps_dirty_wip_and_shows_it(self):
git(self.root, "worktree", "add", "-q", str(self.wt), "-b", "slow/t-three")
(self.wt / "DESIGN.md").write_text("changed\n")
out, _ = self.start("--recovery")
self.assertEqual((self.wt / "DESIGN.md").read_text(), "changed\n")
self.assertIn("recovery: uncommitted:\n M DESIGN.md\n", out)
self.assertIn("recovery: commits master..HEAD: none\n", out)
self.assertIn("(in progress: slow/t-three)", self.tasks())
def test_recovery_dirty_on_other_branch_refused(self):
git(self.root, "worktree", "add", "-q", "--detach", str(self.wt), "master")
(self.wt / "DESIGN.md").write_text("changed\n")
_, err = self.start("--recovery", code=1)
self.assertIn("uncommitted changes on another branch", err)
def test_setup_failure_stops_before_progress(self):
(self.root / "workflow.toml").write_text(self.toml.replace('"echo ran >> log.txt"', '"false"'))
git(self.root, "commit", "-qam", "red setup")
_, err = self.start(code=1)
self.assertIn("worktree_setup 'false' failed", err)
self.assertNotIn("in progress", self.tasks())
def test_unknown_id_creates_nothing(self):
got, out, err = self.wf("start", "t-nope", "--worktree", ".worktrees/slow", "--branch", "b",
project=False, env=NONE)
self.assertEqual(got, 1, out + err)
self.assertFalse(self.wt.exists())
def test_inside_worktree_refused(self):
git(self.root, "worktree", "add", "-q", "--detach", str(self.wt), "master")
got, out, err = self.wf("start", "t-three", "--worktree", str(self.wt), "--branch", "b",
project=False, cwd=self.wt, env=NONE)
self.assertEqual((got, err), (1, "wf: start runs in the main tree (it creates the worktree)\n"))
def subprocess_out(cwd, *args):
import subprocess
return subprocess.run(["git", "-C", str(cwd), *args], capture_output=True, text=True).stdout.strip()
if __name__ == "__main__":
unittest.main()
|