# Author: Simon-Pierre Boucher # Mail: contact@spboucher.ai """Unit checks for the RPC layer (plain asserts — run with python).""" import pathlib import sys import time sys.path.insert(0, str(pathlib.Path(__file__).parent.parent)) from indexer.rpc import Endpoint, RpcPool, TokenBucket, _is_rate_limit def test_token_bucket_paces(): b = TokenBucket(rate=10, burst=2) t0 = time.monotonic() for _ in range(6): # burst of 2 free, then 4 paced at 10/s ≈ 0.4s b.acquire() elapsed = time.monotonic() - t0 assert 0.3 <= elapsed <= 1.0, f"pacing off: {elapsed:.2f}s" def test_rate_limit_classification(): # failover-worthy (rate limits dressed as JSON-RPC errors) assert _is_rate_limit("rate limited") assert _is_rate_limit("Too many requests, slow down") assert _is_rate_limit("You've reached the usage limit for your current plan") # caller-worthy (query too big — range halving is the right response) assert not _is_rate_limit("query returned more than 10000 results") assert not _is_rate_limit("block range is too wide") assert not _is_rate_limit("Log response size exceeded") assert not _is_rate_limit("limit exceeded") # BSC getLogs cap — ambiguous, caller decides def test_health_scoring_and_cooldown(): e = Endpoint("https://example.invalid") assert e.score == 1.0 and e.available() e.record(False) e.record(False) assert e.score < 1.0 and not e.available() # cooling down assert e.consec_fail == 2 e.cooldown_until = 0 # simulate recovery e.record(True, latency_ms=50) assert e.consec_fail == 0 and e.available() def test_pool_prefers_healthy_endpoint(): pool = RpcPool(["https://a.invalid", "https://b.invalid"]) a, b = pool.endpoints a.record(False) # a benched + score down assert pool._pick() is b assert pool.current_url == "https://b.invalid" def test_endpoint_spec_dict(): e = Endpoint({"url": "https://x.invalid", "rps": 1, "burst": 3}) assert e.bucket.rate == 1 and e.bucket.burst == 3 if __name__ == "__main__": for name, fn in sorted(globals().items()): if name.startswith("test_"): fn() print(f"ok {name}") print("all rpc tests passed")