rmi-backend/tests/unit/test_cross_chain_whale.py
opencode c762564d40 style(rmi-backend): complete lint cleanup — 1175→0 ruff errors
- Fix 71 invalid-syntax files (class-body newline-broken assignments)
- Add from/None chain to 307 B904 raise-without-from sites
- Add B008 ignore to ruff.toml (already in pyproject.toml)
- Noqa F401 on __init__.py re-exports (137 sites)
- Noqa E402 on deferred imports (63 sites)
- Bulk-add stdlib/FastAPI/project imports for F821 (127 sites)
- Replace ×→x, –→-, …→... in docstrings (4093 chars)
- Manual refactor of 5 SIM103/SIM116 patterns

Tests: 791 passed (66 deselected due to pre-existing Redis issues in test_rag.py)
Co-authored-by: opencode <opencode@rugmunch.io>
2026-07-06 15:43:20 +02:00

351 lines
12 KiB
Python

"""
Tests for cross_chain_whale.py - Cross-Chain Whale Tracker.
"""
from unittest.mock import patch
import pytest
from app.cross_chain_whale import (
CrossChainWhale,
CrossChainWhaleTracker,
WhalePosition,
WhaleTrackerReport,
_compute_concentration,
_truncate_address,
format_whale_report,
get_whale_tracker,
is_valid_address,
)
# ── Address Validation Tests ──────────────────────────────────────
def test_is_valid_address_solana():
"""Valid Solana address (base58, 32-44 chars)."""
assert is_valid_address("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v") is True
def test_is_valid_address_evm():
"""Valid EVM address (0x + 40 hex chars)."""
assert is_valid_address("0xdAC17F958D2ee523a2206206994597C13D831ec7") is True
def test_is_valid_address_too_short():
assert is_valid_address("0x1234") is False
def test_is_valid_address_empty():
assert is_valid_address("") is False
def test_is_valid_address_invalid_evm():
assert is_valid_address("0xZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ") is False
# ── Truncation Tests ─────────────────────────────────────────────
def test_truncate_address_long():
result = _truncate_address("0xdAC17F958D2ee523a2206206994597C13D831ec7")
assert result == "0xdAC1...1ec7"
assert len(result) <= 13
def test_truncate_address_short():
result = _truncate_address("abc123def456")
assert result == "abc123def456"
# ── Concentration Tests ──────────────────────────────────────────
def test_compute_concentration_empty():
assert _compute_concentration([]) == 0.0
def test_compute_concentration_single():
positions = [WhalePosition(chain="solana", token_address="x", token_symbol="TEST", balance_usd=1000.0)]
result = _compute_concentration(positions)
assert result == 100.0 # 100% concentrated
def test_compute_concentration_equal():
positions = [
WhalePosition(chain="solana", token_address="x", token_symbol="TEST", balance_usd=1000.0),
WhalePosition(chain="ethereum", token_address="y", token_symbol="TEST", balance_usd=1000.0),
]
result = _compute_concentration(positions)
assert 49.0 < result < 51.0 # ~50%
# ── Whale Tracker Report Tests ───────────────────────────────────
def test_whale_tracker_report_defaults():
report = WhaleTrackerReport(token_address="EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v")
assert report.token_symbol == ""
assert report.total_holders == 0
assert report.chains_found == []
assert report.whales == []
assert report.cross_chain_whales == []
assert report.errors == []
def test_whale_tracker_report_full():
positions = [
WhalePosition(
chain="solana",
token_address="EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
token_symbol="USDC",
balance=1000.0,
balance_usd=1000.0,
percentage=0.01,
)
]
whale = CrossChainWhale(
primary_address="4k3Dyjzvzp8e5qKBGq2ZwK5Uo5xT6sQd4u3UxLonGJcH",
total_value_usd=1000.0,
chain_count=1,
token_count=1,
positions=positions,
)
report = WhaleTrackerReport(
token_address="EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
token_symbol="USDC",
token_name="USD Coin",
total_holders=100,
chains_found=["solana"],
whales=[whale],
cross_chain_whales=[whale],
concentration_score=45.0,
)
assert report.token_symbol == "USDC"
assert report.total_holders == 100
assert len(report.whales) == 1
assert report.whales[0].chain_count == 1
# ── Formatting Tests ─────────────────────────────────────────────
def test_format_whale_report_empty():
report = WhaleTrackerReport(
token_address="EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
token_symbol="USDC",
)
output = format_whale_report(report)
assert "USDC" in output
assert "Scan:" in output
def test_format_whale_report_with_whales():
positions = [
WhalePosition(
chain="solana",
token_address="x",
token_symbol="TOKEN",
balance=50000.0,
balance_usd=50000.0,
percentage=5.0,
)
]
whale = CrossChainWhale(
primary_address="4k3Dyjzvzp8e5qKBGq2ZwK5Uo5xT6sQd4u3UxLonGJcH",
total_value_usd=50000.0,
chain_count=2,
token_count=2,
positions=positions,
risk_score=25.0,
risk_factors=["Significant cross-chain presence (3 chains)"],
)
report = WhaleTrackerReport(
token_address="x",
token_symbol="TOKEN",
chains_found=["solana", "ethereum"],
whales=[whale],
cross_chain_whales=[whale],
)
output = format_whale_report(report)
assert "Cross-Chain Whales" in output
assert "TOKEN" in output
assert "Risk: 25" in output
# ── Singleton Tests ──────────────────────────────────────────────
def test_get_whale_tracker_singleton():
t1 = get_whale_tracker()
t2 = get_whale_tracker()
assert t1 is t2
# ── Async Tests (with mocked HTTP) ───────────────────────────────
@pytest.mark.asyncio
async def test_track_token_mocked():
"""Test track_token with mocked HTTP responses."""
tracker = CrossChainWhaleTracker()
# Mock the DexScreener fetch to return a known token
async def mock_fetch_json(url, headers=None, timeout=15.0):
if "dexscreener" in url:
return {
"pairs": [
{
"chainId": "solana",
"dexId": "raydium",
"baseToken": {"symbol": "TEST", "name": "Test Token"},
"priceUsd": "1.23",
"liquidity": {"usd": 50000.0},
"fdv": 1000000.0,
"volume": {"h24": 10000.0},
}
]
}
if "birdeye" in url:
return {
"success": True,
"data": {
"holders": [
{
"address": "4k3Dyjzvzp8e5qKBGq2ZwK5Uo5xT6sQd4u3UxLonGJcH",
"balance": 50000.0,
"balanceUsd": 61500.0,
"percent": 5.0,
},
{
"address": "3xAbC...Wxyz",
"balance": 25000.0,
"balanceUsd": 30750.0,
"percent": 2.5,
},
]
},
}
return None
with patch("app.cross_chain_whale._fetch_json", side_effect=mock_fetch_json):
report = await tracker.track_token(
"TESTx0000000000000000000000000000000000000",
chains=["solana"],
)
assert report.token_symbol == "TEST"
assert report.token_name == "Test Token"
assert report.concentration_score >= 0
@pytest.mark.asyncio
async def test_track_token_graceful_degradation():
"""Test that track_token handles all API failures gracefully."""
tracker = CrossChainWhaleTracker()
async def mock_fetch_json_fail(url, headers=None, timeout=15.0):
return None
with patch("app.cross_chain_whale._fetch_json", side_effect=mock_fetch_json_fail):
report = await tracker.track_token(
"UNKNOWNx00000000000000000000000000000000000",
chains=["solana", "ethereum"],
)
# Should still return a report, not crash
assert report is not None
assert report.token_address == "UNKNOWNx00000000000000000000000000000000000"
# Symbol should be truncated address as fallback
assert report.token_symbol is not None
# ── Cross-Reference Tests ────────────────────────────────────────
@pytest.mark.asyncio
async def test_track_token_cross_chain_detection():
"""Test that a whale on multiple chains is detected as cross-chain."""
tracker = CrossChainWhaleTracker()
async def mock_fetch_json(url, headers=None, timeout=15.0):
if "dexscreener" in url:
return {
"pairs": [
{
"chainId": "solana",
"dexId": "raydium",
"baseToken": {"symbol": "CROSS", "name": "Cross Token"},
"priceUsd": "1.0",
"liquidity": {"usd": 100000.0},
"fdv": 5000000.0,
"volume": {"h24": 50000.0},
},
{
"chainId": "ethereum",
"dexId": "uniswap",
"baseToken": {"symbol": "CROSS", "name": "Cross Token"},
"priceUsd": "1.01",
"liquidity": {"usd": 200000.0},
"fdv": 5000000.0,
"volume": {"h24": 30000.0},
},
]
}
if "birdeye" in url:
return {
"success": True,
"data": {
"holders": [
{
"address": "WHALExAcross1234567890123456789012345678901",
"balance": 100000.0,
"balanceUsd": 100000.0,
"percent": 2.0,
},
]
},
}
# Simulate Etherscan holding the same whale
if "etherscan" in url or "basescan" in url:
return {
"status": "1",
"result": [
{
"address": "0xWHALE1234567890123456789012345678901234567",
"balance": "100000000000000000000000",
"tokenDecimal": "18",
"percentage": "2.0",
},
],
}
return None
with patch("app.cross_chain_whale._fetch_json", side_effect=mock_fetch_json):
report = await tracker.track_token(
"CROSSx000000000000000000000000000000000000",
chains=["solana", "ethereum"],
)
assert report.token_symbol == "CROSS"
# Should have found data on at least one chain
assert len(report.chains_with_data) > 0 or len(report.errors) == 0
# ── Edge Case Tests ──────────────────────────────────────────────
def test_whale_position_defaults():
wp = WhalePosition(chain="solana", token_address="addr", token_symbol="TKN")
assert wp.balance == 0.0
assert wp.balance_usd == 0.0
assert wp.percentage == 0.0
assert wp.rank == 0
assert wp.source == ""
def test_cross_chain_whale_default_risk():
cw = CrossChainWhale(primary_address="abc123")
assert cw.risk_score == 0.0
assert cw.risk_factors == []
assert cw.is_exchange is False
assert cw.is_scammer is False