2026-09-02 13:01:12 +01:00
|
|
|
#!/bin/bash
|
|
|
|
|
# Tests for the usage-API cache in claude/statusline.isaacaudet.sh: how long a
|
|
|
|
|
# cached response is reused before the 5h/7d bars are refetched, and that any
|
|
|
|
|
# render which SHOWS the bars is also a render that refreshes them.
|
|
|
|
|
#
|
|
|
|
|
# Widths are given in USABLE columns, as in the sibling width test: the
|
|
|
|
|
# script derives those from COLUMNS minus statusLine.padding on both sides
|
|
|
|
|
# minus a margin, so a raw TERM_WIDTH would make these assertions depend on
|
|
|
|
|
# the padding in the live settings.json.
|
|
|
|
|
#
|
|
|
|
|
# Sibling variants in claude/ (statusline.burnrate.sh, statusline.original.sh)
|
|
|
|
|
# are NOT covered by these tests.
|
|
|
|
|
SCRIPT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)/statusline.isaacaudet.sh"
|
|
|
|
|
PAD=$(jq -r '.statusLine.padding // 0' "$HOME/.claude/settings.json" 2>/dev/null || echo 0)
|
|
|
|
|
OVERHEAD=$(( 2 * PAD + 1 ))
|
|
|
|
|
|
|
|
|
|
# Render into a throwaway cache directory. The live cache must not be touched:
|
|
|
|
|
# ~/.claude/statusline.sh is a symlink to the script under test, so a fixture
|
|
|
|
|
# written there is shown in the running TUI as real usage until it expires.
|
|
|
|
|
export STATUSLINE_CACHE_DIR="$(mktemp -d)"
|
|
|
|
|
CACHE="$STATUSLINE_CACHE_DIR/statusline-usage-cache.json"
|
Back off after a failed usage fetch instead of retrying every redraw
A failed fetch left the cache mtime untouched, so with no network every
redraw — and a redraw happens on every keystroke — tried curl again and
could wait --max-time 10 for it. Cutting the TTL from an hour to five
minutes made that start 55 minutes sooner, so it is worth fixing now.
The marker is a separate file rather than a touch of the cached
response: a cold /tmp has no response to touch, and that is exactly the
case with no stale data to fall back on, where the timeout is paid in
full with nothing to show for it.
It goes down before the request and is cleared when that lands, not
written afterwards. A fetch is in flight for as long as curl takes to
give up, and this cache dir is shared by every session, so redraws
starting inside that window are real rather than hypothetical: they now
serve stale data instead of each launching their own doomed request.
Every attempt writes it, so a repeated failure restarts the backoff, and
only an attempt does, so the redraws it suppresses cannot keep
re-stamping it and it always lapses.
Missing credentials stays outside the backoff — it costs no timeout, and
the redraw after a login should show the bars rather than wait out a
retry window it had no part in earning.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-02 13:12:45 +01:00
|
|
|
FAIL_MARKER="$STATUSLINE_CACHE_DIR/statusline-usage-fail"
|
2026-09-02 13:01:12 +01:00
|
|
|
REPO="$(mktemp -d)"
|
|
|
|
|
STUB="$(mktemp -d)"
|
|
|
|
|
|
|
|
|
|
cleanup() { rm -rf "$STATUSLINE_CACHE_DIR" "$REPO" "$STUB"; }
|
|
|
|
|
trap cleanup EXIT
|
|
|
|
|
|
|
|
|
|
git -C "$REPO" init -q 2>/dev/null
|
|
|
|
|
git -C "$REPO" -c user.email=t@t -c user.name=t commit -q --allow-empty -m init 2>/dev/null
|
|
|
|
|
|
|
|
|
|
# A curl stub, first on PATH, records each call and replies with $CURL_REPLY.
|
|
|
|
|
# Every refresh in this file goes through it, so no test can reach the real
|
|
|
|
|
# usage API. The token is faked too, so the keychain is never read.
|
|
|
|
|
export CLAUDE_CODE_OAUTH_TOKEN=test-token
|
|
|
|
|
export CURL_CALLS="$STUB/calls"
|
Back off after a failed usage fetch instead of retrying every redraw
A failed fetch left the cache mtime untouched, so with no network every
redraw — and a redraw happens on every keystroke — tried curl again and
could wait --max-time 10 for it. Cutting the TTL from an hour to five
minutes made that start 55 minutes sooner, so it is worth fixing now.
The marker is a separate file rather than a touch of the cached
response: a cold /tmp has no response to touch, and that is exactly the
case with no stale data to fall back on, where the timeout is paid in
full with nothing to show for it.
It goes down before the request and is cleared when that lands, not
written afterwards. A fetch is in flight for as long as curl takes to
give up, and this cache dir is shared by every session, so redraws
starting inside that window are real rather than hypothetical: they now
serve stale data instead of each launching their own doomed request.
Every attempt writes it, so a repeated failure restarts the backoff, and
only an attempt does, so the redraws it suppresses cannot keep
re-stamping it and it always lapses.
Missing credentials stays outside the backoff — it costs no timeout, and
the redraw after a login should show the bars rather than wait out a
retry window it had no part in earning.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-02 13:12:45 +01:00
|
|
|
export CURL_MARKER_SEEN="$STUB/marker-seen"
|
|
|
|
|
export FAIL_MARKER
|
2026-09-02 13:01:12 +01:00
|
|
|
cat > "$STUB/curl" <<'SH'
|
|
|
|
|
#!/bin/bash
|
|
|
|
|
echo call >> "$CURL_CALLS"
|
Back off after a failed usage fetch instead of retrying every redraw
A failed fetch left the cache mtime untouched, so with no network every
redraw — and a redraw happens on every keystroke — tried curl again and
could wait --max-time 10 for it. Cutting the TTL from an hour to five
minutes made that start 55 minutes sooner, so it is worth fixing now.
The marker is a separate file rather than a touch of the cached
response: a cold /tmp has no response to touch, and that is exactly the
case with no stale data to fall back on, where the timeout is paid in
full with nothing to show for it.
It goes down before the request and is cleared when that lands, not
written afterwards. A fetch is in flight for as long as curl takes to
give up, and this cache dir is shared by every session, so redraws
starting inside that window are real rather than hypothetical: they now
serve stale data instead of each launching their own doomed request.
Every attempt writes it, so a repeated failure restarts the backoff, and
only an attempt does, so the redraws it suppresses cannot keep
re-stamping it and it always lapses.
Missing credentials stays outside the backoff — it costs no timeout, and
the redraw after a login should show the bars rather than wait out a
retry window it had no part in earning.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-02 13:12:45 +01:00
|
|
|
# Record whether the backoff marker was already written when the fetch
|
|
|
|
|
# started, which is what a concurrent redraw would see.
|
|
|
|
|
[ -f "$FAIL_MARKER" ] && echo seen >> "$CURL_MARKER_SEEN"
|
|
|
|
|
# With $CURL_FAIL set, behave as curl does with no network: nothing on
|
|
|
|
|
# stdout and a non-zero exit.
|
|
|
|
|
[ -n "$CURL_FAIL" ] && exit 6
|
2026-09-02 13:01:12 +01:00
|
|
|
printf '%s' "$CURL_REPLY"
|
|
|
|
|
SH
|
|
|
|
|
chmod +x "$STUB/curl"
|
|
|
|
|
export PATH="$STUB:$PATH"
|
|
|
|
|
export CURL_REPLY='{"five_hour":{"utilization":42.0,"resets_at":"2026-08-27T15:40:00+00:00"},
|
|
|
|
|
"seven_day":{"utilization":44.0,"resets_at":"2026-08-28T02:00:00+00:00"},
|
|
|
|
|
"extra_usage":{"is_enabled":false},"limits":[]}'
|
|
|
|
|
|
|
|
|
|
pass=0; fail=0
|
|
|
|
|
ok() { echo " PASS $1"; pass=$((pass+1)); }
|
|
|
|
|
bad() { echo " FAIL $1"; [ -n "$2" ] && printf '%s\n' "$2" | sed 's/^/ /'; fail=$((fail+1)); }
|
|
|
|
|
|
|
|
|
|
stdin_json() {
|
|
|
|
|
CWD="$1" python3 -c '
|
|
|
|
|
import json, os
|
|
|
|
|
print(json.dumps({"model": {"display_name": "Opus 5"},
|
|
|
|
|
"cwd": os.environ["CWD"],
|
|
|
|
|
"cost": {"total_cost_usd": 0.5},
|
|
|
|
|
"context_window": {"context_window_size": 200000,
|
|
|
|
|
"current_usage": {"input_tokens": 1000}}}))'
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# Render, with ANSI stripped. $1 = USABLE width.
|
|
|
|
|
render() {
|
|
|
|
|
stdin_json "$REPO" | TERM_WIDTH=$(( $1 + OVERHEAD )) bash "$SCRIPT" 2>&1 \
|
|
|
|
|
| sed $'s/\033\[[0-9;]*m//g'
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# A cached response whose bars read 5% / 7%, aged $1 seconds.
|
|
|
|
|
cached_response_aged() {
|
|
|
|
|
cat > "$CACHE" <<'JSON'
|
|
|
|
|
{"five_hour":{"utilization":5.0,"resets_at":"2026-08-27T15:40:00+00:00"},
|
|
|
|
|
"seven_day":{"utilization":7.0,"resets_at":"2026-08-28T02:00:00+00:00"},
|
|
|
|
|
"extra_usage":{"is_enabled":false},"limits":[]}
|
|
|
|
|
JSON
|
Back off after a failed usage fetch instead of retrying every redraw
A failed fetch left the cache mtime untouched, so with no network every
redraw — and a redraw happens on every keystroke — tried curl again and
could wait --max-time 10 for it. Cutting the TTL from an hour to five
minutes made that start 55 minutes sooner, so it is worth fixing now.
The marker is a separate file rather than a touch of the cached
response: a cold /tmp has no response to touch, and that is exactly the
case with no stale data to fall back on, where the timeout is paid in
full with nothing to show for it.
It goes down before the request and is cleared when that lands, not
written afterwards. A fetch is in flight for as long as curl takes to
give up, and this cache dir is shared by every session, so redraws
starting inside that window are real rather than hypothetical: they now
serve stale data instead of each launching their own doomed request.
Every attempt writes it, so a repeated failure restarts the backoff, and
only an attempt does, so the redraws it suppresses cannot keep
re-stamping it and it always lapses.
Missing credentials stays outside the backoff — it costs no timeout, and
the redraw after a login should show the bars rather than wait out a
retry window it had no part in earning.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-02 13:12:45 +01:00
|
|
|
age_file "$CACHE" "$1"
|
|
|
|
|
rm -f "$FAIL_MARKER"
|
|
|
|
|
: > "$CURL_CALLS"
|
|
|
|
|
: > "$CURL_MARKER_SEEN"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# Backdate $1 by $2 seconds.
|
|
|
|
|
age_file() {
|
|
|
|
|
FILE="$1" AGE="$2" python3 -c '
|
2026-09-02 13:01:12 +01:00
|
|
|
import os, time
|
|
|
|
|
f = os.environ["FILE"]; t = time.time() - int(os.environ["AGE"])
|
|
|
|
|
os.utime(f, (t, t))'
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
calls() { wc -l < "$CURL_CALLS" | tr -d ' '; }
|
|
|
|
|
|
Back off after a failed usage fetch instead of retrying every redraw
A failed fetch left the cache mtime untouched, so with no network every
redraw — and a redraw happens on every keystroke — tried curl again and
could wait --max-time 10 for it. Cutting the TTL from an hour to five
minutes made that start 55 minutes sooner, so it is worth fixing now.
The marker is a separate file rather than a touch of the cached
response: a cold /tmp has no response to touch, and that is exactly the
case with no stale data to fall back on, where the timeout is paid in
full with nothing to show for it.
It goes down before the request and is cleared when that lands, not
written afterwards. A fetch is in flight for as long as curl takes to
give up, and this cache dir is shared by every session, so redraws
starting inside that window are real rather than hypothetical: they now
serve stale data instead of each launching their own doomed request.
Every attempt writes it, so a repeated failure restarts the backoff, and
only an attempt does, so the redraws it suppresses cannot keep
re-stamping it and it always lapses.
Missing credentials stays outside the backoff — it costs no timeout, and
the redraw after a login should show the bars rather than wait out a
retry window it had no part in earning.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-02 13:12:45 +01:00
|
|
|
# No cached response and no backoff marker: a cold /tmp.
|
|
|
|
|
cold_cache() { rm -f "$CACHE" "$FAIL_MARKER"; : > "$CURL_CALLS"; : > "$CURL_MARKER_SEEN"; }
|
|
|
|
|
|
2026-09-02 13:01:12 +01:00
|
|
|
# ------------------------------------------------------------------- 5min TTL
|
|
|
|
|
echo "Usage cache TTL (5 minutes):"
|
|
|
|
|
|
|
|
|
|
cached_response_aged 240
|
|
|
|
|
out=$(render 245)
|
|
|
|
|
[ "$(calls)" = "0" ] && ok "4min old: served from cache, no API call" \
|
|
|
|
|
|| bad "4min old: served from cache, no API call" "$out"
|
|
|
|
|
case "$out" in *"5%"*) ok "4min old: renders the cached percentage" ;;
|
|
|
|
|
*) bad "4min old: renders the cached percentage" "$out" ;; esac
|
|
|
|
|
|
|
|
|
|
cached_response_aged 360
|
|
|
|
|
out=$(render 245)
|
|
|
|
|
[ "$(calls)" = "1" ] && ok "6min old: refetched" || bad "6min old: refetched" "$out"
|
|
|
|
|
case "$out" in *"42%"*) ok "6min old: renders the fresh percentage" ;;
|
|
|
|
|
*) bad "6min old: renders the fresh percentage" "$out" ;; esac
|
|
|
|
|
case "$(cat "$CACHE")" in *"42.0"*) ok "6min old: fresh response written back" ;;
|
|
|
|
|
*) bad "6min old: fresh response written back" "$(cat "$CACHE")" ;; esac
|
|
|
|
|
|
|
|
|
|
# Half an hour was inside the old one-hour TTL: the bars would sit unchanged.
|
|
|
|
|
cached_response_aged 1800
|
|
|
|
|
out=$(render 245)
|
|
|
|
|
[ "$(calls)" = "1" ] && ok "30min old: refetched, not held for the hour" \
|
|
|
|
|
|| bad "30min old: refetched, not held for the hour" "$out"
|
|
|
|
|
|
|
|
|
|
# ------------------------------------------------- bars shown => bars refreshed
|
Give the usage bars a line of their own, always
Squeezing the 5h/7d group onto line one was what cost the reset times and
the pace figure at laptop width, and it made the layout jump between one
and two lines as the branch name or the cost changed width. Line two is
now the group's own at every terminal size, so both timestamps and the
burn rate fit; the ladder only starts giving things up below ~80 columns.
The bars also survive down to 35 columns now rather than being dropped
below 68, since a line of their own is all they need. Every rung of the
ladder is fit-checked, the last one included: with the floor that low, a
percentage the API reports in more digits than anyone expects would
otherwise have overflowed the line rather than dropped the group.
The two timestamps still cost ~10 subprocesses to format on a line that
redraws per keystroke, so that is skipped when line two provably cannot
show them -- an under-estimate, so the fit check stays the decider.
With nothing left to squeeze, WRAP_NARROW, the wrap band and the split
tier go: line one is the full tier down to 150, wide to 68, then narrow.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-03 16:51:05 +01:00
|
|
|
# The refresh is gated on the same width check as the bars themselves, so the
|
|
|
|
|
# two must agree at every width, 35 being RL_MIN_WIDTH -- the narrowest usable
|
|
|
|
|
# width the bars still fit on their own line.
|
2026-09-02 13:01:12 +01:00
|
|
|
echo
|
|
|
|
|
echo "Every width that shows the bars refreshes them:"
|
|
|
|
|
|
Give the usage bars a line of their own, always
Squeezing the 5h/7d group onto line one was what cost the reset times and
the pace figure at laptop width, and it made the layout jump between one
and two lines as the branch name or the cost changed width. Line two is
now the group's own at every terminal size, so both timestamps and the
burn rate fit; the ladder only starts giving things up below ~80 columns.
The bars also survive down to 35 columns now rather than being dropped
below 68, since a line of their own is all they need. Every rung of the
ladder is fit-checked, the last one included: with the floor that low, a
percentage the API reports in more digits than anyone expects would
otherwise have overflowed the line rather than dropped the group.
The two timestamps still cost ~10 subprocesses to format on a line that
redraws per keystroke, so that is skipped when line two provably cannot
show them -- an under-estimate, so the fit check stays the decider.
With nothing left to squeeze, WRAP_NARROW, the wrap band and the split
tier go: line one is the full tier down to 150, wide to 68, then narrow.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-03 16:51:05 +01:00
|
|
|
for u in 245 115 85 68 50 35; do
|
2026-09-02 13:01:12 +01:00
|
|
|
cached_response_aged 360
|
|
|
|
|
out=$(render "$u")
|
|
|
|
|
if case "$out" in *"5h"*) true ;; *) false ;; esac; then
|
|
|
|
|
[ "$(calls)" = "1" ] && ok "usable $u: bars shown and refreshed" \
|
|
|
|
|
|| bad "usable $u: bars shown but served stale" "$out"
|
|
|
|
|
else
|
|
|
|
|
bad "usable $u: expected the bars to be shown" "$out"
|
|
|
|
|
fi
|
|
|
|
|
done
|
|
|
|
|
|
Give the usage bars a line of their own, always
Squeezing the 5h/7d group onto line one was what cost the reset times and
the pace figure at laptop width, and it made the layout jump between one
and two lines as the branch name or the cost changed width. Line two is
now the group's own at every terminal size, so both timestamps and the
burn rate fit; the ladder only starts giving things up below ~80 columns.
The bars also survive down to 35 columns now rather than being dropped
below 68, since a line of their own is all they need. Every rung of the
ladder is fit-checked, the last one included: with the floor that low, a
percentage the API reports in more digits than anyone expects would
otherwise have overflowed the line rather than dropped the group.
The two timestamps still cost ~10 subprocesses to format on a line that
redraws per keystroke, so that is skipped when line two provably cannot
show them -- an under-estimate, so the fit check stays the decider.
With nothing left to squeeze, WRAP_NARROW, the wrap band and the split
tier go: line one is the full tier down to 150, wide to 68, then narrow.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-03 16:51:05 +01:00
|
|
|
# One column below RL_MIN_WIDTH the group is dropped entirely — nothing is
|
2026-09-02 13:01:12 +01:00
|
|
|
# shown, so nothing should be paid for either.
|
|
|
|
|
cached_response_aged 360
|
Give the usage bars a line of their own, always
Squeezing the 5h/7d group onto line one was what cost the reset times and
the pace figure at laptop width, and it made the layout jump between one
and two lines as the branch name or the cost changed width. Line two is
now the group's own at every terminal size, so both timestamps and the
burn rate fit; the ladder only starts giving things up below ~80 columns.
The bars also survive down to 35 columns now rather than being dropped
below 68, since a line of their own is all they need. Every rung of the
ladder is fit-checked, the last one included: with the floor that low, a
percentage the API reports in more digits than anyone expects would
otherwise have overflowed the line rather than dropped the group.
The two timestamps still cost ~10 subprocesses to format on a line that
redraws per keystroke, so that is skipped when line two provably cannot
show them -- an under-estimate, so the fit check stays the decider.
With nothing left to squeeze, WRAP_NARROW, the wrap band and the split
tier go: line one is the full tier down to 150, wide to 68, then narrow.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-03 16:51:05 +01:00
|
|
|
out=$(render 34)
|
|
|
|
|
case "$out" in *"5h"*) bad "usable 34: no bars below the group's floor" "$out" ;;
|
|
|
|
|
*) ok "usable 34: no bars below the group's floor" ;; esac
|
|
|
|
|
[ "$(calls)" = "0" ] && ok "usable 34: no API call when no bars are shown" \
|
|
|
|
|
|| bad "usable 34: no API call when no bars are shown" "$out"
|
2026-09-02 13:01:12 +01:00
|
|
|
|
Back off after a failed usage fetch instead of retrying every redraw
A failed fetch left the cache mtime untouched, so with no network every
redraw — and a redraw happens on every keystroke — tried curl again and
could wait --max-time 10 for it. Cutting the TTL from an hour to five
minutes made that start 55 minutes sooner, so it is worth fixing now.
The marker is a separate file rather than a touch of the cached
response: a cold /tmp has no response to touch, and that is exactly the
case with no stale data to fall back on, where the timeout is paid in
full with nothing to show for it.
It goes down before the request and is cleared when that lands, not
written afterwards. A fetch is in flight for as long as curl takes to
give up, and this cache dir is shared by every session, so redraws
starting inside that window are real rather than hypothetical: they now
serve stale data instead of each launching their own doomed request.
Every attempt writes it, so a repeated failure restarts the backoff, and
only an attempt does, so the redraws it suppresses cannot keep
re-stamping it and it always lapses.
Missing credentials stays outside the backoff — it costs no timeout, and
the redraw after a login should show the bars rather than wait out a
retry window it had no part in earning.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-02 13:12:45 +01:00
|
|
|
# --------------------------------------------------------- offline retry backoff
|
|
|
|
|
# With no network, curl can burn --max-time 10 before giving up. A failed fetch
|
|
|
|
|
# is remembered so redraws in the next minute do not each pay for that.
|
|
|
|
|
echo
|
|
|
|
|
echo "A failed fetch backs off instead of retrying every redraw:"
|
|
|
|
|
|
|
|
|
|
export CURL_FAIL=1
|
|
|
|
|
|
|
|
|
|
cached_response_aged 360
|
|
|
|
|
out=$(render 245)
|
|
|
|
|
[ "$(calls)" = "1" ] && ok "offline: first redraw attempts the fetch" \
|
|
|
|
|
|| bad "offline: first redraw attempts the fetch" "$out"
|
|
|
|
|
case "$out" in *"5%"*) ok "offline: falls back to the stale response" ;;
|
|
|
|
|
*) bad "offline: falls back to the stale response" "$out" ;; esac
|
|
|
|
|
out=$(render 245)
|
|
|
|
|
[ "$(calls)" = "1" ] && ok "offline: the next redraw does not retry" \
|
|
|
|
|
|| bad "offline: the next redraw does not retry" "$out"
|
|
|
|
|
case "$out" in *"5%"*) ok "offline: still shows the stale response while backed off" ;;
|
|
|
|
|
*) bad "offline: still shows the stale response while backed off" "$out" ;; esac
|
|
|
|
|
|
|
|
|
|
# The marker must be the thing that ages, not be re-stamped by the redraws it
|
|
|
|
|
# suppresses — otherwise the backoff never lapses and the bars never return.
|
|
|
|
|
age_file "$FAIL_MARKER" 90
|
|
|
|
|
out=$(render 245)
|
|
|
|
|
[ "$(calls)" = "2" ] && ok "offline: retries once the backoff lapses" \
|
|
|
|
|
|| bad "offline: retries once the backoff lapses" "$out"
|
|
|
|
|
out=$(render 245)
|
|
|
|
|
[ "$(calls)" = "2" ] && ok "offline: the second failure restarts the backoff" \
|
|
|
|
|
|| bad "offline: the second failure restarts the backoff" "$out"
|
|
|
|
|
|
|
|
|
|
# The marker goes down before the request, not after it: a fetch is in flight
|
|
|
|
|
# for as long as curl takes to time out, and every redraw starting inside that
|
|
|
|
|
# window would otherwise launch its own. The cache dir is shared between
|
|
|
|
|
# sessions, so those redraws are not hypothetical. Starting from no marker at
|
|
|
|
|
# all, so only this attempt's own write can satisfy it.
|
|
|
|
|
cold_cache
|
|
|
|
|
out=$(render 245)
|
|
|
|
|
case "$(cat "$CURL_MARKER_SEEN")" in *seen*) ok "offline: the in-flight fetch marked itself before calling" ;;
|
|
|
|
|
*) bad "offline: the in-flight fetch marked itself before calling" "$out" ;; esac
|
|
|
|
|
|
|
|
|
|
# A cold /tmp is the case a touch of the response file cannot cover: there is
|
|
|
|
|
# no response to touch, so every redraw would pay the timeout.
|
|
|
|
|
cold_cache
|
|
|
|
|
out=$(render 245)
|
|
|
|
|
[ "$(calls)" = "1" ] && ok "offline, no cache: first redraw attempts the fetch" \
|
|
|
|
|
|| bad "offline, no cache: first redraw attempts the fetch" "$out"
|
|
|
|
|
out=$(render 245)
|
|
|
|
|
[ "$(calls)" = "1" ] && ok "offline, no cache: the next redraw does not retry" \
|
|
|
|
|
|| bad "offline, no cache: the next redraw does not retry" "$out"
|
|
|
|
|
case "$out" in *"5h"*) bad "offline, no cache: no bars, having no data" "$out" ;;
|
|
|
|
|
*) ok "offline, no cache: no bars, having no data" ;; esac
|
|
|
|
|
|
|
|
|
|
unset CURL_FAIL
|
|
|
|
|
|
|
|
|
|
# Back online, a marker that has lapsed must not keep suppressing fetches.
|
|
|
|
|
cold_cache
|
|
|
|
|
: > "$FAIL_MARKER"
|
|
|
|
|
age_file "$FAIL_MARKER" 90
|
|
|
|
|
out=$(render 245)
|
|
|
|
|
case "$out" in *"42%"*) ok "back online: fetches and renders the fresh percentage" ;;
|
|
|
|
|
*) bad "back online: fetches and renders the fresh percentage" "$out" ;; esac
|
|
|
|
|
[ ! -f "$FAIL_MARKER" ] && ok "back online: a success clears the backoff marker" \
|
|
|
|
|
|| bad "back online: a success clears the backoff marker"
|
|
|
|
|
cold_cache
|
|
|
|
|
out=$(render 245)
|
|
|
|
|
case "$(cat "$CURL_MARKER_SEEN")" in *seen*) ok "back online: a succeeding fetch is marked while it runs" ;;
|
|
|
|
|
*) bad "back online: a succeeding fetch is marked while it runs" "$out" ;; esac
|
|
|
|
|
[ ! -f "$FAIL_MARKER" ] && ok "back online: and unmarked once it lands" \
|
|
|
|
|
|| bad "back online: and unmarked once it lands" "$out"
|
|
|
|
|
|
|
|
|
|
# ------------------------------------------------------------ nothing to fetch
|
|
|
|
|
# No credentials is not a network failure: it costs no timeout, and the next
|
|
|
|
|
# redraw could succeed the instant a login lands. Backing off would hide the
|
|
|
|
|
# bars for a minute for nothing.
|
|
|
|
|
echo
|
|
|
|
|
echo "No credentials is not a failure to back off from:"
|
|
|
|
|
|
|
|
|
|
cat > "$STUB/security" <<'SH'
|
|
|
|
|
#!/bin/bash
|
|
|
|
|
exit 44
|
|
|
|
|
SH
|
|
|
|
|
chmod +x "$STUB/security"
|
|
|
|
|
NOHOME="$(mktemp -d)"
|
|
|
|
|
|
|
|
|
|
cold_cache
|
|
|
|
|
out=$(stdin_json "$REPO" \
|
|
|
|
|
| env -u CLAUDE_CODE_OAUTH_TOKEN HOME="$NOHOME" TERM_WIDTH=$(( 245 + OVERHEAD )) \
|
|
|
|
|
bash "$SCRIPT" 2>&1 | sed $'s/\033\[[0-9;]*m//g')
|
|
|
|
|
[ "$(calls)" = "0" ] && ok "no token: no fetch attempted" \
|
|
|
|
|
|| bad "no token: no fetch attempted" "$out"
|
|
|
|
|
[ ! -f "$FAIL_MARKER" ] && ok "no token: no backoff marker written" \
|
|
|
|
|
|| bad "no token: no backoff marker written" "$out"
|
|
|
|
|
|
|
|
|
|
rm -f "$STUB/security"
|
|
|
|
|
rm -rf "$NOHOME"
|
|
|
|
|
|
2026-09-02 13:01:12 +01:00
|
|
|
echo
|
|
|
|
|
echo " $pass passed, $fail failed"
|
|
|
|
|
[ "$fail" -eq 0 ]
|