tmux-party: share a tmux session with people on the same UNIX host. Single-file POSIX shell (party) with a filesystem + tmux server-access trust model. See README.md.
135 lines
4.3 KiB
Bash
135 lines
4.3 KiB
Bash
#!/usr/bin/env bats
|
|
|
|
load 'helpers'
|
|
|
|
setup() {
|
|
setup_party_sandbox
|
|
require_party_group
|
|
}
|
|
|
|
teardown() { teardown_party_sandbox; }
|
|
|
|
party_rec() { printf '%s/party-%s:%s.d/roster\n' "$PARTY_SOCKET_DIR" "$USER" "$1"; }
|
|
|
|
@test "party list shows live parties only" {
|
|
"$PARTY_BIN" host alpha
|
|
"$PARTY_BIN" host bravo
|
|
|
|
# Inject a fake stale record. The per-party dir must exist with the
|
|
# right group + mode so roster_list can find the record inside.
|
|
zdir="$PARTY_SOCKET_DIR/party-$USER:zombie.d"
|
|
mkdir -p "$zdir"
|
|
chgrp "$TMUX_PARTY_GROUP" "$zdir"
|
|
chmod 0750 "$zdir"
|
|
rec="$zdir/roster"
|
|
cat > "$rec" <<EOF
|
|
HOST_USER=$USER
|
|
PARTY_NAME=zombie
|
|
SOCKET=$zdir/sock
|
|
SERVER_PID=999999
|
|
CREATED=2026-04-26T18:42:00Z
|
|
EOF
|
|
chgrp "$TMUX_PARTY_GROUP" "$rec"
|
|
chmod 0640 "$rec"
|
|
|
|
run "$PARTY_BIN" list
|
|
[ "$status" -eq 0 ]
|
|
[[ "$output" == *"alpha"* ]]
|
|
[[ "$output" == *"bravo"* ]]
|
|
[[ "$output" != *"zombie"* ]]
|
|
}
|
|
|
|
@test "party list with no parties is friendly, not noisy" {
|
|
run "$PARTY_BIN" list
|
|
[ "$status" -eq 0 ]
|
|
[[ "$output" == *"no parties"* ]]
|
|
}
|
|
|
|
# Cross-user discovery regression. Earlier revisions of is_party_alive
|
|
# probed the PID with `kill -0`, which returns EPERM under POSIX semantics
|
|
# when the caller can't signal the target — illumos, all BSDs, and macOS
|
|
# honor that, so guests couldn't discover parties hosted by other users
|
|
# on the same box. To simulate the foreign-uid case without privilege,
|
|
# rewrite the roster's SERVER_PID to 1 (init/launchd, root-owned on every
|
|
# supported platform) and confirm `party list`/`join` still find it. With
|
|
# the bug present this test fails on every non-Linux target; on Linux and
|
|
# Linux-ABI zones it passes regardless because Linux's kill(2) returns 0
|
|
# for "exists, even if not signalable."
|
|
@test "party list discovers parties whose host PID we cannot signal" {
|
|
"$PARTY_BIN" host crossuser
|
|
|
|
rec=$(party_rec crossuser)
|
|
[ -f "$rec" ]
|
|
# Rewrite SERVER_PID=<our-pid> -> SERVER_PID=1 in place. Avoids `sed -i`
|
|
# portability snags between GNU and BSD sed.
|
|
awk -F= 'BEGIN{OFS="="} $1=="SERVER_PID"{$2="1"} {print}' "$rec" >"$rec.new"
|
|
cat "$rec.new" >"$rec"
|
|
rm -f "$rec.new"
|
|
|
|
run "$PARTY_BIN" list
|
|
[ "$status" -eq 0 ]
|
|
[[ "$output" == *"crossuser"* ]]
|
|
|
|
PARTY_DRY_RUN=1 run "$PARTY_BIN" join crossuser
|
|
[ "$status" -eq 0 ]
|
|
}
|
|
|
|
@test "party join <name> attaches via a guest session in group mode" {
|
|
"$PARTY_BIN" host joinable
|
|
|
|
rec=$(party_rec joinable)
|
|
sock=$(awk -F= '$1=="SOCKET"{print $2}' "$rec")
|
|
|
|
# Joining is interactive (it execs into tmux). We can't test the attach
|
|
# without a real terminal, so we exercise the underlying path: with
|
|
# PARTY_DRY_RUN=1 the join function builds the guest session and prints
|
|
# the command that WOULD be exec'd, then exits.
|
|
PARTY_DRY_RUN=1 "$PARTY_BIN" join joinable
|
|
|
|
tmux -S "$sock" has-session -t "__party_guest_$USER"
|
|
}
|
|
|
|
@test "party join with no name auto-attaches single live party" {
|
|
"$PARTY_BIN" host onlyone
|
|
PARTY_DRY_RUN=1 "$PARTY_BIN" join
|
|
rec=$(party_rec onlyone)
|
|
sock=$(awk -F= '$1=="SOCKET"{print $2}' "$rec")
|
|
tmux -S "$sock" has-session -t "__party_guest_$USER"
|
|
}
|
|
|
|
@test "party join with no name and no parties errors clearly" {
|
|
run "$PARTY_BIN" join
|
|
[ "$status" -ne 0 ]
|
|
[[ "$output" == *"no parties"* ]]
|
|
}
|
|
|
|
@test "party join --passive attaches read-only to host session, no guest session" {
|
|
"$PARTY_BIN" host mirror
|
|
rec=$(party_rec mirror)
|
|
sock=$(awk -F= '$1=="SOCKET"{print $2}' "$rec")
|
|
# --passive must use `attach -r` so a write-capable invitee who chooses
|
|
# passive can't type into the host's panes (the README's "watcher"
|
|
# boundary). PARTY_DRY_RUN=1 prints the attach command instead of execing.
|
|
run env PARTY_DRY_RUN=1 "$PARTY_BIN" join mirror --passive
|
|
[ "$status" -eq 0 ]
|
|
[[ "$output" == *"attach-session -r -t mirror"* ]]
|
|
! tmux -S "$sock" has-session -t "__party_guest_$USER" 2>/dev/null
|
|
}
|
|
|
|
@test "party leave kills the guest session and detaches the client" {
|
|
"$PARTY_BIN" host leaveme
|
|
rec=$(party_rec leaveme)
|
|
sock=$(awk -F= '$1=="SOCKET"{print $2}' "$rec")
|
|
|
|
PARTY_DRY_RUN=1 "$PARTY_BIN" join leaveme
|
|
tmux -S "$sock" has-session -t "__party_guest_$USER"
|
|
|
|
"$PARTY_BIN" leave
|
|
! tmux -S "$sock" has-session -t "__party_guest_$USER" 2>/dev/null
|
|
}
|
|
|
|
@test "party leave with no joined party errors helpfully" {
|
|
run "$PARTY_BIN" leave
|
|
[ "$status" -ne 0 ]
|
|
[[ "$output" == *"not joined"* ]]
|
|
}
|