aboutsummaryrefslogtreecommitdiff
path: root/tests/tmux-util/test_tmux_util.py
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2026-05-19 13:01:19 -0500
committerCraig Jennings <c@cjennings.net>2026-05-19 13:01:19 -0500
commit685272399d7dbc35aea6028d6741963399d84e3f (patch)
tree33bf2ccae4ca1e1d11029b57be04af3e6ad561bd /tests/tmux-util/test_tmux_util.py
parent924fec1f00e7ef5e497575488701e8c9eb2606f0 (diff)
downloadarchsetup-685272399d7dbc35aea6028d6741963399d84e3f.tar.gz
archsetup-685272399d7dbc35aea6028d6741963399d84e3f.zip
feat(tmux-util): add go subcommand (attach-or-create)
tmux-util go <name> attaches to a session named <name> if it exists, creates it otherwise. Behavior depends on whether the caller is already inside tmux: - Outside tmux: `tmux attach-session -t <name>` (existing) or `tmux new-session -s <name> -c $PWD` (new). - Inside tmux (TMUX env set): `tmux switch-client -t <name>` (existing) or `tmux new-session -d -s <name> -c $PWD` followed by `switch-client` (new). Attaching from inside tmux would nest sessions and break the outer view, so the inside path uses switch-client instead. The existence check uses `tmux has-session -t =<name>` with the leading `=` to force exact-match. Without it, tmux does prefix matching, which would let `go foo` resolve to a session named `foobar`. I added 6 new tests covering both inside/outside-tmux paths, both create/attach paths, plus error handling for missing or empty name arguments. fake-tmux picked up handlers for new-session (mutates state), attach-session and switch-client (record-only), and the `=`-prefix form of has-session. Total suite: 32 tests, all green.
Diffstat (limited to 'tests/tmux-util/test_tmux_util.py')
-rw-r--r--tests/tmux-util/test_tmux_util.py103
1 files changed, 103 insertions, 0 deletions
diff --git a/tests/tmux-util/test_tmux_util.py b/tests/tmux-util/test_tmux_util.py
index 283c3c7..24cc335 100644
--- a/tests/tmux-util/test_tmux_util.py
+++ b/tests/tmux-util/test_tmux_util.py
@@ -374,5 +374,108 @@ class TestLsBoundary(TmuxUtilHarness):
self.assertNotIn(cwd_under_home, result.stdout)
+# -----------------------------------------------------------------------------
+# go — Normal cases
+# -----------------------------------------------------------------------------
+
+class TestGoNormal(TmuxUtilHarness):
+
+ def test_go_existing_session_outside_tmux_attaches(self):
+ self.set_sessions([
+ ("work", 0, [201], 900, 1, "/tmp/work"),
+ ])
+ # Outside tmux: TMUX env should be unset.
+ env = {k: v for k, v in os.environ.items() if k != "TMUX"}
+ result = subprocess.run(
+ [SCRIPT, "go", "work"],
+ env={**env, "PATH": self.bin_dir + os.pathsep + env.get("PATH", ""),
+ "FAKE_TMUX_DIR": self.tmp},
+ capture_output=True, text=True, timeout=10,
+ )
+ self.assertEqual(result.returncode, 0, msg=result.stderr)
+ calls = self.tmux_calls()
+ # has-session should be the first existence check (exact-match form)
+ self.assertTrue(any("has-session" in c and "work" in c for c in calls),
+ f"expected has-session probe in {calls!r}")
+ self.assertTrue(any("attach-session -t work" in c for c in calls),
+ f"expected attach-session -t work in {calls!r}")
+ # Must NOT create a new session
+ self.assertFalse(any("new-session" in c for c in calls),
+ f"unexpected new-session in {calls!r}")
+
+ def test_go_existing_session_inside_tmux_switches_client(self):
+ self.set_sessions([
+ ("work", 0, [201], 900, 1, "/tmp/work"),
+ ])
+ result = self.run_script("go", "work", env_extra={"TMUX": "/tmp/fake-tmux-socket,1234,0"})
+ self.assertEqual(result.returncode, 0, msg=result.stderr)
+ calls = self.tmux_calls()
+ self.assertTrue(any("switch-client -t work" in c for c in calls),
+ f"expected switch-client in {calls!r}")
+ # attach-session shouldn't be called inside tmux
+ self.assertFalse(any("attach-session" in c for c in calls),
+ f"unexpected attach-session in {calls!r}")
+
+ def test_go_missing_session_outside_tmux_creates(self):
+ self.set_sessions([])
+ env = {k: v for k, v in os.environ.items() if k != "TMUX"}
+ result = subprocess.run(
+ [SCRIPT, "go", "fresh"],
+ env={**env, "PATH": self.bin_dir + os.pathsep + env.get("PATH", ""),
+ "FAKE_TMUX_DIR": self.tmp},
+ cwd="/tmp",
+ capture_output=True, text=True, timeout=10,
+ )
+ self.assertEqual(result.returncode, 0, msg=result.stderr)
+ calls = self.tmux_calls()
+ # new-session with -s and -c pointing at the subprocess's cwd
+ self.assertTrue(
+ any("new-session" in c and "-s fresh" in c and "-c /tmp" in c
+ for c in calls),
+ f"expected new-session -s fresh -c /tmp in {calls!r}",
+ )
+
+ def test_go_missing_session_inside_tmux_creates_detached_then_switches(self):
+ self.set_sessions([])
+ env = os.environ.copy()
+ env.update({
+ "PATH": self.bin_dir + os.pathsep + env.get("PATH", ""),
+ "FAKE_TMUX_DIR": self.tmp,
+ "TMUX": "/tmp/fake-tmux-socket,1234,0",
+ })
+ result = subprocess.run(
+ [SCRIPT, "go", "fresh"],
+ env=env,
+ cwd="/tmp",
+ capture_output=True, text=True, timeout=10,
+ )
+ self.assertEqual(result.returncode, 0, msg=result.stderr)
+ calls = self.tmux_calls()
+ self.assertTrue(
+ any("new-session" in c and "-d" in c and "-s fresh" in c and "-c /tmp" in c
+ for c in calls),
+ f"expected detached new-session in {calls!r}",
+ )
+ self.assertTrue(any("switch-client -t fresh" in c for c in calls),
+ f"expected switch-client after create in {calls!r}")
+
+
+# -----------------------------------------------------------------------------
+# go — Error / Boundary cases
+# -----------------------------------------------------------------------------
+
+class TestGoErrors(TmuxUtilHarness):
+
+ def test_go_with_no_name_exits_nonzero(self):
+ result = self.run_script("go")
+ self.assertNotEqual(result.returncode, 0)
+ self.assertIn("missing session name", result.stderr)
+
+ def test_go_with_empty_string_name_exits_nonzero(self):
+ result = self.run_script("go", "")
+ self.assertNotEqual(result.returncode, 0)
+ self.assertIn("missing session name", result.stderr)
+
+
if __name__ == "__main__":
unittest.main()