#!/usr/bin/env bash # agent-send.test.sh — regression + grammar lock for agent-send.sh --class. # # Strategy: inject a capture stub via AGENT_SEND_SENDER that decodes the -b # base64 payload and prints the FULL message (preamble + body) so we can assert # the exact bytes on the wire. Local path only (no ssh), -n pins the dst host so # the preamble is deterministic across machines. # # Guarantees locked here: # 1. REGRESSION BAR — no --class => preamble byte-for-byte identical to classic. # 2. --class => ` class=` token emitted inside the bracket. # 3. --class= (equals form) parses identically to the space form. # 4. -C short form parses identically. # 5. invalid class => exit 3, nothing sent. # 6. --class with no value => exit 3. # 7. the documented consumer regex parses producer output for every class. # 8. MOSAIC_AGENT_NAME is authoritative for sender identity. # 9. sender fallback queries local tmux, never the destination -L socket. # 10. an undeterminable sender is stamped as "?". set -uo pipefail HERE=$(cd -- "$(dirname -- "$0")" && pwd) TOOL="$HERE/agent-send.sh" # Capture stub: stands in for send-message.sh. Decodes -b and prints the payload. STUB=$(mktemp) FAKE_BIN=$(mktemp -d) trap 'rm -f "$STUB"; rm -rf "$FAKE_BIN"' EXIT cat >"$STUB" <<'STUB_EOF' #!/usr/bin/env bash set -uo pipefail b64="" while getopts "L:t:b:r:v" o; do case "$o" in b) b64=$OPTARG ;; *) : ;; esac; done printf '%s' "$b64" | base64 -d STUB_EOF chmod +x "$STUB" # Fake tmux distinguishes the sender's default socket from a destination socket. cat >"$FAKE_BIN/tmux" <<'TMUX_EOF' #!/usr/bin/env bash set -uo pipefail case "${FAKE_TMUX_MODE:-sessions}" in unavailable) exit 1 ;; sessions) if [ "${1:-}" = "-L" ]; then printf '%s\n' 'destination-holder' else printf '%s\n' 'local-agent' fi ;; esac TMUX_EOF chmod +x "$FAKE_BIN/tmux" PASS=0; FAIL=0 ok() { PASS=$((PASS+1)); printf 'ok %s\n' "$1"; } no() { FAIL=$((FAIL+1)); printf 'FAIL %s\n %s\n' "$1" "$2"; } # Run the tool with the stub injected; echoes captured payload on stdout. run() { AGENT_SEND_SENDER="$STUB" bash "$TOOL" -S a:src -n dsthost "$@"; } run_auto() { env -u MOSAIC_AGENT_NAME \ AGENT_SEND_SENDER="$STUB" PATH="$FAKE_BIN:$PATH" \ bash "$TOOL" -n dsthost "$@" } # Documented consumer grammar — the daemon will mirror exactly this. GRAMMAR='^\[(\S+) -> (\S+) class=(terminal-log|actionable|human|reaction)\] (.*)$' GRAMMAR_NOCLASS='^\[(\S+) -> (\S+)\] (.*)$' # 1. REGRESSION BAR: classic preamble, byte-for-byte. got=$(run -s mos -m "hello world") want='[a:src -> dsthost:mos] hello world' [ "$got" = "$want" ] && ok "regression: no --class is byte-identical" \ || no "regression: no --class is byte-identical" "got=[$got] want=[$want]" # 2. --class space form emits the token. got=$(run -s mos --class terminal-log -m "ACK") want='[a:src -> dsthost:mos class=terminal-log] ACK' [ "$got" = "$want" ] && ok "--class terminal-log emits token" \ || no "--class terminal-log emits token" "got=[$got] want=[$want]" # 3. --class=value equals form. got=$(run -s mos --class=actionable -m "decide X") want='[a:src -> dsthost:mos class=actionable] decide X' [ "$got" = "$want" ] && ok "--class=actionable (equals form)" \ || no "--class=actionable (equals form)" "got=[$got] want=[$want]" # 4. -C short form. got=$(run -s mos -C human -m "from a person") want='[a:src -> dsthost:mos class=human] from a person' [ "$got" = "$want" ] && ok "-C human (short form)" \ || no "-C human (short form)" "got=[$got] want=[$want]" # 5. invalid class => exit 3, no send. if out=$(run -s mos --class bogus -m "x" 2>/dev/null); then no "invalid class rejected" "expected non-zero exit, got 0 (out=[$out])" else rc=$? [ "$rc" = 3 ] && [ -z "$out" ] && ok "invalid class => exit 3, nothing sent" \ || no "invalid class => exit 3, nothing sent" "rc=$rc out=[$out]" fi # 6. --class with no value => exit 3. if run -s mos -m "x" --class 2>/dev/null; then no "--class with no value rejected" "expected non-zero exit, got 0" else [ "$?" = 3 ] && ok "--class with no value => exit 3" || no "--class with no value => exit 3" "wrong rc" fi # 7. consumer grammar parses every class + classic line. for c in terminal-log actionable human reaction; do line=$(run -s mos --class "$c" -m "body $c") [[ "$line" =~ $GRAMMAR ]] && [ "${BASH_REMATCH[3]}" = "$c" ] && [ "${BASH_REMATCH[4]}" = "body $c" ] \ && ok "grammar parses class=$c" || no "grammar parses class=$c" "line=[$line]" done classic=$(run -s mos -m "plain body") [[ "$classic" =~ $GRAMMAR_NOCLASS ]] && [ "${BASH_REMATCH[3]}" = "plain body" ] \ && ok "grammar (no-class) parses classic line" || no "grammar (no-class) parses classic line" "line=[$classic]" # 8. Exported pane identity wins even when dispatch targets another tmux socket. src_host=$(hostname -s) got=$(MOSAIC_AGENT_NAME=authoritative-agent FAKE_TMUX_MODE=sessions \ AGENT_SEND_SENDER="$STUB" PATH="$FAKE_BIN:$PATH" \ bash "$TOOL" -L destination-socket -n dsthost -s mos -m "env identity") want="[$src_host:authoritative-agent -> dsthost:mos] env identity" [ "$got" = "$want" ] && ok "MOSAIC_AGENT_NAME is authoritative across sockets" \ || no "MOSAIC_AGENT_NAME is authoritative across sockets" "got=[$got] want=[$want]" # 9. Without the env identity, self-lookup uses local tmux, not destination -L. got=$(FAKE_TMUX_MODE=sessions run_auto -L destination-socket -s mos -m "local fallback") want="[$src_host:local-agent -> dsthost:mos] local fallback" [ "$got" = "$want" ] && ok "cross-socket fallback uses local sender session" \ || no "cross-socket fallback uses local sender session" "got=[$got] want=[$want]" [[ "$got" != *":destination-holder ->"* ]] \ && ok "cross-socket fallback rejects destination holder identity" \ || no "cross-socket fallback rejects destination holder identity" "got=[$got]" # 10. If neither env nor local tmux identifies the sender, preserve '?'. got=$(FAKE_TMUX_MODE=unavailable run_auto -L destination-socket -s mos -m "unknown fallback") want="[$src_host:? -> dsthost:mos] unknown fallback" [ "$got" = "$want" ] && ok "unknown sender falls back to ?" \ || no "unknown sender falls back to ?" "got=[$got] want=[$want]" echo "---" echo "PASS=$PASS FAIL=$FAIL" [ "$FAIL" -eq 0 ]