aboutsummaryrefslogtreecommitdiffziptar.gz
path: root/tests/test_setup.py
diff options
context:
space:
mode:
authorgodosa <godosa@godosa.eu>2026-10-07 07:27:17 +0200
committergodosa <godosa@godosa.eu>2026-10-07 07:27:17 +0200
commit81d4e80fd5aabe4e80f58e960affa795cf7d34ec (patch)
treee98eeac2af6af63aa4287bba1f6d4a3af26b5727 /tests/test_setup.py
downloadworkflow-81d4e80fd5aabe4e80f58e960affa795cf7d34ec.tar.gz
workflow-81d4e80fd5aabe4e80f58e960affa795cf7d34ec.zip
workflow: initial public history
Diffstat (limited to 'tests/test_setup.py')
-rw-r--r--tests/test_setup.py164
1 files changed, 164 insertions, 0 deletions
diff --git a/tests/test_setup.py b/tests/test_setup.py
new file mode 100644
index 0000000..ea44c33
--- /dev/null
+++ b/tests/test_setup.py
@@ -0,0 +1,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()