#!/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 ]