rmi-backend/tests/test_profile_flip_detector.py

148 lines
4.6 KiB
Python

"""
Tests for Profile Flip / Identity Change Detector.
"""
import pytest
from app.profile_flip_detector import (
_check_cross_project_laundering,
_compute_identity_flip_score,
_detect_branding_flip,
_detect_wallet_behavior_flip,
_domain_age_risk,
_extract_domain,
detect_profile_flip,
)
class TestDomainRisk:
def test_suspicious_tld(self):
result = _domain_age_risk("project.xyz")
assert result["risk"] >= 0.3
assert any("suspicious_tld" in f for f in result["flags"])
def test_auto_generated_domain(self):
result = _domain_age_risk("abcdefghijklmnopqrstuvwxyz.com")
assert result["risk"] > 0.1
def test_clean_domain(self):
result = _domain_age_risk("rugmunch.io")
assert result["risk"] == 0.0
assert result["flags"] == []
class TestBrandingFlip:
def test_rebrand_keyword(self):
result = _detect_branding_flip("SafeMoon", "we are rebranding to V2")
assert result["risk"] > 0.1
assert any("rebrand_keyword" in f for f in result["flags"])
def test_generic_description(self):
result = _detect_branding_flip("TokenX", "the next 100x moon shot")
assert result["risk"] >= 0.1
def test_consistent_branding(self):
result = _detect_branding_flip("RugMunch", "RugMunch is a crypto security scanner")
assert result["risk"] == 0.0
class TestWalletBehavior:
def test_holder_to_drainer_high_risk(self):
result = _detect_wallet_behavior_flip(
{
"previous_behavior": "holder",
"current_behavior": "drainer",
"days_active": 365,
"tx_count": 100,
"eth_balance_change": -50,
}
)
assert result["risk"] >= 0.9
assert any("behavior_transition" in f for f in result["flags"])
def test_dormant_wallet_reactivated(self):
result = _detect_wallet_behavior_flip(
{
"previous_behavior": "holder",
"current_behavior": "trader",
"days_active": 200,
"tx_count": 20,
"eth_balance_change": 0,
}
)
assert any("dormant_wallet_reactivated" in f for f in result["flags"])
def test_no_data(self):
result = _detect_wallet_behavior_flip(None)
assert result["risk"] == 0.0
class TestIdentityFlipScore:
def test_critical_flip(self):
result = _compute_identity_flip_score(
social_risk=0.9,
domain_risk=0.8,
branding_risk=0.7,
wallet_behavior_risk=0.9,
cross_project_signals=["previous_scam_project:TestScam"],
age_days=5,
)
assert result["score"] >= 70
assert result["classification"] == "critical"
def test_low_risk(self):
result = _compute_identity_flip_score(
social_risk=0.0,
domain_risk=0.0,
branding_risk=0.0,
wallet_behavior_risk=0.0,
cross_project_signals=[],
age_days=365,
)
assert result["score"] < 25
assert result["classification"] == "low"
def test_young_project_bonus(self):
young = _compute_identity_flip_score(0.5, 0, 0, 0, [], 5)
old = _compute_identity_flip_score(0.5, 0, 0, 0, [], 365)
assert young["score"] > old["score"]
class TestCrossProjectLaundering:
def test_previous_scam(self):
signals = _check_cross_project_laundering(
"0xabc",
[
{"name": "SafeProject", "deployer": "0xabc", "status": "active"},
{"name": "RugProject", "deployer": "0xabc", "status": "rug_pull"},
],
)
assert len(signals) >= 1
assert any("previous_scam_project" in s for s in signals)
def test_no_laundering(self):
signals = _check_cross_project_laundering(None, [])
assert signals == []
class TestExtractDomain:
def test_full_url(self):
assert _extract_domain("https://rugmunch.io/page") == "rugmunch.io"
def test_no_domain(self):
assert _extract_domain("not-a-url") is None
class TestDetectProfileFlip:
@pytest.mark.asyncio
async def test_with_project_name(self):
result = await detect_profile_flip(
project_name="TestProject",
project_url="https://testproject.xyz",
social_handles={"twitter": "test12345"},
)
assert "score" in result
assert "classification" in result
assert "signals" in result
assert "breakdown" in result
assert isinstance(result["score"], float)