diff --git a/packages/mosaic/framework/tools/wake/test-wake-store-ack.sh b/packages/mosaic/framework/tools/wake/test-wake-store-ack.sh index 250c5881..4b3f5ce9 100755 --- a/packages/mosaic/framework/tools/wake/test-wake-store-ack.sh +++ b/packages/mosaic/framework/tools/wake/test-wake-store-ack.sh @@ -496,12 +496,24 @@ echo "== T11: final observed_seq cursor write FAILURE — fail loud + observed.s # observed.set write). The mount lives inside a private mount+user namespace # (unshare --mount --user --map-root-user) so it is identical unprivileged (dev) # or as root (CI) and vanishes the instant the namespace exits — no cleanup. + # + # AVAILABILITY GUARD (matches T9): this injection needs unshare + a working + # bind-mount, which a NON-PRIVILEGED container (the real CI runner) DENIES — + # `unshare --mount` fails to set up ("unshare failed: Operation not permitted" / + # "can't mount none on /") and the inner enqueue NEVER runs. When the injection + # cannot be constructed we SKIP the failure-path assertions (there is no forced + # failure to observe) and keep the harness GREEN, exactly like T9 tolerates the + # same unavailability. The inner script drops a MARKER only AFTER the mount is in + # place and immediately before the enqueue, so its presence is an exact witness + # that the cursor-write fault was really injected; its absence => unavailable. errfile="$TMP_ROOT/t11.err" + marker="$TMP_ROOT/t11.injected" : >"$errfile" + rm -f "$marker" rc=0 + injected=0 if ! command -v unshare >/dev/null 2>&1; then - fail_msg "T11: 'unshare' unavailable — cannot construct the privilege-invariant fault injection in this sandbox" - rc=99 + echo " SKIP: T11 — 'unshare' unavailable; cannot construct the cursor-write fault injection (like T9)" else ro_src="$TMP_ROOT/t11-ro-src" mkdir -p "$ro_src" @@ -510,7 +522,7 @@ echo "== T11: final observed_seq cursor write FAILURE — fail loud + observed.s # mis-allocate seq=1, never exercising the cursor-WRITE failure). Only the # final `mv -f tmp observed_seq` commit is blocked by the mountpoint. printf '%s' "$obs_before" >"$ro_src/observed_seq" - # shellcheck disable=SC2016 # deliberate: $1..$4 expand in the NESTED bash + # shellcheck disable=SC2016 # deliberate: $1..$5 expand in the NESTED bash # (inside the unshare'd namespace), not this outer single-quoted one. unshare --mount --user --map-root-user bash -c ' set -u @@ -518,40 +530,49 @@ echo "== T11: final observed_seq cursor write FAILURE — fail loud + observed.s state_dir="$2" store="$3" errfile="$4" + marker="$5" if ! mount --bind "$ro_src/observed_seq" "$state_dir/observed_seq" 2>/dev/null; then exit 98 fi mount -o remount,ro,bind "$state_dir/observed_seq" 2>/dev/null || true + : >"$marker" # WITNESS: mount is in place; the enqueue below WILL hit the mounted cursor. "$store" enqueue --class actionable --locators "{\"n\":2}" >/dev/null 2>"$errfile" - ' _ "$ro_src" "$STATE_DIR" "$STORE" "$errfile" + ' _ "$ro_src" "$STATE_DIR" "$STORE" "$errfile" "$marker" 2>/dev/null rc=$? - if [ "$rc" -eq 98 ]; then - fail_msg "T11: could not bind-mount over observed_seq (mount unavailable in this sandbox) — fault injection not constructed" + if [ -f "$marker" ]; then + injected=1 + else + # unshare --mount denied (non-priv CI) or bind-mount unavailable (exit 98): + # the inner enqueue never ran, so there is no forced cursor-write failure to + # assert. SKIP gracefully instead of asserting a failure that was not injected. + echo " SKIP: T11 — unshare/bind-mount fault injection unavailable in this sandbox (e.g. non-privileged CI); cursor-write failure path not exercised here (like T9)" fi fi - # (1) FAIL LOUD: the cursor-write failure must EXIT NON-ZERO, never a silent - # success (RED against pre-#917 code, whose ungated final cursor write swallows - # the _atomic_write failure and returns 0). - [ "$rc" -ne 0 ] || fail_msg "T11: an enqueue whose FINAL cursor write fails must EXIT NON-ZERO (fail loud), got rc=$rc" - # The loud diagnostic names the cursor write (not a generic error). - grep -qi 'cursor' "$errfile" || fail_msg "T11: the failure diagnostic must name the observed_seq cursor write [$(cat "$errfile")]" + if [ "$injected" -eq 1 ]; then + # (1) FAIL LOUD: the cursor-write failure must EXIT NON-ZERO, never a silent + # success (RED against pre-#917 code, whose ungated final cursor write swallows + # the _atomic_write failure and returns 0). + [ "$rc" -ne 0 ] || fail_msg "T11: an enqueue whose FINAL cursor write fails must EXIT NON-ZERO (fail loud), got rc=$rc" + # The loud diagnostic names the cursor write (not a generic error). + grep -qi 'cursor' "$errfile" || fail_msg "T11: the failure diagnostic must name the observed_seq cursor write [$(cat "$errfile")]" - # (2) The cursor did NOT advance — the allocation is NOT committed. - obs_after="$("$STORE" cursors | sed -n 's/^observed_seq=//p')" - [ "$obs_after" = "1" ] || fail_msg "T11: a failed cursor write must NOT leave the cursor advanced, got '$obs_after'" + # (2) The cursor did NOT advance — the allocation is NOT committed. + obs_after="$("$STORE" cursors | sed -n 's/^observed_seq=//p')" + [ "$obs_after" = "1" ] || fail_msg "T11: a failed cursor write must NOT leave the cursor advanced, got '$obs_after'" - # (3) THE #917 CONSISTENCY ASSERTION: observed.set and the cursor must NEVER be - # left cross-file inconsistent. observed.set must carry NO seq greater than the - # cursor — either the observed.set advance was rolled back (neither advances) or - # both advanced together; there is no state where observed.set is ahead of the - # cursor. RED against pre-#917 code: observed.set={1,2} while the cursor lags at - # 1, so seq 2 is stranded above the cursor. - stranded="$(awk -v c="$obs_after" 'NF && $1+0 > c' "$STATE_DIR/observed.set" 2>/dev/null || true)" - [ -z "$stranded" ] || fail_msg "T11: observed.set left CROSS-FILE INCONSISTENT with the cursor — seq(s) above cursor $obs_after: [$stranded]" + # (3) THE #917 CONSISTENCY ASSERTION: observed.set and the cursor must NEVER be + # left cross-file inconsistent. observed.set must carry NO seq greater than the + # cursor — either the observed.set advance was rolled back (neither advances) or + # both advanced together; there is no state where observed.set is ahead of the + # cursor. RED against pre-#917 code: observed.set={1,2} while the cursor lags at + # 1, so seq 2 is stranded above the cursor. + stranded="$(awk -v c="$obs_after" 'NF && $1+0 > c' "$STATE_DIR/observed.set" 2>/dev/null || true)" + [ -z "$stranded" ] || fail_msg "T11: observed.set left CROSS-FILE INCONSISTENT with the cursor — seq(s) above cursor $obs_after: [$stranded]" - # (4) The consumed prefix is intact — no interior gap corrupts future consume. - "$STORE" consume --upto 1 >/dev/null 2>&1 || fail_msg "T11: CONSUMED 1 must remain valid after the failed cursor write (no gap)" + # (4) The consumed prefix is intact — no interior gap corrupts future consume. + "$STORE" consume --upto 1 >/dev/null 2>&1 || fail_msg "T11: CONSUMED 1 must remain valid after the failed cursor write (no gap)" + fi ) && ok echo