mirror of
https://github.com/kuhyx/testsAndMisc.git
synced 2026-11-10 19:07:04 +01:00
Some checks are pending
dependency freshness / dependencies on newest stable (push) Waiting to run
md-naming / markdown naming convention (push) Waiting to run
Pre-commit checks / pre-commit (${{ matrix.group }}) (mypy, mypy) (push) Waiting to run
Pre-commit checks / pre-commit (${{ matrix.group }}) (pylint, pylint) (push) Waiting to run
Pre-commit checks / pre-commit (${{ matrix.group }}) (rest, ) (push) Waiting to run
Pre-commit checks / pre-commit (${{ matrix.group }}) (shellcheck, shellcheck) (push) Waiting to run
Python tests / test (push) Waiting to run
repo-contract / repo contract (push) Waiting to run
Shell tests / shell-tests (push) Waiting to run
Shell tests / lib-tests (push) Waiting to run
Shell tests / arch-tests (push) Waiting to run
A daily-limits cache without a numeric generated_at made the age arithmetic hit "null: unbound variable" under set -u: exit 1 and an empty block. The render now floors a numeric generated_at and the script emits the orange "SHADOW no data" fallback for anything else, still exiting 0. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Dsh4Znwh2KnC7q6vbGpX1N
153 lines
6.0 KiB
Bash
Executable File
153 lines
6.0 KiB
Bash
Executable File
#!/bin/bash
|
|
# Behaviour tests for the i3blocks leisure_shadow block: it renders the
|
|
# `leisure_shadow` part of daily-limits' cache ($DAILY_LIMITS_CACHE, contract
|
|
# C5). Covers text, the yellow/red thresholds, the "no data" states (no cache,
|
|
# no key, null numbers), stale/garbage caches, "never urgent" and the
|
|
# "re-parse only when the cache changes" contract.
|
|
|
|
set -euo pipefail
|
|
|
|
SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)
|
|
REPO_DIR=$(cd -- "$SCRIPT_DIR/.." && pwd)
|
|
BLOCK="$REPO_DIR/i3blocks/leisure_shadow.sh"
|
|
TMP_DIR=$(mktemp -d)
|
|
|
|
cleanup() {
|
|
rm -rf "$TMP_DIR"
|
|
}
|
|
trap cleanup EXIT
|
|
|
|
fail() {
|
|
printf 'FAIL: %s\n' "$1" >&2
|
|
exit 1
|
|
}
|
|
|
|
assert_eq() {
|
|
local expected=$1
|
|
local actual=$2
|
|
local context=$3
|
|
if [[ "$expected" != "$actual" ]]; then
|
|
fail "$context (expected '$expected', actual '$actual')"
|
|
fi
|
|
}
|
|
|
|
printf '{}' | jq -e . >/dev/null 2>&1 ||
|
|
fail 'a working jq is required for the leisure_shadow tests (pacman -S jq)'
|
|
|
|
CACHE="$TMP_DIR/daily-limits.json"
|
|
RENDER="$TMP_DIR/render"
|
|
NOW=1790000000
|
|
|
|
# write_cache USED BUDGET [GENERATED_AT] -- USED/BUDGET may be "null";
|
|
# would_block and left_minutes follow from them as daily-limits writes them.
|
|
write_cache() {
|
|
local used=$1 budget=$2 generated=${3:-$NOW} left=null block=false
|
|
if [[ $used != null && $budget != null ]]; then
|
|
left=$((budget > used ? budget - used : 0))
|
|
((used >= budget)) && block=true
|
|
fi
|
|
printf '{"date":"2026-10-19","generated_at":%s,"gaming":{"day":"2026-10-19","budget_minutes":300,"used_minutes":0,"ceiling_minutes":480},"leisure_shadow":{"day":"2026-10-19","budget_minutes":%s,"used_minutes":%s,"left_minutes":%s,"would_block":%s,"sources":[]}}\n' \
|
|
"$generated" "$budget" "$used" "$left" "$block" >"$CACHE"
|
|
rm -f "$RENDER" # force a re-render for the new content
|
|
}
|
|
|
|
run_block() {
|
|
NOW_EPOCH="$NOW" DAILY_LIMITS_CACHE="$CACHE" LEISURE_SHADOW_RENDER_CACHE="$RENDER" bash "$BLOCK"
|
|
}
|
|
|
|
line() {
|
|
run_block | sed -n "${1}p"
|
|
}
|
|
|
|
printf 'Checking the normal day: time left and used...\n'
|
|
write_cache 108 300
|
|
assert_eq '🛋 SHADOW 3h12 left · 1h48 used' "$(line 1)" 'full text'
|
|
assert_eq '🛋 3h12' "$(line 2)" 'short text'
|
|
assert_eq '#F8F8F2' "$(line 3)" 'plain foreground above 30 min'
|
|
assert_eq '3' "$(run_block | wc -l)" 'exactly full/short/colour, no urgent line'
|
|
|
|
printf 'Checking yellow strictly under 30 min...\n'
|
|
write_cache 270 300
|
|
assert_eq '#F8F8F2' "$(line 3)" '30 min left is not yet yellow'
|
|
write_cache 271 300
|
|
assert_eq '#F1FA8C' "$(line 3)" '29 min left is yellow'
|
|
assert_eq '🛋 SHADOW 0h29 left · 4h31 used' "$(line 1)" 'yellow text'
|
|
write_cache 299 300
|
|
assert_eq '#F1FA8C' "$(line 3)" '1 min left is still yellow, not red'
|
|
|
|
printf 'Checking would-block at and over 0, never urgent...\n'
|
|
write_cache 300 300
|
|
assert_eq '🛋 SHADOW WOULD BLOCK · 5h00 used' "$(line 1)" 'at 0'
|
|
assert_eq '🛋 WOULD BLOCK' "$(line 2)" 'would-block short text'
|
|
assert_eq '#FF5555' "$(line 3)" 'would-block is red'
|
|
write_cache 605 600
|
|
assert_eq '🛋 SHADOW WOULD BLOCK · 10h05 used' "$(line 1)" 'over'
|
|
set +e
|
|
run_block >/dev/null
|
|
status=$?
|
|
set -e
|
|
assert_eq '0' "$status" 'exit 0: i3blocks marks exit 33 urgent (flashing)'
|
|
assert_eq '3' "$(run_block | wc -l)" 'would-block emits no urgent line'
|
|
|
|
printf 'Checking "no data" is said, never faked as zero...\n'
|
|
write_cache null null
|
|
assert_eq '🛋 SHADOW ⏱? no data' "$(line 1)" 'C4 fields absent'
|
|
assert_eq '🛋 ⏱?' "$(line 2)" 'no data short text'
|
|
assert_eq '#FFB86C' "$(line 3)" 'no data is orange'
|
|
printf '{"date":"2026-10-19","generated_at":%s,"gaming":{}}\n' "$NOW" >"$CACHE"
|
|
rm -f "$RENDER"
|
|
assert_eq '🛋 SHADOW ⏱? no data' "$(line 1)" 'a cache without leisure_shadow'
|
|
rm -f "$CACHE"
|
|
assert_eq '🛋 SHADOW ⏱? no data' "$(line 1)" 'missing cache'
|
|
|
|
printf 'Checking a cache without a usable generated_at says no data, exit 0...\n'
|
|
for generated in '' '"generated_at":null,' '"generated_at":"soon",'; do
|
|
printf '{%s"leisure_shadow":{"budget_minutes":300,"used_minutes":60,"left_minutes":240,"would_block":false}}\n' \
|
|
"$generated" >"$CACHE"
|
|
rm -f "$RENDER"
|
|
set +e
|
|
output=$(run_block 2>&1)
|
|
status=$?
|
|
set -e
|
|
assert_eq '0' "$status" "generated_at '${generated:-absent}' still exits 0"
|
|
assert_eq '🛋 SHADOW ⏱? no data' "$(sed -n 1p <<<"$output")" "generated_at '${generated:-absent}'"
|
|
assert_eq '#FFB86C' "$(sed -n 3p <<<"$output")" "generated_at '${generated:-absent}' is orange"
|
|
done
|
|
write_cache 60 300 "$NOW.75"
|
|
assert_eq '🛋 SHADOW 4h00 left · 1h00 used' "$(line 1)" 'a fractional generated_at is a timestamp'
|
|
|
|
printf 'Checking a stale or garbage cache...\n'
|
|
write_cache 60 300 "$((NOW - 181))"
|
|
assert_eq '🛋 SHADOW ⏱? stale 3m' "$(line 1)" 'older than 180 s'
|
|
assert_eq '#FFB86C' "$(line 3)" 'stale is orange'
|
|
write_cache 60 300 "$((NOW - 180))"
|
|
assert_eq '🛋 SHADOW 4h00 left · 1h00 used' "$(line 1)" '180 s is still fresh'
|
|
printf 'not json' >"$CACHE"
|
|
rm -f "$RENDER"
|
|
assert_eq '🛋 SHADOW unreadable cache' "$(line 1)" 'garbage body'
|
|
assert_eq '#FF5555' "$(line 3)" 'garbage is red'
|
|
|
|
printf 'Checking jq runs only when the cache file changes...\n'
|
|
write_cache 60 300
|
|
mkdir -p "$TMP_DIR/bin"
|
|
printf '#!/bin/bash\necho x >>"%s/jq.calls"\nexec %s "$@"\n' "$TMP_DIR" "$(command -v jq)" >"$TMP_DIR/bin/jq"
|
|
chmod +x "$TMP_DIR/bin/jq"
|
|
PATH="$TMP_DIR/bin:$PATH" run_block >/dev/null
|
|
PATH="$TMP_DIR/bin:$PATH" run_block >/dev/null
|
|
assert_eq '1' "$(wc -l <"$TMP_DIR/jq.calls")" 'two ticks, one unchanged cache: one jq run'
|
|
sleep 0.05
|
|
touch "$CACHE"
|
|
PATH="$TMP_DIR/bin:$PATH" run_block >/dev/null
|
|
assert_eq '2' "$(wc -l <"$TMP_DIR/jq.calls")" 'a rewritten cache is re-parsed'
|
|
|
|
printf 'Checking the config wires the block next to gaming_budget...\n'
|
|
CONFIG="$REPO_DIR/i3blocks/config"
|
|
grep -A3 '^\[leisure_shadow\]$' "$CONFIG" | grep -q '^command=~/.config/i3blocks/leisure_shadow.sh$' ||
|
|
fail '[leisure_shadow] should run leisure_shadow.sh'
|
|
next_section=$(sed -n '/^\[gaming_budget\]$/,$p' "$CONFIG" | grep -m2 '^\[' | tail -1)
|
|
assert_eq '[leisure_shadow]' "$next_section" '[leisure_shadow] sits right after [gaming_budget]'
|
|
! grep -A4 '^\[leisure_shadow\]$' "$CONFIG" | grep -q '^urgent' ||
|
|
fail '[leisure_shadow] must never be urgent'
|
|
|
|
printf 'All leisure_shadow checks passed.\n'
|