raw ยท 7293 bytes
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 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 | import os import sys import tempfile import unittest from pathlib import Path sys.path.insert(0, str(Path(__file__).resolve().parent.parent)) from wflib import config as C from test_claims import FOUR, git from test_cli import TOML, Cli SETUP = TOML + 'worktree_setup = ["mkdir -p out && ln -sfn \\"$WF_MAIN/out/data\\" out/data", "echo ran >> log.txt"]\n' class SetupConfigTest(unittest.TestCase): def setUp(self): self.tmp = tempfile.TemporaryDirectory() self.root = Path(self.tmp.name).resolve() def tearDown(self): self.tmp.cleanup() def load(self, extra): (self.root / "workflow.toml").write_text('format = 1\ntasks = "T.md"\narchive = "a.md"\n' + extra) return C.load(self.root) def test_default_empty(self): self.assertEqual(self.load("").worktree_setup, []) def test_list_of_commands(self): self.assertEqual(self.load('worktree_setup = ["a", "b c"]\n').worktree_setup, ["a", "b c"]) def test_wrong_type(self): with self.assertRaisesRegex(C.ConfigError, "'worktree_setup' must be a list of strings"): self.load('worktree_setup = "a"\n') def test_unknown_key_still_errors(self): with self.assertRaisesRegex(C.ConfigError, "unknown key 'worktree_setp'"): self.load('worktree_setp = []\n') class GitCli(Cli): tasks_text = FOUR toml = SETUP def setUp(self): super().setUp() git(self.root, "init", "-q", "-b", "master") (self.root / ".gitignore").write_text(".worktrees/\n.wf/\nout/\n") git(self.root, "add", "-A") git(self.root, "commit", "-qm", "init") class WorktreeCli(GitCli): def setUp(self): super().setUp() self.wt = self.root / ".worktrees" / "opus" git(self.root, "worktree", "add", "-q", str(self.wt), "-b", "opus/t-three") (self.root / "out" / "data").mkdir(parents=True) (self.root / "out" / "data" / "x.bin").write_text("main data\n") def setup_(self, *args, cwd=None): return self.wf("setup", *args, project=False, cwd=cwd or self.wt) class SetupCliTest(WorktreeCli): def test_runs_commands_in_worktree_with_wf_main(self): code, out, err = self.setup_() self.assertEqual((code, err), (0, ""), out) self.assertEqual((self.wt / "out" / "data" / "x.bin").read_text(), "main data\n") self.assertEqual((self.wt / "log.txt").read_text(), "ran\n") self.assertFalse((self.root / "log.txt").exists()) self.assertIn("$ echo ran >> log.txt\n", out) self.assertTrue(out.endswith("worktree_setup: 2 commands ok in .worktrees/opus\n"), out) def test_idempotent_rerun(self): self.assertEqual(self.setup_()[0], 0) self.assertEqual(self.setup_()[0], 0) self.assertEqual((self.wt / "out" / "data" / "x.bin").read_text(), "main data\n") def test_from_subfolder_runs_at_worktree_project_root(self): code, out, err = self.setup_(cwd=self.wt / "docs") self.assertEqual((code, err), (0, ""), out) self.assertTrue((self.wt / "log.txt").is_file()) def test_nonzero_exit_reported_and_rest_skipped(self): (self.wt / "workflow.toml").write_text(TOML + 'worktree_setup = ["echo a > a.txt", "exit 3", "echo c > c.txt"]\n') code, out, err = self.setup_() self.assertEqual(code, 1, out + err) self.assertEqual(err, "wf: worktree_setup 'exit 3' failed (exit 3): later commands skipped\n") self.assertTrue((self.wt / "a.txt").is_file()) self.assertFalse((self.wt / "c.txt").exists()) def test_outside_worktree_fails(self): code, out, err = self.wf("setup", project=False, cwd=self.root) self.assertEqual((code, err), (1, "wf: setup runs inside a linked git worktree (lane worktree)\n")) def test_none_configured(self): (self.wt / "workflow.toml").write_text(TOML) code, out, err = self.setup_() self.assertEqual((code, out, err), (0, "no worktree_setup in workflow.toml: nothing to do\n", "")) class WorktreeConfigTest(WorktreeCli): """A lane worktree's own workflow.toml / area file: setup, gate, areas read it; --mark writes it.""" def edit_wt_toml(self, extra): (self.wt / "workflow.toml").write_text(TOML + extra) def test_setup_and_gate_read_worktree_toml(self): self.edit_wt_toml('worktree_setup = ["echo branch > b.txt"]\nquick_gate = ["echo g > g.txt"]\n') code, out, err = self.setup_() self.assertEqual((code, err), (0, ""), out) self.assertEqual((self.wt / "b.txt").read_text(), "branch\n") self.assertFalse((self.wt / "log.txt").exists()) code, out, err = self.wf("gate", project=False, cwd=self.wt) self.assertEqual((code, err), (0, ""), out) self.assertTrue((self.wt / "g.txt").is_file()) def test_books_stay_main_tree(self): self.edit_wt_toml("") (self.wt / "TASKS.md").write_text("# other\n") out = self.ok("show", "t-three", project=False, cwd=self.wt) self.assertIn("t-three", out) cfg = C.load_at(self.wt) self.assertEqual((cfg.root, cfg.local, cfg.tasks), (self.root, self.wt, self.root / "TASKS.md")) self.assertEqual(cfg.areas_file, self.wt / "CLAUDE.md") def test_areas_read_and_mark_worktree_copy(self): notes = "## Areas\n### Core\n- Code map: `nothing_here`\n" (self.root / "CLAUDE.md").write_text(notes.replace("Core", "Main")) (self.wt / "CLAUDE.md").write_text(notes) self.assertIn("Core: ", self.ok("areas", project=False, cwd=self.wt)) self.ok("areas", "--mark", "Core", project=False, cwd=self.wt) self.assertIn("- Checked: ", (self.wt / "CLAUDE.md").read_text()) self.assertEqual((self.root / "CLAUDE.md").read_text(), notes.replace("Core", "Main")) self.assertIn("Main: ", self.ok("areas", project=False, cwd=self.root)) def test_main_tree_ignores_worktree(self): self.edit_wt_toml('quick_gate = ["exit 9"]\n') self.assertIsNone(C.find_local(self.root)) code, out, err = self.wf("gate", project=False, cwd=self.root) self.assertEqual((code, out), (0, "no quick_gate in workflow.toml: nothing to do\n")) class SetupHintTest(GitCli): def env(self, name, pid): sock = self.root / f"{name}.sock" sock.write_text("") return {"CLAUDE_CODE_MESSAGING_SOCKET": str(sock), "CLAUDE_PID": str(pid), "CLAUDE_CODE_SESSION_ID": name} def test_multi_session_hint_adds_setup(self): son, me = self.env("s", os.getppid()), self.env("o", os.getpid()) self.ok("next", "--lane", "slow", "--as", "sonnet", "--brief", env=son) out = self.ok("next", "--lane", "fast", "--as", "opus", env=me) self.assertIn(" git worktree add .worktrees/fast -b fast/<task> master && cd .worktrees/fast && wf setup" " (wf there writes this TASKS.md)\n", out) (self.root / ".worktrees" / "fast").mkdir(parents=True) out = self.ok("next", "--lane", "fast", "--as", "opus", env=me) self.assertIn(" cd .worktrees/fast && git switch -c fast/<task> master && wf setup" " (wf there writes this TASKS.md)\n", out) if __name__ == "__main__": unittest.main() |