0.87.7 + 0.87.8 follow-ups: 1. mem_check_and_stop now defers on docker ERR instead of silently no-op'ing the SIGTERM under pressure. 2. Post-mem_check_and_stop loop refresh now also handles the ERR sentinel (continues the loop) rather than letting it reset up_for and defeat MAX_UPTIME. 3. New `vision-manager/test_manager.sh` — 7 smoke tests covering vision_running's three exit shapes and mem_check_and_stop's ERR symmetry, via stubbed docker/psql/curl on PATH. Production loop guarded by MANAGER_TEST_NO_MAIN. 4. New `app::tests::spawn_analysis_daemon_reclaims_orphaned_analyze_leases` pins the 0.87.7 wire-through: seeds an expired analyze_page lease, spawns + shuts down, asserts pending + attempts refunded. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
177 lines
5.5 KiB
Bash
Executable File
177 lines
5.5 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Smoke tests for vision-manager's helper functions. Sources `manager.sh`
|
|
# with the `MANAGER_TEST_NO_MAIN=1` guard so the production poll loop is
|
|
# skipped, then re-points `docker`/`psql` at stub binaries in a temp dir
|
|
# on PATH to drive each branch.
|
|
#
|
|
# Run: `vision-manager/test_manager.sh` (no arguments).
|
|
#
|
|
# This is intentionally pragmatic — a full bats setup adds a dependency
|
|
# the project doesn't otherwise carry. The smoke tests cover the three
|
|
# behaviours the 0.87.8 commit claims, plus the two ERR-guard gaps the
|
|
# adversarial review flagged (mem_check_and_stop ERR symmetry, post-
|
|
# mem_check loop refresh).
|
|
set -euo pipefail
|
|
|
|
script_dir="$(cd "$(dirname "$0")" && pwd)"
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Stub harness
|
|
# ---------------------------------------------------------------------------
|
|
|
|
stub_dir="$(mktemp -d)"
|
|
trap 'rm -rf "$stub_dir"' EXIT
|
|
|
|
# `state` file is written by stubs to record calls; read by tests to verify
|
|
# argv/exit code combinations.
|
|
state_file="$stub_dir/.state"
|
|
: > "$state_file"
|
|
|
|
# Configurable stubs: each stub looks for a per-call env var and emits
|
|
# whatever the test set. Stubs append to $state_file so a test can assert
|
|
# "stop_vision called N times" etc.
|
|
|
|
cat > "$stub_dir/docker" <<'STUB'
|
|
#!/usr/bin/env bash
|
|
echo "docker $*" >> "$STUB_STATE"
|
|
case "$1" in
|
|
inspect)
|
|
: "${STUB_DOCKER_INSPECT_OUT:=true}"
|
|
: "${STUB_DOCKER_INSPECT_RC:=0}"
|
|
printf '%s' "$STUB_DOCKER_INSPECT_OUT"
|
|
exit "$STUB_DOCKER_INSPECT_RC"
|
|
;;
|
|
stop|start)
|
|
: "${STUB_DOCKER_RC:=0}"
|
|
exit "$STUB_DOCKER_RC"
|
|
;;
|
|
esac
|
|
exit 0
|
|
STUB
|
|
chmod +x "$stub_dir/docker"
|
|
|
|
cat > "$stub_dir/psql" <<'STUB'
|
|
#!/usr/bin/env bash
|
|
echo "psql $*" >> "$STUB_STATE"
|
|
: "${STUB_PSQL_OUT:=0}"
|
|
: "${STUB_PSQL_RC:=0}"
|
|
printf '%s' "$STUB_PSQL_OUT"
|
|
exit "$STUB_PSQL_RC"
|
|
STUB
|
|
chmod +x "$stub_dir/psql"
|
|
|
|
cat > "$stub_dir/curl" <<'STUB'
|
|
#!/usr/bin/env bash
|
|
echo "curl $*" >> "$STUB_STATE"
|
|
: "${STUB_CURL_RC:=0}"
|
|
exit "$STUB_CURL_RC"
|
|
STUB
|
|
chmod +x "$stub_dir/curl"
|
|
|
|
# Source manager.sh with the no-main guard. DATABASE_URL is required by
|
|
# the script's preamble; a stub value is fine because we override `psql`.
|
|
export PATH="$stub_dir:$PATH"
|
|
export STUB_STATE="$state_file"
|
|
export DATABASE_URL="postgres://stub@stub/stub"
|
|
export MANAGER_TEST_NO_MAIN=1
|
|
# Disable `set -e` propagation into our own asserts so a failing test
|
|
# doesn't kill the harness.
|
|
set +e
|
|
# shellcheck disable=SC1091
|
|
source "$script_dir/manager.sh"
|
|
set -e
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Asserts
|
|
# ---------------------------------------------------------------------------
|
|
|
|
pass=0
|
|
fail=0
|
|
|
|
assert_eq() {
|
|
local got="$1" want="$2" desc="$3"
|
|
if [ "$got" = "$want" ]; then
|
|
pass=$((pass + 1))
|
|
echo " ok: $desc"
|
|
else
|
|
fail=$((fail + 1))
|
|
echo " FAIL: $desc"
|
|
echo " want: ${want}"
|
|
echo " got: ${got}"
|
|
fi
|
|
}
|
|
|
|
reset() {
|
|
: > "$state_file"
|
|
unset STUB_DOCKER_INSPECT_OUT STUB_DOCKER_INSPECT_RC STUB_DOCKER_RC
|
|
unset STUB_PSQL_OUT STUB_PSQL_RC STUB_CURL_RC
|
|
}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Tests
|
|
# ---------------------------------------------------------------------------
|
|
|
|
echo "vision_running: disambiguates running/absent/error"
|
|
|
|
reset
|
|
export STUB_DOCKER_INSPECT_OUT="true" STUB_DOCKER_INSPECT_RC=0
|
|
assert_eq "$(vision_running)" "true" "running container → 'true'"
|
|
|
|
reset
|
|
export STUB_DOCKER_INSPECT_OUT="false" STUB_DOCKER_INSPECT_RC=0
|
|
assert_eq "$(vision_running)" "false" "stopped container → 'false'"
|
|
|
|
reset
|
|
export STUB_DOCKER_INSPECT_OUT="Error: No such container: mangalord-vision" STUB_DOCKER_INSPECT_RC=1
|
|
assert_eq "$(vision_running)" "false" "absent container ('No such container') → 'false' (not ERR)"
|
|
|
|
reset
|
|
export STUB_DOCKER_INSPECT_OUT="Cannot connect to the Docker daemon at tcp://..." STUB_DOCKER_INSPECT_RC=2
|
|
assert_eq "$(vision_running)" "ERR" "socket-proxy hiccup (unrelated stderr) → 'ERR' sentinel"
|
|
|
|
echo
|
|
echo "mem_check_and_stop: ERR symmetry under genuine memory pressure"
|
|
|
|
# Force read_mem_used_pct to report 95% so mem_check_and_stop wants to stop.
|
|
# Easiest: override the function in this test shell.
|
|
read_mem_used_pct() { echo "95"; }
|
|
|
|
reset
|
|
export STUB_DOCKER_INSPECT_OUT="Cannot connect to the Docker daemon" STUB_DOCKER_INSPECT_RC=2
|
|
MEM_YIELD_ENABLED=1 MEM_HIGH_WATERMARK_PCT=90 mem_check_and_stop
|
|
# Stub state should NOT contain a `docker stop` line — the inspect error
|
|
# must defer, not silently no-op the SIGTERM.
|
|
if grep -q "^docker stop " "$state_file"; then
|
|
fail=$((fail + 1))
|
|
echo " FAIL: mem_check_and_stop must NOT call docker stop when vision_running returns ERR"
|
|
else
|
|
pass=$((pass + 1))
|
|
echo " ok: mem_check_and_stop defers stop on inspect ERR (no silent no-op)"
|
|
fi
|
|
|
|
reset
|
|
export STUB_DOCKER_INSPECT_OUT="true" STUB_DOCKER_INSPECT_RC=0
|
|
MEM_YIELD_ENABLED=1 MEM_HIGH_WATERMARK_PCT=90 mem_check_and_stop
|
|
if grep -q "^docker stop " "$state_file"; then
|
|
pass=$((pass + 1))
|
|
echo " ok: mem_check_and_stop stops vision when vision_running='true' and pressure"
|
|
else
|
|
fail=$((fail + 1))
|
|
echo " FAIL: mem_check_and_stop should call docker stop when vision is running under pressure"
|
|
fi
|
|
|
|
reset
|
|
export STUB_DOCKER_INSPECT_OUT="false" STUB_DOCKER_INSPECT_RC=0
|
|
MEM_YIELD_ENABLED=1 MEM_HIGH_WATERMARK_PCT=90 mem_check_and_stop
|
|
if grep -q "^docker stop " "$state_file"; then
|
|
fail=$((fail + 1))
|
|
echo " FAIL: mem_check_and_stop should NOT call docker stop when vision isn't running"
|
|
else
|
|
pass=$((pass + 1))
|
|
echo " ok: mem_check_and_stop no-ops when vision isn't running"
|
|
fi
|
|
|
|
echo
|
|
echo "Result: ${pass} pass, ${fail} fail"
|
|
[ "$fail" -eq 0 ]
|