Files
brk/scripts/mempool_compat/test_mining.py
2026-04-12 18:00:02 +02:00

240 lines
9.2 KiB
Python

"""
Mining endpoint compatibility tests.
Endpoints covered:
GET /api/v1/mining/pools
GET /api/v1/mining/pools/{period}
GET /api/v1/mining/pool/{slug}
GET /api/v1/mining/pool/{slug}/hashrate
GET /api/v1/mining/pool/{slug}/blocks
GET /api/v1/mining/pool/{slug}/blocks/{height}
GET /api/v1/mining/hashrate/{period}
GET /api/v1/mining/hashrate/pools/{period}
GET /api/v1/mining/difficulty-adjustments/{period}
GET /api/v1/mining/reward-stats/{block_count}
GET /api/v1/mining/blocks/fees/{period}
GET /api/v1/mining/blocks/rewards/{period}
GET /api/v1/mining/blocks/fee-rates/{period}
GET /api/v1/mining/blocks/sizes-weights/{period}
GET /api/v1/mining/blocks/timestamp/{timestamp}
"""
import pytest
from conftest import show, assert_same_structure
@pytest.fixture(scope="module")
def pool_slugs(mempool):
"""Discover the top 3 active pool slugs from the last week."""
data = mempool.get_json("/api/v1/mining/pools/1w")
pools = data.get("pools", []) if isinstance(data, dict) else []
slugs = [p["slug"] for p in pools if p.get("blockCount", 0) > 0][:3]
return slugs or ["foundryusa"]
@pytest.fixture(scope="module")
def pool_slug(pool_slugs):
return pool_slugs[0]
# ── /api/v1/mining/pools ─────────────────────────────────────────────
def test_mining_pools_list(brk, mempool):
"""Pool list must have the same element structure."""
path = "/api/v1/mining/pools"
b = brk.get_json(path)
m = mempool.get_json(path)
show("GET", path, b[:3] if isinstance(b, list) else b, m[:3] if isinstance(m, list) else m)
assert_same_structure(b, m)
@pytest.mark.parametrize("period", ["24h", "3d", "1w", "1m", "3m", "6m", "1y", "2y", "3y", "all"])
def test_mining_pools_by_period(brk, mempool, period):
"""Pool stats for a time period must have the same structure."""
path = f"/api/v1/mining/pools/{period}"
b = brk.get_json(path)
m = mempool.get_json(path)
show("GET", path, _summary(b), _summary(m))
assert_same_structure(b, m)
# ── /api/v1/mining/pool/{slug} ───────────────────────────────────────
def test_mining_pool_detail(brk, mempool, pool_slugs):
"""Pool detail must have the same structure for top pools."""
for slug in pool_slugs:
path = f"/api/v1/mining/pool/{slug}"
b = brk.get_json(path)
m = mempool.get_json(path)
show("GET", path, _summary(b), _summary(m))
assert_same_structure(b, m)
def test_mining_pool_hashrate(brk, mempool, pool_slugs):
"""Pool hashrate history must have the same structure for top pools."""
for slug in pool_slugs:
path = f"/api/v1/mining/pool/{slug}/hashrate"
b = brk.get_json(path)
m = mempool.get_json(path)
show("GET", path, _summary(b), _summary(m))
assert_same_structure(b, m)
def test_mining_pool_blocks(brk, mempool, pool_slugs):
"""Recent blocks by pool must have the same element structure."""
for slug in pool_slugs:
path = f"/api/v1/mining/pool/{slug}/blocks"
b = brk.get_json(path)
m = mempool.get_json(path)
show("GET", path, f"({len(b)} blocks)", f"({len(m)} blocks)")
assert isinstance(b, list) and isinstance(m, list)
if b and m:
assert_same_structure(b[0], m[0])
def test_mining_pool_blocks_at_height(brk, mempool, pool_slug, live):
"""Pool blocks before various heights must have the same element structure."""
for block in live.blocks[::2]: # every other block
path = f"/api/v1/mining/pool/{pool_slug}/blocks/{block.height}"
b = brk.get_json(path)
m = mempool.get_json(path)
show("GET", path, f"({len(b)} blocks)", f"({len(m)} blocks)")
assert isinstance(b, list) and isinstance(m, list)
if b and m:
assert_same_structure(b[0], m[0])
# ── /api/v1/mining/hashrate ──────────────────────────────────────────
@pytest.mark.parametrize("period", ["24h", "3d", "1w", "1m", "3m", "6m", "1y", "2y", "3y"])
def test_mining_hashrate(brk, mempool, period):
"""Network hashrate + difficulty must have the same structure."""
path = f"/api/v1/mining/hashrate/{period}"
b = brk.get_json(path)
m = mempool.get_json(path)
show("GET", path, _summary(b), _summary(m))
assert_same_structure(b, m)
# ── /api/v1/mining/hashrate/pools ────────────────────────────────────
@pytest.mark.parametrize("period", ["24h", "3d", "1w", "1m", "3m", "1y"])
def test_mining_hashrate_pools(brk, mempool, period):
"""Per-pool hashrate must have the same structure."""
path = f"/api/v1/mining/hashrate/pools/{period}"
b = brk.get_json(path)
m = mempool.get_json(path)
show("GET", path, _summary(b), _summary(m))
assert_same_structure(b, m)
# ── /api/v1/mining/difficulty-adjustments ─────────────────────────────
@pytest.mark.parametrize("period", ["24h", "3d", "1w", "1m", "3m", "6m", "1y", "2y", "3y"])
def test_mining_difficulty_adjustments(brk, mempool, period):
"""Historical difficulty adjustments must have the same structure."""
path = f"/api/v1/mining/difficulty-adjustments/{period}"
b = brk.get_json(path)
m = mempool.get_json(path)
show("GET", path, _summary(b), _summary(m))
assert_same_structure(b, m)
# ── /api/v1/mining/reward-stats ──────────────────────────────────────
@pytest.mark.parametrize("block_count", [10, 100, 500])
def test_mining_reward_stats(brk, mempool, block_count):
"""Reward stats must have the same structure."""
path = f"/api/v1/mining/reward-stats/{block_count}"
b = brk.get_json(path)
m = mempool.get_json(path)
show("GET", path, b, m)
assert_same_structure(b, m)
# ── /api/v1/mining/blocks/fees ───────────────────────────────────────
@pytest.mark.parametrize("period", ["24h", "3d", "1w", "1m", "3m", "6m", "1y"])
def test_mining_blocks_fees(brk, mempool, period):
"""Average block fees must have the same element structure."""
path = f"/api/v1/mining/blocks/fees/{period}"
b = brk.get_json(path)
m = mempool.get_json(path)
show("GET", path, _summary(b), _summary(m))
assert_same_structure(b, m)
# ── /api/v1/mining/blocks/rewards ────────────────────────────────────
@pytest.mark.parametrize("period", ["24h", "3d", "1w", "1m", "3m", "6m", "1y"])
def test_mining_blocks_rewards(brk, mempool, period):
"""Average block rewards must have the same element structure."""
path = f"/api/v1/mining/blocks/rewards/{period}"
b = brk.get_json(path)
m = mempool.get_json(path)
show("GET", path, _summary(b), _summary(m))
assert_same_structure(b, m)
# ── /api/v1/mining/blocks/fee-rates ──────────────────────────────────
@pytest.mark.parametrize("period", ["24h", "3d", "1w", "1m", "3m", "6m", "1y"])
def test_mining_blocks_fee_rates(brk, mempool, period):
"""Block fee-rate percentiles must have the same element structure."""
path = f"/api/v1/mining/blocks/fee-rates/{period}"
b = brk.get_json(path)
m = mempool.get_json(path)
show("GET", path, _summary(b), _summary(m))
assert_same_structure(b, m)
# ── /api/v1/mining/blocks/sizes-weights ──────────────────────────────
@pytest.mark.parametrize("period", ["24h", "3d", "1w", "1m", "3m", "6m", "1y"])
def test_mining_blocks_sizes_weights(brk, mempool, period):
"""Block sizes and weights must have the same structure."""
path = f"/api/v1/mining/blocks/sizes-weights/{period}"
b = brk.get_json(path)
m = mempool.get_json(path)
show("GET", path, _summary(b), _summary(m))
assert_same_structure(b, m)
# ── /api/v1/mining/blocks/timestamp ──────────────────────────────────
def test_mining_blocks_timestamp(brk, mempool, live):
"""Block lookup by timestamp must have the same structure for various eras."""
for block in live.blocks:
# Get the block timestamp from brk
info = brk.get_json(f"/api/block/{block.hash}")
ts = info["timestamp"]
path = f"/api/v1/mining/blocks/timestamp/{ts}"
b = brk.get_json(path)
m = mempool.get_json(path)
show("GET", path, b, m)
assert_same_structure(b, m)
# ── helpers ──────────────────────────────────────────────────────────
def _summary(data):
"""Short description of a response for the show() call."""
if isinstance(data, list):
return f"({len(data)} items)"
if isinstance(data, dict):
return f"(keys: {list(data.keys())})"
return str(data)