"""Suche und Filter auf der Buchungsliste: ``q``, ``tag_id``, ``subscription_id``. Bisher siebte die Weboberflaeche im Browser und lud dafuer alles, was es gab - das Limit steht nicht umsonst auf 5000. Am Telefon ist das der Unterschied zwischen "laedt" und "benutzbar", deshalb sitzen die drei Filter jetzt im Server. Die Datenbank lebt ueber den ganzen Testlauf. Texte deshalb eindeutig wuerfeln, sonst findet die Suche die Daten anderer Tests mit. """ import secrets from conftest import login_ready, new_user def _konto(admin): return admin.post("/api/accounts", json={ "name": f"Konto {secrets.token_hex(3)}", "type": "girokonto", "currency": "EUR", "opening_balance_cents": 0}).json() def _buchung(admin, konto, **felder): r = admin.post("/api/transactions", json={ "type": "expense", "date": "2026-09-15", "account_id": konto["id"], "amount_cents": 100 + secrets.randbelow(9000), **felder}) assert r.status_code == 201, r.text return r.json() def _suche(admin, **filter_): r = admin.get("/api/transactions", params=filter_) assert r.status_code == 200, r.text return [t["id"] for t in r.json()] def test_suche_findet_empfaenger_verwendungszweck_und_notiz(admin): konto = _konto(admin) wort = f"Zebra{secrets.token_hex(4)}" ueber_empfaenger = _buchung(admin, konto, payee=f"Laden {wort}") ueber_zweck = _buchung(admin, konto, payee="Egal", description=f"Rechnung {wort}") ueber_notiz = _buchung(admin, konto, payee="Egal", notes=f"wegen {wort}") daneben = _buchung(admin, konto, payee="Etwas voellig anderes") gefunden = _suche(admin, q=wort) assert set(gefunden) == {ueber_empfaenger["id"], ueber_zweck["id"], ueber_notiz["id"]} assert daneben["id"] not in gefunden def test_suche_achtet_nicht_auf_gross_und_kleinschreibung(admin): konto = _konto(admin) wort = f"Kiosk{secrets.token_hex(4)}" txn = _buchung(admin, konto, payee=wort.upper()) assert txn["id"] in _suche(admin, q=wort.lower()) def test_prozentzeichen_im_suchtext_bleibt_ein_zeichen(admin): """Sonst waere jede Eingabe mit % ein Platzhalter und faende alles.""" konto = _konto(admin) marke = secrets.token_hex(4) mit_prozent = _buchung(admin, konto, payee=f"Rabatt%{marke}") ohne = _buchung(admin, konto, payee=f"Rabatt {marke}") gefunden = _suche(admin, q=f"Rabatt%{marke}") assert gefunden == [mit_prozent["id"]] assert ohne["id"] not in gefunden def test_tag_filter_zeigt_nur_buchungen_mit_diesem_tag(admin): konto = _konto(admin) tag = admin.post("/api/tags", json={ "name": f"Tag {secrets.token_hex(4)}", "color": "#3f51b5"}).json() mit = _buchung(admin, konto, payee="Mit Etikett") ohne = _buchung(admin, konto, payee="Ohne Etikett") admin.patch(f"/api/transactions/{mit['id']}", json={"tag_ids": [tag["id"]]}) gefunden = _suche(admin, tag_id=tag["id"]) assert gefunden == [mit["id"]] assert ohne["id"] not in gefunden def test_tag_eines_fremden_nutzers_zeigt_keine_buchungen(admin): """Mandanten-Isolation: Eine geratene fremde ID darf nichts aufschliessen.""" konto = _konto(admin) meine = _buchung(admin, konto, payee="Meine Buchung") name, passwort = new_user(admin) fremd, _ = login_ready(name, passwort) fremder_tag = fremd.post("/api/tags", json={ "name": f"Fremd {secrets.token_hex(4)}", "color": "#eb6834"}).json() gefunden = _suche(admin, tag_id=fremder_tag["id"]) assert gefunden == [] assert meine["id"] not in gefunden def test_abo_filter_zeigt_nur_buchungen_dieses_vertrags(admin): konto = _konto(admin) abo = admin.post("/api/subscriptions", json={ "name": f"Dienst {secrets.token_hex(4)}", "amount_cents": 999, "currency": "EUR", "interval_months": 1}).json() zugeordnet = _buchung(admin, konto, payee="Monatsbeitrag") frei = _buchung(admin, konto, payee="Einmalkauf") r = admin.post(f"/api/subscriptions/{abo['id']}/buchungen/{zugeordnet['id']}") assert r.status_code == 200, r.text gefunden = _suche(admin, subscription_id=abo["id"]) assert gefunden == [zugeordnet["id"]] assert frei["id"] not in gefunden def test_suche_und_tag_filter_greifen_zusammen(admin): konto = _konto(admin) wort = f"Baumarkt{secrets.token_hex(4)}" tag = admin.post("/api/tags", json={ "name": f"Tag {secrets.token_hex(4)}", "color": "#2a78d6"}).json() beides = _buchung(admin, konto, payee=f"{wort} Farbe") nur_text = _buchung(admin, konto, payee=f"{wort} Schrauben") nur_tag = _buchung(admin, konto, payee="Etwas anderes") for txn in (beides, nur_tag): admin.patch(f"/api/transactions/{txn['id']}", json={"tag_ids": [tag["id"]]}) gefunden = _suche(admin, q=wort, tag_id=tag["id"]) assert gefunden == [beides["id"]] assert nur_text["id"] not in gefunden and nur_tag["id"] not in gefunden