rmi-backend/tests/unit/test_governance_attack_detector.py

218 lines
7.3 KiB
Python

"""
Tests for Governance Attack & Concentration Risk Detector
==========================================================
Tests holder concentration analysis, governance parameter extraction,
flash-loan feasibility assessment, and risk scoring.
"""
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
from app.governance_attack_detector import (
DEXSCREENER_API,
LOW_QUORUM_PCT,
TOP_10_CRITICAL_PCT,
TOP_HOLDER_CRITICAL_PCT,
GovernanceParams,
_parse_holders,
_score_governance_risk,
)
class TestHolderParsing:
"""Holder data parsing tests."""
def test_parse_empty(self):
holders = _parse_holders([])
assert holders == []
def test_parse_single_holder(self):
raw = [{"address": "0xabc", "percentage": "50.5"}]
holders = _parse_holders(raw)
assert len(holders) == 1
assert holders[0].address == "0xabc"
assert holders[0].percentage == 50.5
def test_parse_exchange_label(self):
raw = [
{"address": "0xbinance1", "percentage": "10.0", "label": "Binance"},
{"address": "0xrandom", "percentage": "5.0", "label": ""},
]
holders = _parse_holders(raw)
assert holders[0].is_exchange is True
assert holders[1].is_exchange is False
def test_parse_sorted(self):
raw = [
{"address": "0xsmall", "percentage": "1.0"},
{"address": "0xbig", "percentage": "50.0"},
{"address": "0xmedium", "percentage": "10.0"},
]
holders = _parse_holders(raw)
assert holders[0].address == "0xbig"
assert holders[1].address == "0xmedium"
assert holders[2].address == "0xsmall"
def test_parse_etherscan_format(self):
raw = [{"TokenHolderAddress": "0xdef", "TokenHolderQuantity": "25.0"}]
holders = _parse_holders(raw)
assert len(holders) == 1
assert holders[0].address == "0xdef"
assert holders[0].percentage == 25.0
def test_parse_contract_flag(self):
raw = [{"address": "0xcontract", "percentage": 30, "is_contract": True}]
holders = _parse_holders(raw)
assert holders[0].is_contract is True
def test_parse_malformed_pct(self):
raw = [{"address": "0xbad", "percentage": "N/A"}]
holders = _parse_holders(raw)
assert holders[0].percentage == 0.0
class TestRiskScoring:
"""Governance risk scoring tests."""
def test_no_holders_no_gov(self):
score, level, _flags = _score_governance_risk(0.0, 0.0, None)
assert score >= 0 # No risk with no holders
assert level in ("LOW", "MEDIUM")
def test_critical_top_holder(self):
score, level, flags = _score_governance_risk(51.0, 60.0, None)
assert score >= 35
assert level == "CRITICAL" or level == "HIGH"
assert any("CRITICAL" in f for f in flags)
def test_high_top_holder(self):
score, level, _flags = _score_governance_risk(35.0, 55.0, None)
assert score >= 20
assert level == "HIGH" or level == "MEDIUM"
def test_top_10_cartel(self):
score, _level, flags = _score_governance_risk(10.0, 85.0, None)
assert score >= 20
assert any("cartel" in f.lower() for f in flags)
def test_no_timelock_critical(self):
params = GovernanceParams(is_governance_contract=True, has_timelock=False)
score, _level, flags = _score_governance_risk(5.0, 10.0, params)
assert score >= 25
assert any("CRITICAL" in f or "No timelock" in f for f in flags)
def test_low_quorum(self):
params = GovernanceParams(
is_governance_contract=True,
has_timelock=True,
quorum_threshold_pct=0.5,
timelock_delay_seconds=86400,
)
score, _level, flags = _score_governance_risk(5.0, 10.0, params)
assert score >= 20
assert any("quorum" in f.lower() for f in flags)
assert any("flash" in f.lower() for f in flags)
def test_safe_governance(self):
params = GovernanceParams(
is_governance_contract=True,
has_timelock=True,
quorum_threshold_pct=4.0,
timelock_delay_seconds=172800,
voting_period_blocks=50000,
proposal_threshold_pct=1.0,
)
score, level, _flags = _score_governance_risk(5.0, 20.0, params)
assert score < 30
assert level == "LOW" or level == "MEDIUM"
def test_very_short_voting(self):
params = GovernanceParams(
is_governance_contract=True,
has_timelock=True,
voting_period_blocks=50,
quorum_threshold_pct=5.0,
timelock_delay_seconds=86400,
)
score, _level, flags = _score_governance_risk(5.0, 10.0, params)
assert score >= 15
assert any("voting period" in f.lower() for f in flags)
def test_short_timelock(self):
params = GovernanceParams(
is_governance_contract=True,
has_timelock=True,
timelock_delay_seconds=3600, # 1 hour
quorum_threshold_pct=5.0,
)
_score, _level, flags = _score_governance_risk(5.0, 20.0, params)
assert any("timelock" in f.lower() for f in flags)
def test_flash_loan_flag(self):
params = GovernanceParams(
is_governance_contract=True,
has_timelock=True,
quorum_threshold_pct=0.3,
timelock_delay_seconds=86400,
)
score, _level, flags = _score_governance_risk(5.0, 15.0, params)
# Should have flash-loan governance attack flag
assert any("flash-loan" in f.lower() for f in flags)
assert score >= 30 # Flash-loan governance attack detected
def test_score_capped_at_100(self):
params = GovernanceParams(
is_governance_contract=True,
has_timelock=False,
quorum_threshold_pct=0.1,
timelock_delay_seconds=0,
voting_period_blocks=50,
proposal_threshold_pct=0.01,
)
score, level, _flags = _score_governance_risk(TOP_HOLDER_CRITICAL_PCT + 10, TOP_10_CRITICAL_PCT + 10, params)
assert score <= 100
assert level == "CRITICAL"
class TestGovernanceParams:
"""Governance parameters data class tests."""
def test_defaults(self):
p = GovernanceParams()
assert p.has_timelock is False
assert p.timelock_delay_seconds == 0
assert p.quorum_threshold_pct == 0.0
assert p.voting_period_blocks == 0
def test_governance_contract_detected(self):
p = GovernanceParams(
is_governance_contract=True,
quorum_threshold_pct=4.0,
voting_period_blocks=10000,
)
assert p.is_governance_contract is True
assert p.quorum_threshold_pct > 0
assert p.voting_period_blocks > 0
class TestConstants:
"""Constant threshold tests."""
def test_low_quorum_under_1_pct(self):
assert LOW_QUORUM_PCT == 1.0
def test_critical_holder_50_pct(self):
assert TOP_HOLDER_CRITICAL_PCT == 50.0
def test_top_10_critical_80_pct(self):
assert TOP_10_CRITICAL_PCT == 80.0
class TestEndpointReferences:
"""Verify API endpoint constants are well-formed."""
def test_dexscreener_url(self):
assert "{}" in DEXSCREENER_API
assert DEXSCREENER_API.startswith("https://")