mirror of
https://github.com/escalante29/WealthySmart.git
synced 2026-07-17 10:08:46 +02:00
Constraint and query pass: FK delete rules, grouped queries, tiebreakers
- FK rules via migration 7884505b16b3 (verified on dev with a rolled- back probe): transactions/recurring items SET NULL on category delete (this also fixes the 500 that DELETE /categories raised for in-use categories), water readings CASCADE with their receipt. Models declare ondelete to stay in sync. (ARCH-09, BE-11) - compute_actuals_by_source: 3 grouped queries replace 10 single- aggregate round-trips; behavior pinned by the existing cycle suite. (BE-21) - compute_cc_by_category prefetches category names (ARCH-15); agent pension latest-per-fund resolved in SQL (BE-04); municipal water consumption batched into one grouped query (BE-05). - Deterministic pagination: date DESC, id DESC on all transaction listings. (ARCH-12) - New agent-tool tests (session binding, JSON-safe output, batched queries): 64 tests green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
151
backend/tests/test_agent_tools.py
Normal file
151
backend/tests/test_agent_tools.py
Normal file
@@ -0,0 +1,151 @@
|
||||
"""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
|
||||
Reference in New Issue
Block a user