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>
This commit is contained in:
Patrick Selamy
2026-01-04 17:09:14 -05:00
co-authored by Claude Opus 4.5
parent a15086688a
commit 1f4f1fa557
31 changed files with 180 additions and 311 deletions
+7 -21
View File
@@ -75,9 +75,7 @@ class TestDiscordChannel:
@pytest.mark.asyncio
async def test_send_success(self, sample_alert: FormattedAlert) -> None:
"""Test successful Discord message send."""
channel = DiscordChannel(
webhook_url="https://discord.com/api/webhooks/123/abc"
)
channel = DiscordChannel(webhook_url="https://discord.com/api/webhooks/123/abc")
with patch("httpx.AsyncClient") as mock_client_class:
mock_response = MagicMock()
@@ -310,9 +308,7 @@ class TestAlertDispatcher:
mock_telegram_channel: MagicMock,
) -> None:
"""Test dispatcher initialization."""
dispatcher = AlertDispatcher(
channels=[mock_discord_channel, mock_telegram_channel]
)
dispatcher = AlertDispatcher(channels=[mock_discord_channel, mock_telegram_channel])
assert len(dispatcher.channels) == 2
assert "discord" in dispatcher._circuit_state
assert "telegram" in dispatcher._circuit_state
@@ -325,9 +321,7 @@ class TestAlertDispatcher:
mock_telegram_channel: MagicMock,
) -> None:
"""Test successful dispatch to all channels."""
dispatcher = AlertDispatcher(
channels=[mock_discord_channel, mock_telegram_channel]
)
dispatcher = AlertDispatcher(channels=[mock_discord_channel, mock_telegram_channel])
result = await dispatcher.dispatch(sample_alert)
@@ -345,9 +339,7 @@ class TestAlertDispatcher:
"""Test dispatch with one channel failing."""
mock_telegram_channel.send.return_value = False
dispatcher = AlertDispatcher(
channels=[mock_discord_channel, mock_telegram_channel]
)
dispatcher = AlertDispatcher(channels=[mock_discord_channel, mock_telegram_channel])
result = await dispatcher.dispatch(sample_alert)
@@ -357,9 +349,7 @@ class TestAlertDispatcher:
assert result.channel_results["telegram"] is False
@pytest.mark.asyncio
async def test_dispatch_no_channels(
self, sample_alert: FormattedAlert
) -> None:
async def test_dispatch_no_channels(self, sample_alert: FormattedAlert) -> None:
"""Test dispatch with no channels configured."""
dispatcher = AlertDispatcher(channels=[])
@@ -435,9 +425,7 @@ class TestAlertDispatcher:
# Now succeed
mock_discord_channel.send.return_value = True
# Force half-open by resetting last_failure to past
dispatcher._circuit_state["discord"].last_failure_time = datetime(
2020, 1, 1, tzinfo=UTC
)
dispatcher._circuit_state["discord"].last_failure_time = datetime(2020, 1, 1, tzinfo=UTC)
result = await dispatcher.dispatch(sample_alert)
@@ -465,9 +453,7 @@ class TestAlertDispatcher:
mock_telegram_channel: MagicMock,
) -> None:
"""Test getting circuit status."""
dispatcher = AlertDispatcher(
channels=[mock_discord_channel, mock_telegram_channel]
)
dispatcher = AlertDispatcher(channels=[mock_discord_channel, mock_telegram_channel])
status = dispatcher.get_circuit_status()
+17 -51
View File
@@ -284,17 +284,13 @@ class TestAlertFormatterInit:
class TestAlertFormatterFormat:
"""Tests for AlertFormatter.format method."""
def test_format_returns_formatted_alert(
self, high_risk_assessment: RiskAssessment
) -> None:
def test_format_returns_formatted_alert(self, high_risk_assessment: RiskAssessment) -> None:
"""Test that format returns a FormattedAlert."""
formatter = AlertFormatter()
result = formatter.format(high_risk_assessment)
assert isinstance(result, FormattedAlert)
def test_format_includes_all_fields(
self, high_risk_assessment: RiskAssessment
) -> None:
def test_format_includes_all_fields(self, high_risk_assessment: RiskAssessment) -> None:
"""Test that all fields are populated."""
formatter = AlertFormatter()
result = formatter.format(high_risk_assessment)
@@ -306,26 +302,20 @@ class TestAlertFormatterFormat:
assert result.plain_text != ""
assert result.links != {}
def test_format_title_includes_risk_level(
self, high_risk_assessment: RiskAssessment
) -> None:
def test_format_title_includes_risk_level(self, high_risk_assessment: RiskAssessment) -> None:
"""Test that title includes risk level."""
formatter = AlertFormatter()
result = formatter.format(high_risk_assessment)
assert "HIGH" in result.title
def test_format_includes_wallet_link(
self, high_risk_assessment: RiskAssessment
) -> None:
def test_format_includes_wallet_link(self, high_risk_assessment: RiskAssessment) -> None:
"""Test that wallet explorer link is included."""
formatter = AlertFormatter()
result = formatter.format(high_risk_assessment)
assert "wallet" in result.links
assert "polygonscan.com" in result.links["wallet"]
def test_format_includes_market_link(
self, high_risk_assessment: RiskAssessment
) -> None:
def test_format_includes_market_link(self, high_risk_assessment: RiskAssessment) -> None:
"""Test that market link is included when slug available."""
formatter = AlertFormatter()
result = formatter.format(high_risk_assessment)
@@ -336,9 +326,7 @@ class TestAlertFormatterFormat:
class TestDiscordEmbed:
"""Tests for Discord embed format."""
def test_embed_has_required_fields(
self, high_risk_assessment: RiskAssessment
) -> None:
def test_embed_has_required_fields(self, high_risk_assessment: RiskAssessment) -> None:
"""Test that embed has required Discord fields."""
formatter = AlertFormatter()
result = formatter.format(high_risk_assessment)
@@ -349,17 +337,13 @@ class TestDiscordEmbed:
assert "fields" in embed
assert "footer" in embed
def test_embed_color_reflects_risk(
self, high_risk_assessment: RiskAssessment
) -> None:
def test_embed_color_reflects_risk(self, high_risk_assessment: RiskAssessment) -> None:
"""Test that embed color matches risk level."""
formatter = AlertFormatter()
result = formatter.format(high_risk_assessment)
assert result.discord_embed["color"] == COLOR_HIGH_RISK
def test_embed_includes_wallet_field(
self, high_risk_assessment: RiskAssessment
) -> None:
def test_embed_includes_wallet_field(self, high_risk_assessment: RiskAssessment) -> None:
"""Test that embed includes wallet field."""
formatter = AlertFormatter()
result = formatter.format(high_risk_assessment)
@@ -369,9 +353,7 @@ class TestDiscordEmbed:
assert wallet_field is not None
assert "0x1234" in wallet_field["value"]
def test_embed_includes_wallet_age(
self, high_risk_assessment: RiskAssessment
) -> None:
def test_embed_includes_wallet_age(self, high_risk_assessment: RiskAssessment) -> None:
"""Test that wallet age is shown when fresh wallet signal present."""
formatter = AlertFormatter()
result = formatter.format(high_risk_assessment)
@@ -380,9 +362,7 @@ class TestDiscordEmbed:
wallet_field = next((f for f in fields if f["name"] == "Wallet"), None)
assert "Age:" in wallet_field["value"]
def test_embed_includes_trade_details(
self, high_risk_assessment: RiskAssessment
) -> None:
def test_embed_includes_trade_details(self, high_risk_assessment: RiskAssessment) -> None:
"""Test that trade details are in embed."""
formatter = AlertFormatter()
result = formatter.format(high_risk_assessment)
@@ -393,9 +373,7 @@ class TestDiscordEmbed:
assert "BUY" in trade_field["value"]
assert "Yes" in trade_field["value"]
def test_embed_includes_signals_field(
self, high_risk_assessment: RiskAssessment
) -> None:
def test_embed_includes_signals_field(self, high_risk_assessment: RiskAssessment) -> None:
"""Test that signals are listed in embed."""
formatter = AlertFormatter()
result = formatter.format(high_risk_assessment)
@@ -405,9 +383,7 @@ class TestDiscordEmbed:
assert signals_field is not None
assert "Fresh Wallet" in signals_field["value"]
def test_detailed_embed_includes_confidence(
self, high_risk_assessment: RiskAssessment
) -> None:
def test_detailed_embed_includes_confidence(self, high_risk_assessment: RiskAssessment) -> None:
"""Test that detailed mode includes confidence breakdown."""
formatter = AlertFormatter(verbosity="detailed")
result = formatter.format(high_risk_assessment)
@@ -420,34 +396,26 @@ class TestDiscordEmbed:
class TestTelegramMarkdown:
"""Tests for Telegram markdown format."""
def test_telegram_includes_header(
self, high_risk_assessment: RiskAssessment
) -> None:
def test_telegram_includes_header(self, high_risk_assessment: RiskAssessment) -> None:
"""Test that Telegram message has header."""
formatter = AlertFormatter()
result = formatter.format(high_risk_assessment)
assert "*Suspicious Activity Detected*" in result.telegram_markdown
def test_telegram_includes_wallet(
self, high_risk_assessment: RiskAssessment
) -> None:
def test_telegram_includes_wallet(self, high_risk_assessment: RiskAssessment) -> None:
"""Test that Telegram message includes wallet."""
formatter = AlertFormatter()
result = formatter.format(high_risk_assessment)
assert "`0x1234...5678`" in result.telegram_markdown
def test_telegram_includes_risk_score(
self, high_risk_assessment: RiskAssessment
) -> None:
def test_telegram_includes_risk_score(self, high_risk_assessment: RiskAssessment) -> None:
"""Test that Telegram message includes risk score."""
formatter = AlertFormatter()
result = formatter.format(high_risk_assessment)
assert "0.82" in result.telegram_markdown
assert "HIGH" in result.telegram_markdown
def test_telegram_includes_links(
self, high_risk_assessment: RiskAssessment
) -> None:
def test_telegram_includes_links(self, high_risk_assessment: RiskAssessment) -> None:
"""Test that Telegram message includes links."""
formatter = AlertFormatter()
result = formatter.format(high_risk_assessment)
@@ -489,9 +457,7 @@ class TestPlainText:
class TestCompactVerbosity:
"""Tests for compact verbosity mode."""
def test_compact_body_is_shorter(
self, high_risk_assessment: RiskAssessment
) -> None:
def test_compact_body_is_shorter(self, high_risk_assessment: RiskAssessment) -> None:
"""Test that compact mode produces shorter body."""
detailed_formatter = AlertFormatter(verbosity="detailed")
compact_formatter = AlertFormatter(verbosity="compact")
+6 -19
View File
@@ -76,9 +76,7 @@ def sample_metadata() -> MarketMetadata:
condition_id="market_abc123",
question="Will it rain tomorrow?",
description="Weather prediction market",
tokens=(
Token(token_id="token_123", outcome="Yes", price=Decimal("0.65")),
),
tokens=(Token(token_id="token_123", outcome="Yes", price=Decimal("0.65")),),
category="science",
)
@@ -310,9 +308,7 @@ class TestRiskScorerInit:
class TestWeightedScoreCalculation:
"""Tests for weighted score calculation."""
def test_no_signals_zero_score(
self, mock_redis: AsyncMock, sample_trade: TradeEvent
) -> None:
def test_no_signals_zero_score(self, mock_redis: AsyncMock, sample_trade: TradeEvent) -> None:
"""Test score is zero when no signals present."""
scorer = RiskScorer(mock_redis)
bundle = SignalBundle(trade_event=sample_trade)
@@ -358,10 +354,7 @@ class TestWeightedScoreCalculation:
score, count = scorer.calculate_weighted_score(bundle)
# 0.7 confidence * 0.35 weight + 0.7 * 0.25 niche weight = 0.42
expected = (
0.7 * DEFAULT_WEIGHTS["size_anomaly"]
+ 0.7 * DEFAULT_WEIGHTS["niche_market"]
)
expected = 0.7 * DEFAULT_WEIGHTS["size_anomaly"] + 0.7 * DEFAULT_WEIGHTS["niche_market"]
assert score == pytest.approx(expected)
assert count == 1
@@ -559,9 +552,7 @@ class TestDeduplication:
"""Tests for deduplication functionality."""
@pytest.mark.asyncio
async def test_check_and_set_dedup_new_key(
self, mock_redis: AsyncMock
) -> None:
async def test_check_and_set_dedup_new_key(self, mock_redis: AsyncMock) -> None:
"""Test dedup returns False for new key."""
mock_redis.set.return_value = True
@@ -572,9 +563,7 @@ class TestDeduplication:
mock_redis.set.assert_called_once()
@pytest.mark.asyncio
async def test_check_and_set_dedup_existing_key(
self, mock_redis: AsyncMock
) -> None:
async def test_check_and_set_dedup_existing_key(self, mock_redis: AsyncMock) -> None:
"""Test dedup returns True for existing key."""
mock_redis.set.return_value = False # Key exists, NX failed
@@ -632,9 +621,7 @@ class TestBatchAnalysis:
confidence=0.8,
factors={},
)
bundles.append(
SignalBundle(trade_event=trade, fresh_wallet_signal=signal)
)
bundles.append(SignalBundle(trade_event=trade, fresh_wallet_signal=signal))
assessments = await scorer.assess_batch(bundles)
+4 -12
View File
@@ -290,9 +290,7 @@ class TestVolumeImpactCalculation:
detector = SizeAnomalyDetector(mock_metadata_sync)
# Trade size $1000, daily volume $50000 = 2% impact
impact = detector._calculate_volume_impact(
Decimal("1000"), Decimal("50000")
)
impact = detector._calculate_volume_impact(Decimal("1000"), Decimal("50000"))
assert impact == pytest.approx(0.02)
def test_volume_impact_none_volume(self, mock_metadata_sync: AsyncMock) -> None:
@@ -309,9 +307,7 @@ class TestVolumeImpactCalculation:
impact = detector._calculate_volume_impact(Decimal("1000"), Decimal("0"))
assert impact == 0.0
def test_volume_impact_negative_volume(
self, mock_metadata_sync: AsyncMock
) -> None:
def test_volume_impact_negative_volume(self, mock_metadata_sync: AsyncMock) -> None:
"""Test volume impact returns 0 when volume is negative."""
detector = SizeAnomalyDetector(mock_metadata_sync)
@@ -422,9 +418,7 @@ class TestNicheMarketDetection:
class TestConfidenceScoring:
"""Tests for confidence score calculation."""
def test_confidence_volume_impact_only(
self, mock_metadata_sync: AsyncMock
) -> None:
def test_confidence_volume_impact_only(self, mock_metadata_sync: AsyncMock) -> None:
"""Test confidence with only volume impact."""
detector = SizeAnomalyDetector(mock_metadata_sync)
@@ -819,9 +813,7 @@ class TestBatchAnalysis:
assert len(signals) == 2
@pytest.mark.asyncio
async def test_analyze_batch_empty_list(
self, mock_metadata_sync: AsyncMock
) -> None:
async def test_analyze_batch_empty_list(self, mock_metadata_sync: AsyncMock) -> None:
"""Test batch analysis with empty list."""
detector = SizeAnomalyDetector(mock_metadata_sync)
signals = await detector.analyze_batch([])
+4 -6
View File
@@ -115,25 +115,23 @@ class TestClobClient:
@pytest.fixture
def mock_base_client(self) -> MagicMock:
"""Create a mock base CLOB client."""
with patch(
"polymarket_insider_tracker.ingestor.clob_client.BaseClobClient"
) as mock:
with patch("polymarket_insider_tracker.ingestor.clob_client.BaseClobClient") as mock:
yield mock.return_value
def test_init_defaults(self, mock_base_client: MagicMock) -> None:
def test_init_defaults(self, _mock_base_client: MagicMock) -> None:
"""Test client initialization with defaults."""
client = ClobClient()
assert client._host == "https://clob.polymarket.com"
assert client._max_retries == 3
def test_init_with_env_api_key(self, mock_base_client: MagicMock) -> None:
def test_init_with_env_api_key(self, _mock_base_client: MagicMock) -> None:
"""Test client reads API key from environment."""
with patch.dict("os.environ", {"POLYMARKET_API_KEY": "test-key"}):
client = ClobClient()
assert client._api_key == "test-key"
def test_init_with_explicit_api_key(self, mock_base_client: MagicMock) -> None:
def test_init_with_explicit_api_key(self, _mock_base_client: MagicMock) -> None:
"""Test client uses explicitly provided API key."""
client = ClobClient(api_key="explicit-key")
assert client._api_key == "explicit-key"
+2 -6
View File
@@ -448,9 +448,7 @@ class TestHealthMonitorHTTPEndpoints:
assert data["status"] == "unhealthy"
@pytest.mark.asyncio
async def test_metrics_endpoint(
self, monitor: HealthMonitor, app: web.Application
) -> None:
async def test_metrics_endpoint(self, monitor: HealthMonitor, app: web.Application) -> None:
"""Test /metrics endpoint returns Prometheus format."""
from aiohttp.test_utils import TestClient, TestServer
@@ -468,9 +466,7 @@ class TestHealthMonitorHTTPEndpoints:
assert "polymarket_health_status" in text
@pytest.mark.asyncio
async def test_ready_endpoint_ready(
self, monitor: HealthMonitor, app: web.Application
) -> None:
async def test_ready_endpoint_ready(self, monitor: HealthMonitor, app: web.Application) -> None:
"""Test /ready endpoint when ready."""
from aiohttp.test_utils import TestClient, TestServer
+7 -7
View File
@@ -1,6 +1,6 @@
"""Tests for ingestor data models."""
from datetime import datetime, timezone
from datetime import UTC, datetime
from decimal import Decimal
from unittest.mock import MagicMock
@@ -75,7 +75,7 @@ class TestMarket:
assert len(market.tokens) == 2
assert market.tokens[0].outcome == "Yes"
assert market.tokens[1].outcome == "No"
assert market.end_date == datetime(2024, 12, 31, 23, 59, 59, tzinfo=timezone.utc)
assert market.end_date == datetime(2024, 12, 31, 23, 59, 59, tzinfo=UTC)
assert market.active is True
assert market.closed is False
@@ -302,7 +302,7 @@ class TestTradeEvent:
assert trade.outcome_index == 0
assert trade.price == Decimal("0.65")
assert trade.size == Decimal("100")
assert trade.timestamp == datetime(2024, 1, 1, 0, 0, 0, tzinfo=timezone.utc)
assert trade.timestamp == datetime(2024, 1, 1, 0, 0, 0, tzinfo=UTC)
assert trade.asset_id == "token123"
assert trade.market_slug == "will-it-rain"
assert trade.event_slug == "weather-markets"
@@ -364,7 +364,7 @@ class TestTradeEvent:
outcome_index=0,
price=Decimal("0.5"),
size=Decimal("10"),
timestamp=datetime.now(timezone.utc),
timestamp=datetime.now(UTC),
asset_id="",
)
sell_trade = TradeEvent(
@@ -376,7 +376,7 @@ class TestTradeEvent:
outcome_index=0,
price=Decimal("0.5"),
size=Decimal("10"),
timestamp=datetime.now(timezone.utc),
timestamp=datetime.now(UTC),
asset_id="",
)
@@ -396,7 +396,7 @@ class TestTradeEvent:
outcome_index=0,
price=Decimal("0.65"),
size=Decimal("100"),
timestamp=datetime.now(timezone.utc),
timestamp=datetime.now(UTC),
asset_id="",
)
@@ -413,7 +413,7 @@ class TestTradeEvent:
outcome_index=0,
price=Decimal("0.5"),
size=Decimal("10"),
timestamp=datetime.now(timezone.utc),
timestamp=datetime.now(UTC),
asset_id="token",
)
with pytest.raises(AttributeError):
+9 -23
View File
@@ -80,19 +80,13 @@ class TestTradeStreamHandler:
assert handler._event_filter == "presidential-election-2024"
def test_build_subscription_message_no_filter(
self, handler: TradeStreamHandler
) -> None:
def test_build_subscription_message_no_filter(self, handler: TradeStreamHandler) -> None:
"""Test building subscription message without filters."""
msg = handler._build_subscription_message()
assert msg == {
"subscriptions": [{"topic": "activity", "type": "trades"}]
}
assert msg == {"subscriptions": [{"topic": "activity", "type": "trades"}]}
def test_build_subscription_message_with_event_filter(
self, on_trade_mock: AsyncMock
) -> None:
def test_build_subscription_message_with_event_filter(self, on_trade_mock: AsyncMock) -> None:
"""Test building subscription message with event filter."""
handler = TradeStreamHandler(
on_trade=on_trade_mock,
@@ -100,13 +94,9 @@ class TestTradeStreamHandler:
)
msg = handler._build_subscription_message()
assert msg["subscriptions"][0]["filters"] == json.dumps(
{"event_slug": "test-event"}
)
assert msg["subscriptions"][0]["filters"] == json.dumps({"event_slug": "test-event"})
def test_build_subscription_message_with_market_filter(
self, on_trade_mock: AsyncMock
) -> None:
def test_build_subscription_message_with_market_filter(self, on_trade_mock: AsyncMock) -> None:
"""Test building subscription message with market filter."""
handler = TradeStreamHandler(
on_trade=on_trade_mock,
@@ -114,9 +104,7 @@ class TestTradeStreamHandler:
)
msg = handler._build_subscription_message()
assert msg["subscriptions"][0]["filters"] == json.dumps(
{"market_slug": "test-market"}
)
assert msg["subscriptions"][0]["filters"] == json.dumps({"market_slug": "test-market"})
@pytest.mark.asyncio
async def test_handle_message_trade(
@@ -235,7 +223,7 @@ class TestTradeStreamHandler:
@pytest.mark.asyncio
async def test_connect_sends_subscription(
self, handler: TradeStreamHandler, on_state_change_mock: AsyncMock
self, handler: TradeStreamHandler, _on_state_change_mock: AsyncMock
) -> None:
"""Test that connection sends subscription message."""
mock_ws = AsyncMock()
@@ -254,9 +242,7 @@ class TestTradeStreamHandler:
assert sent_msg["subscriptions"][0]["type"] == "trades"
@pytest.mark.asyncio
async def test_cleanup_closes_websocket(
self, handler: TradeStreamHandler
) -> None:
async def test_cleanup_closes_websocket(self, handler: TradeStreamHandler) -> None:
"""Test that cleanup closes the WebSocket."""
mock_ws = AsyncMock()
mock_ws.close = AsyncMock()
@@ -333,7 +319,7 @@ class TestTradeStreamHandlerIntegration:
# Run with timeout to prevent hanging
try:
await asyncio.wait_for(handler.start(), timeout=1.0)
except asyncio.TimeoutError:
except TimeoutError:
await handler.stop()
# Verify trade was received
+2 -3
View File
@@ -144,9 +144,7 @@ class TestWalletAnalyzerAnalyze:
assert profile.age_hours is None
@pytest.mark.asyncio
async def test_analyze_uses_cache(
self, mock_client: AsyncMock, mock_redis: AsyncMock
) -> None:
async def test_analyze_uses_cache(self, mock_client: AsyncMock, mock_redis: AsyncMock) -> None:
"""Test that analyze uses cached data."""
cached_data = {
"address": VALID_ADDRESS.lower(),
@@ -164,6 +162,7 @@ class TestWalletAnalyzerAnalyze:
# Actually mock it properly with json
import json
mock_redis.get = AsyncMock(return_value=json.dumps(cached_data).encode())
analyzer = WalletAnalyzer(mock_client, redis=mock_redis)
+3 -9
View File
@@ -166,9 +166,7 @@ class TestPolygonClient:
await client._set_cached("test:key", "value")
mock_redis.set.assert_called_once_with(
"test:key", "value", ex=DEFAULT_CACHE_TTL_SECONDS
)
mock_redis.set.assert_called_once_with("test:key", "value", ex=DEFAULT_CACHE_TTL_SECONDS)
@pytest.mark.asyncio
async def test_set_cached_custom_ttl(self, mock_redis: AsyncMock) -> None:
@@ -274,9 +272,7 @@ class TestPolygonClient:
assert info.first_transaction is None
@pytest.mark.asyncio
async def test_get_first_transaction_no_transactions(
self, mock_redis: AsyncMock
) -> None:
async def test_get_first_transaction_no_transactions(self, mock_redis: AsyncMock) -> None:
"""Test get_first_transaction when wallet has no transactions."""
client = PolygonClient("https://polygon-rpc.com", redis=mock_redis)
@@ -451,9 +447,7 @@ class TestPolygonClientTokenBalance:
# Mock the contract call
mock_contract = MagicMock()
mock_contract.functions.balanceOf.return_value.call = AsyncMock(
return_value=5000000
)
mock_contract.functions.balanceOf.return_value.call = AsyncMock(return_value=5000000)
client._w3.eth.contract = MagicMock(return_value=mock_contract)
balance = await client.get_token_balance(VALID_ADDRESS, VALID_TOKEN)
+2 -6
View File
@@ -59,9 +59,7 @@ class TestEntityData:
"""Test that CEX addresses are populated."""
assert len(CEX_ADDRESSES) > 0
# Check Binance address is present
binance_found = any(
entity == EntityType.CEX_BINANCE for entity in CEX_ADDRESSES.values()
)
binance_found = any(entity == EntityType.CEX_BINANCE for entity in CEX_ADDRESSES.values())
assert binance_found
def test_bridge_addresses_populated(self) -> None:
@@ -72,9 +70,7 @@ class TestEntityData:
"""Test that DEX addresses are populated."""
assert len(DEX_ADDRESSES) > 0
# Check Uniswap is present
uniswap_found = any(
entity == EntityType.DEX_UNISWAP for entity in DEX_ADDRESSES.values()
)
uniswap_found = any(entity == EntityType.DEX_UNISWAP for entity in DEX_ADDRESSES.values())
assert uniswap_found
def test_token_addresses_include_usdc(self) -> None:
+19 -41
View File
@@ -74,27 +74,19 @@ class TestFundingTracerInit:
tracer = FundingTracer(mock_polygon_client, max_hops=5)
assert tracer.max_hops == 5
def test_init_with_custom_usdc_addresses(
self, mock_polygon_client: MagicMock
) -> None:
def test_init_with_custom_usdc_addresses(self, mock_polygon_client: MagicMock) -> None:
"""Test initialization with custom USDC addresses."""
custom_addresses = ["0x1111111111111111111111111111111111111111"]
tracer = FundingTracer(
mock_polygon_client, usdc_addresses=custom_addresses
)
tracer = FundingTracer(mock_polygon_client, usdc_addresses=custom_addresses)
assert tracer._usdc_addresses == [custom_addresses[0].lower()]
def test_init_with_custom_entity_registry(
self, mock_polygon_client: MagicMock
) -> None:
def test_init_with_custom_entity_registry(self, mock_polygon_client: MagicMock) -> None:
"""Test initialization with custom entity registry."""
registry = EntityRegistry()
tracer = FundingTracer(mock_polygon_client, entity_registry=registry)
assert tracer.entity_registry is registry
def test_init_creates_default_entity_registry(
self, mock_polygon_client: MagicMock
) -> None:
def test_init_creates_default_entity_registry(self, mock_polygon_client: MagicMock) -> None:
"""Test initialization creates default EntityRegistry if None."""
tracer = FundingTracer(mock_polygon_client, entity_registry=None)
assert isinstance(tracer.entity_registry, EntityRegistry)
@@ -186,9 +178,7 @@ class TestFundingTracerTrace:
call_count = 0
async def mock_get_logs(
*_args: Any, **_kwargs: Any
) -> list[dict[str, Any]]:
async def mock_get_logs(*_args: Any, **_kwargs: Any) -> list[dict[str, Any]]:
nonlocal call_count
result = [mock_logs[call_count]] if call_count < len(mock_logs) else []
call_count += 1
@@ -213,9 +203,7 @@ class TestFundingTracerTrace:
call_count = 0
async def mock_get_logs(
*_args: Any, **_kwargs: Any
) -> list[dict[str, Any]]:
async def mock_get_logs(*_args: Any, **_kwargs: Any) -> list[dict[str, Any]]:
nonlocal call_count
if call_count < len(wallets) - 1:
log = _create_mock_log(
@@ -286,9 +274,7 @@ class TestGetFirstUsdcTransfer:
"""Test fallback to native USDC contract."""
call_count = 0
async def mock_get_logs(
*_args: Any, **_kwargs: Any
) -> list[dict[str, Any]]:
async def mock_get_logs(*_args: Any, **_kwargs: Any) -> list[dict[str, Any]]:
nonlocal call_count
call_count += 1
if call_count == 1: # First call (bridged) returns nothing
@@ -410,9 +396,7 @@ class TestLogToFundingTransfer:
block_number=50000000,
)
result = await funding_tracer._log_to_funding_transfer(
mock_log, USDC_BRIDGED
)
result = await funding_tracer._log_to_funding_transfer(mock_log, USDC_BRIDGED)
assert result.from_address == TEST_SOURCE.lower()
assert result.to_address == TEST_WALLET.lower()
@@ -438,9 +422,7 @@ class TestLogToFundingTransfer:
block_number=50000000,
)
result = await funding_tracer._log_to_funding_transfer(
mock_log, USDC_BRIDGED
)
result = await funding_tracer._log_to_funding_transfer(mock_log, USDC_BRIDGED)
# Should still return a valid transfer with current timestamp
assert result.from_address == TEST_SOURCE.lower()
@@ -460,7 +442,9 @@ class TestGetFundingChainsBatch:
# Mock trace to return simple chains
async def mock_trace(
addr: str, *, max_hops: int | None = None # noqa: ARG001
addr: str,
*,
max_hops: int | None = None, # noqa: ARG001
) -> FundingChain:
return FundingChain(
target_address=addr.lower(),
@@ -486,7 +470,9 @@ class TestGetFundingChainsBatch:
call_count = 0
async def mock_trace(
addr: str, *, max_hops: int | None = None # noqa: ARG001
addr: str,
*,
max_hops: int | None = None, # noqa: ARG001
) -> FundingChain:
nonlocal call_count
call_count += 1
@@ -525,9 +511,7 @@ class TestGetFundingChainsBatch:
addresses = ["0x" + "11" * 20]
captured_max_hops: list[int | None] = []
async def mock_trace(
addr: str, max_hops: int | None = None
) -> FundingChain:
async def mock_trace(addr: str, max_hops: int | None = None) -> FundingChain:
captured_max_hops.append(max_hops)
return FundingChain(target_address=addr.lower())
@@ -565,9 +549,7 @@ class TestGetSuspiciousnessScore:
assert score == 0.3
def test_unknown_no_transfers_high_score(
self, funding_tracer: FundingTracer
) -> None:
def test_unknown_no_transfers_high_score(self, funding_tracer: FundingTracer) -> None:
"""Test unknown origin with no transfers is most suspicious."""
chain = FundingChain(
target_address=TEST_WALLET,
@@ -579,9 +561,7 @@ class TestGetSuspiciousnessScore:
assert score == 1.0
def test_unknown_max_hops_high_score(
self, funding_tracer: FundingTracer
) -> None:
def test_unknown_max_hops_high_score(self, funding_tracer: FundingTracer) -> None:
"""Test unknown origin at max hops is suspicious."""
chain = FundingChain(
target_address=TEST_WALLET,
@@ -593,9 +573,7 @@ class TestGetSuspiciousnessScore:
assert score == 0.7
def test_unknown_partial_hops_medium_score(
self, funding_tracer: FundingTracer
) -> None:
def test_unknown_partial_hops_medium_score(self, funding_tracer: FundingTracer) -> None:
"""Test unknown origin with partial hops is moderately suspicious."""
chain = FundingChain(
target_address=TEST_WALLET,
+1 -3
View File
@@ -160,9 +160,7 @@ class TestWalletInfo:
"""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:
def test_wallet_age_days_with_transaction(self, wallet_with_transaction: WalletInfo) -> None:
"""Test wallet age calculation."""
age = wallet_with_transaction.wallet_age_days
+1 -5
View File
@@ -10,11 +10,7 @@ def test_version() -> None:
def test_import_modules() -> None:
"""Test that all submodules can be imported."""
from polymarket_insider_tracker import ingestor
from polymarket_insider_tracker import profiler
from polymarket_insider_tracker import detector
from polymarket_insider_tracker import alerter
from polymarket_insider_tracker import storage
from polymarket_insider_tracker import alerter, detector, ingestor, profiler, storage
# Just verify imports work
assert ingestor is not None