Files
bal-server/tests/test_endpoints.sh
svatantrya 8f764f06b2 fix: resolve PostgreSQL type mismatches and add willexecutor debug logging
- Fix get_next_address_index: use try_get::<i32> for PG SERIAL/INTEGER columns
- Fix search_tx: use try_get::<i32> for PG status column
- Fix execute_insert: parse locktime (String→i64) and in_vout (String→i32)
  before binding to PG INTEGER columns
- Fix execute_insert: bind tbl_out vout as i32, amount as String for PG
- Fix save_new_address: cast xpub i64 to i32 for PG INTEGER column
- Fix get_pending_txs: cast i64 bind params to i32, use try_get::<i32> for reads
- Fix get_stats: use try_get::<i32> for all numeric PG INTEGER columns
- Add trace logging in parse_request_transactions for xpub address matching
- Add trace logging in get_all_addresses_by_xpub for query debugging
2026-08-18 03:14:03 -04:00

288 lines
12 KiB
Bash
Executable File

#!/bin/bash
# tests/test_endpoints.sh — Integration tests for bal-server endpoints
# Usage: ./tests/test_endpoints.sh <base_url>
# Example: ./tests/test_endpoints.sh http://127.0.0.1:9133
# Returns 0 if all tests pass, 1 otherwise
set -uo pipefail
BASE_URL="${1:?Usage: $0 <base_url>}"
# Delay between requests to avoid rate limiter (actix-governor)
DELAY=1.0
PASS=0
FAIL=0
TOTAL=0
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
# Known network for testing (must be enabled in server config)
TEST_NETWORK="regtest"
# A valid 64-char hex txid that does NOT exist in the database
NONEXISTENT_TXID="551dc4841830e457b0932b81eb458a00f87e5342b70333bb92df7475d6ca90f4"
# A valid raw transaction hex (minimal valid tx for testing)
VALID_TX_HEX="020000000100000000000000000000000000000000000000000000000000000000000000000000000000ffffffff0100f2052a0100000043410496b538e853519c726a2c91e61ec112f826d36b0c8080a01e2894d24b051f05e0f03ed7790b09fd327518756ff2a55ecee44b5e08d76f994a7c5f3ffcac88bac"
# ─────────────────────────────────────────────
# Helpers
# ─────────────────────────────────────────────
log_pass() {
PASS=$((PASS + 1))
TOTAL=$((TOTAL + 1))
echo -e " ${GREEN}PASS${NC} $1"
}
log_fail() {
FAIL=$((FAIL + 1))
TOTAL=$((TOTAL + 1))
echo -e " ${RED}FAIL${NC} $1"
if [ -n "${2:-}" ]; then
echo -e " Expected: ${YELLOW}$2${NC}"
echo -e " Got: ${YELLOW}$3${NC}"
fi
}
# Perform a GET request with rate-limit delay, split into BODY and STATUS
curl_get() {
sleep "$DELAY"
local resp
resp=$(curl -s -w "\n%{http_code}" "$@")
BODY=$(echo "$resp" | sed '$d')
STATUS=$(echo "$resp" | tail -1)
}
# Perform a POST request with rate-limit delay, split into BODY and STATUS
curl_post() {
sleep "$DELAY"
local resp
resp=$(curl -s -w "\n%{http_code}" "$@")
BODY=$(echo "$resp" | sed '$d')
STATUS=$(echo "$resp" | tail -1)
}
assert_status() {
local expected="$1" actual="$2" name="$3"
if [ "$actual" = "$expected" ]; then
log_pass "$name (HTTP $actual)"
else
log_fail "$name" "HTTP $expected" "HTTP $actual"
fi
}
assert_body_contains() {
local pattern="$1" body="$2" name="$3"
if echo "$body" | grep -q "$pattern"; then
log_pass "$name (contains '$pattern')"
else
log_fail "$name" "body contains '$pattern'" "body: '$(echo "$body" | head -c 80)'"
fi
}
assert_body_not_empty() {
local body="$1" name="$2"
if [ -n "$body" ]; then
log_pass "$name (non-empty response)"
else
log_fail "$name" "non-empty body" "empty body"
fi
}
assert_json_valid() {
local body="$1" name="$2"
if echo "$body" | jq . >/dev/null 2>&1; then
log_pass "$name (valid JSON)"
else
log_fail "$name" "valid JSON" "invalid JSON: $(echo "$body" | head -c 100)"
fi
}
assert_json_has_key() {
local key="$1" body="$2" name="$3"
if echo "$body" | jq -e ".$key" >/dev/null 2>&1; then
log_pass "$name (has key '$key')"
else
log_fail "$name" "JSON has key '$key'" "key not found"
fi
}
# ─────────────────────────────────────────────
# 1. GET /
# ─────────────────────────────────────────────
echo ""
echo -e "${YELLOW}[1] GET /${NC}"
curl_get "$BASE_URL/"
assert_status "200" "$STATUS" "GET / returns 200"
assert_body_not_empty "$BODY" "GET / returns non-empty body"
# ─────────────────────────────────────────────
# 2. GET /.pub_key.pem
# ─────────────────────────────────────────────
echo ""
echo -e "${YELLOW}[2] GET /.pub_key.pem${NC}"
curl_get "$BASE_URL/.pub_key.pem"
assert_status "200" "$STATUS" "GET /.pub_key.pem returns 200"
assert_body_contains "BEGIN PUBLIC KEY" "$BODY" "GET /.pub_key.pem contains PEM header"
# ─────────────────────────────────────────────
# 3. GET /version
# ─────────────────────────────────────────────
echo ""
echo -e "${YELLOW}[3] GET /version${NC}"
curl_get "$BASE_URL/version"
assert_status "200" "$STATUS" "GET /version returns 200"
assert_body_not_empty "$BODY" "GET /version returns non-empty body"
# ─────────────────────────────────────────────
# 4. GET /{network}/info
# ─────────────────────────────────────────────
echo ""
echo -e "${YELLOW}[4] GET /$TEST_NETWORK/info${NC}"
curl_get "$BASE_URL/$TEST_NETWORK/info"
assert_status "200" "$STATUS" "GET /$TEST_NETWORK/info returns 200"
assert_json_valid "$BODY" "GET /$TEST_NETWORK/info returns valid JSON"
assert_json_has_key "chain" "$BODY" "GET /$TEST_NETWORK/info has 'chain' key"
assert_json_has_key "address" "$BODY" "GET /$TEST_NETWORK/info has 'address' key"
assert_json_has_key "base_fee" "$BODY" "GET /$TEST_NETWORK/info has 'base_fee' key"
assert_json_has_key "info" "$BODY" "GET /$TEST_NETWORK/info has 'info' key"
assert_json_has_key "version" "$BODY" "GET /$TEST_NETWORK/info has 'version' key"
echo ""
echo -e "${YELLOW}[4b] GET /invalidnet/info${NC}"
curl_get "$BASE_URL/invalidnet/info"
assert_status "404" "$STATUS" "GET /invalidnet/info returns 404"
assert_body_contains "Unknown network" "$BODY" "GET /invalidnet/info body says 'Unknown network'"
# ─────────────────────────────────────────────
# 5. GET /{network}/stats
# ─────────────────────────────────────────────
echo ""
echo -e "${YELLOW}[5] GET /$TEST_NETWORK/stats${NC}"
curl_get "$BASE_URL/$TEST_NETWORK/stats"
assert_status "200" "$STATUS" "GET /$TEST_NETWORK/stats returns 200"
assert_json_valid "$BODY" "GET /$TEST_NETWORK/stats returns valid JSON"
echo ""
echo -e "${YELLOW}[5b] GET /invalidnet/stats${NC}"
curl_get "$BASE_URL/invalidnet/stats"
assert_status "404" "$STATUS" "GET /invalidnet/stats returns 404"
assert_body_contains "Unknown network" "$BODY" "GET /invalidnet/stats body says 'Unknown network'"
# ─────────────────────────────────────────────
# 6. POST /searchtx
# ─────────────────────────────────────────────
echo ""
echo -e "${YELLOW}[6] POST /searchtx — empty body${NC}"
curl_post -X POST -d "" "$BASE_URL/searchtx"
assert_status "400" "$STATUS" "POST /searchtx empty body returns 400"
assert_body_contains "Invalid txid" "$BODY" "POST /searchtx empty body says 'Invalid txid'"
echo ""
echo -e "${YELLOW}[6b] POST /searchtx — short txid${NC}"
curl_post -X POST -d "abc123" "$BASE_URL/searchtx"
assert_status "400" "$STATUS" "POST /searchtx short txid returns 400"
assert_body_contains "Invalid txid" "$BODY" "POST /searchtx short txid says 'Invalid txid'"
echo ""
echo -e "${YELLOW}[6c] POST /searchtx — non-hex txid${NC}"
curl_post -X POST -d "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz" "$BASE_URL/searchtx"
assert_status "400" "$STATUS" "POST /searchtx non-hex txid returns 400"
assert_body_contains "Invalid txid" "$BODY" "POST /searchtx non-hex txid says 'Invalid txid'"
echo ""
echo -e "${YELLOW}[6d] POST /searchtx — nonexistent valid hex txid${NC}"
curl_post -X POST -d "$NONEXISTENT_TXID" "$BASE_URL/searchtx"
# When txid is valid hex but not in DB: either 200 with empty JSON or 404
if [ "$STATUS" = "200" ] || [ "$STATUS" = "404" ]; then
log_pass "POST /searchtx nonexistent txid returns HTTP $STATUS (expected)"
else
log_fail "POST /searchtx nonexistent txid" "HTTP 200 or 404" "HTTP $STATUS"
fi
# Verify Content-Type is application/json for successful searchtx responses
if [ "$STATUS" = "200" ]; then
CONTENT_TYPE=$(curl -s -D - --max-time 3 -X POST -d "$NONEXISTENT_TXID" "$BASE_URL/searchtx" 2>/dev/null | grep -i "^content-type:" | tr -d '\r')
if echo "$CONTENT_TYPE" | grep -q "application/json"; then
log_pass "POST /searchtx Content-Type is application/json"
else
log_fail "POST /searchtx Content-Type" "application/json" "$CONTENT_TYPE"
fi
fi
echo ""
echo -e "${YELLOW}[6e] POST /searchtx — binary non-UTF8 body${NC}"
curl_post -X POST --data-binary $'\xff\xfe\xfd' "$BASE_URL/searchtx"
assert_status "400" "$STATUS" "POST /searchtx binary body returns 400"
assert_body_contains "Invalid UTF-8 body" "$BODY" "POST /searchtx binary body says 'Invalid UTF-8 body'"
# ─────────────────────────────────────────────
# 7. POST /{network}/pushtxs
# ─────────────────────────────────────────────
echo ""
echo -e "${YELLOW}[7] POST /invalidnet/pushtxs${NC}"
curl_post -X POST -d "" "$BASE_URL/invalidnet/pushtxs"
assert_status "404" "$STATUS" "POST /invalidnet/pushtxs returns 404"
assert_body_contains "Unknown network" "$BODY" "POST /invalidnet/pushtxs body says 'Unknown network'"
echo ""
echo -e "${YELLOW}[7b] POST /$TEST_NETWORK/pushtxs — empty body${NC}"
curl_post -X POST -d "" "$BASE_URL/$TEST_NETWORK/pushtxs"
assert_status "200" "$STATUS" "POST /$TEST_NETWORK/pushtxs empty body returns 200"
echo ""
echo -e "${YELLOW}[7c] POST /$TEST_NETWORK/pushtxs — valid hex tx${NC}"
curl_post -X POST -d "$VALID_TX_HEX" "$BASE_URL/$TEST_NETWORK/pushtxs"
# Server returns 200 with "thx" or "already present" or "Bad data received"
if [ "$STATUS" = "200" ]; then
if echo "$BODY" | grep -qE "^(thx|already present|Bad data received)$"; then
log_pass "POST /$TEST_NETWORK/pushtxs valid hex returns HTTP 200 with '$BODY'"
else
log_pass "POST /$TEST_NETWORK/pushtxs valid hex returns HTTP 200 (body: $(echo "$BODY" | head -c 50))"
fi
else
log_fail "POST /$TEST_NETWORK/pushtxs valid hex" "HTTP 200" "HTTP $STATUS"
fi
echo ""
echo -e "${YELLOW}[7d] POST /$TEST_NETWORK/pushtxs — non-hex garbage${NC}"
curl_post -X POST -d "not-a-transaction" "$BASE_URL/$TEST_NETWORK/pushtxs"
assert_status "200" "$STATUS" "POST /$TEST_NETWORK/pushtxs garbage returns 200"
echo ""
echo -e "${YELLOW}[7e] POST /$TEST_NETWORK/pushtxs — binary non-UTF8 body${NC}"
curl_post -X POST --data-binary $'\xff\xfe\xfd' "$BASE_URL/$TEST_NETWORK/pushtxs"
# Invalid UTF-8 should be rejected
if [ "$STATUS" = "400" ]; then
assert_body_contains "Invalid UTF-8 body" "$BODY" "POST /$TEST_NETWORK/pushtxs binary body says 'Invalid UTF-8 body'"
else
# Server may accept binary as latin-1 and skip invalid lines gracefully
log_pass "POST /$TEST_NETWORK/pushtxs binary body returns HTTP $STATUS (skipped gracefully)"
fi
# ─────────────────────────────────────────────
# 8. GET on unknown routes
# ─────────────────────────────────────────────
echo ""
echo -e "${YELLOW}[8] GET /nonexistent${NC}"
curl_get "$BASE_URL/nonexistent"
assert_status "404" "$STATUS" "GET /nonexistent returns 404"
# ─────────────────────────────────────────────
# Summary
# ─────────────────────────────────────────────
echo ""
echo "========================================="
echo -e "Results: ${GREEN}$PASS passed${NC}, ${RED}$FAIL failed${NC} ($TOTAL total)"
echo "========================================="
if [ "$FAIL" -gt 0 ]; then
exit 1
fi
exit 0