-
Notifications
You must be signed in to change notification settings - Fork 13
Expand file tree
/
Copy pathbump-objectui.selftest.sh
More file actions
executable file
·492 lines (454 loc) · 21.3 KB
/
Copy pathbump-objectui.selftest.sh
File metadata and controls
executable file
·492 lines (454 loc) · 21.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
#!/usr/bin/env bash
# Self-test for scripts/bump-objectui.sh — the WRITE-ORDERING invariant (#10797).
#
# bash scripts/bump-objectui.selftest.sh
#
# THE INVARIANT UNDER TEST
# A bump that cannot read the objectui commit object REFUSES, and leaves
# `.objectui-sha` BYTE-IDENTICAL to what it was before the run.
#
# The bill this pins: the pin file used to be written BEFORE the run read the
# commit subject, so an unreadable commit object killed the script under `set -e`
# with `.objectui-sha` already rewritten — no changeset, no commit, and a bare
# `fatal: bad object` as the whole explanation. Re-running did not self-correct:
# the pin file now held the bad SHA, so the next run compared against it.
#
# WHY THE ASSERTION IS ON BYTES, NOT ON THE MESSAGE. Both the broken and the
# fixed script exit non-zero on this input — 128 from `set -e` versus 1 from the
# refusal — so "did it fail?" does not discriminate between them and a test that
# asked only that would have passed against the defect. What discriminates is the
# file: the broken ordering rewrites it, the fixed one does not. So every refusal
# case snapshots `.objectui-sha` and compares with `cmp` (byte-for-byte, and it
# needs no hashing tool), and the absent-pin case asserts the file was not
# CREATED. Verified by ablation, 2026-08-21: with the reads moved back after the
# write, cases 1 and 2 both go red on exactly that assertion.
#
# AND THE GUARD MUST NOT BE VACUOUS. `exit 1` at the top of bump-objectui.sh
# would satisfy every refusal case above, so cases 3 and 4 drive readable commits
# all the way through and assert the pin MOVES (and that an already-current pin
# is left alone). A guard that refuses everything fails them.
#
# CASE 5 PINS A SECOND, NARROWER REFUSAL (#14393): the range can WALK
# COMPLETELY (every commit and tree object present) and the bump still must
# refuse, because the changeset BLOB the digest needs to derive a bump level is
# unreadable at both revisions. This is not the #9408/#14178 shape above — the
# discriminator is `RANGE_OK`, settled by the commit/tree walk alone — so this
# case builds a real objectui changeset commit and deletes only its blob
# object, then asserts `--check-walkable` still exits 0 on the broken tree
# before asserting the bump itself refuses. This bump used to emit a degraded
# `patch` changeset here instead (silently, exit 0) — see the reproduction log
# in the #14393 PR body for the byte-for-byte before/after.
#
# Cases 1-4 stay offline, no node, no network: throwaway git repos under
# `mktemp -d`, and the script under test is exercised as a byte copy inside a
# throwaway FRAMEWORK_ROOT (the real one is derived from `${BASH_SOURCE[0]}/..`,
# so a copy is the only way to run it without writing into this repo's own
# `.objectui-sha`). Case 5 additionally needs node, offline still: it drives
# the real `objectui-changeset-digest.mjs` (a byte copy, like the script under
# test) through a throwaway objectui repo with a real changeset commit.
#
# dispatch-gates: no-path-population -- every path this file writes or reads lives inside a disposable checkout under mktemp -d (a throwaway objectui and a throwaway FRAMEWORK_ROOT, both destroyed by the EXIT trap below), so no quoted literal in this file names a path this repo tracks. Case 5's fixture changeset filename is assembled from CHANGESET_NAME by interpolation everywhere it is used, deliberately never spelled as one bare quoted token, so it does not read as a declared population here either.
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
UNDER_TEST="${SCRIPT_DIR}/bump-objectui.sh"
if [[ ! -f "$UNDER_TEST" ]]; then
echo "✗ cannot find the script under test at ${UNDER_TEST}" >&2
exit 1
fi
TMPROOT="$(mktemp -d)"
trap 'rm -rf "$TMPROOT"' EXIT
PASSED=0
FAILED=0
CASE=""
ok() { echo " ✓ $*"; PASSED=$((PASSED + 1)); }
bad() { echo " ✗ $*" >&2; FAILED=$((FAILED + 1)); }
case_begin() { CASE="$1"; echo " • ${CASE}"; }
# case_5 additionally needs a copy of the digest script `bump-objectui.sh`
# calls, plus every first-party module that script imports, alongside the script
# under test — mirroring `objectui-changeset-digest.mjs`'s own self-test
# fixtures, which stage the same closure for the same reason.
#
# ⭐ The module list is DERIVED, not typed (#16421). It was two names here
# (`objectui-changeset-digest.mjs`, `invoked-as.mjs`) and the same two names in
# three other places; when the digest script gained one import, this file was the
# THIRD staging site to go red, and what a reader saw was not an
# `ERR_MODULE_NOT_FOUND` they could act on: the staged digest died as a
# subprocess, so `range_walkable` never returned one of its two verdicts and
# case 5 failed on the refusal's wording instead. (⚠️ `bump-objectui.sh` itself
# is NOT at fault and is not to be touched for this: it swallows no stderr, and
# its `WALK_RC` branch already separates a verdict from a no-answer and refuses
# to offer `--unshallow` for a crash. See #18354 for what IS carded.) The
# derivation lives in `first-party-closure.mjs`, shared with the JS sites.
#
# ⚠️ THE BASENAME IS SPELLED ALONE AND THE DIRECTORY IS INTERPOLATED ONTO IT —
# the same discipline the declaration above states for CHANGESET_NAME, and for
# the same machine reason: `dispatch-gates` reads a quoted literal carrying a
# separator as a DECLARED WATCHED PATH, so writing the repo-relative path as one
# bare token here gives this gate a path population and CONTRADICTS the
# `no-path-population` marker above. Measured, not theorised —
# `check:pm-dispatch-gates`: "no family both DECLARES no path population and
# names paths anyway (contradicted: check:objectui-bump)". ⛔ Do not tidy this
# back into a single literal.
DIGEST_BASENAME='objectui-changeset-digest.mjs'
REPO_ROOT_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"
DIGEST_ENTRY="scripts/${DIGEST_BASENAME}"
# The same script, absolute, for the two cases that run it from THIS checkout
# rather than from a sandbox — those need no staging at all.
DIGEST_SCRIPT="${REPO_ROOT_DIR}/${DIGEST_ENTRY}"
# Case 5's fixture changeset BASENAME — interpolated into a changeset path
# everywhere it is used, never spelled as one bare path literal: that path
# lives only inside a disposable objectui checkout under mktemp -d and names
# no file this repo tracks, so a literal quoted token would be misread as a
# declared population by check-declared-population-live.
CHANGESET_NAME='widget-refresh'
# --- fixtures ----------------------------------------------------------------
# A throwaway objectui with two commits and an `origin/main` that matches HEAD,
# so the #10495 reachability report answers "on origin/main" instead of "UNKNOWN"
# and cannot be mistaken for the thing being tested here.
new_objectui() {
local d="$1"
mkdir -p "$d"
git -C "$d" init -q
git -C "$d" symbolic-ref HEAD refs/heads/main
git -C "$d" config user.email '[email protected]'
git -C "$d" config user.name 'selftest'
git -C "$d" config commit.gpgsign false
# No commit-graph and no auto-gc: both can answer for an object that is no
# longer in the store, which would quietly defuse the fixture below.
git -C "$d" config gc.auto 0
git -C "$d" config core.commitGraph false
git -C "$d" config fetch.writeCommitGraph false
git -C "$d" commit -q --allow-empty -m 'feat(console): the previous pin'
git -C "$d" commit -q --allow-empty -m 'feat(console): the commit being pinned'
git -C "$d" update-ref refs/remotes/origin/main HEAD
git -C "$d" rev-parse HEAD
}
# A throwaway FRAMEWORK_ROOT holding a byte copy of the script under test.
new_framework() {
local d="$1" pin="${2-}"
mkdir -p "${d}/scripts" "${d}/.changeset"
cp "$UNDER_TEST" "${d}/scripts/bump-objectui.sh"
if [[ -n "$pin" ]]; then printf '%s\n' "$pin" > "${d}/.objectui-sha"; fi
}
# Same, plus a byte copy of the digest script `bump-objectui.sh` shells out to
# and every first-party module that script imports — needed only by cases that
# do NOT pass `--no-changeset` and so actually reach the changeset section.
#
# ⛔ The closure is written to a file and its exit code read on its own line: a
# `node ... | while read` pipeline would hand this function the exit code of
# `while`, so a derivation that died would stage NOTHING and look like it worked,
# which is the failure shape this whole change exists to remove.
new_framework_with_digest() {
local d="$1" pin="${2-}"
new_framework "$d" "$pin"
local list="${d}/.closure.txt" rel
# ⚠️ Each path is emitted with its OWN trailing newline, and the loop below
# still reads an unterminated last line. Both halves, because the first time
# this was written with `join('\n')` the final path — `first-party-closure.mjs`
# itself — was silently dropped by `read`, and the sandbox then failed with the
# SAME ERR_MODULE_NOT_FOUND this derivation exists to prevent, from a list that
# had named the file correctly.
node --input-type=module -e "
import { firstPartyModuleClosure } from '${REPO_ROOT_DIR}/scripts/first-party-closure.mjs';
for (const rel of firstPartyModuleClosure('${DIGEST_ENTRY}', { root: '${REPO_ROOT_DIR}' })) console.log(rel);
" > "$list" 2>"${list}.err"
local ec=$?
if [[ $ec -ne 0 ]]; then
printf 'bump-objectui.selftest: could not derive the digest closure (exit %s)\n' "$ec" >&2
cat "${list}.err" >&2
return "$ec"
fi
local staged_count=0
while read -r rel || [[ -n "$rel" ]]; do
[[ -z "$rel" ]] && continue
mkdir -p "${d}/$(dirname "$rel")"
cp "${REPO_ROOT_DIR}/${rel}" "${d}/${rel}"
staged_count=$((staged_count + 1))
done < "$list"
# A derivation that came back short stages a sandbox that looks runnable and
# is not, so the count is ASSERTED here rather than discovered three cases
# later as "the objectui range does not walk".
if [[ "$staged_count" -lt 2 ]]; then
printf 'bump-objectui.selftest: the digest closure staged only %s file(s) — expected the script and its imports\n' "$staged_count" >&2
return 1
fi
rm -f "$list" "${list}.err"
}
# A throwaway objectui repo with a REAL changeset commit — commit A (the
# previous pin), commit B adding `.changeset/<name>.md` declaring a real
# package/level, so the range A..B WALKS COMPLETELY and the digest has a real
# blob to read (#14393). Prints "OLD_SHA NEW_SHA" on stdout.
new_objectui_with_changeset() {
local d="$1"
mkdir -p "$d"
git -C "$d" init -q
git -C "$d" symbolic-ref HEAD refs/heads/main
git -C "$d" config user.email '[email protected]'
git -C "$d" config user.name 'selftest'
git -C "$d" config commit.gpgsign false
git -C "$d" config gc.auto 0
git -C "$d" config core.commitGraph false
git -C "$d" config fetch.writeCommitGraph false
git -C "$d" commit -q --allow-empty -m 'feat(console): the previous pin'
local old_sha
old_sha="$(git -C "$d" rev-parse HEAD)"
mkdir -p "${d}/.changeset"
cat > "${d}/.changeset/${CHANGESET_NAME}.md" <<'EOF'
---
"@object-ui/core": minor
---
Refresh the widget palette.
EOF
git -C "$d" add ".changeset/${CHANGESET_NAME}.md"
git -C "$d" commit -q -m 'feat(core): refresh the widget palette'
local new_sha
new_sha="$(git -C "$d" rev-parse HEAD)"
git -C "$d" update-ref refs/remotes/origin/main HEAD
printf '%s %s\n' "$old_sha" "$new_sha"
}
# Delete the changeset blob added at `sha` under `path` from the object store,
# then PROVE it is gone — same discipline as `break_commit_object`, one layer
# further in (a blob, not a commit object). `--check-walkable` walks commits
# and trees, never blobs, so this leaves the range walk green (asserted by the
# caller) while making `readAt` fail at BOTH `to` and the commit that added it
# — `to` and `sha` are the SAME commit in this fixture (the changeset is read
# at the tip that added it, before any release consumes it), so a single
# deleted blob object answers both reads identically.
break_changeset_blob() {
local d="$1" sha="$2" path="$3"
local blob
blob="$(git -C "$d" rev-parse "${sha}:${path}")"
local loose="${d}/.git/objects/${blob:0:2}/${blob:2}"
if [[ ! -f "$loose" ]]; then
bad "fixture: expected a loose blob object at ${loose}, found none (packed already?)"
return 1
fi
rm -f "$loose"
if git -C "$d" cat-file -e "$blob" 2>/dev/null; then
bad "fixture: blob ${blob:0:12} is still readable after deleting it"
return 1
fi
printf '%s\n' "$blob"
return 0
}
# Delete a commit object from the store, then PROVE it is gone. A fixture that
# silently failed to break anything is the one way this whole file could go
# green over nothing, so the proof is an assertion, not a comment.
break_commit_object() {
local d="$1" sha="$2"
local loose="${d}/.git/objects/${sha:0:2}/${sha:2}"
if [[ ! -f "$loose" ]]; then
bad "fixture: expected a loose object at ${loose}, found none (packed already?)"
return 1
fi
rm -f "$loose"
if git -C "$d" cat-file -e "${sha}^{commit}" 2>/dev/null; then
bad "fixture: commit object ${sha:0:12} is still readable after deleting it"
return 1
fi
if git -C "$d" log -1 --format=%s "$sha" >/dev/null 2>&1; then
bad "fixture: 'git log' still reads ${sha:0:12} after deleting the object"
return 1
fi
# The card's own measurement, re-asserted here because it is what makes the
# DEFAULT path (no argument) able to reach the bug at all: `rev-parse HEAD`
# resolves the ref without reading the object, and exits 0.
local resolved rc=0
resolved="$(git -C "$d" rev-parse HEAD 2>/dev/null)" || rc=$?
if [[ "$rc" -ne 0 || "$resolved" != "$sha" ]]; then
bad "fixture: 'git rev-parse HEAD' no longer resolves to ${sha:0:12} (rc=${rc})"
return 1
fi
return 0
}
# Run the copy under test. No pipe anywhere near the exit code: redirect to a
# file, THEN read `$?`.
LOG=""
EC=0
run_bump() {
local fw="$1" oui="$2"; shift 2
LOG="${TMPROOT}/run-$$-${RANDOM}.log"
EC=0
OBJECTUI_ROOT="$oui" bash "${fw}/scripts/bump-objectui.sh" "$@" > "$LOG" 2>&1 || EC=$?
}
log_has() { grep -qF -- "$1" "$LOG"; }
# --- case 1: unreadable commit object ⇒ refusal, pin file untouched ----------
case_1() {
case_begin 'unreadable commit object ⇒ refuses, .objectui-sha byte-identical'
local fw="${TMPROOT}/c1/fw" oui="${TMPROOT}/c1/objectui"
local new_sha old_sha
new_sha="$(new_objectui "$oui")"
old_sha="$(git -C "$oui" rev-parse 'HEAD~1')"
new_framework "$fw" "$old_sha"
break_commit_object "$oui" "$new_sha" || return 0
cp "${fw}/.objectui-sha" "${TMPROOT}/c1.before"
run_bump "$fw" "$oui" --no-commit --no-changeset
if [[ "$EC" -eq 0 ]]; then
bad "expected a non-zero exit, got 0 — the bump did not refuse"
else
ok "refused (exit ${EC})"
fi
# THE discriminating assertion.
if cmp -s "${TMPROOT}/c1.before" "${fw}/.objectui-sha"; then
ok ".objectui-sha is byte-identical to before the run"
else
bad ".objectui-sha CHANGED — half-applied state: $(cat "${fw}/.objectui-sha")"
fi
if log_has 'REFUSING to bump'; then
ok 'the refusal names itself rather than leaving git'"'"'s fatal as the whole message'
else
bad "no 'REFUSING to bump' in the output; got: $(head -3 "$LOG" | tr '\n' '|')"
fi
if log_has 'NOTHING WAS WRITTEN'; then
ok 'the message states that nothing was written'
else
bad "the refusal does not tell the operator the tree is clean"
fi
if [[ ! -e "${fw}/.changeset/console-${new_sha:0:12}.md" ]]; then
ok 'no changeset was emitted'
else
bad 'a changeset was emitted for a commit that cannot be read'
fi
}
# --- case 2: same, with no pin file at all (initial pin) ---------------------
# Byte-identity of a file that does not exist is "it still does not exist". The
# broken ordering CREATES it here, which is the same defect wearing the
# first-ever-bump disguise.
case_2() {
case_begin 'unreadable commit object + no existing pin file ⇒ the file is not created'
local fw="${TMPROOT}/c2/fw" oui="${TMPROOT}/c2/objectui"
local new_sha
new_sha="$(new_objectui "$oui")"
new_framework "$fw"
break_commit_object "$oui" "$new_sha" || return 0
if [[ -e "${fw}/.objectui-sha" ]]; then
bad 'fixture: the pin file should not exist yet'
return 0
fi
run_bump "$fw" "$oui" --no-commit --no-changeset
if [[ "$EC" -ne 0 ]]; then ok "refused (exit ${EC})"; else bad 'expected a non-zero exit, got 0'; fi
if [[ ! -e "${fw}/.objectui-sha" ]]; then
ok '.objectui-sha was not created'
else
bad ".objectui-sha was CREATED holding $(cat "${fw}/.objectui-sha")"
fi
}
# --- case 3: a readable commit still bumps (the guard is not vacuous) -------
case_3() {
case_begin 'readable commit ⇒ the bump proceeds and the pin moves'
local fw="${TMPROOT}/c3/fw" oui="${TMPROOT}/c3/objectui"
local new_sha old_sha
new_sha="$(new_objectui "$oui")"
old_sha="$(git -C "$oui" rev-parse 'HEAD~1')"
new_framework "$fw" "$old_sha"
run_bump "$fw" "$oui" --no-commit --no-changeset
if [[ "$EC" -eq 0 ]]; then ok 'exited 0'; else bad "expected exit 0, got ${EC}: $(tail -5 "$LOG" | tr '\n' '|')"; fi
local written
written="$(tr -d '[:space:]' < "${fw}/.objectui-sha" 2>/dev/null || true)"
if [[ "$written" == "$new_sha" ]]; then
ok ".objectui-sha now holds ${new_sha:0:12}"
else
bad ".objectui-sha holds '${written}', expected ${new_sha}"
fi
if log_has '(on origin/main)'; then
ok 'the #10495 reachability report still runs and answers "on origin/main"'
else
bad "the reachability report did not answer: $(head -5 "$LOG" | tr '\n' '|')"
fi
}
# --- case 4: an already-current pin is left alone ---------------------------
case_4() {
case_begin 'readable commit already pinned ⇒ nothing to do, pin file untouched'
local fw="${TMPROOT}/c4/fw" oui="${TMPROOT}/c4/objectui"
local new_sha
new_sha="$(new_objectui "$oui")"
new_framework "$fw" "$new_sha"
cp "${fw}/.objectui-sha" "${TMPROOT}/c4.before"
run_bump "$fw" "$oui" --no-commit --no-changeset
if [[ "$EC" -eq 0 ]]; then ok 'exited 0'; else bad "expected exit 0, got ${EC}"; fi
if log_has 'nothing to do'; then ok 'reported "nothing to do"'; else bad 'did not report "nothing to do"'; fi
if cmp -s "${TMPROOT}/c4.before" "${fw}/.objectui-sha"; then
ok '.objectui-sha is byte-identical to before the run'
else
bad '.objectui-sha was rewritten on a no-op bump'
fi
}
# --- case 5: range WALKS, changeset blob unreadable at both revisions -------
# (#14393.) Discriminator from case 1/2: the COMMIT object is fully readable
# and the range walks completely — only the changeset BLOB the digest needs to
# derive a level is gone. Discriminator from the initial-pin degrade (which
# this file does not otherwise exercise): there IS a previous SHA and the
# range DOES walk, so a level was derivable in principle and the derivation
# failed rather than never having an input.
case_5() {
case_begin 'range walks, changeset blob unreadable at both revisions ⇒ refuses, .objectui-sha byte-identical'
local fw="${TMPROOT}/c5/fw" oui="${TMPROOT}/c5/objectui"
local shas old_sha new_sha
shas="$(new_objectui_with_changeset "$oui")"
old_sha="${shas%% *}"
new_sha="${shas##* }"
new_framework_with_digest "$fw" "$old_sha"
local walk_rc=0
node "$DIGEST_SCRIPT" --objectui-root "$oui" --from "$old_sha" --to "$new_sha" \
--check-walkable >/dev/null 2>&1 || walk_rc=$?
if [[ "$walk_rc" -ne 0 ]]; then
bad "fixture: the range does not walk BEFORE breaking the blob (rc=${walk_rc}) — not this card's state"
return 0
fi
break_changeset_blob "$oui" "$new_sha" ".changeset/${CHANGESET_NAME}.md" >/dev/null || return 0
# Re-assert walkability AFTER breaking the blob — the whole point of this
# case is that the commit/tree walk stays green while the blob read fails.
walk_rc=0
node "$DIGEST_SCRIPT" --objectui-root "$oui" --from "$old_sha" --to "$new_sha" \
--check-walkable >/dev/null 2>&1 || walk_rc=$?
if [[ "$walk_rc" -eq 0 ]]; then
ok 'fixture: --check-walkable still exits 0 after the blob is deleted (walk is commits/trees, not blobs)'
else
bad "fixture: --check-walkable now exits ${walk_rc} — the blob deletion broke the WALK, not just the blob read"
return 0
fi
cp "${fw}/.objectui-sha" "${TMPROOT}/c5.before"
run_bump "$fw" "$oui" --no-commit "$new_sha"
if [[ "$EC" -eq 0 ]]; then
bad "expected a non-zero exit, got 0 — the bump did not refuse"
else
ok "refused (exit ${EC})"
fi
if cmp -s "${TMPROOT}/c5.before" "${fw}/.objectui-sha"; then
ok ".objectui-sha is byte-identical to before the run"
else
bad ".objectui-sha CHANGED — half-applied state: $(cat "${fw}/.objectui-sha")"
fi
if log_has 'REFUSING to bump'; then
ok 'the refusal names itself'
else
bad "no 'REFUSING to bump' in the output; got: $(tail -10 "$LOG" | tr '\n' '|')"
fi
if log_has 'walks'; then
ok 'the refusal says the range WALKS (the discriminator from the unwalkable-range refusal)'
else
bad "the refusal does not say the range walks: $(tail -10 "$LOG" | tr '\n' '|')"
fi
if log_has 'NOTHING WAS WRITTEN'; then
ok 'the message states that nothing was written'
else
bad "the refusal does not tell the operator the tree is clean"
fi
if [[ ! -e "${fw}/.changeset/console-${new_sha:0:12}.md" ]]; then
ok 'no changeset was emitted'
else
bad 'a changeset was emitted for a range whose derivation failed'
fi
}
echo "bump-objectui.sh self-test — write-ordering invariant (#10797)"
case_1
case_2
case_3
case_4
case_5
echo
if [[ "$FAILED" -gt 0 ]]; then
echo "✗ bump-objectui self-test FAILED — ${FAILED} assertion(s) failed, ${PASSED} passed." >&2
exit 1
fi
echo "✓ bump-objectui self-test PASSED — ${PASSED} assertions across 5 cases."