""" 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://")