solana-proxy/test_e2e.py
afd afa26d0e29 This is a bunch of untested AI slop, first pass.
Implement Solana RPC proxy with automatic failover and caching

- Add multi-provider support for 5 free Solana RPC endpoints (Alchemy, PublicNode, Helius, QuickNode, Solana Public)
- Implement automatic failover with 30-minute backoff for failed providers
- Add disk-based response caching with 100GB LRU eviction
- Create SQLite error logging with UUID tracking
- Support both HTTP JSON-RPC and WebSocket connections
- Include provider-specific authentication handling
- Add response normalization for consistent output
- Write end-to-end tests for core functionality

The proxy provides a unified endpoint that automatically routes requests to available providers, caches responses to reduce load, and logs all errors with retrievable UUIDs for debugging.
2025-07-15 22:10:06 -04:00

180 lines
5.6 KiB
Python

import asyncio
import json
import aiohttp
import pytest
from main import create_app, load_config
@pytest.fixture
async def app():
config = load_config()
config["proxy_port"] = 8546 # Use different port for testing
app = create_app(config)
return app
@pytest.fixture
async def client(app, aiohttp_client):
return await aiohttp_client(app)
async def test_http_proxy_getBalance(client):
"""Test basic HTTP RPC request"""
request_data = {
"jsonrpc": "2.0",
"id": 1,
"method": "getBalance",
"params": ["11111111111111111111111111111112"] # System program ID
}
async with client.post('/', json=request_data) as response:
assert response.status == 200
data = await response.json()
assert data["jsonrpc"] == "2.0"
assert data["id"] == 1
assert "result" in data or "error" in data
assert "_provider" in data
assert data["_provider"] != "proxy_error"
async def test_cache_functionality(client):
"""Test that responses are cached"""
request_data = {
"jsonrpc": "2.0",
"id": 1,
"method": "getBalance",
"params": ["11111111111111111111111111111112"]
}
# First request
async with client.post('/', json=request_data) as response:
data1 = await response.json()
provider1 = data1["_provider"]
cached1 = data1["_cached"]
# Second request (should be cached)
async with client.post('/', json=request_data) as response:
data2 = await response.json()
provider2 = data2["_provider"]
cached2 = data2["_cached"]
# First request shouldn't be cached, second should be
assert not cached1
assert cached2
assert provider2 == "cache"
async def test_invalid_json_request(client):
"""Test invalid JSON handling"""
async with client.post('/', data="invalid json") as response:
assert response.status == 400
data = await response.json()
assert "error" in data
assert data["error"]["code"] == -32700
async def test_missing_method(client):
"""Test missing method handling"""
request_data = {
"jsonrpc": "2.0",
"id": 1,
"params": []
}
async with client.post('/', json=request_data) as response:
assert response.status == 400
data = await response.json()
assert "error" in data
assert data["error"]["code"] == -32600
async def test_websocket_connection(client):
"""Test WebSocket connection establishment"""
try:
async with client.ws_connect('/ws') as ws:
# Send a simple subscription request
subscribe_request = {
"jsonrpc": "2.0",
"id": 1,
"method": "accountSubscribe",
"params": [
"11111111111111111111111111111112",
{"encoding": "jsonParsed"}
]
}
await ws.send_str(json.dumps(subscribe_request))
# Wait for response (with timeout)
try:
msg = await asyncio.wait_for(ws.receive(), timeout=10.0)
if msg.type == aiohttp.WSMsgType.TEXT:
response = json.loads(msg.data)
assert "result" in response or "error" in response
if "_provider" in response:
assert response["_provider"] != "proxy_error"
except asyncio.TimeoutError:
# WebSocket might timeout if providers are unavailable
# This is acceptable for the test
pass
except Exception as e:
# WebSocket connection might fail if no providers are available
# This is acceptable for testing environment
pytest.skip(f"WebSocket test skipped due to provider unavailability: {e}")
if __name__ == "__main__":
# Run tests manually if executed directly
import sys
async def run_tests():
config = load_config()
config["proxy_port"] = 8546
app = create_app(config)
# Start the application
runner = aiohttp.web.AppRunner(app)
await runner.setup()
site = aiohttp.web.TCPSite(runner, 'localhost', config["proxy_port"])
await site.start()
print(f"Test server started on port {config['proxy_port']}")
try:
# Simple manual test
async with aiohttp.ClientSession() as session:
request_data = {
"jsonrpc": "2.0",
"id": 1,
"method": "getBalance",
"params": ["11111111111111111111111111111112"]
}
async with session.post(
f"http://localhost:{config['proxy_port']}/",
json=request_data
) as response:
data = await response.json()
print("Response:", json.dumps(data, indent=2))
# Test second request (should be cached)
async with session.post(
f"http://localhost:{config['proxy_port']}/",
json=request_data
) as response:
cached_data = await response.json()
print("Cached response:", json.dumps(cached_data, indent=2))
except Exception as e:
print(f"Test error: {e}")
finally:
await runner.cleanup()
asyncio.run(run_tests())