testsAndMisc/linux_configuration/tests/lib/i3blocks_claude_usage.sh
Krzysztof kuhy Rudnicki c4bdaad038 Split the last two over-cap shell test suites
test_pacman_wrapper_security.sh (287) and test_i3blocks_efficiency.sh
(345). Both keep their filenames -- shell-tests.yml runs them by name --
and both were verified by diffing a full run against the pre-split code:
byte-identical output, exit 0 either side.

The i3blocks setup half stays in the entry on purpose. Its five fake
$BIN_DIR binaries are written with heredocs, and a seam inside a heredoc
yields a lib that will not parse -- the worst failure this effort has
produced so far. Only the two heredoc-free regions moved.

Both i3blocks lib functions end in an explicit `return 0`. Their last
statement is a `|| fail ...`, which as a function tail under set -e
becomes the return value and would abort a run in which every check
passed. `fail` exits on its own, so reaching the end means success.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-16 22:27:44 +02:00

90 lines
4.6 KiB
Bash
Executable File

#!/bin/bash
# i3blocks efficiency: the claude_usage block's output and fork count.
# Sourced by test_i3blocks_efficiency.sh; inherits its strict mode and its
# helpers (fail, assert_equals, assert_le, epoch_utc, count_execs) plus the
# BIN_DIR / TMP_DIR / CONFIG_FILE / I3BLOCKS_DIR paths it sets up.
i3_tests_claude_usage() {
printf 'Checking Claude usage block behavior and fork count...\n'
# claude_usage.sh parses its cache with jq and degrades to "no data" when jq is
# missing. That is the right runtime behaviour, but it would make every
# assertion below fail with a misleading message about percentages, so name the
# real cause instead of letting a missing tool look like a logic bug.
# Probe by running jq, not with `command -v`: a jq that exists but cannot
# execute produces exactly the same "no data" output as one that is absent.
printf '{}' | jq -e . >/dev/null 2>&1 \
|| fail 'a working jq is required for the claude_usage tests (pacman -S jq)'
CLAUDE_STATE_DIR="$TMP_DIR/limit-state"
mkdir -p "$CLAUDE_STATE_DIR"
claude_now=1786366861
write_claude_state() {
printf '{"five_hour_pct":%s,"five_hour_resets_at":%s,"seven_day_pct":%s,"seven_day_resets_at":%s,"updated_at":%s}\n' \
"$1" "$2" "$3" "$4" "$5" >"$CLAUDE_STATE_DIR/state.json"
}
run_claude_usage() {
LIMIT_STATE_DIR="$CLAUDE_STATE_DIR" NOW_EPOCH="$claude_now" \
bash "$I3BLOCKS_DIR/claude_usage.sh"
}
# Percentages arrive as floats (e.g. 55.00000000000001) and must be truncated.
write_claude_state 34 "$((claude_now + 3600))" 55.00000000000001 "$((claude_now + 99999))" "$claude_now"
claude_output=$(run_claude_usage)
assert_equals '🤖 5h 34% · 7d 55%' "$(printf '%s\n' "$claude_output" | sed -n '1p')" \
'claude usage should show both windows as whole percentages'
assert_equals '#50FA7B' "$(printf '%s\n' "$claude_output" | sed -n '3p')" \
'claude usage should be green well below the limit'
write_claude_state 70 "$((claude_now + 3600))" 12 "$((claude_now + 99999))" "$claude_now"
assert_equals '#F1FA8C' "$(run_claude_usage | sed -n '3p')" \
'claude usage should warn when a window passes 60%'
write_claude_state 91 "$((claude_now + 3600))" 12 "$((claude_now + 99999))" "$claude_now"
assert_equals '#FF5555' "$(run_claude_usage | sed -n '3p')" \
'claude usage should go critical when a window passes 85%'
# The writer only runs while a Claude session is open, so old data must be
# labelled rather than presented as the current figure.
write_claude_state 34 "$((claude_now + 3600))" 55 "$((claude_now + 99999))" "$((claude_now - 5000))"
assert_equals '🤖 5h 34% · 7d 55% (stale)' "$(run_claude_usage | sed -n '1p')" \
'claude usage should mark stale cache data'
# A window whose reset time has passed has rolled over: its cached percentage is
# meaningless, so it must read as unknown rather than a misleading value.
write_claude_state 99 "$((claude_now - 10))" 55 "$((claude_now + 99999))" "$claude_now"
assert_equals '🤖 5h ?% · 7d 55%' "$(run_claude_usage | sed -n '1p')" \
'claude usage should not report a rolled-over window as current'
write_claude_state 34 "$((claude_now + 3600))" 55 null "$claude_now"
assert_equals '🤖 5h 34% · 7d 55%' "$(run_claude_usage | sed -n '1p')" \
'claude usage should tolerate a null seven_day reset time'
printf 'not json\n' >"$CLAUDE_STATE_DIR/state.json"
assert_equals '🤖 no data' "$(run_claude_usage | sed -n '1p')" \
'claude usage should degrade gracefully on unparsable cache data'
claude_missing_output=$(LIMIT_STATE_DIR="$TMP_DIR/no-such-dir" NOW_EPOCH="$claude_now" \
bash "$I3BLOCKS_DIR/claude_usage.sh")
assert_equals '🤖 no data' "$(printf '%s\n' "$claude_missing_output" | sed -n '1p')" \
'claude usage should degrade gracefully when the cache directory is absent'
write_claude_state 34 "$((claude_now + 3600))" 55 "$((claude_now + 99999))" "$claude_now"
assert_le "$(LIMIT_STATE_DIR="$CLAUDE_STATE_DIR" NOW_EPOCH="$claude_now" \
count_execs "$I3BLOCKS_DIR/claude_usage.sh")" 3 \
'claude usage should stay within bash plus jq'
# The real cache holds one file per project (~170 on this machine), so the
# fork budget has to be measured against a populated directory. Measuring it
# against a single-file dir hid a `stat` call sitting inside the scan loop.
for filler in $(seq 1 200); do
cp "$CLAUDE_STATE_DIR/state.json" "$CLAUDE_STATE_DIR/filler-$filler.json"
done
assert_le "$(LIMIT_STATE_DIR="$CLAUDE_STATE_DIR" NOW_EPOCH="$claude_now" \
count_execs "$I3BLOCKS_DIR/claude_usage.sh")" 3 \
'claude usage fork count must not scale with the number of cached projects'
rm -f "$CLAUDE_STATE_DIR"/filler-*.json
return 0
}