Files
neuron-tai/tests/test_registry_persistence.py
Dobromir Popov 7cf8d9bcf3 test descriptions
2026-07-11 22:25:30 +03:00

135 lines
4.6 KiB
Python

"""Issue 05 (A1/A5) tests: strike/ban/reputation state survives tracker restart.
Registry wallet state was RAM-only (`_LocalContractState.registry`) — a tracker
restart wiped strikes, bans, and job counts. These tests pin the event-sourced
SQLite persistence (same replication model as BillingLedger / AccountStore)
per ADR-0016 §4 and ADR-0018 §6.
"""
import json
import urllib.error
import urllib.request
import pytest
from meshnet_contracts import LocalSolanaContracts, RegistryWallet
from meshnet_tracker.server import TrackerServer
def _post_json(url: str, payload: dict) -> dict:
data = json.dumps(payload).encode()
req = urllib.request.Request(
url, data=data, headers={"Content-Type": "application/json"}, method="POST"
)
with urllib.request.urlopen(req) as r:
return json.loads(r.read())
# ---- schema: new reputation fields ----
def test_registry_wallet_has_reputation_fields():
"Registry wallet has reputation fields\n\nTags: security, wallet"
wallet = RegistryWallet()
assert wallet.reputation == 1.0
assert wallet.last_audit_ts is None
# ---- persistence across restart ----
def test_strike_survives_restart(tmp_path):
"Strike survives restart\n\nTags: general"
db = str(tmp_path / "registry.sqlite")
contracts = LocalSolanaContracts(registry_db=db)
contracts.registry.record_strike("wallet-a")
contracts.save_to_db()
reopened = LocalSolanaContracts(registry_db=db)
assert reopened.registry.get_wallet("wallet-a").strike_count == 1
def test_ban_stake_and_jobs_survive_restart(tmp_path):
"Ban stake and jobs survive restart\n\nTags: general"
db = str(tmp_path / "registry.sqlite")
contracts = LocalSolanaContracts(registry_db=db)
contracts.registry.submit_stake("wallet-b", 500)
contracts.registry.submit_slash_proof(
wallet_address="wallet-b", slash_amount=200, strike_threshold=1
)
contracts.registry.record_completed_job("wallet-b")
contracts.save_to_db()
reopened = LocalSolanaContracts(registry_db=db)
wallet = reopened.registry.get_wallet("wallet-b")
assert wallet.banned is True
assert wallet.strike_count == 1
assert wallet.stake_balance == 300
assert wallet.completed_job_count == 1
# list_wallets must reflect persisted state
assert "wallet-b" in reopened.registry.list_wallets()
def test_reputation_and_audit_ts_survive_restart(tmp_path):
"Reputation and audit ts survive restart\n\nTags: general"
db = str(tmp_path / "registry.sqlite")
contracts = LocalSolanaContracts(registry_db=db)
contracts.registry.set_reputation("wallet-c", 0.8)
contracts.registry.record_audit("wallet-c", ts=1234.5)
contracts.save_to_db()
reopened = LocalSolanaContracts(registry_db=db)
wallet = reopened.registry.get_wallet("wallet-c")
assert wallet.reputation == pytest.approx(0.8)
assert wallet.last_audit_ts == pytest.approx(1234.5)
# ---- event replication (multi-tracker gossip / future Raft) ----
def test_registry_events_replicate_between_instances():
"Registry events replicate between instances\n\nTags: general"
a = LocalSolanaContracts()
b = LocalSolanaContracts()
a.registry.record_strike("wallet-d")
a.registry.submit_stake("wallet-d", 100)
events, cursor = a.registry_log.events_since(0)
assert cursor == len(events) >= 2
applied = b.registry_log.apply_events(events)
assert applied == len(events)
assert b.registry.get_wallet("wallet-d") == a.registry.get_wallet("wallet-d")
# idempotent: re-applying the same events is a no-op
assert b.registry_log.apply_events(events) == 0
# ---- banned wallet rejected at registration after restart ----
def test_banned_wallet_rejected_at_registration_after_restart(tmp_path):
"Banned wallet rejected at registration after restart\n\nTags: security, wallet"
db = str(tmp_path / "registry.sqlite")
contracts = LocalSolanaContracts(registry_db=db)
contracts.registry.ban_wallet("wallet-evil")
contracts.save_to_db()
tracker = TrackerServer(contracts=LocalSolanaContracts(registry_db=db))
port = tracker.start()
try:
with pytest.raises(urllib.error.HTTPError) as excinfo:
_post_json(
f"http://127.0.0.1:{port}/v1/nodes/register",
{
"endpoint": "http://127.0.0.1:7900",
"model": "test-model",
"num_layers": 4,
"shard_start": 0,
"shard_end": 3,
"wallet_address": "wallet-evil",
},
)
assert excinfo.value.code == 403
finally:
tracker.stop()