test(git-tools): classify tool failure at every needle site (#1380-FF)
ci/woodpecker/pr/ci Pipeline was successful
ci/woodpecker/pr/ci Pipeline was successful
Pipeline 2637 red on a SECOND needle arm (home_case symlink message) proved the flake shape is not one site: every 'if ! ... | grep -Fq' assertion in the harness printed the identical FAIL line for a transient grep failure (rc>=2, fork/alloc under node pressure) and a real mismatch. The retry/classify helper is hoisted to one shared function and applied at all three needle sites (fixture loop, home_case, standalone case): retry tool errors 3x, rc=1 is the real mismatch, persistent tool failure reports TOOL-ERROR (fail stays 1). 292/292 x2 + busybox.
This commit is contained in:
@@ -515,6 +515,25 @@ FIXTURES="$TMP/fixtures.tsv"
|
||||
printf '0\t{"tool_input":{"command":"curl -s https://git.example.invalid/api/v1/repos/a/b/issues?q=a%%20b"}}\ta percent-escape in a READ is not this hook'"'"'s business\n'
|
||||
} > "$FIXTURES"
|
||||
|
||||
# Flake containment (#1380-FF, pipelines 2635/2637): a transient failure of the
|
||||
# ASSERTION TOOLING (grep rc>=2 — fork/alloc error under node pressure) is not a
|
||||
# contract drift, but the `if ! ... | grep -Fq` shape printed the identical
|
||||
# FAIL line for both, failing merge verifies on correct guard output. Classify
|
||||
# instead: retry tool errors 3x; rc=1 is the real mismatch; persistent tool
|
||||
# failure reports TOOL-ERROR (fail stays 1 — never green on infra noise — but
|
||||
# the line names the class so a re-run can be judged, not debugged).
|
||||
assert_out_contains() { # needle why
|
||||
local attempt rc1
|
||||
for attempt in 1 2 3; do
|
||||
printf '%s' "$out" | grep -Fq -- "$1" && return 0
|
||||
rc1=$?
|
||||
[ "$rc1" -eq 1 ] && return 1 # grep answered NO — real mismatch
|
||||
sleep 0.2 # rc>=2: grep itself errored — retry
|
||||
done
|
||||
printf 'TOOL-ERROR %s (assertion grep failed 3x — infra/tooling, not guard drift)\n' "$2" >&2
|
||||
return 2
|
||||
}
|
||||
|
||||
fail=0 n=0
|
||||
while IFS=$'\t' read -r want payload why remedy; do
|
||||
[ -n "${want:-}" ] || continue
|
||||
@@ -530,17 +549,6 @@ while IFS=$'\t' read -r want payload why remedy; do
|
||||
# if it never succeeds the case reports a TOOL-ERROR line (fail=1 stays, so
|
||||
# the run is never green-on-infra-noise, but the line names the real class
|
||||
# and a re-run can be judged instead of debugged as a guard defect).
|
||||
assert_out_names() {
|
||||
local attempt rc1
|
||||
for attempt in 1 2 3; do
|
||||
printf '%s' "$out" | grep -Fq -- "$1" && return 0
|
||||
rc1=$?
|
||||
[ "$rc1" -eq 1 ] && return 1 # grep answered NO — real mismatch
|
||||
sleep 0.2 # rc>=2: grep itself errored — retry
|
||||
done
|
||||
printf 'TOOL-ERROR %s (assertion grep failed 3x; guard output was correct class — infra/tooling, not guard drift)\n' "$why" >&2
|
||||
return 2
|
||||
}
|
||||
if [ "$got" != "$want" ]; then
|
||||
printf 'FAIL %s (want exit %s, got %s)\n' "$why" "$want" "$got"
|
||||
fail=1
|
||||
@@ -551,7 +559,7 @@ while IFS=$'\t' read -r want payload why remedy; do
|
||||
# so /issues/1/labels blocking with "use issue-create.sh" passed every run for
|
||||
# six rounds. Where a fixture states the remediation it expects, assert it.
|
||||
if [ -n "${remedy:-}" ]; then
|
||||
assert_out_names "$remedy"
|
||||
assert_out_contains "$remedy" "$why"
|
||||
arcret=$?
|
||||
if [ "$arcret" -eq 1 ]; then
|
||||
printf 'FAIL %s (blocked, but the advice does not name %s)\n' "$why" "$remedy"
|
||||
@@ -597,10 +605,15 @@ home_case() {
|
||||
fail=1
|
||||
return
|
||||
fi
|
||||
if [ -n "$needle" ] && ! printf '%s' "$out" | grep -Fq -- "$needle"; then
|
||||
printf 'FAIL %s (exit %s, but the message does not say %s)\n' "$why" "$got" "$needle"
|
||||
fail=1
|
||||
return
|
||||
if [ -n "$needle" ]; then
|
||||
assert_out_contains "$needle" "$why"
|
||||
arcret=$?
|
||||
if [ "$arcret" -eq 1 ]; then
|
||||
printf 'FAIL %s (exit %s, but the message does not say %s)\n' "$why" "$got" "$needle"
|
||||
fail=1
|
||||
return
|
||||
fi
|
||||
[ "$arcret" -eq 0 ] || { fail=1; return; }
|
||||
fi
|
||||
printf 'ok %s\n' "$why"
|
||||
}
|
||||
@@ -690,10 +703,15 @@ lone_case() {
|
||||
fail=1
|
||||
return
|
||||
fi
|
||||
if [ -n "$needle" ] && ! printf '%s' "$out" | grep -Fq -- "$needle"; then
|
||||
printf 'FAIL %s [standalone] (exit %s, but the message does not say %s)\n' "$why" "$got" "$needle"
|
||||
fail=1
|
||||
return
|
||||
if [ -n "$needle" ]; then
|
||||
assert_out_contains "$needle" "$why"
|
||||
arcret=$?
|
||||
if [ "$arcret" -eq 1 ]; then
|
||||
printf 'FAIL %s [standalone] (exit %s, but the message does not say %s)\n' "$why" "$got" "$needle"
|
||||
fail=1
|
||||
return
|
||||
fi
|
||||
[ "$arcret" -eq 0 ] || { fail=1; return; }
|
||||
fi
|
||||
printf 'ok %s [standalone]\n' "$why"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user