xworkmate-bridge/scripts/github-actions/test-validate-deploy.sh
2026-04-21 10:34:44 +08:00

257 lines
7.4 KiB
Bash

#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
SCRIPT_PATH="${ROOT_DIR}/scripts/github-actions/validate-deploy.sh"
IMAGE_REF="ghcr.io/x-evor/xworkmate-bridge:425a38f1e8076899400d4a858d4678dffd876afb"
RUN_OUTPUT=""
RUN_STATUS=0
RUN_TMP_DIR=""
RUN_STATE_DIR=""
fail() {
printf 'FAIL: %s\n' "$*" >&2
exit 1
}
assert_contains() {
local haystack="$1"
local needle="$2"
if [[ "${haystack}" != *"${needle}"* ]]; then
fail "expected output to contain: ${needle}"
fi
}
assert_not_contains() {
local haystack="$1"
local needle="$2"
if [[ "${haystack}" == *"${needle}"* ]]; then
fail "expected output to not contain: ${needle}"
fi
}
cleanup_run() {
if [[ -n "${RUN_TMP_DIR}" && -d "${RUN_TMP_DIR}" ]]; then
rm -rf "${RUN_TMP_DIR}"
fi
RUN_OUTPUT=""
RUN_STATUS=0
RUN_TMP_DIR=""
RUN_STATE_DIR=""
}
create_fake_tools() {
local scenario="$1"
local tmp_dir="$2"
mkdir -p "${tmp_dir}/bin" "${tmp_dir}/state"
cat >"${tmp_dir}/bin/curl" <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
state_dir="${FAKE_CURL_STATE_DIR:?}"
scenario="${FAKE_CURL_SCENARIO:?}"
url="${@: -1}"
data=""
write_out=""
for ((i = 1; i <= $#; i += 1)); do
arg="${!i}"
case "${arg}" in
--data)
next_index=$((i + 1))
data="${!next_index}"
;;
--write-out)
next_index=$((i + 1))
write_out="${!next_index}"
;;
esac
done
counter_file() {
printf '%s/%s.count\n' "${state_dir}" "$1"
}
read_count() {
local file
file="$(counter_file "$1")"
if [[ -f "${file}" ]]; then
cat "${file}"
return
fi
printf '0\n'
}
bump_count() {
local name="$1"
local file value
file="$(counter_file "${name}")"
value=$(( $(read_count "${name}") + 1 ))
printf '%s\n' "${value}" >"${file}"
printf '%s\n' "${value}"
}
if [[ -n "${write_out}" ]]; then
printf '200'
exit 0
fi
case "${scenario}" in
bridge-timeout)
case "${url}" in
https://xworkmate-bridge.svc.plus/api/ping)
printf '{"status":"ok","image":"ghcr.io/x-evor/xworkmate-bridge:425a38f1e8076899400d4a858d4678dffd876afb","tag":"425a38f1e8076899400d4a858d4678dffd876afb","commit":"425a38f1e8076899400d4a858d4678dffd876afb","version":"425a38f1e8076899400d4a858d4678dffd876afb"}\n'
;;
https://xworkmate-bridge.svc.plus/)
printf 'xworkmate-bridge is running\n'
;;
https://xworkmate-bridge.svc.plus/acp-server/*/acp/rpc)
printf '{"jsonrpc":"2.0","result":{"providers":["ok"]}}\n'
;;
https://xworkmate-bridge.svc.plus/acp-server/*)
printf '{"jsonrpc":"2.0","result":{"providers":["ok"]}}\n'
;;
https://xworkmate-bridge.svc.plus/gateway/openclaw)
printf '{"jsonrpc":"2.0","result":{"providers":["ok"]}}\n'
;;
https://xworkmate-bridge.svc.plus/acp/rpc)
printf 'curl: (28) Operation timed out after 20001 milliseconds with 0 bytes received\n' >&2
exit 1
;;
*)
printf 'unexpected url in bridge-timeout scenario: %s\n' "${url}" >&2
exit 1
;;
esac
;;
retry-success)
case "${url}" in
https://xworkmate-bridge.svc.plus/api/ping)
ping_attempt="$(bump_count ping)"
if (( ping_attempt < 3 )); then
printf 'curl: (28) Operation timed out after 20001 milliseconds with 0 bytes received\n' >&2
exit 1
fi
printf '{"status":"ok","image":"ghcr.io/x-evor/xworkmate-bridge:425a38f1e8076899400d4a858d4678dffd876afb","tag":"425a38f1e8076899400d4a858d4678dffd876afb","commit":"425a38f1e8076899400d4a858d4678dffd876afb","version":"425a38f1e8076899400d4a858d4678dffd876afb"}\n'
;;
https://xworkmate-bridge.svc.plus/)
printf 'xworkmate-bridge is running\n'
;;
https://xworkmate-bridge.svc.plus/acp-server/*/acp/rpc)
printf '{"jsonrpc":"2.0","result":{"providers":["ok"]}}\n'
;;
https://xworkmate-bridge.svc.plus/acp-server/*)
printf '{"jsonrpc":"2.0","result":{"providers":["ok"]}}\n'
;;
https://xworkmate-bridge.svc.plus/gateway/openclaw)
printf '{"jsonrpc":"2.0","result":{"providers":["ok"]}}\n'
;;
https://xworkmate-bridge.svc.plus/acp/rpc)
if [[ "${data}" == *'"providerId":"codex"'* ]]; then
printf '{"jsonrpc":"2.0","result":{"success":true,"providerId":"codex","capabilities":{"providers":["codex"]}}}\n'
exit 0
fi
if [[ "${data}" == *'"providerId":"opencode"'* ]]; then
printf '{"jsonrpc":"2.0","result":{"success":true,"providerId":"opencode","capabilities":{"providers":["opencode"]}}}\n'
exit 0
fi
if [[ "${data}" == *'"providerId":"gemini"'* ]]; then
printf '{"jsonrpc":"2.0","result":{"success":true,"providerId":"gemini","capabilities":{"providers":["gemini"]}}}\n'
exit 0
fi
if [[ "${data}" == *'"providerId":"hermes"'* ]]; then
printf '{"jsonrpc":"2.0","result":{"success":true,"providerId":"hermes","capabilities":{"providers":["hermes"]}}}\n'
exit 0
fi
printf 'unexpected bridge probe payload in retry-success scenario: %s\n' "${data}" >&2
exit 1
;;
*)
printf 'unexpected url in retry-success scenario: %s\n' "${url}" >&2
exit 1
;;
esac
;;
*)
printf 'unsupported fake curl scenario: %s\n' "${scenario}" >&2
exit 1
;;
esac
EOF
cat >"${tmp_dir}/bin/sleep" <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
exit 0
EOF
chmod +x "${tmp_dir}/bin/curl" "${tmp_dir}/bin/sleep"
}
run_validate_capture() {
local scenario="$1"
cleanup_run
RUN_TMP_DIR="$(mktemp -d)"
RUN_STATE_DIR="${RUN_TMP_DIR}/state"
create_fake_tools "${scenario}" "${RUN_TMP_DIR}"
set +e
RUN_OUTPUT="$(
PATH="${RUN_TMP_DIR}/bin:${PATH}" \
FAKE_CURL_SCENARIO="${scenario}" \
FAKE_CURL_STATE_DIR="${RUN_STATE_DIR}" \
BRIDGE_SERVER_URL="https://xworkmate-bridge.svc.plus" \
OPENCLAW_URL="https://xworkmate-bridge.svc.plus/gateway/openclaw" \
CODEX_RPC_URL="https://xworkmate-bridge.svc.plus/acp-server/codex" \
OPENCODE_RPC_URL="https://xworkmate-bridge.svc.plus/acp-server/opencode" \
GEMINI_RPC_URL="https://xworkmate-bridge.svc.plus/acp-server/gemini" \
HERMES_RPC_URL="https://xworkmate-bridge.svc.plus/acp-server/hermes" \
BRIDGE_AUTH_TOKEN="test-token" \
bash "${SCRIPT_PATH}" "${IMAGE_REF}" 2>&1
)"
RUN_STATUS=$?
set -e
}
test_bridge_timeout_stops_without_json_decode_noise() {
run_validate_capture "bridge-timeout"
if [[ "${RUN_STATUS}" -eq 0 ]]; then
fail "expected bridge-timeout scenario to fail"
fi
assert_contains "${RUN_OUTPUT}" "bridge rpc https://xworkmate-bridge.svc.plus/acp/rpc request failed"
assert_not_contains "${RUN_OUTPUT}" "JSONDecodeError"
cleanup_run
}
test_ping_retry_reaches_successful_release_validation() {
run_validate_capture "retry-success"
if [[ "${RUN_STATUS}" -ne 0 ]]; then
printf '%s\n' "${RUN_OUTPUT}" >&2
fail "expected retry-success scenario to pass"
fi
if [[ ! -f "${RUN_STATE_DIR}/ping.count" ]]; then
fail "expected ping retry counter to be recorded"
fi
ping_attempts="$(tr -d '\n' <"${RUN_STATE_DIR}/ping.count")"
if [[ "${ping_attempts}" != "3" ]]; then
fail "expected ping to succeed on third attempt, got ${ping_attempts}"
fi
cleanup_run
}
test_bridge_timeout_stops_without_json_decode_noise
test_ping_retry_reaches_successful_release_validation
printf 'validate-deploy regression tests passed\n'