global: fixes

This commit is contained in:
nym21
2026-04-29 16:51:01 +02:00
parent a7e41df1c6
commit 43f3be4924
101 changed files with 3074 additions and 2869 deletions

View File

@@ -0,0 +1,48 @@
"""GET /api/address/{address}"""
import pytest
from _lib import assert_same_structure, show
@pytest.fixture(params=[
"12cbQLTFMXRnSzktFkuoG3eHoMeFtpTu3S", # P2PKH — early block reward
"3D2oetdNuZUqQHPJmcMDDHYoqkyNVsFk9r", # P2SH
], ids=["p2pkh", "p2sh"])
def static_addr(request):
"""Well-known addresses that always exist."""
return request.param
def test_address_info_static(brk, mempool, static_addr):
"""Address stats structure must match for well-known addresses."""
path = f"/api/address/{static_addr}"
b = brk.get_json(path)
m = mempool.get_json(path)
show("GET", path, b, m)
assert_same_structure(b, m)
assert b["address"] == m["address"]
def test_address_info_discovered(brk, mempool, live_addrs):
"""Address stats structure must match for each discovered type."""
for atype, addr in live_addrs:
path = f"/api/address/{addr}"
b = brk.get_json(path)
m = mempool.get_json(path)
show("GET", f"{path} [{atype}]", b, m)
assert_same_structure(b, m)
assert b["address"] == m["address"]
def test_address_chain_stats_close(brk, mempool, live_addrs):
"""Chain stats values must be close for each discovered address."""
for atype, addr in live_addrs:
path = f"/api/address/{addr}"
b = brk.get_json(path)["chain_stats"]
m = mempool.get_json(path)["chain_stats"]
show("GET", f"{path} [chain_stats, {atype}]", b, m)
assert_same_structure(b, m)
assert abs(b["tx_count"] - m["tx_count"]) <= 5, (
f"{atype} tx_count: brk={b['tx_count']} vs mempool={m['tx_count']}"
)

View File

@@ -0,0 +1,49 @@
"""GET /api/address/{address}/txs"""
import pytest
from _lib import assert_same_structure, show
@pytest.fixture(params=[
"12cbQLTFMXRnSzktFkuoG3eHoMeFtpTu3S",
"3D2oetdNuZUqQHPJmcMDDHYoqkyNVsFk9r",
], ids=["p2pkh", "p2sh"])
def static_addr(request):
return request.param
def test_address_txs_static(brk, mempool, static_addr):
"""Confirmed+mempool tx list structure must match for well-known addresses."""
path = f"/api/address/{static_addr}/txs"
b = brk.get_json(path)
m = mempool.get_json(path)
show("GET", path, f"({len(b)} txs)", f"({len(m)} txs)")
assert isinstance(b, list) and isinstance(m, list)
if b and m:
assert_same_structure(b[0], m[0])
def test_address_txs_discovered(brk, mempool, live_addrs):
"""Confirmed+mempool tx list structure must match for each discovered type."""
for atype, addr in live_addrs:
path = f"/api/address/{addr}/txs"
b = brk.get_json(path)
m = mempool.get_json(path)
show("GET", f"{path} [{atype}]", f"({len(b)} txs)", f"({len(m)} txs)")
assert isinstance(b, list) and isinstance(m, list)
if b and m:
assert_same_structure(b[0], m[0])
def test_address_txs_fields(brk, mempool, live):
"""Every tx in the list must carry the core mempool.space fields."""
path = f"/api/address/{live.sample_address}/txs"
b = brk.get_json(path)
show("GET", path, f"({len(b)} txs)", "")
if not b:
pytest.skip("address has no txs in brk")
required = {"txid", "version", "locktime", "vin", "vout", "size", "weight", "fee", "status"}
for tx in b[:5]:
missing = required - set(tx.keys())
assert not missing, f"tx {tx.get('txid', '?')} missing fields: {missing}"

View File

@@ -0,0 +1,49 @@
"""GET /api/address/{address}/txs/chain"""
import pytest
from _lib import assert_same_structure, show
@pytest.fixture(params=[
"12cbQLTFMXRnSzktFkuoG3eHoMeFtpTu3S",
"3D2oetdNuZUqQHPJmcMDDHYoqkyNVsFk9r",
], ids=["p2pkh", "p2sh"])
def static_addr(request):
return request.param
def test_address_txs_chain_static(brk, mempool, static_addr):
"""Confirmed-only tx list structure must match for well-known addresses."""
path = f"/api/address/{static_addr}/txs/chain"
b = brk.get_json(path)
m = mempool.get_json(path)
show("GET", path, f"({len(b)} txs)", f"({len(m)} txs)")
assert isinstance(b, list) and isinstance(m, list)
if b and m:
assert_same_structure(b[0], m[0])
def test_address_txs_chain_discovered(brk, mempool, live_addrs):
"""Confirmed-only tx list structure must match for each discovered type."""
for atype, addr in live_addrs:
path = f"/api/address/{addr}/txs/chain"
b = brk.get_json(path)
m = mempool.get_json(path)
show("GET", f"{path} [{atype}]", f"({len(b)} txs)", f"({len(m)} txs)")
assert isinstance(b, list) and isinstance(m, list)
if b and m:
assert_same_structure(b[0], m[0])
def test_address_txs_chain_all_confirmed(brk, live):
"""Every tx returned by /txs/chain must have confirmed=True in its status."""
path = f"/api/address/{live.sample_address}/txs/chain"
b = brk.get_json(path)
show("GET", path, f"({len(b)} txs)", "")
if not b:
pytest.skip("address has no confirmed txs in brk")
unconfirmed = [t for t in b if not t.get("status", {}).get("confirmed", False)]
assert not unconfirmed, (
f"{len(unconfirmed)} unconfirmed tx(s) returned by /txs/chain"
)

View File

@@ -0,0 +1,33 @@
"""GET /api/address/{address}/txs/mempool"""
from _lib import show
def test_address_txs_mempool_sample(brk, mempool, live):
"""Mempool tx list must be an array (contents are volatile)."""
path = f"/api/address/{live.sample_address}/txs/mempool"
b = brk.get_json(path)
m = mempool.get_json(path)
show("GET", path, f"({len(b)} txs)", f"({len(m)} txs)")
assert isinstance(b, list) and isinstance(m, list)
def test_address_txs_mempool_discovered(brk, mempool, live_addrs):
"""Mempool tx list must be a (possibly empty) array for each discovered type."""
for atype, addr in live_addrs:
path = f"/api/address/{addr}/txs/mempool"
b = brk.get_json(path)
m = mempool.get_json(path)
show("GET", f"{path} [{atype}]", f"({len(b)} txs)", f"({len(m)} txs)")
assert isinstance(b, list) and isinstance(m, list)
def test_address_txs_mempool_all_unconfirmed(brk, live):
"""Every tx returned by /txs/mempool must have confirmed=False (if any)."""
path = f"/api/address/{live.sample_address}/txs/mempool"
b = brk.get_json(path)
show("GET", path, f"({len(b)} txs)", "")
confirmed = [t for t in b if t.get("status", {}).get("confirmed", False)]
assert not confirmed, (
f"{len(confirmed)} confirmed tx(s) returned by /txs/mempool"
)

View File

@@ -0,0 +1,52 @@
"""GET /api/address/{address}/utxo"""
import pytest
from _lib import assert_same_values, show
@pytest.fixture(params=[
"12cbQLTFMXRnSzktFkuoG3eHoMeFtpTu3S",
"3D2oetdNuZUqQHPJmcMDDHYoqkyNVsFk9r",
], ids=["p2pkh", "p2sh"])
def static_addr(request):
return request.param
def test_address_utxo_static(brk, mempool, static_addr):
"""UTXO list must match — same txids, values, and statuses."""
path = f"/api/address/{static_addr}/utxo"
b = brk.get_json(path)
m = mempool.get_json(path)
show("GET", path, f"({len(b)} utxos)", f"({len(m)} utxos)")
assert isinstance(b, list) and isinstance(m, list)
key = lambda u: (u.get("txid", ""), u.get("vout", 0))
b_sorted = sorted(b, key=key)
m_sorted = sorted(m, key=key)
assert_same_values(b_sorted, m_sorted)
def test_address_utxo_discovered(brk, mempool, live_addrs):
"""UTXO list must match for each discovered address type — same txids, values, and statuses."""
for atype, addr in live_addrs:
path = f"/api/address/{addr}/utxo"
b = brk.get_json(path)
m = mempool.get_json(path)
show("GET", f"{path} [{atype}]", f"({len(b)} utxos)", f"({len(m)} utxos)")
assert isinstance(b, list) and isinstance(m, list)
key = lambda u: (u.get("txid", ""), u.get("vout", 0))
assert_same_values(sorted(b, key=key), sorted(m, key=key))
def test_address_utxo_fields(brk, live):
"""Every utxo must carry the core mempool.space fields."""
path = f"/api/address/{live.sample_address}/utxo"
b = brk.get_json(path)
show("GET", path, f"({len(b)} utxos)", "")
if not b:
pytest.skip("address has no utxos in brk")
required = {"txid", "vout", "value", "status"}
for u in b[:5]:
missing = required - set(u.keys())
assert not missing, f"utxo {u.get('txid', '?')}:{u.get('vout', '?')} missing fields: {missing}"
assert isinstance(u["value"], int) and u["value"] > 0

View File

@@ -0,0 +1,53 @@
"""GET /api/v1/validate-address/{address}"""
import pytest
from _lib import assert_same_structure, assert_same_values, show
def test_validate_address_discovered(brk, mempool, live_addrs):
"""Validation of each discovered address type must match exactly."""
for atype, addr in live_addrs:
path = f"/api/v1/validate-address/{addr}"
b = brk.get_json(path)
m = mempool.get_json(path)
show("GET", f"{path} [{atype}]", b, m)
assert_same_values(b, m)
assert b["isvalid"] is True
@pytest.mark.parametrize("addr,kind", [
("1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa", "p2pkh-genesis"),
("3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy", "p2sh"),
("bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4", "p2wpkh"),
("bc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vqzk5jj0", "p2tr"),
])
def test_validate_address_static_valid(brk, mempool, addr, kind):
"""Well-known addresses across all script types must validate identically."""
path = f"/api/v1/validate-address/{addr}"
b = brk.get_json(path)
m = mempool.get_json(path)
show("GET", f"{path} [{kind}]", b, m)
assert_same_values(b, m)
assert b["isvalid"] is True
@pytest.mark.parametrize("addr,kind", [
("notanaddress123", "garbage"),
("", "empty"),
("1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNb", "bad-checksum-p2pkh"),
("bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t5", "bad-checksum-p2wpkh"),
("3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLz", "bad-checksum-p2sh"),
])
def test_validate_address_invalid(brk, mempool, addr, kind):
"""Invalid addresses must produce the same rejection structure."""
path = f"/api/v1/validate-address/{addr}"
if kind == "empty":
# An empty path segment routes to a different endpoint — skip.
pytest.skip("empty address routes to a different endpoint")
b = brk.get_json(path)
m = mempool.get_json(path)
show("GET", f"{path} [{kind}]", b, m)
assert b["isvalid"] is False
assert m["isvalid"] is False
assert_same_structure(b, m)