#!/usr/bin/env bash set -euo pipefail ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" fail() { echo "FAIL: $*" >&2 exit 1 } assert_contains() { local haystack="$1" local needle="$2" if [[ "$haystack" != *"$needle"* ]]; then fail "expected to find [$needle] in [$haystack]" fi } run_test_build_subscription_access_prep_sql() { # shellcheck disable=SC1091 source "$ROOT_DIR/scripts/host_access_prep_lib.sh" local sql sql="$(build_subscription_access_prep_sql 42 'sk-test-123' 7 10 30 1 'hermes remote subscription validation')" assert_contains "$sql" "UPDATE users" assert_contains "$sql" "balance < 10" assert_contains "$sql" "UPDATE api_keys" assert_contains "$sql" "group_id = 7" assert_contains "$sql" "key = 'sk-test-123'" assert_contains "$sql" "INSERT INTO user_subscriptions" assert_contains "$sql" "ON CONFLICT (user_id, group_id) WHERE deleted_at IS NULL" assert_contains "$sql" "now() + interval '30 days'" local quoted_sql quoted_sql="$(build_bind_api_key_group_sql "sk-o'reilly" 7)" assert_contains "$quoted_sql" "WHERE key = 'sk-o''reilly'" local auth_cache_key balance_cache_key subscription_cache_key auth_cache_key="$(build_api_key_auth_cache_key 'user-key')" balance_cache_key="$(build_user_balance_cache_key 42)" subscription_cache_key="$(build_subscription_billing_cache_key 42 7)" assert_contains "$auth_cache_key" "apikey:auth:" assert_contains "$balance_cache_key" "billing:balance:42" assert_contains "$subscription_cache_key" "billing:sub:42:7" } run_test_real_host_acceptance_after_import_hook() { local tmpdir fakebin artifact_dir hook_file tmpdir="$(mktemp -d)" trap 'rm -rf "$tmpdir"' RETURN fakebin="$tmpdir/bin" artifact_dir="$tmpdir/artifacts" hook_file="$artifact_dir/hook.txt" mkdir -p "$fakebin" cat > "$fakebin/curl" <<'EOF' #!/usr/bin/env bash set -euo pipefail url="" for arg in "$@"; do if [[ "$arg" == *'***'* ]]; then echo "unexpected redacted auth placeholder in curl args: $*" >&2 exit 1 fi if [[ "$arg" == http://* || "$arg" == https://* ]]; then url="$arg" fi done [[ -n "$url" ]] || { echo "missing url in curl args: $*" >&2 exit 1 } case "$url" in */api/hosts) printf '%s\n' '{"host_id":"test-host"}' ;; */api/hosts/test-host) printf '%s\n' '{"host_id":"test-host"}' ;; */api/hosts/test-host/probe) printf '%s\n' '{"ok":true}' ;; */api/packs/install) printf '%s\n' '{"pack_id":1}' ;; */api/providers/deepseek/preview-import) printf '%s\n' '{"available":true}' ;; */api/providers/deepseek/import) printf '%s\n' '{"batch_id":123,"batch_status":"partially_succeeded","access_status":"broken"}' ;; */api/import-batches/123) printf '%s\n' '{"managed_resources":[{"ResourceType":"group","HostResourceID":"7","ResourceName":"DeepSeek 默认分组"}]}' ;; */api/providers/deepseek/access/preview) printf '%s\n' '{"available":true}' ;; */api/providers/deepseek/access/status) printf '%s\n' '{"latest_access_status":"subscription_ready"}' ;; */api/providers/deepseek/status) printf '%s\n' '{"status":"ready"}' ;; */api/providers/deepseek/reconcile) printf '%s\n' '{"status":"in_sync"}' ;; */api/import-batches/123/rollback) printf '%s\n' '{"status":"rolled_back"}' ;; *) echo "unexpected curl url: $url" >&2 exit 1 ;; esac EOF chmod +x "$fakebin/curl" PATH="$fakebin:$PATH" \ ARTIFACT_DIR="$artifact_dir" \ CRM_BASE_URL="http://crm.example.com" \ CRM_ADMIN_TOKEN="token" \ HOST_NAME="test-host" \ HOST_BASE_URL="http://host.example.com" \ PACK_PATH="/tmp/openai-pack" \ PROVIDER_ID="deepseek" \ HOST_API_KEY="host-key" \ REMOTE_PG_CONTAINER="fresh-pg" \ REMOTE_REDIS_CONTAINER="fresh-redis" \ MODE="partial" \ ACCESS_MODE="subscription" \ ACCESS_API_KEY="user-key" \ SUBSCRIPTION_USERS="42" \ SKIP_ROLLBACK="1" \ AFTER_IMPORT_HOOK_COMMAND='printf "%s\n" "$BATCH_ID:$BATCH_DETAIL_FILE:$ACCESS_MODE" > "$ARTIFACT_DIR/hook.txt"' \ "$ROOT_DIR/scripts/real_host_acceptance.sh" >/dev/null [[ -f "$hook_file" ]] || fail "after-import hook did not create $hook_file" local hook_contents hook_contents="$(cat "$hook_file")" assert_contains "$hook_contents" "123:" assert_contains "$hook_contents" "05a-batch-detail-pre-access.json:subscription" } run_test_import_remote43_provider_subscription_prep() { local tmpdir fakebin artifact_dir ssh_log psql_sql tmpdir="$(mktemp -d)" trap 'rm -rf "$tmpdir"' RETURN fakebin="$tmpdir/bin" artifact_dir="$tmpdir/artifacts" ssh_log="$artifact_dir/ssh-log.txt" psql_sql="$artifact_dir/prep.sql" mkdir -p "$fakebin" cat > "$fakebin/curl" <<'EOF' #!/usr/bin/env bash set -euo pipefail headers_file="" body_file="" url="" prev="" for arg in "$@"; do if [[ "$arg" == *'***'* ]]; then echo "unexpected redacted auth placeholder in curl args: $*" >&2 exit 1 fi case "$prev" in -D) headers_file="$arg" prev="" continue ;; -o) body_file="$arg" prev="" continue ;; esac case "$arg" in -D|-o) prev="$arg" continue ;; http://*|https://*) url="$arg" ;; esac done write_headers() { [[ -n "$headers_file" ]] && printf '%s\n' 'HTTP/1.1 200 OK' > "$headers_file" } write_body() { local body="$1" if [[ -n "$body_file" ]]; then printf '%s\n' "$body" > "$body_file" else printf '%s\n' "$body" fi } case "$url" in */api/hosts) write_body '{"host_id":"remote43-current-host"}' ;; */api/providers/deepseek/import) write_headers write_body '{"batch_id":123,"batch_status":"partially_succeeded","access_status":"broken","provider_status":"ready","accepted_keys_count":1,"group":{"id":"7","name":"DeepSeek 默认分组"}}' ;; */api/import-batches/123) write_body '{"managed_resources":[{"ResourceType":"group","HostResourceID":"7","ResourceName":"DeepSeek 默认分组"}]}' ;; */api/providers/deepseek/status) write_body '{"status":"ready"}' ;; */api/providers/deepseek/access/status) write_body '{"latest_access_status":"subscription_ready"}' ;; */api/providers/deepseek/access/preview) write_body '{"available":true}' ;; *) echo "unexpected curl url: $url" >&2 exit 1 ;; esac EOF chmod +x "$fakebin/curl" cat > "$fakebin/ssh" <<'EOF' #!/usr/bin/env bash set -euo pipefail log_dir="${FAKE_REMOTE_LOG_DIR:?missing FAKE_REMOTE_LOG_DIR}" cmd="${*: -1}" printf '%s\n' "$cmd" >> "$log_dir/ssh-log.txt" if [[ "$cmd" == *'***'* ]]; then echo "unexpected redacted auth placeholder in ssh command: $cmd" >&2 exit 1 fi case "$cmd" in *"/api/v1/auth/login"*) printf '%s\n' 'host-bearer-token' ;; *"grep ^SUB2API_CRM_ADMIN_TOKEN="*) printf '%s\n' 'crm-token' ;; *"select value from settings where key='admin_api_key'"*) printf '%s\n' 'admin-key' ;; *"select id from users where role='admin'"*) printf '%s\n' '1' ;; *"select id from users where email like 'relay-sub-%@sub2api.local'"*) printf '%s\n' '42' ;; *"select k.key from users u join api_keys k on k.user_id=u.id"*) printf '%s\n' 'user-key' ;; *"/api/providers/deepseek/import"*) printf '%s\n' '{"batch_id":123,"batch_status":"partially_succeeded","access_status":"broken","group":{"id":"7","name":"DeepSeek 默认分组"}}' > /tmp/import_body.json printf '%s\n' 'HTTP/1.1 200 OK' > /tmp/import_headers.txt ;; "cat /tmp/import_headers.txt") cat /tmp/import_headers.txt ;; "cat /tmp/import_body.json") cat /tmp/import_body.json ;; *"/api/import-batches/123"*) printf '%s\n' '{"managed_resources":[{"ResourceType":"account","HostResourceID":"8","ResourceName":"deepseek-01"}]}' ;; *"curl -sS -D /tmp/models_headers.txt"*) printf '%s\n' 'HTTP/1.1 200 OK' > /tmp/models_headers.txt printf '%s\n' '{"data":[{"id":"gpt-4"},{"id":"gpt-4.1"}]}' > /tmp/models_body.json ;; "cat /tmp/models_headers.txt") cat /tmp/models_headers.txt ;; "cat /tmp/models_body.json") cat /tmp/models_body.json ;; *"curl -sS -D /tmp/chat_headers.txt"*) printf '%s\n' 'HTTP/1.1 200 OK' > /tmp/chat_headers.txt printf '%s\n' '{"choices":[{"message":{"content":"pong"}}]}' > /tmp/chat_body.json ;; "cat /tmp/chat_headers.txt") cat /tmp/chat_headers.txt ;; "cat /tmp/chat_body.json") cat /tmp/chat_body.json ;; *"/api/providers/deepseek/status"*) printf '%s\n' '{"status":"ready"}' ;; *"/api/providers/deepseek/access/status"*) printf '%s\n' '{"latest_access_status":"subscription_ready"}' ;; *"/api/providers/deepseek/access/preview"*) printf '%s\n' '{"available":true}' ;; *"/api/providers/deepseek/reconcile"*) printf '%s\n' '{"status":"in_sync"}' ;; *"sudo -n docker exec -i fresh-pg psql -U sub2api -d sub2api -At -F ''"*) printf '%s\n' '{"group_id":7,"subscription":{"status":"active"},"key":{"group_id":7}}' ;; *"sudo -n docker exec -i fresh-pg psql -U sub2api -d sub2api"*) CMD="$cmd" LOG_DIR="$log_dir" python3 - <<'PY' import base64, os, re, pathlib, sys cmd = os.environ['CMD'] log_dir = pathlib.Path(os.environ['LOG_DIR']) match = re.search(r"printf '%s' '([^']+)' \| base64 -d", cmd) if not match: raise SystemExit(f'failed to extract base64 payload from: {cmd}') sql = base64.b64decode(match.group(1)).decode() if "select id from users where email like 'relay-sub-%@sub2api.local' and not exists" in sql: print('') elif "select k.key from users u join api_keys k on k.user_id=u.id" in sql and "not exists" in sql: print('') elif "UPDATE users" in sql and "INSERT INTO user_subscriptions" in sql: log_dir.joinpath('prep.sql').write_text(sql, encoding='utf-8') print('') elif "INSERT INTO users" in sql and "INSERT INTO api_keys" in sql: log_dir.joinpath('create-user.sql').write_text(sql, encoding='utf-8') print('84\tuser-key-fresh') elif "SELECT json_build_object(" in sql: log_dir.joinpath('group-state.sql').write_text(sql, encoding='utf-8') print('{"group_id":7,"subscription":{"status":"active"},"key":{"group_id":7}}') else: print('') PY ;; *"sudo -n docker exec fresh-redis redis-cli DEL apikey:auth:"*" billing:balance:"*" billing:sub:"*":7"*) printf '%s\n' '3' ;; *) echo "unexpected ssh command: $cmd" >&2 exit 1 ;; esac EOF chmod +x "$fakebin/ssh" PATH="$fakebin:$PATH" \ FAKE_REMOTE_LOG_DIR="$artifact_dir" \ KEY="/does/not/matter" \ REMOTE="fake@host" \ CRM_BASE="http://127.0.0.1:18088" \ HOST_BASE="http://127.0.0.1:18087" \ CRM_HOST_BASE="http://127.0.0.1:18093" \ ROOT="$artifact_dir/root" \ ART="$artifact_dir/run" \ PACK_PATH="/tmp/openai-pack" \ REMOTE_PG_CONTAINER="fresh-pg" \ REMOTE_REDIS_CONTAINER="fresh-redis" \ UPSTREAM_KEY="upstream-test-key" \ SUBSCRIPTION_DAYS=30 \ MIN_BALANCE=10 \ SKIP_ROLLBACK=1 \ bash "$ROOT_DIR/scripts/import_remote43_provider.sh" deepseek gpt-4 UPSTREAM_KEY >/dev/null [[ -f "$psql_sql" ]] || fail "prep sql was not captured" local prep_sql prep_sql="$(cat "$psql_sql")" assert_contains "$prep_sql" "UPDATE users" assert_contains "$prep_sql" "UPDATE api_keys" assert_contains "$prep_sql" "INSERT INTO user_subscriptions" assert_contains "$prep_sql" "group_id = 7" local runtime_context invalidation_log runtime_context="$(cat "$artifact_dir/run/01-runtime-context.json")" assert_contains "$runtime_context" '"crm_host_base": "http://127.0.0.1:18093"' invalidation_log="$(cat "$artifact_dir/run/07-redis-targeted-invalidation.txt")" assert_contains "$invalidation_log" "auth_cache_key=apikey:auth:" assert_contains "$invalidation_log" "balance_cache_key=billing:balance:84" assert_contains "$invalidation_log" "subscription_cache_key=billing:sub:84:7" local subscription_state models_body chat_body subscription_state="$(cat "$artifact_dir/run/08-subscription-group-state.json")" assert_contains "$subscription_state" '"group_id":7' assert_contains "$subscription_state" '"status":"active"' models_body="$(cat "$artifact_dir/run/10-models.body.json")" chat_body="$(cat "$artifact_dir/run/12-chat.body.json")" assert_contains "$models_body" '"id":"gpt-4"' assert_contains "$chat_body" '"content":"pong"' [[ -s "$ssh_log" ]] || fail "ssh log was empty" } run_test_build_subscription_access_prep_sql run_test_real_host_acceptance_after_import_hook run_test_import_remote43_provider_subscription_prep echo "PASS: real host script regression checks"