#!/usr/bin/env bats load 'helpers' setup() { setup_party_sandbox load_party_lib } # party name validation # ===================== @test "validate_party_name accepts plain names" { validate_party_name "debug-the-deploy" validate_party_name "mob_coding" validate_party_name "p" validate_party_name "Party-2026" } @test "validate_party_name rejects empty" { run validate_party_name "" [ "$status" -ne 0 ] } @test "validate_party_name rejects path separators" { run validate_party_name "../etc" [ "$status" -ne 0 ] run validate_party_name "a/b" [ "$status" -ne 0 ] } @test "validate_party_name rejects names over 63 chars" { long=$(printf 'a%.0s' $(seq 1 64)) run validate_party_name "$long" [ "$status" -ne 0 ] } @test "validate_party_name rejects whitespace and shell metas" { for bad in "a b" "a;b" 'a$b' 'a`b' "a'b" 'a"b'; do run validate_party_name "$bad" [ "$status" -ne 0 ] || { echo "should have rejected: $bad"; false; } done } @test "validate_party_name rejects dots (tmux rewrites '.' in session names)" { # tmux session_check_name() silently maps '.' and ':' to '_', so a # dotted party name desyncs from its tmux session and every later # `-t ` target fails. Verified on tmux 3.3a through 3.6b. for bad in "my.party" "." ".." "a.d"; do run validate_party_name "$bad" [ "$status" -ne 0 ] || { echo "should have rejected: $bad"; false; } done } @test "validate_party_name rejects leading dash (unusable through the CLI)" { run validate_party_name "-flag" [ "$status" -ne 0 ] } # time # ==== @test "iso_now produces ISO 8601 UTC with Z suffix" { out=$(iso_now) case "$out" in [0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]T[0-9][0-9]:[0-9][0-9]:[0-9][0-9]Z) ;; *) echo "bad timestamp: $out"; false ;; esac } # user existence # ============== @test "user_exists returns 0 for the running user" { user_exists "$USER" } @test "user_exists returns nonzero for a fictional user" { ! user_exists "nonexistent_$(date +%s)_user" } # liveness # ======== # # party_conn_state must not probe PIDs with kill -0. POSIX kill(2) is # allowed to return EPERM when the caller can't signal the target, and # illumos/BSD/macOS honor that, so kill -0 against another user's PID # returns nonzero even when the process exists. Linux and Linux-ABI # zones (LX-branded) are the outliers that return 0; that's why the # bug hid in the test matrix until a native illumos run surfaced it. # Liveness rides on `tmux -S list-clients` alone, which is strictly # stronger (filters PID-reuse, dead servers, AF_UNIX impostors). @test "party_conn_state does not reach for kill -0 (cross-user discovery)" { body=$(declare -f party_conn_state) [[ "$body" != *"kill "* ]] || { echo "$body"; false; } } @test "party_conn_state answers dead when no tmux server is at the socket" { [ "$(party_conn_state "$PARTY_TMP/no-such-sock")" = dead ] } @test "validate_socket_dir_parent refuses whitespace in PARTY_SOCKET_DIR" { spaced="$PARTY_TMP/with space" mkdir -p "$spaced" chmod 0700 "$spaced" PARTY_SOCKET_DIR="$spaced" run validate_socket_dir_parent [ "$status" -ne 0 ] [[ "$output" == *"whitespace"* ]] } @test "pick_live_party without a tty fails with guidance, not a silent death" { # Two live-looking parties force the numbered picker; stdin is not a # tty under bats, so the guard must fire before read. stub="$PARTY_TMP/tmux-ok" printf '#!/bin/sh\nexit 0\n' > "$stub" chmod +x "$stub" export PARTY_TMUX="$stub" ensure_party_dir "$USER" one ensure_party_dir "$USER" two for n in one two; do d="$PARTY_SOCKET_DIR/party-$USER:$n.d" printf 'SERVER_PID=1\nGROUP=%s\n' "$(id -gn)" > "$d/roster" done run pick_live_party [ "$status" -ne 0 ] [[ "$output" == *"pass a name"* ]] } @test "party_conn_state classifies ok / unauthorized / dead" { stub="$PARTY_TMP/conn-stub" cat > "$stub" <<'S' #!/bin/sh case "${MODE:-}" in ok) exit 0 ;; deny) echo "access not allowed" >&2; exit 0 ;; deny2) echo "connection declined by policy (hypothetical future wording)" >&2; exit 0 ;; *) echo "error connecting to /x (No such file or directory)" >&2; exit 1 ;; esac S chmod +x "$stub" export PARTY_TMUX="$stub" export MODE=ok; [ "$(party_conn_state /x)" = "ok" ] # The trap this function exists for: tmux answers unauthorized users # with EXIT 0 + a stderr message, so rc alone says "authorized". export MODE=deny; [ "$(party_conn_state /x)" = "unauthorized" ] # Classification must not depend on tmux's denial wording (adversarial # review finding): rc 0 with ANY stderr output is a refusal. export MODE=deny2; [ "$(party_conn_state /x)" = "unauthorized" ] export MODE=dead; [ "$(party_conn_state /x)" = "dead" ] }