#!/usr/bin/env bash # Regression harness for lane-brief.sh PR->issue linkage classification. # # Covers the #546/#547 defect: lane-brief.sh inspected only the PR index/title/head # fields and never the PR BODY, so an open PR whose body says "Closes #546" did not # mark issue #546 as work-underway — #546 was listed as a DISPATCH CANDIDATE and was # re-dispatchable in-flight work. # # Asserts: # 1. an open issue closed-keyword-linked from a PR BODY ("Closes #546") is # classified WORK UNDERWAY, not a dispatch candidate. # 2. a BARE "#777" prose mention in a PR body does NOT classify #777 as # work-underway (only Gitea closing keywords are a real link) — #777 stays a # dispatch candidate. # 3. NON-VACUITY / RED-ON-REVERT: a copy of the script with the body-scan removed # misclassifies #546 as a dispatch candidate — proving the body-scan is exactly # what fixes the defect and that assertion 1 fails if the fix is reverted. set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" LANE_BRIEF="$SCRIPT_DIR/lane-brief.sh" WORK_DIR="${MOSAIC_TEST_WORK_DIR:-$PWD/.mosaic-test-work/lane-brief-pr-linkage}" BIN_DIR="$WORK_DIR/bin" rm -rf "$WORK_DIR" mkdir -p "$BIN_DIR" # --- fake `tea`: serves a fixed open-issue set and one open PR. ---------------- # PR #547 body uses a closing keyword for #546 ("Closes #546") and a BARE mention # of #777 ("the #777 line of work"). #777 must NOT be treated as linked. cat > "$BIN_DIR/tea" <<'SH' #!/usr/bin/env bash set -euo pipefail case "${1:-} ${2:-}" in "issues list") cat <<'JSON' [ {"index":"546","title":"lane-brief + ci-wait orchestration tooling","assignees":[],"milestone":null,"labels":""}, {"index":"777","title":"unrelated downstream item","assignees":[],"milestone":null,"labels":""}, {"index":"999","title":"item only named inside the word hotfix","assignees":[],"milestone":null,"labels":""} ] JSON ;; "pulls list") cat <<'JSON' [ {"index":"547","title":"feat(framework/tools): orchestration helpers","head":"feat/orchestration-tools-lane-brief-ci-wait","body":"Two additive orchestration tools.\n\nCloses #546.\n\nLogin resolution is relevant to the #777 line of work but does not touch it.\nThis shipped as a hotfix #999 earlier — that bare reference must not link it.\n\nFixes #546\n"} ] JSON ;; *) echo "fake-tea: unhandled: $*" >&2; exit 1 ;; esac SH chmod +x "$BIN_DIR/tea" run_brief() { # $1 = script path PATH="$BIN_DIR:$PATH" "$1" -r mosaic/stack -L test-login 2>/dev/null } # Extract the issue numbers under a named section header until the next blank line. section_nums() { # $1 = output $2 = header-prefix printf '%s\n' "$1" | awk -v h="$2" ' index($0,h)==1 {grab=1; next} grab && /^[[:space:]]*$/ {grab=0} grab && match($0, /#[0-9]+/) { print substr($0, RSTART+1, RLENGTH-1) } ' } fail() { echo "FAIL: $1" >&2; exit 1; } contains() { printf '%s\n' "$1" | grep -qx "$2"; } # --------------------------------------------------------------------------- # Fixed (current) script behavior # --------------------------------------------------------------------------- OUT="$(run_brief "$LANE_BRIEF")" CAND="$(section_nums "$OUT" 'DISPATCH CANDIDATES')" UNDER="$(section_nums "$OUT" 'WORK UNDERWAY')" echo "--- lane-brief output (fixed) ---"; printf '%s\n' "$OUT" echo "--- candidates: [$(printf '%s' "$CAND" | tr '\n' ' ')] underway: [$(printf '%s' "$UNDER" | tr '\n' ' ')] ---" contains "$UNDER" 546 || fail "#546 (PR body 'Closes #546') should be WORK UNDERWAY" contains "$CAND" 546 && fail "#546 must NOT be a dispatch candidate (it has an open PR)" contains "$CAND" 777 || fail "#777 (only a bare prose mention) should remain a dispatch candidate" contains "$UNDER" 777 && fail "#777 must NOT be work-underway — bare body mentions are not links" contains "$CAND" 999 || fail "#999 ('hotfix #999' — keyword is a substring) should remain a candidate" contains "$UNDER" 999 && fail "#999 must NOT be work-underway — word-boundary must reject 'hotfix'" echo "PASS: body closing-keyword link classifies #546 underway; bare #777 / substring #999 stay candidates" # --------------------------------------------------------------------------- # NON-VACUITY: revert the body-scan and prove #546 regresses to a candidate. # --------------------------------------------------------------------------- REVERTED="$SCRIPT_DIR/.lane-brief.reverted.$$.sh" trap 'rm -f "$REVERTED"' EXIT # Drop the PR_BODY_REFS contribution from the union (simulates the pre-fix script # that only looked at index/title/head). Sibling `source detect-platform.sh` still # resolves because the copy lives in the same dir. # shellcheck disable=SC2016 # single-quoted on purpose: sed needs the literal $PR_BODY_REFS sed 's/"\$PR_BODY_REFS"/""/' "$LANE_BRIEF" > "$REVERTED" chmod +x "$REVERTED" grep -q 'PR_BODY_REFS' "$REVERTED" || fail "revert sed anchor not found — test is stale" ROUT="$(run_brief "$REVERTED")" RCAND="$(section_nums "$ROUT" 'DISPATCH CANDIDATES')" RUNDER="$(section_nums "$ROUT" 'WORK UNDERWAY')" echo "--- candidates(reverted): [$(printf '%s' "$RCAND" | tr '\n' ' ')] underway: [$(printf '%s' "$RUNDER" | tr '\n' ' ')] ---" contains "$RCAND" 546 || fail "non-vacuity broken: reverted script should misclassify #546 as a candidate" contains "$RUNDER" 546 && fail "non-vacuity broken: reverted script should NOT mark #546 underway" echo "PASS (RED-on-revert): without the body-scan, #546 regresses to a dispatch candidate" echo "ALL PASS: test-lane-brief-pr-linkage.sh"