133 lines
4.8 KiB
Bash
Executable File
133 lines
4.8 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Usage-error contract for milestone-close.sh (R4, 2026-08-28).
|
|
#
|
|
# issue-edit already uses long-flag-first parsing (-i/--issue, -t/--title,
|
|
# -b/--body, -l/--labels, -m/--milestone); this adds the rc=2 usage-error
|
|
# contract, value checks, and the no-provider-contact proof. Required: -i.
|
|
#
|
|
# Arms:
|
|
# 1. --help and -h exit 0 and print usage.
|
|
# 2. Unknown option exits 2 with the message on stderr.
|
|
# 3. Missing required -i exits 2 (stderr).
|
|
# 4. A value-less flag (-i -b -c and long forms) exits 2 (stderr).
|
|
# 5. -b and -c both pass parsing (sandboxed runner: the run then fails
|
|
# at credential resolution, nonzero and NOT 2) — no real token is
|
|
# ever read and no provider is contacted.
|
|
# 6. No arm performs any provider request (PATH shims record every
|
|
# invocation; the probe log must stay empty).
|
|
set -euo pipefail
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
WORK_DIR="${MOSAIC_TEST_WORK_DIR:-$PWD/.mosaic-test-work/milestone-close-usage}"
|
|
BIN_DIR="$WORK_DIR/bin"
|
|
PROBE_LOG="$WORK_DIR/provider-probes.log"
|
|
OUT_FILE="$WORK_DIR/out.log"
|
|
ERR_FILE="$WORK_DIR/err.log"
|
|
|
|
cleanup() {
|
|
rm -rf "$WORK_DIR"
|
|
}
|
|
trap cleanup EXIT
|
|
|
|
mkdir -p "$BIN_DIR"
|
|
: > "$PROBE_LOG"
|
|
|
|
# Unlike the issue suites, these stubs FAIL (exit 99): pr-close has an
|
|
# API fallback that treats a successful curl as a closed PR, so exit-0
|
|
# stubs would let the sandbox arms "succeed" (measured 2026-08-28).
|
|
for tool in gh tea curl; do
|
|
cat > "$BIN_DIR/$tool" <<STUB
|
|
#!/usr/bin/env bash
|
|
echo "$tool \$*" >> "$PROBE_LOG"
|
|
exit 99
|
|
STUB
|
|
chmod +x "$BIN_DIR/$tool"
|
|
done
|
|
|
|
run_wrapper() {
|
|
( cd "$WORK_DIR" && PATH="$BIN_DIR:$PATH" "$SCRIPT_DIR/milestone-close.sh" "$@" )
|
|
}
|
|
|
|
# Hermetic variant: neutralizes every identity/credential source the wrapper
|
|
# consults so parse-acceptance arms fail at credential resolution in ANY cwd
|
|
# repo (see test-issue-comment-usage-contract.sh for the measured incident).
|
|
run_wrapper_sandboxed() {
|
|
mkdir -p "$WORK_DIR/home" "$WORK_DIR/xdg"
|
|
(
|
|
cd "$WORK_DIR"
|
|
PATH="$BIN_DIR:$PATH" HOME="$WORK_DIR/home" XDG_CONFIG_HOME="$WORK_DIR/xdg" \
|
|
MOSAIC_GIT_IDENTITY="" MOSAIC_BRAIN_HOME="" \
|
|
"$SCRIPT_DIR/milestone-close.sh" "$@"
|
|
)
|
|
}
|
|
|
|
fail() {
|
|
echo "FAIL: $*" >&2
|
|
echo "--- stderr ---" >&2
|
|
cat "$ERR_FILE" >&2
|
|
exit 1
|
|
}
|
|
|
|
expect_rc() { # expect_rc <want> <desc> <args...>
|
|
local want="$1" desc="$2" rc=0
|
|
shift 2
|
|
run_wrapper "$@" >"$OUT_FILE" 2>"$ERR_FILE" || rc=$?
|
|
[[ "$rc" -eq "$want" ]] || fail "$desc: rc=$rc, want $want"
|
|
}
|
|
|
|
expect_stderr() { # expect_stderr <pattern> <desc>
|
|
grep -q "$1" "$ERR_FILE" || fail "$2: stderr missing '$1'"
|
|
}
|
|
|
|
# 1. Help exits 0 and prints usage.
|
|
expect_rc 0 "--help exits 0" --help
|
|
grep -q "Usage: milestone-close.sh" "$OUT_FILE" || fail "--help did not print usage"
|
|
expect_rc 0 "-h exits 0" -h
|
|
|
|
# 2. Unknown option: rc 2, stderr.
|
|
expect_rc 2 "unknown option exits 2" --bogus
|
|
expect_stderr "[Uu]nknown option" "unknown option names itself on stderr"
|
|
|
|
# 3. Missing required PR number: rc 2, stderr.
|
|
expect_rc 2 "missing -t exits 2"
|
|
expect_stderr "Milestone title is required" "missing -t message on stderr"
|
|
|
|
|
|
# 4. Value-less flags: rc 2 with "requires a value" on stderr.
|
|
for flag in -t --title; do
|
|
expect_rc 2 "value-less $flag exits 2" "$flag"
|
|
expect_stderr "requires a value" "value-less $flag message on stderr"
|
|
done
|
|
|
|
# 4a. An option-like value is a MISSING value, not a value (codex PR #1464:
|
|
# -b --help previously consumed --help as the body and performed the write).
|
|
expect_rc 2 "option-like value rejected" -t --help --help
|
|
expect_rc 2 "short flag value rejected" -t --help -h
|
|
expect_stderr "requires a value" "short flag value message on stderr"
|
|
expect_stderr "requires a value" "option-like value message on stderr"
|
|
|
|
# 4b. Parser-failure arms (1-4) must have performed ZERO provider contact.
|
|
if [[ -s "$PROBE_LOG" ]]; then
|
|
echo "FAIL: a parser-failure arm contacted a provider:" >&2
|
|
cat "$PROBE_LOG" >&2
|
|
exit 1
|
|
fi
|
|
|
|
# 5. Alias acceptance under the sandbox: both -b and -c carry a value past
|
|
# parsing; the run fails at credential resolution nonzero and NOT 2.
|
|
for flag in -t; do
|
|
rc=0
|
|
run_wrapper_sandboxed -t "smoke" >"$OUT_FILE" 2>"$ERR_FILE" || rc=$?
|
|
[[ "$rc" -ne 0 ]] || fail "$flag arm unexpectedly succeeded in the sandbox"
|
|
[[ "$rc" -ne 2 ]] || fail "$flag arm misclassified credential failure as a usage error"
|
|
done
|
|
|
|
# 6. Post-sandbox provider assertions are intentionally NOT applied here:
|
|
# pr-close's gitea path attempts a tea WRITE (tea pr comment) when a
|
|
# comment parses, then falls back to the API. Hermeticity for this
|
|
# wrapper comes from the FAILING stubs (exit 99), not from non-contact —
|
|
# the arm above proves only parse acceptance and non-usage classification.
|
|
# Parser-failure arms (1-4) remain zero-contact (asserted at 4b).
|
|
|
|
echo "milestone-close.sh usage-contract regression passed (R1/R4)"
|