- Зачем: - claude/codex должны ходить через hproxy.local, а ручной proxy_on легко забыть; нужны явные команды, которые не стартуют молча при недоступном прокси. - Что: - bash: добавлены _proxy_export/_proxy_reachable/_proxy_run и pclaude/pcodex; proxy_on рефакторен на единый источник правды без смены поведения. - powershell: добавлены Get-ProxyEndpoint/Set-ProxyEnv/Test-ProxyReachable/Invoke-WithProxy и pclaude/pcodex; proxy_on рефакторен аналогично. - таймаут pre-flight вынесен в PROXY_PREFLIGHT_TIMEOUT (дефолт 2 c). - добавлен tests/test_proxy_launchers.sh (fail-fast, изоляция env, проброс кода, аргументы, регресс proxy_on). - Проверка: - bash tests/test_proxy_launchers.sh.
121 lines
5.6 KiB
Bash
Executable File
121 lines
5.6 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
FUNCTIONS_FILE="$REPO_ROOT/bash/.bash_functions"
|
|
|
|
pass_count=0
|
|
|
|
fail() {
|
|
echo "FAIL: $*" >&2
|
|
exit 1
|
|
}
|
|
|
|
ok() {
|
|
pass_count=$((pass_count + 1))
|
|
echo "ok - $*"
|
|
}
|
|
|
|
assert_eq() {
|
|
local expected="$1" actual="$2" msg="$3"
|
|
[ "$expected" = "$actual" ] || fail "$msg (ожидалось '$expected', получено '$actual')"
|
|
}
|
|
|
|
# Изолированный fake-CLI на PATH: печатает env и аргументы, отдаёт заданный код.
|
|
make_fake_bin() {
|
|
local dir
|
|
dir="$(mktemp -d /tmp/proxy-launchers-test.XXXXXX)"
|
|
cat >"$dir/fakecli" <<'EOF'
|
|
#!/usr/bin/env bash
|
|
echo "HTTP_PROXY=${HTTP_PROXY:-}"
|
|
echo "NO_PROXY=${NO_PROXY:-}"
|
|
for a in "$@"; do echo "ARG=$a"; done
|
|
exit "${FAKE_EXIT:-0}"
|
|
EOF
|
|
chmod +x "$dir/fakecli"
|
|
echo "$dir"
|
|
}
|
|
|
|
# --- Каждый кейс в отдельном subshell, чтобы env/функции не протекали ---
|
|
|
|
# 1. Прокси недоступен → бинарник НЕ вызывается, ненулевой код.
|
|
(
|
|
source "$FUNCTIONS_FILE"
|
|
_proxy_reachable() { return 1; }
|
|
bindir="$(make_fake_bin)"; export PATH="$bindir:$PATH"
|
|
out="$(_proxy_run fakecli 2>&1)"; rc=$?
|
|
[ "$rc" -ne 0 ] || fail "недоступный прокси: ожидался ненулевой код"
|
|
case "$out" in *ARG=*) fail "недоступный прокси: бинарник не должен запускаться";; esac
|
|
case "$out" in *"недоступен"*) ;; *) fail "недоступный прокси: нет понятного сообщения";; esac
|
|
) && ok "недоступный прокси → fail-fast без запуска бинарника"
|
|
|
|
# 2. proxy-env виден дочернему процессу и НЕ протекает в родительский shell.
|
|
(
|
|
source "$FUNCTIONS_FILE"
|
|
_proxy_reachable() { return 0; }
|
|
bindir="$(make_fake_bin)"; export PATH="$bindir:$PATH"
|
|
unset HTTP_PROXY NO_PROXY || true
|
|
out="$(_proxy_run fakecli)"
|
|
case "$out" in *"HTTP_PROXY=http://hproxy.local:12334"*) ;; *) fail "child: HTTP_PROXY не выставлен";; esac
|
|
case "$out" in *"NO_PROXY=hproxy.local,localhost,127.0.0.1,::1,[::1],.concept-software.ru"*) ;; *) fail "child: NO_PROXY неверен";; esac
|
|
[ -z "${HTTP_PROXY:-}" ] || fail "leak: HTTP_PROXY протёк в родительский shell"
|
|
[ -z "${NO_PROXY:-}" ] || fail "leak: NO_PROXY протёк в родительский shell"
|
|
) && ok "proxy-env в дочернем процессе, без протечки в shell"
|
|
|
|
# 3. Проброс кода возврата дочернего процесса.
|
|
(
|
|
source "$FUNCTIONS_FILE"
|
|
_proxy_reachable() { return 0; }
|
|
bindir="$(make_fake_bin)"; export PATH="$bindir:$PATH"
|
|
FAKE_EXIT=42 _proxy_run fakecli >/dev/null; rc=$?
|
|
assert_eq 42 "$rc" "код возврата дочернего процесса не проброшен"
|
|
) && ok "проброс кода возврата дочернего процесса"
|
|
|
|
# 4. Аргументы с пробелами и `--` доходят без искажений.
|
|
(
|
|
source "$FUNCTIONS_FILE"
|
|
_proxy_reachable() { return 0; }
|
|
bindir="$(make_fake_bin)"; export PATH="$bindir:$PATH"
|
|
out="$(_proxy_run fakecli "a b" -- --flag)"
|
|
case "$out" in *$'ARG=a b\n'*) ;; *) fail "arg с пробелом потерян";; esac
|
|
case "$out" in *"ARG=--"*) ;; *) fail "разделитель -- потерян";; esac
|
|
case "$out" in *"ARG=--flag"*) ;; *) fail "флаг после -- потерян";; esac
|
|
) && ok "аргументы с пробелами и -- проброшены без искажений"
|
|
|
|
# 5. Команда не в PATH → отдельное сообщение, код 127.
|
|
(
|
|
source "$FUNCTIONS_FILE"
|
|
_proxy_reachable() { return 0; }
|
|
out="$(_proxy_run definitely-no-such-cli-xyz 2>&1)"; rc=$?
|
|
assert_eq 127 "$rc" "отсутствующая команда: ожидался код 127"
|
|
case "$out" in *"не найдена"*) ;; *) fail "отсутствующая команда: нет понятного сообщения";; esac
|
|
) && ok "команда не в PATH → код 127 с сообщением"
|
|
|
|
# 6. Регресс proxy_on: точный контракт экспортируемых переменных.
|
|
(
|
|
source "$FUNCTIONS_FILE"
|
|
curl() { echo "1.2.3.4"; } # без сети
|
|
unset HTTP_PROXY HTTPS_PROXY ALL_PROXY NO_PROXY \
|
|
http_proxy https_proxy all_proxy no_proxy || true
|
|
proxy_on >/dev/null
|
|
np="hproxy.local,localhost,127.0.0.1,::1,[::1],.concept-software.ru"
|
|
assert_eq "http://hproxy.local:12334" "${HTTP_PROXY}" "proxy_on HTTP_PROXY"
|
|
assert_eq "http://hproxy.local:12334" "${HTTPS_PROXY}" "proxy_on HTTPS_PROXY"
|
|
assert_eq "socks5://hproxy.local:12334" "${ALL_PROXY}" "proxy_on ALL_PROXY"
|
|
assert_eq "$np" "${NO_PROXY}" "proxy_on NO_PROXY"
|
|
assert_eq "${HTTP_PROXY}" "${http_proxy}" "proxy_on http_proxy дубль"
|
|
assert_eq "${HTTPS_PROXY}" "${https_proxy}" "proxy_on https_proxy дубль"
|
|
assert_eq "${ALL_PROXY}" "${all_proxy}" "proxy_on all_proxy дубль"
|
|
assert_eq "$np" "${no_proxy}" "proxy_on no_proxy дубль"
|
|
) && ok "регресс proxy_on: контракт переменных не изменился"
|
|
|
|
# 7. shellcheck (если доступен).
|
|
if command -v shellcheck >/dev/null 2>&1; then
|
|
shellcheck -s bash "$FUNCTIONS_FILE" || fail "shellcheck нашёл проблемы в bash/.bash_functions"
|
|
ok "shellcheck чист"
|
|
else
|
|
echo "skip - shellcheck не установлен"
|
|
fi
|
|
|
|
echo "PASS: $pass_count проверок пройдено"
|