"""Agent tool coverage: session binding, JSON-safe output (no Decimal leaks), and the batched queries introduced in Phase 2.""" import json from datetime import date, datetime from decimal import Decimal import pytest from app.agent import tools from app.agent.tools import reset_session, set_session from app.models.models import ( Account, AccountType, Bank, Currency, MunicipalReceipt, PensionSnapshot, Transaction, WaterMeterReading, ) @pytest.fixture() def bound_session(session): token = set_session(session) yield session reset_session(token) def make_snapshot(fund: Bank, start: date, end: date, saldo: str) -> PensionSnapshot: zero = Decimal("0") return PensionSnapshot( fund=fund, contract_number="C-1", period_start=start, period_end=end, saldo_anterior=zero, aportes=zero, rendimientos=zero, retiros=zero, traslados=zero, comision=zero, correccion=zero, bonificacion=zero, saldo_final=Decimal(saldo), source_filename="t.pdf", ) def make_receipt(period: str) -> MunicipalReceipt: zero = Decimal("0") return MunicipalReceipt( receipt_date=date(2026, 4, 1), due_date=date(2026, 4, 15), period=period, account="A1", finca="F1", holder_name="x", holder_cedula="x", holder_address="x", subtotal=Decimal("100"), interests=zero, iva=Decimal("13"), total=Decimal("113"), source_filename="r.pdf", ) def test_unbound_session_raises(session): with pytest.raises(LookupError): tools.get_accounts() def test_accounts_output_is_json_safe(bound_session): bound_session.add( Account( bank=Bank.BAC, currency=Currency.CRC, label="Main", balance=Decimal("1234.56"), account_type=AccountType.BANK, ) ) bound_session.commit() out = tools.get_accounts() json.dumps(out) # would raise on Decimal leakage assert out[0]["balance"] == 1234.56 def test_pension_latest_only_per_fund(bound_session): bound_session.add( make_snapshot(Bank.FCL, date(2026, 1, 1), date(2026, 1, 31), "100") ) bound_session.add( make_snapshot(Bank.FCL, date(2026, 2, 1), date(2026, 2, 28), "200") ) bound_session.add( make_snapshot(Bank.ROP, date(2026, 1, 1), date(2026, 1, 31), "900") ) bound_session.commit() out = tools.get_pension_snapshots(latest_only=True) json.dumps(out) assert len(out) == 2 by_fund = {o["fund"]: o for o in out} assert by_fund["FCL"]["saldo_final"] == 200.0 # latest period wins assert by_fund["ROP"]["saldo_final"] == 900.0 full = tools.get_pension_snapshots(latest_only=False) assert len(full) == 3 def test_municipal_receipts_batched_consumption(bound_session): r1, r2 = make_receipt("2026-03"), make_receipt("2026-04") r2.account = "A2" bound_session.add(r1) bound_session.add(r2) bound_session.commit() bound_session.refresh(r1) bound_session.refresh(r2) for rid, m3 in ((r1.id, "10.5"), (r1.id, "4.5"), (r2.id, "7")): bound_session.add( WaterMeterReading( meter_id="7335", period=f"p{m3}", consumption_m3=Decimal(m3), receipt_id=rid, ) ) bound_session.commit() out = tools.get_municipal_receipts() json.dumps(out) by_account = {o["account"]: o for o in out} assert by_account["A1"]["water_consumption_m3"] == 15.0 assert by_account["A2"]["water_consumption_m3"] == 7.0 def test_recent_transactions_json_safe(bound_session): bound_session.add( Transaction( amount=Decimal("42.42"), merchant="M", date=datetime(2026, 4, 1), ) ) bound_session.commit() out = tools.get_recent_transactions() json.dumps(out) assert out[0]["amount"] == 42.42