- PR1: Project scaffold, DB models, price loader - PR2: Congressional trade ingestion (House Stock Watcher) - PR3: Security enrichment + deployment infrastructure - 37 passing tests, 87%+ coverage - Docker + Proxmox deployment ready - Complete documentation - Works 100% offline with fixtures
126 lines
4.0 KiB
Python
126 lines
4.0 KiB
Python
"""
|
|
Tests for House Stock Watcher client.
|
|
"""
|
|
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
from pote.ingestion.house_watcher import (
|
|
HouseWatcherClient,
|
|
normalize_transaction_type,
|
|
parse_amount_range,
|
|
)
|
|
|
|
|
|
def test_parse_amount_range_with_range():
|
|
"""Test parsing amount range string."""
|
|
min_val, max_val = parse_amount_range("$1,001 - $15,000")
|
|
assert min_val == 1001.0
|
|
assert max_val == 15000.0
|
|
|
|
|
|
def test_parse_amount_range_single_value():
|
|
"""Test parsing single value."""
|
|
min_val, max_val = parse_amount_range("$25,000")
|
|
assert min_val == 25000.0
|
|
assert max_val == 25000.0
|
|
|
|
|
|
def test_parse_amount_range_invalid():
|
|
"""Test parsing invalid amount."""
|
|
min_val, max_val = parse_amount_range("N/A")
|
|
assert min_val is None
|
|
assert max_val is None
|
|
|
|
|
|
def test_normalize_transaction_type():
|
|
"""Test normalizing transaction types."""
|
|
assert normalize_transaction_type("Purchase") == "buy"
|
|
assert normalize_transaction_type("Sale") == "sell"
|
|
assert normalize_transaction_type("Exchange") == "exchange"
|
|
assert normalize_transaction_type("purchase") == "buy"
|
|
assert normalize_transaction_type("SALE") == "sell"
|
|
|
|
|
|
@patch("pote.ingestion.house_watcher.httpx.Client")
|
|
def test_fetch_all_transactions(mock_client_class):
|
|
"""Test fetching all transactions."""
|
|
# Mock response
|
|
mock_response = MagicMock()
|
|
mock_response.json.return_value = [
|
|
{
|
|
"representative": "Test Official",
|
|
"ticker": "AAPL",
|
|
"transaction_date": "2024-01-15",
|
|
"disclosure_date": "2024-02-01",
|
|
"transaction": "Purchase",
|
|
"amount": "$1,001 - $15,000",
|
|
"house": "House",
|
|
"party": "Independent",
|
|
}
|
|
]
|
|
mock_response.raise_for_status = MagicMock()
|
|
|
|
mock_client_instance = MagicMock()
|
|
mock_client_instance.get.return_value = mock_response
|
|
mock_client_class.return_value = mock_client_instance
|
|
|
|
with HouseWatcherClient() as client:
|
|
txns = client.fetch_all_transactions()
|
|
|
|
assert len(txns) == 1
|
|
assert txns[0]["ticker"] == "AAPL"
|
|
assert txns[0]["representative"] == "Test Official"
|
|
|
|
|
|
@patch("pote.ingestion.house_watcher.httpx.Client")
|
|
def test_fetch_all_transactions_with_limit(mock_client_class):
|
|
"""Test fetching transactions with limit."""
|
|
mock_response = MagicMock()
|
|
mock_response.json.return_value = [{"id": i} for i in range(100)]
|
|
mock_response.raise_for_status = MagicMock()
|
|
|
|
mock_client_instance = MagicMock()
|
|
mock_client_instance.get.return_value = mock_response
|
|
mock_client_class.return_value = mock_client_instance
|
|
|
|
with HouseWatcherClient() as client:
|
|
txns = client.fetch_all_transactions(limit=10)
|
|
|
|
assert len(txns) == 10
|
|
|
|
|
|
@patch("pote.ingestion.house_watcher.httpx.Client")
|
|
def test_fetch_recent_transactions(mock_client_class):
|
|
"""Test filtering to recent transactions."""
|
|
from datetime import date, timedelta
|
|
|
|
today = date.today()
|
|
recent_date = (today - timedelta(days=5)).strftime("%m/%d/%Y")
|
|
old_date = (today - timedelta(days=100)).strftime("%m/%d/%Y")
|
|
|
|
mock_response = MagicMock()
|
|
mock_response.json.return_value = [
|
|
{"disclosure_date": recent_date, "ticker": "AAPL"},
|
|
{"disclosure_date": old_date, "ticker": "MSFT"},
|
|
{"disclosure_date": recent_date, "ticker": "GOOGL"},
|
|
]
|
|
mock_response.raise_for_status = MagicMock()
|
|
|
|
mock_client_instance = MagicMock()
|
|
mock_client_instance.get.return_value = mock_response
|
|
mock_client_class.return_value = mock_client_instance
|
|
|
|
with HouseWatcherClient() as client:
|
|
recent = client.fetch_recent_transactions(days=30)
|
|
|
|
assert len(recent) == 2
|
|
assert recent[0]["ticker"] == "AAPL"
|
|
assert recent[1]["ticker"] == "GOOGL"
|
|
|
|
|
|
def test_house_watcher_client_context_manager():
|
|
"""Test client as context manager."""
|
|
with HouseWatcherClient() as client:
|
|
assert client is not None
|
|
# Verify close was called (client should be closed after context)
|