a73x

01255d37

test: the e2e suite pins the refusal's words for run, status and send

a73x   2026-08-26 06:24

Commit message
test: the e2e suite pins the refusal's words for run, status and send

Each verb twice over: what it must say and what it must not. A reply
that added "attach refused" while still claiming a session ended would
pass a one-sided grep, and that reply is the whole regression. The leg
runs on its own daemon so "no such session" is a property of the daemon
rather than of the run order, and ends by asking the live session for
status: refusing is an answer, not a fault.

test/e2e.sh
Old New
@@ -396,6 +396,13 @@ HYKEY="${TMPDIR:-/tmp}/mux-e2e-hydkey-$$"
396 HYSTATE="${TMPDIR:-/tmp}/mux-e2e-hyd-state-$$" 396 HYSTATE="${TMPDIR:-/tmp}/mux-e2e-hyd-state-$$"
397 HYWALL="$HYSTATE/mux/wall" 397 HYWALL="$HYSTATE/mux/wall"
398 D65PID="" 398 D65PID=""
399 # The refused-attach leg: its own daemon, because its assertions are about
400 # what a daemon says to a name it does NOT have, and a socket shared with
401 # another leg would let that leg's sessions decide what "does not have"
402 # means.
403 SOCK66="${TMPDIR:-/tmp}/muxd-e2e-refuse-$$.sock"
404 REFSTATE="${TMPDIR:-/tmp}/mux-e2e-refuse-state-$$"
405 D66PID=""
399 D54PID="" 406 D54PID=""
400 D55PID="" 407 D55PID=""
401 D56PID="" 408 D56PID=""
@@ -1352,6 +1359,7 @@ cleanup() {
1352 [ -n "${D63PID:-}" ] && kill "$D63PID" 2>/dev/null || true 1359 [ -n "${D63PID:-}" ] && kill "$D63PID" 2>/dev/null || true
1353 [ -n "${D64PID:-}" ] && kill "$D64PID" 2>/dev/null || true 1360 [ -n "${D64PID:-}" ] && kill "$D64PID" 2>/dev/null || true
1354 [ -n "${D65PID:-}" ] && kill "$D65PID" 2>/dev/null || true 1361 [ -n "${D65PID:-}" ] && kill "$D65PID" 2>/dev/null || true
1362 [ -n "${D66PID:-}" ] && kill "$D66PID" 2>/dev/null || true
1355 # The ssh-agents the forwarding legs start. Not mux processes and so not 1363 # The ssh-agents the forwarding legs start. Not mux processes and so not
1356 # the leak sweep's business, but they are daemons this file forked: left 1364 # the leak sweep's business, but they are daemons this file forked: left
1357 # alive they outlive the suite holding a private key, which is the one 1365 # alive they outlive the suite holding a private key, which is the one
@@ -1405,6 +1413,7 @@ cleanup() {
1405 [ -S "$SOCK60" ] && "$MUXD" stop --sock "$SOCK60" 2>/dev/null || true 1413 [ -S "$SOCK60" ] && "$MUXD" stop --sock "$SOCK60" 2>/dev/null || true
1406 [ -S "$SOCK61" ] && "$MUXD" stop --sock "$SOCK61" 2>/dev/null || true 1414 [ -S "$SOCK61" ] && "$MUXD" stop --sock "$SOCK61" 2>/dev/null || true
1407 [ -S "$SOCK64" ] && "$MUXD" stop --sock "$SOCK64" 2>/dev/null || true 1415 [ -S "$SOCK64" ] && "$MUXD" stop --sock "$SOCK64" 2>/dev/null || true
1416 [ -S "$SOCK66" ] && "$MUXD" stop --sock "$SOCK66" 2>/dev/null || true
1408 1417
1409 # ---- the leak sweep (hygiene kit, 6a) ---- 1418 # ---- the leak sweep (hygiene kit, 6a) ----
1410 # Here rather than at the bottom of the file, which `set -e` reaches only 1419 # Here rather than at the bottom of the file, which `set -e` reaches only
@@ -1421,7 +1430,8 @@ cleanup() {
1421 "$D32PID" "$D33PID" "$D34PID" "$D35PID" "$D36PID" "$D37PID" \ 1430 "$D32PID" "$D33PID" "$D34PID" "$D35PID" "$D36PID" "$D37PID" \
1422 "$D38PID" "$D39PID" "$D40PID" "$D41PID" "$D42PID" "$D43PID" "$D54PID" \ 1431 "$D38PID" "$D39PID" "$D40PID" "$D41PID" "$D42PID" "$D43PID" "$D54PID" \
1423 "$D55PID" "$D56PID" "$D57PID" "$D58PID" "$D59PID" \ 1432 "$D55PID" "$D56PID" "$D57PID" "$D58PID" "$D59PID" \
1424 "$D60PID" "$D61PID" "$D62PID" "$D63PID" "$D64PID" "$D65PID" 1433 "$D60PID" "$D61PID" "$D62PID" "$D63PID" "$D64PID" "$D65PID" \
1434 "$D66PID"
1425 _leak=0 1435 _leak=0
1426 leak_sweep "$_rc" || _leak=1 1436 leak_sweep "$_rc" || _leak=1
1427 1437
@@ -1521,7 +1531,9 @@ cleanup() {
1521 "$OUT.hyd.d" "$OUT.hydcap" "$OUT.hydcap.err" "$OUT.hydpc" "$OUT.hydgrid" \ 1531 "$OUT.hyd.d" "$OUT.hydcap" "$OUT.hydcap.err" "$OUT.hydpc" "$OUT.hydgrid" \
1522 "$OUT.hydsta" "$OUT.hydstb" "$OUT.hydghost" "$OUT.hydpre" "$OUT.hydfa" \ 1532 "$OUT.hydsta" "$OUT.hydstb" "$OUT.hydghost" "$OUT.hydpre" "$OUT.hydfa" \
1523 "$OUT.hydstop" "$OUT.hydfcap" "$OUT.hydfcap.err" "$OUT.hydfpc" \ 1533 "$OUT.hydstop" "$OUT.hydfcap" "$OUT.hydfcap.err" "$OUT.hydfpc" \
1524 "$OUT.hydfold" "$OUT.hydfgrid" "$SOCK64" "$HYKEY" 1534 "$OUT.hydfold" "$OUT.hydfgrid" "$SOCK64" "$HYKEY" \
1535 "$OUT.ref.d" "$OUT.refrun" "$OUT.refstatus" "$OUT.refsend" \
1536 "$OUT.refalive" "$OUT.refstop" "$SOCK66"
1525 # ...and the non-tty capture that leg's session feeds. 1537 # ...and the non-tty capture that leg's session feeds.
1526 rm -f "$OUT.nogate" 1538 rm -f "$OUT.nogate"
1527 # ...and its other half: the paste capture and the file nvim wrote, which 1539 # ...and its other half: the paste capture and the file nvim wrote, which
@@ -8719,8 +8731,78 @@ D65PID=""
8719 rm -rf "$HYSTATE" 8731 rm -rf "$HYSTATE"
8720 ok "a saved local line comes back as a fresh session, by wall and by fold; a remote one joins only" 8732 ok "a saved local line comes back as a fresh session, by wall and by fold; a remote one joins only"
8721 8733
8722 [ "$OK_COUNT" = "74" ] || { 8734 # ---------------------------------------------------------------------------
8723 echo "e2e FAIL: $OK_COUNT scenario checkpoints ran, the pin says 74 —" 8735 # A refused attach is not a shell that exited.
8736 #
8737 # muxa attaches at 0x0, and a 0x0 attach is join-only: the daemon refuses a
8738 # name it does not hold by sending exit_status 1 and closing — the same frame
8739 # a real shell's exit sends. muxa used to report that as
8740 # `{"reason":"session_ended","exit_code":1}` and `{"error":"session ended"}`,
8741 # telling an agent a command had run and failed when no shell ever existed,
8742 # while `status` got no answer at all and blamed the daemon (`DaemonGone`).
8743 # The snapshot tells the two apart — a served attach always sends one first
8744 # — and what this leg pins is the words an agent actually parses.
8745 mkdir -p "$REFSTATE"
8746 XDG_STATE_HOME="$REFSTATE" "$MUXD" run --sock "$SOCK66" --shell /bin/sh > "$OUT.ref.d" 2>&1 &
8747 D66PID=$!
8748 wait_sock "$SOCK66" "$OUT.ref.d" "refused-attach daemon never bound"
8749
8750 # Each verb twice over: the words it must say, and the words it must not.
8751 # The negative half is the whole regression — a reply that added "attach
8752 # refused" while still claiming a session ended would pass a one-sided grep.
8753 refuse_leg() {
8754 _out="$OUT.ref$1"
8755 shift
8756 set +e
8757 XDG_STATE_HOME="$REFSTATE" timeout 20 "$MUXA" "$@" --sock "$SOCK66" \
8758 --session nosuch --timeout 5000 > "$_out" 2>&1
8759 _rc=$?
8760 set -e
8761 [ "$_rc" -eq 1 ] || {
8762 echo "e2e FAIL: refused-attach: muxa $1 on a missing session exited $_rc, want 1:"
8763 cat "$_out"; exit 1; }
8764 grep -qF '"error":"attach refused"' "$_out" || {
8765 echo "e2e FAIL: refused-attach: muxa $1 did not name the refusal:"
8766 cat "$_out"; exit 1; }
8767 grep -qF 'so nosuch must already exist' "$_out" || {
8768 echo "e2e FAIL: refused-attach: muxa $1 did not name the session asked for:"
8769 cat "$_out"; exit 1; }
8770 grep -qF 'room for one more client' "$_out" || {
8771 echo "e2e FAIL: refused-attach: muxa $1 blamed absence for a refusal that has two causes:"
8772 cat "$_out"; exit 1; }
8773 grep -qF 'session_ended' "$_out" && {
8774 echo "e2e FAIL: refused-attach: muxa $1 still reports a session that ended:"
8775 cat "$_out"; exit 1; }
8776 grep -qF 'session ended' "$_out" && {
8777 echo "e2e FAIL: refused-attach: muxa $1 still reports a session that ended:"
8778 cat "$_out"; exit 1; }
8779 grep -qF 'DaemonGone' "$_out" && {
8780 echo "e2e FAIL: refused-attach: muxa $1 blamed the daemon for a missing name:"
8781 cat "$_out"; exit 1; }
8782 return 0
8783 }
8784 refuse_leg run run true
8785 refuse_leg status status
8786 refuse_leg send send x
8787
8788 # ...and the daemon that said no three times is still serving. Refusing is
8789 # an answer, not a fault: a daemon that died on a typed `--session` would
8790 # turn one bad name into everyone else's outage.
8791 XDG_STATE_HOME="$REFSTATE" timeout 20 "$MUXA" status --sock "$SOCK66" \
8792 --session 0 > "$OUT.refalive" 2>&1 || {
8793 echo "e2e FAIL: refused-attach: the daemon stopped answering after refusing:"
8794 cat "$OUT.refalive"; exit 1; }
8795 grep -qF '"cols":' "$OUT.refalive" || {
8796 echo "e2e FAIL: refused-attach: status on the live session is not a status reply:"
8797 cat "$OUT.refalive"; exit 1; }
8798
8799 assert_stopped "$SOCK66" "$D66PID" "refused-attach" "$OUT.refstop"
8800 D66PID=""
8801 rm -rf "$REFSTATE"
8802 ok "muxa names a refused attach instead of a shell that never ran"
8803
8804 [ "$OK_COUNT" = "75" ] || {
8805 echo "e2e FAIL: $OK_COUNT scenario checkpoints ran, the pin says 75 —"
8724 echo " a scenario was added (update the pin) or silently lost" 8806 echo " a scenario was added (update the pin) or silently lost"
8725 exit 1 8807 exit 1
8726 } 8808 }