mirror of
https://github.com/escalante29/WealthySmart.git
synced 2026-07-17 09:08:46 +02:00
Covers: billing-cycle characterization (the BE-06 convention, boundary instants, year wraps, deferred transactions), projection engine (recurring schedules, no-double-count, cumulative balances, overrides, fresh start), auth (JWT roundtrip/expiry, API tokens, constant-time creds, rate-limit window), fail-fast settings, and the BAC paste parser. SQLite in-memory fixtures, exchange rates pinned — no network. Also doubles as the float-behavior baseline for the Phase 2 Decimal migration (plan 1.5). config.py moves to SettingsConfigDict to clear the pydantic deprecation warning from test output. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
61 lines
2.4 KiB
Python
61 lines
2.4 KiB
Python
"""Characterization of the BAC paste-import line parser."""
|
|
|
|
from datetime import datetime
|
|
|
|
from app.api.v1.endpoints.import_transactions import (
|
|
make_reference_hash,
|
|
parse_bac_line,
|
|
)
|
|
from app.models.models import TransactionType
|
|
|
|
|
|
class TestParseBacLine:
|
|
def test_tab_separated_purchase(self):
|
|
line = "15/04/2026\tWALMART\\SAN JOSE\\CR\t25,000.50 CRC"
|
|
parsed = parse_bac_line(line)
|
|
assert parsed is not None
|
|
assert parsed["date"] == datetime(2026, 4, 15)
|
|
assert parsed["merchant"] == "WALMART"
|
|
assert parsed["city"] == "SAN JOSE"
|
|
assert parsed["amount"] == 25000.50
|
|
assert parsed["currency"] == "CRC"
|
|
assert parsed["transaction_type"] == TransactionType.COMPRA
|
|
|
|
def test_multispace_separated_usd(self):
|
|
line = "01/05/2026 AMAZON\\SEATTLE\\US 12.99 USD"
|
|
parsed = parse_bac_line(line)
|
|
assert parsed is not None
|
|
assert parsed["amount"] == 12.99
|
|
assert parsed["currency"] == "USD"
|
|
|
|
def test_negative_amount_is_refund(self):
|
|
parsed = parse_bac_line("15/04/2026\tSTORE\\X\\CR\t-5,000.00 CRC")
|
|
assert parsed is not None
|
|
assert parsed["transaction_type"] == TransactionType.DEVOLUCION
|
|
assert parsed["amount"] == 5000.0 # stored as absolute value
|
|
|
|
def test_pago_recibido_is_refund(self):
|
|
parsed = parse_bac_line("15/04/2026\tPAGO RECIBIDO\\\\\t100.00 CRC")
|
|
assert parsed is not None
|
|
assert parsed["transaction_type"] == TransactionType.DEVOLUCION
|
|
|
|
def test_unparseable_lines_return_none(self):
|
|
assert parse_bac_line("") is None
|
|
assert parse_bac_line("not a transaction") is None
|
|
assert parse_bac_line("99/99/2026\tX\\Y\\Z\t1.00 CRC") is None
|
|
assert parse_bac_line("15/04/2026\tX\\Y\\Z\t1.00 GBP") is None
|
|
|
|
|
|
class TestReferenceHash:
|
|
def test_deterministic_and_merchant_normalized(self):
|
|
a = make_reference_hash(datetime(2026, 4, 15), " walmart ", 100.0, "CRC")
|
|
b = make_reference_hash(datetime(2026, 4, 15), "WALMART", 100.0, "CRC")
|
|
assert a == b
|
|
assert len(a) == 16
|
|
|
|
def test_different_inputs_differ(self):
|
|
base = make_reference_hash(datetime(2026, 4, 15), "X", 100.0, "CRC")
|
|
assert make_reference_hash(datetime(2026, 4, 16), "X", 100.0, "CRC") != base
|
|
assert make_reference_hash(datetime(2026, 4, 15), "X", 100.1, "CRC") != base
|
|
assert make_reference_hash(datetime(2026, 4, 15), "X", 100.0, "USD") != base
|