#!/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. 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) trap 'rm -f "$STUB"' EXIT cat >"$STUB" <<'STUB_EOF' #!/usr/bin/env bash set -uo pipefail b64="" while getopts "t:b:r:v" o; do case "$o" in b) b64=$OPTARG ;; *) : ;; esac; done printf '%s' "$b64" | base64 -d STUB_EOF chmod +x "$STUB" 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 "$@"; } # 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]" echo "---" echo "PASS=$PASS FAIL=$FAIL" [ "$FAIL" -eq 0 ]