POTE/tests/test_house_watcher.py
ilia 204cd0e75b Initial commit: POTE Phase 1 complete
- 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
2025-12-14 20:45:34 -05:00

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)