forked from heavy-duty/ceremony
test(labels-reconcile): fixture roster replaces the live panel by slot
The state-machine fixtures bound BOT1/BOT2/BOT3 to REQUIRED_BOTS by index off the shipped .github/labels.conf, so three fixtures silently required a four-member panel=. Shrinking it to three left the third slot unbound and set -u aborted the file before assertion 1: 217 assertions became 0, on main and on every branch cut from it. The fixtures now write their own conf, in test/labels.test.sh's shape, at all three load sites (top of file, the #205 re-drafted-round block, and the mutant_blockers subshell). One live-file case survives as a property — the shipped conf parses and recuses each member from its own panel — with no index and no expected size, and a copy whose panel= names nobody proves it still has teeth. Refs #304
This commit is contained in:
parent
97cc00e268
commit
66a0eb5d65
2 changed files with 77 additions and 10 deletions
11
changelog.d/304.md
Normal file
11
changelog.d/304.md
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- A roster edit no longer reds the whole suite: the labels-reconcile
|
||||||
|
state-machine fixtures name their own panel instead of binding
|
||||||
|
`.github/labels.conf` by slot (#304).
|
||||||
|
- Shrinking `panel=` to three had left that binding's third slot unbound, and
|
||||||
|
`set -u` aborted the file before its first assertion — 217 assertions
|
||||||
|
became 0, on `main` and on every branch cut from it (#304).
|
||||||
|
- The one case still reading the shipped roster asserts a property, not a
|
||||||
|
size: it parses, and each member is recused from its own panel. Any
|
||||||
|
`panel=` of one or more members leaves `test/run.sh` green (#304).
|
||||||
|
|
@ -13,13 +13,29 @@ export LC_ALL=C
|
||||||
cd "$(dirname "$0")/.."
|
cd "$(dirname "$0")/.."
|
||||||
# shellcheck source=actions/labels-reconcile/labels-reconcile.sh
|
# shellcheck source=actions/labels-reconcile/labels-reconcile.sh
|
||||||
. actions/labels-reconcile/labels-reconcile.sh
|
. actions/labels-reconcile/labels-reconcile.sh
|
||||||
load_config .github/labels.conf
|
|
||||||
set_required_bots codex-bot-andresmgsl
|
|
||||||
|
|
||||||
|
RTMP="$(mktemp -d)"
|
||||||
|
trap 'rm -rf "$RTMP"' EXIT
|
||||||
|
|
||||||
|
# The fixture roster is the test's own, and deliberately not the shipped one
|
||||||
|
# (#304). The state machine is roster-agnostic — it needs three distinct
|
||||||
|
# required logins, not THESE three — so binding the fixtures to
|
||||||
|
# .github/labels.conf by slot bought nothing and cost the file: when the
|
||||||
|
# operator shrank panel= from four members to three, the recused author left
|
||||||
|
# two, the third slot came up unbound, and set -u aborted this file before
|
||||||
|
# its first assertion. 217 assertions became 0, on main and on every branch cut
|
||||||
|
# from it, and no fixture here was about the panel's size. The shape below is
|
||||||
|
# test/labels.test.sh's, which has always written its own conf.
|
||||||
|
FIXTURE_CONF="$RTMP/fixture-labels.conf"
|
||||||
|
FIXTURE_AUTHOR=fixture-builder
|
||||||
# The DRAFT/HEAD_SHA/REQUESTED/REVIEWS_JSON assignments below are the state
|
# The DRAFT/HEAD_SHA/REQUESTED/REVIEWS_JSON assignments below are the state
|
||||||
# machine's inputs, consumed inside the sourced decide_state — not unused.
|
# machine's inputs, consumed inside the sourced decide_state — not unused.
|
||||||
# shellcheck disable=SC2034
|
# shellcheck disable=SC2034
|
||||||
BOT1="${REQUIRED_BOTS[0]}" BOT2="${REQUIRED_BOTS[1]}" BOT3="${REQUIRED_BOTS[2]}"
|
BOT1=fixture-bot-one BOT2=fixture-bot-two BOT3=fixture-bot-three
|
||||||
|
printf 'panel=%s %s %s %s\n' "$BOT1" "$BOT2" "$BOT3" "$FIXTURE_AUTHOR" \
|
||||||
|
>"$FIXTURE_CONF"
|
||||||
|
load_config "$FIXTURE_CONF"
|
||||||
|
set_required_bots "$FIXTURE_AUTHOR"
|
||||||
pass=0 fail=0
|
pass=0 fail=0
|
||||||
|
|
||||||
expect() { # $1 = description, $2 = want, $3 = got
|
expect() { # $1 = description, $2 = want, $3 = got
|
||||||
|
|
@ -696,8 +712,6 @@ expect "...and an already-applied stale comes off" \
|
||||||
# posted comments appended back into the fixture so a second sweep sees the
|
# posted comments appended back into the fixture so a second sweep sees the
|
||||||
# first one's writes, and every label edit recorded.
|
# first one's writes, and every label edit recorded.
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
RTMP="$(mktemp -d)"
|
|
||||||
trap 'rm -rf "$RTMP"' EXIT
|
|
||||||
iso_at() { date -u -d "@$1" +%Y-%m-%dT%H:%M:%SZ; }
|
iso_at() { date -u -d "@$1" +%Y-%m-%dT%H:%M:%SZ; }
|
||||||
RNOW=2000000000
|
RNOW=2000000000
|
||||||
|
|
||||||
|
|
@ -1139,8 +1153,8 @@ done
|
||||||
# a PR carrying a standing CHANGES_REQUESTED that its builder converted back
|
# a PR carrying a standing CHANGES_REQUESTED that its builder converted back
|
||||||
# to draft read state:building — and the staleness sweep read a dropped fix
|
# to draft read state:building — and the staleness sweep read a dropped fix
|
||||||
# round as a build in progress.
|
# round as a build in progress.
|
||||||
load_config .github/labels.conf
|
load_config "$FIXTURE_CONF"
|
||||||
set_required_bots codex-bot-andresmgsl
|
set_required_bots "$FIXTURE_AUTHOR"
|
||||||
MERGEABLE=MERGEABLE CHECKS=SUCCESS LABELS="" HEAD_SHA=head1
|
MERGEABLE=MERGEABLE CHECKS=SUCCESS LABELS="" HEAD_SHA=head1
|
||||||
DRAFT=true REQUESTED="" REVIEWS_JSON="$(reviews \
|
DRAFT=true REQUESTED="" REVIEWS_JSON="$(reviews \
|
||||||
"$(rev "$BOT1" CHANGES_REQUESTED head1 no t1)" \
|
"$(rev "$BOT1" CHANGES_REQUESTED head1 no t1)" \
|
||||||
|
|
@ -1305,10 +1319,10 @@ mutant_blockers() { # $1 = sed program → blockers() from a copy of the script
|
||||||
RECONCILE_UNREQUESTED_GRACE="$RECONCILE_UNREQUESTED_GRACE" \
|
RECONCILE_UNREQUESTED_GRACE="$RECONCILE_UNREQUESTED_GRACE" \
|
||||||
bash -u -c '
|
bash -u -c '
|
||||||
. "$1"
|
. "$1"
|
||||||
load_config .github/labels.conf
|
load_config "$2"
|
||||||
set_required_bots codex-bot-andresmgsl
|
set_required_bots "$3"
|
||||||
blockers
|
blockers
|
||||||
' bash "$mutated"
|
' bash "$mutated" "$FIXTURE_CONF" "$FIXTURE_AUTHOR"
|
||||||
}
|
}
|
||||||
# the harness itself, unmutated: it must reproduce the verdict the sourced
|
# the harness itself, unmutated: it must reproduce the verdict the sourced
|
||||||
# functions give, or a "flip" below proves nothing about the guard
|
# functions give, or a "flip" below proves nothing about the guard
|
||||||
|
|
@ -1370,5 +1384,47 @@ expect "without the row the same approvals leave the round incomplete" \
|
||||||
expect "...and the owed, unasked verdict is named" \
|
expect "...and the owed, unasked verdict is named" \
|
||||||
blocker:unrequested "$(blockers)"
|
blocker:unrequested "$(blockers)"
|
||||||
|
|
||||||
|
# -- the shipped roster, as a property rather than a slot (#304 D2) ----------
|
||||||
|
# The one case that reads the real .github/labels.conf, and it asserts only
|
||||||
|
# what that file can honestly prove here: it parses, and recusal removes the
|
||||||
|
# author from whatever it names. No index, no expected size — the panel is the
|
||||||
|
# operator's to resize (D3), and the fixtures above no longer care. What this
|
||||||
|
# does catch is a shipped conf that stopped parsing, which must never be
|
||||||
|
# reported as a green suite.
|
||||||
|
#
|
||||||
|
# The probe runs in a subshell so a refusal cannot leave this file's globals
|
||||||
|
# half-loaded, and it quantifies over every member rather than sampling one:
|
||||||
|
# there is no member whose recusal is special. load_config's stderr is dropped
|
||||||
|
# because the exit status is the assertion; the broken-conf case below would
|
||||||
|
# otherwise print its (correct) complaint into a passing run.
|
||||||
|
live_panel_probe() { # $1 = conf → PARSE:<rc> [RECUSED:<yes|no> SHRANK:<yes|no>]
|
||||||
|
bash -u -c '
|
||||||
|
. actions/labels-reconcile/labels-reconcile.sh
|
||||||
|
rc=0
|
||||||
|
load_config "$1" 2>/dev/null || rc=$?
|
||||||
|
printf "PARSE:%s" "$rc"
|
||||||
|
[ "$rc" -eq 0 ] || { printf "\n"; exit 0; }
|
||||||
|
recused=yes shrank=yes
|
||||||
|
for author in "${BOTS[@]}"; do
|
||||||
|
set_required_bots "$author"
|
||||||
|
for bot in ${REQUIRED_BOTS[@]+"${REQUIRED_BOTS[@]}"}; do
|
||||||
|
[ "$bot" != "$author" ] || recused=no
|
||||||
|
done
|
||||||
|
[ "${#REQUIRED_BOTS[@]}" -eq "$((${#BOTS[@]} - 1))" ] || shrank=no
|
||||||
|
done
|
||||||
|
printf " RECUSED:%s SHRANK:%s\n" "$recused" "$shrank"
|
||||||
|
' bash "$1"
|
||||||
|
}
|
||||||
|
expect "the shipped labels.conf parses, and recuses each member from its own panel" \
|
||||||
|
"PARSE:0 RECUSED:yes SHRANK:yes" "$(live_panel_probe .github/labels.conf)"
|
||||||
|
# ...and the teeth: the same probe on a copy whose panel= line names nobody.
|
||||||
|
# A roster edit that empties the line is the shape this catches — the file
|
||||||
|
# still looks like a conf, and every panel in the repo would resolve to
|
||||||
|
# nothing.
|
||||||
|
BROKEN_CONF="$RTMP/broken-labels.conf"
|
||||||
|
sed 's/^panel=.*/panel=/' .github/labels.conf >"$BROKEN_CONF"
|
||||||
|
expect "...and a malformed panel= line in that same file is refused, not passed" \
|
||||||
|
PARSE:1 "$(live_panel_probe "$BROKEN_CONF")"
|
||||||
|
|
||||||
printf 'labels-reconcile tests: %d passed, %d failed\n' "$pass" "$fail"
|
printf 'labels-reconcile tests: %d passed, %d failed\n' "$pass" "$fail"
|
||||||
[ "$fail" -eq 0 ]
|
[ "$fail" -eq 0 ]
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue