from datetime import UTC, date, datetime from decimal import Decimal from pathlib import Path import pytest from broker_sync.fx import FxCache from broker_sync.models import AccountType, Activity, ActivityType, FxRateSource from broker_sync.normaliser import normalise_to_gbp def _buy_usd(amount_usd: Decimal = Decimal("100")) -> Activity: return Activity( external_id="schwab:1", account_id="schwab-rsu", account_type=AccountType.GIA, date=datetime(2026, 4, 1, tzinfo=UTC), activity_type=ActivityType.BUY, symbol="META", quantity=Decimal("1"), unit_price=amount_usd, currency="USD", fee=Decimal("0"), ) def test_gbp_activity_unchanged(tmp_path: Path) -> None: cache = FxCache(tmp_path / "fx.db") a = Activity( external_id="t212:1", account_id="t212-isa", account_type=AccountType.ISA, date=datetime(2026, 4, 1, tzinfo=UTC), activity_type=ActivityType.BUY, symbol="VUAG", quantity=Decimal("1"), unit_price=Decimal("100"), currency="GBP", ) out = normalise_to_gbp(a, cache=cache) assert out.amount_gbp == Decimal("100") assert out.fx_rate_gbp == Decimal("1") assert out.fx_rate_source is FxRateSource.ECB_LIVE def test_usd_buy_converts_using_cache(tmp_path: Path) -> None: cache = FxCache(tmp_path / "fx.db") cache.put("USD", date(2026, 4, 1), Decimal("0.80"), FxRateSource.ECB_LIVE) out = normalise_to_gbp(_buy_usd(Decimal("100")), cache=cache) assert out.amount_gbp == Decimal("80.00") assert out.fx_rate_gbp == Decimal("0.80") assert out.fx_rate_source is FxRateSource.ECB_LIVE def test_dividend_amount_drives_gbp(tmp_path: Path) -> None: cache = FxCache(tmp_path / "fx.db") cache.put("USD", date(2026, 4, 1), Decimal("0.80"), FxRateSource.ECB_LIVE) div = Activity( external_id="schwab:div:1", account_id="schwab-rsu", account_type=AccountType.GIA, date=datetime(2026, 4, 1, tzinfo=UTC), activity_type=ActivityType.DIVIDEND, currency="USD", amount=Decimal("50"), ) out = normalise_to_gbp(div, cache=cache) assert out.amount_gbp == Decimal("40.00") def test_missing_rate_raises(tmp_path: Path) -> None: cache = FxCache(tmp_path / "fx.db") with pytest.raises(LookupError): normalise_to_gbp(_buy_usd(), cache=cache)