Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
90 lines
3.8 KiB
Python
90 lines
3.8 KiB
Python
from datetime import date, timedelta
|
|
from decimal import Decimal
|
|
|
|
import pytest
|
|
|
|
from app.models.tables import Account, Category, Transaction
|
|
|
|
H = {"Authorization": "Bearer test-key"}
|
|
|
|
|
|
def _tx(db, acc, day, amount, purpose="Zweck", counterparty="X", category=None, n=[0]):
|
|
n[0] += 1
|
|
db.add(Transaction(account_id=acc.id, booking_date=day, amount=Decimal(amount),
|
|
purpose=purpose, counterparty=counterparty, status="confirmed",
|
|
category_id=category.id if category else None,
|
|
dedup_hash=f"sort-{n[0]}"))
|
|
|
|
|
|
@pytest.fixture
|
|
def small(db):
|
|
zeta = Account(bank="t", iban="DE-SO-1", name="Zeta", type="giro")
|
|
alpha = Account(bank="t", iban="DE-SO-2", name="alpha", type="giro")
|
|
kat = Category(name="Miete")
|
|
db.add_all([zeta, alpha, kat])
|
|
db.flush()
|
|
_tx(db, zeta, date(2026, 1, 3), "-5.00", "b-Zweck", "Bert", kat)
|
|
_tx(db, alpha, date(2026, 1, 1), "20.00", "A-Zweck", "anna", None)
|
|
_tx(db, zeta, date(2026, 1, 2), "-30.00", "c-Zweck", "Carl", kat)
|
|
db.commit()
|
|
|
|
|
|
def _amounts(client, qs):
|
|
r = client.get(f"/api/transactions?{qs}", headers=H)
|
|
assert r.status_code == 200
|
|
return [Decimal(t["amount"]) for t in r.json()]
|
|
|
|
|
|
def test_standard_bleibt_datum_aufsteigend(client, small):
|
|
assert _amounts(client, "") == [Decimal("20.00"), Decimal("-30.00"), Decimal("-5.00")]
|
|
|
|
|
|
@pytest.mark.parametrize("sort,dir,expected", [
|
|
("betrag", "asc", ["-30.00", "-5.00", "20.00"]),
|
|
("betrag", "desc", ["20.00", "-5.00", "-30.00"]),
|
|
("datum", "desc", ["-5.00", "-30.00", "20.00"]),
|
|
("konto", "asc", ["20.00", "-5.00", "-30.00"]), # alpha vor Zeta (case-insensitiv), Gleichstand -> id
|
|
("zweck", "asc", ["20.00", "-5.00", "-30.00"]), # A-, b-, c-Zweck
|
|
("empfaenger", "desc", ["-30.00", "-5.00", "20.00"]), # Carl, Bert, anna
|
|
("kategorie", "asc", ["-5.00", "-30.00", "20.00"]), # Miete (id-Reihenfolge), unkategorisiert zuletzt
|
|
("kategorie", "desc", ["-30.00", "-5.00", "20.00"]), # unkategorisiert auch absteigend zuletzt
|
|
])
|
|
def test_sortierung_je_spalte(client, small, sort, dir, expected):
|
|
assert _amounts(client, f"sort={sort}&dir={dir}") == [Decimal(e) for e in expected]
|
|
|
|
|
|
def test_ungueltige_werte_fallen_auf_standard(client, small):
|
|
assert _amounts(client, "sort=foo;drop&dir=hoch") == _amounts(client, "")
|
|
client.post("/login", data={"username": "admin", "password": "geheim"})
|
|
assert client.get("/buchungen?sort=foo&dir=bar").status_code == 200
|
|
|
|
|
|
@pytest.fixture
|
|
def many(db):
|
|
acc = Account(bank="t", iban="DE-SO-3", name="Viel", type="giro")
|
|
db.add(acc)
|
|
db.flush()
|
|
for i in range(1, 61): # Beträge i,37; Datum verwürfelt
|
|
_tx(db, acc, date(2026, 1, 1) + timedelta(days=(i * 7) % 60), f"{i}.37", f"Miete {i}")
|
|
db.commit()
|
|
|
|
|
|
def test_sortierung_gilt_ueber_alle_seiten(client, many):
|
|
client.post("/login", data={"username": "admin", "password": "geheim"})
|
|
p1 = client.get("/buchungen?sort=betrag&dir=desc").text
|
|
p2 = client.get("/buchungen?sort=betrag&dir=desc&page=2").text
|
|
assert ">60,37</td>" in p1 and ">1,37</td>" not in p1
|
|
assert ">1,37</td>" in p2 and ">60,37</td>" not in p2
|
|
|
|
|
|
def test_links_erhalten_filter_und_sortierung(client, many):
|
|
client.post("/login", data={"username": "admin", "password": "geheim"})
|
|
html = client.get("/buchungen?q=Miete&sort=betrag&dir=asc").text
|
|
# Aktiver Spaltenkopf kehrt die Richtung um, übernimmt den Filter, ohne page.
|
|
assert 'href="/buchungen?q=Miete&status=confirmed&sort=betrag&dir=desc"' in html
|
|
# Inaktive Spalte startet aufsteigend.
|
|
assert 'href="/buchungen?q=Miete&status=confirmed&sort=datum&dir=asc"' in html
|
|
# Pager behält Filter + Sortierung.
|
|
assert "q=Miete&status=confirmed&sort=betrag&dir=asc&page=2" in html
|
|
assert "▲" in html
|