Files
polymarket-insider-tracker/tests/profiler/test_models.py
T
Patrick Selamy 1f4f1fa557 fix: resolve linting and formatting issues for CI
- Use contextlib.suppress instead of try/except/pass (SIM105)
- Prefix unused fixture arguments with underscore (ARG002)
- Replace asyncio.TimeoutError with TimeoutError (UP041)
- Apply ruff formatting to all files

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-04 17:09:14 -05:00

426 lines
14 KiB
Python

"""Tests for the profiler data models."""
from datetime import UTC, datetime, timedelta
from decimal import Decimal
import pytest
from polymarket_insider_tracker.profiler.models import Transaction, WalletInfo, WalletProfile
class TestTransaction:
"""Tests for the Transaction dataclass."""
@pytest.fixture
def sample_transaction(self) -> Transaction:
"""Create a sample transaction."""
return Transaction(
hash="0xabc123",
block_number=50000000,
timestamp=datetime(2026, 1, 4, 12, 0, 0, tzinfo=UTC),
from_address="0xsender",
to_address="0xreceiver",
value=Decimal("1000000000000000000"), # 1 MATIC
gas_used=21000,
gas_price=Decimal("50000000000"), # 50 Gwei
)
def test_transaction_creation(self, sample_transaction: Transaction) -> None:
"""Test creating a transaction."""
assert sample_transaction.hash == "0xabc123"
assert sample_transaction.block_number == 50000000
assert sample_transaction.from_address == "0xsender"
assert sample_transaction.to_address == "0xreceiver"
def test_value_matic(self, sample_transaction: Transaction) -> None:
"""Test value in MATIC conversion."""
assert sample_transaction.value_matic == Decimal("1")
def test_value_matic_fractional(self) -> None:
"""Test fractional MATIC value."""
tx = Transaction(
hash="0x123",
block_number=1,
timestamp=datetime.now(UTC),
from_address="0x1",
to_address="0x2",
value=Decimal("500000000000000000"), # 0.5 MATIC
gas_used=21000,
gas_price=Decimal("50000000000"),
)
assert tx.value_matic == Decimal("0.5")
def test_gas_cost_wei(self, sample_transaction: Transaction) -> None:
"""Test gas cost in Wei."""
expected = 21000 * 50000000000
assert sample_transaction.gas_cost_wei == Decimal(expected)
def test_gas_cost_matic(self, sample_transaction: Transaction) -> None:
"""Test gas cost in MATIC."""
# 21000 * 50 Gwei = 1050000 Gwei = 0.00105 MATIC
expected = Decimal("21000") * Decimal("50000000000") / Decimal("1000000000000000000")
assert sample_transaction.gas_cost_matic == expected
def test_transaction_frozen(self, sample_transaction: Transaction) -> None:
"""Test that transaction is immutable."""
with pytest.raises(AttributeError):
sample_transaction.hash = "0xnew" # type: ignore[misc]
def test_transaction_no_recipient(self) -> None:
"""Test transaction with no recipient (contract creation)."""
tx = Transaction(
hash="0x123",
block_number=1,
timestamp=datetime.now(UTC),
from_address="0x1",
to_address=None,
value=Decimal("0"),
gas_used=100000,
gas_price=Decimal("50000000000"),
)
assert tx.to_address is None
class TestWalletInfo:
"""Tests for the WalletInfo dataclass."""
@pytest.fixture
def sample_wallet(self) -> WalletInfo:
"""Create a sample wallet info."""
return WalletInfo(
address="0xwallet123",
transaction_count=100,
balance_wei=Decimal("5000000000000000000"), # 5 MATIC
first_transaction=None,
)
@pytest.fixture
def wallet_with_transaction(self) -> WalletInfo:
"""Create a wallet with first transaction."""
first_tx = Transaction(
hash="0xfirst",
block_number=1000000,
timestamp=datetime.now(UTC) - timedelta(days=365),
from_address="0xfaucet",
to_address="0xwallet123",
value=Decimal("1000000000000000000"),
gas_used=21000,
gas_price=Decimal("50000000000"),
)
return WalletInfo(
address="0xwallet123",
transaction_count=100,
balance_wei=Decimal("5000000000000000000"),
first_transaction=first_tx,
)
def test_wallet_creation(self, sample_wallet: WalletInfo) -> None:
"""Test creating wallet info."""
assert sample_wallet.address == "0xwallet123"
assert sample_wallet.transaction_count == 100
assert sample_wallet.balance_wei == Decimal("5000000000000000000")
def test_balance_matic(self, sample_wallet: WalletInfo) -> None:
"""Test balance in MATIC conversion."""
assert sample_wallet.balance_matic == Decimal("5")
def test_is_fresh_false(self, sample_wallet: WalletInfo) -> None:
"""Test that wallet with many transactions is not fresh."""
assert sample_wallet.is_fresh is False
def test_is_fresh_true(self) -> None:
"""Test that wallet with few transactions is fresh."""
wallet = WalletInfo(
address="0xnewwallet",
transaction_count=5,
balance_wei=Decimal("1000000000000000000"),
)
assert wallet.is_fresh is True
def test_is_fresh_boundary(self) -> None:
"""Test fresh wallet boundary (10 transactions)."""
wallet_9 = WalletInfo(
address="0x1",
transaction_count=9,
balance_wei=Decimal("0"),
)
wallet_10 = WalletInfo(
address="0x2",
transaction_count=10,
balance_wei=Decimal("0"),
)
assert wallet_9.is_fresh is True
assert wallet_10.is_fresh is False
def test_wallet_age_days_no_transaction(self, sample_wallet: WalletInfo) -> None:
"""Test wallet age when no first transaction."""
assert sample_wallet.wallet_age_days is None
def test_wallet_age_days_with_transaction(self, wallet_with_transaction: WalletInfo) -> None:
"""Test wallet age calculation."""
age = wallet_with_transaction.wallet_age_days
assert age is not None
# Should be approximately 365 days
assert 364 < age < 366
def test_wallet_frozen(self, sample_wallet: WalletInfo) -> None:
"""Test that wallet info is immutable."""
with pytest.raises(AttributeError):
sample_wallet.address = "0xnew" # type: ignore[misc]
def test_wallet_zero_balance(self) -> None:
"""Test wallet with zero balance."""
wallet = WalletInfo(
address="0xempty",
transaction_count=0,
balance_wei=Decimal("0"),
)
assert wallet.balance_matic == Decimal("0")
assert wallet.is_fresh is True
def test_wallet_very_small_balance(self) -> None:
"""Test wallet with very small balance."""
wallet = WalletInfo(
address="0xdust",
transaction_count=1,
balance_wei=Decimal("1"), # 1 Wei
)
# Should be a very small fraction
expected = Decimal("1") / Decimal("1000000000000000000")
assert wallet.balance_matic == expected
class TestTransactionEquality:
"""Tests for transaction equality and hashing."""
def test_equal_transactions(self) -> None:
"""Test that identical transactions are equal."""
tx1 = Transaction(
hash="0xabc",
block_number=1,
timestamp=datetime(2026, 1, 1, tzinfo=UTC),
from_address="0x1",
to_address="0x2",
value=Decimal("1000"),
gas_used=21000,
gas_price=Decimal("50"),
)
tx2 = Transaction(
hash="0xabc",
block_number=1,
timestamp=datetime(2026, 1, 1, tzinfo=UTC),
from_address="0x1",
to_address="0x2",
value=Decimal("1000"),
gas_used=21000,
gas_price=Decimal("50"),
)
assert tx1 == tx2
def test_different_transactions(self) -> None:
"""Test that different transactions are not equal."""
tx1 = Transaction(
hash="0xabc",
block_number=1,
timestamp=datetime(2026, 1, 1, tzinfo=UTC),
from_address="0x1",
to_address="0x2",
value=Decimal("1000"),
gas_used=21000,
gas_price=Decimal("50"),
)
tx2 = Transaction(
hash="0xdef", # Different hash
block_number=1,
timestamp=datetime(2026, 1, 1, tzinfo=UTC),
from_address="0x1",
to_address="0x2",
value=Decimal("1000"),
gas_used=21000,
gas_price=Decimal("50"),
)
assert tx1 != tx2
def test_transaction_hashable(self) -> None:
"""Test that transactions can be used in sets."""
tx = Transaction(
hash="0xabc",
block_number=1,
timestamp=datetime(2026, 1, 1, tzinfo=UTC),
from_address="0x1",
to_address="0x2",
value=Decimal("1000"),
gas_used=21000,
gas_price=Decimal("50"),
)
tx_set = {tx}
assert tx in tx_set
class TestWalletProfile:
"""Tests for the WalletProfile dataclass."""
@pytest.fixture
def fresh_profile(self) -> WalletProfile:
"""Create a fresh wallet profile."""
return WalletProfile(
address="0xfresh",
nonce=2,
first_seen=datetime.now(UTC) - timedelta(hours=6),
age_hours=6.0,
is_fresh=True,
total_tx_count=2,
matic_balance=Decimal("1000000000000000000"), # 1 MATIC
usdc_balance=Decimal("1000000"), # 1 USDC
fresh_threshold=5,
)
@pytest.fixture
def old_profile(self) -> WalletProfile:
"""Create an old wallet profile."""
return WalletProfile(
address="0xold",
nonce=500,
first_seen=datetime.now(UTC) - timedelta(days=365),
age_hours=365 * 24,
is_fresh=False,
total_tx_count=500,
matic_balance=Decimal("100000000000000000000"), # 100 MATIC
usdc_balance=Decimal("10000000000"), # 10000 USDC
fresh_threshold=5,
)
def test_profile_creation(self, fresh_profile: WalletProfile) -> None:
"""Test creating a wallet profile."""
assert fresh_profile.address == "0xfresh"
assert fresh_profile.nonce == 2
assert fresh_profile.is_fresh is True
assert fresh_profile.age_hours == 6.0
def test_age_days(self, fresh_profile: WalletProfile) -> None:
"""Test age_days property."""
assert fresh_profile.age_days == 0.25 # 6 hours = 0.25 days
def test_age_days_none(self) -> None:
"""Test age_days when age_hours is None."""
profile = WalletProfile(
address="0xnew",
nonce=0,
first_seen=None,
age_hours=None,
is_fresh=True,
total_tx_count=0,
matic_balance=Decimal("0"),
usdc_balance=Decimal("0"),
)
assert profile.age_days is None
def test_matic_balance_formatted(self, fresh_profile: WalletProfile) -> None:
"""Test MATIC balance formatting."""
assert fresh_profile.matic_balance_formatted == Decimal("1")
def test_usdc_balance_formatted(self, fresh_profile: WalletProfile) -> None:
"""Test USDC balance formatting."""
assert fresh_profile.usdc_balance_formatted == Decimal("1")
def test_is_brand_new(self) -> None:
"""Test is_brand_new property."""
brand_new = WalletProfile(
address="0xnew",
nonce=0,
first_seen=None,
age_hours=None,
is_fresh=True,
total_tx_count=0,
matic_balance=Decimal("0"),
usdc_balance=Decimal("0"),
)
assert brand_new.is_brand_new is True
not_brand_new = WalletProfile(
address="0xold",
nonce=1,
first_seen=datetime.now(UTC),
age_hours=1.0,
is_fresh=True,
total_tx_count=1,
matic_balance=Decimal("0"),
usdc_balance=Decimal("0"),
)
assert not_brand_new.is_brand_new is False
def test_freshness_score_brand_new(self) -> None:
"""Test freshness score for brand new wallet."""
profile = WalletProfile(
address="0xnew",
nonce=0,
first_seen=None,
age_hours=None,
is_fresh=True,
total_tx_count=0,
matic_balance=Decimal("0"),
usdc_balance=Decimal("0"),
fresh_threshold=5,
)
# nonce_score = 1.0 (0/5 = 0, 1-0 = 1)
# age_score = 1.0 (None = assumed new)
# score = 0.6 * 1.0 + 0.4 * 1.0 = 1.0
assert profile.freshness_score == 1.0
def test_freshness_score_old_wallet(self, old_profile: WalletProfile) -> None:
"""Test freshness score for old wallet."""
# nonce_score = max(0, 1 - 500/5) = 0
# age_score = max(0, 1 - 8760/48) = 0
# score = 0
assert old_profile.freshness_score == 0.0
def test_freshness_score_moderate(self) -> None:
"""Test freshness score for moderately fresh wallet."""
profile = WalletProfile(
address="0xmoderate",
nonce=2,
first_seen=datetime.now(UTC) - timedelta(hours=24),
age_hours=24.0,
is_fresh=True,
total_tx_count=2,
matic_balance=Decimal("0"),
usdc_balance=Decimal("0"),
fresh_threshold=5,
)
# nonce_score = 1 - 2/5 = 0.6
# age_score = 1 - 24/48 = 0.5
# score = 0.6 * 0.6 + 0.4 * 0.5 = 0.36 + 0.2 = 0.56
assert profile.freshness_score == pytest.approx(0.56)
def test_profile_frozen(self, fresh_profile: WalletProfile) -> None:
"""Test that wallet profile is immutable."""
with pytest.raises(AttributeError):
fresh_profile.nonce = 100 # type: ignore[misc]
def test_analyzed_at_default(self) -> None:
"""Test that analyzed_at has a default."""
before = datetime.now(UTC)
profile = WalletProfile(
address="0x1",
nonce=0,
first_seen=None,
age_hours=None,
is_fresh=True,
total_tx_count=0,
matic_balance=Decimal("0"),
usdc_balance=Decimal("0"),
)
after = datetime.now(UTC)
assert before <= profile.analyzed_at <= after