Files
certain/backend/tests/test_backup.py
T
GuidoandClaude Opus 5 3ee18c4d21 fix: Neue Tabellen ins Backup, toter Transfer-Code raus
Sieben nutzereigene Tabellen aus den Migrationen 0015-0020 standen in keiner
Backup-Liste. Am schwersten wiegt inbox_items: Der Eingang haelt fest, welche
Zahlungen von aussen schon erfasst wurden. Fehlt er nach dem Wiedereinspielen,
erkennt der naechste Kontoauszug sie nicht wieder und bucht sie ein zweites
Mal – genau der Fehler, den die Tabelle verhindern soll.

Aufgenommen: inbox_items, transfer_links, link_ignored, api_tokens.
Bewusst nicht: mail_sources/mail_senders (tragen das verschluesselte
IMAP-Passwort; ein Backup ist eine Datei, die man herumreicht) und
pending_uploads (fluechtige Rohdateien). Beides steht begruendet im Code.

Ein Test laeuft jetzt ueber Base.registry und fordert fuer jedes Modell mit
owner_id eine bewusste Entscheidung – sichern oder begruendet ausnehmen.
Genau diese Pruefung fehlte, weshalb die sieben unbemerkt durchrutschten.

Dazu der tote Code aus dem Transfer-Ausbau: is_counterpart und braucht_ziel
waren feste False, _apply_counterpart_amount damit unerreichbar. Die Tests
sichern dieselbe Garantie jetzt ueber das tatsaechliche Verhalten.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-26 21:16:33 +02:00

89 lines
3.0 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import json
from datetime import date
from conftest import login_ready, new_user
def _setup(admin):
acc = admin.post("/api/accounts", json={"name": "Exp", "currency": "EUR"}).json()
admin.post("/api/transactions", json={
"type": "expense", "date": str(date.today()), "account_id": acc["id"],
"amount_cents": 4200, "payee": "Testladen", "description": "Etwas",
})
return acc
def test_export_transactions_csv(admin):
_setup(admin)
r = admin.get("/api/export/transactions.csv")
assert r.status_code == 200
assert "text/csv" in r.headers["content-type"]
body = r.content.decode("utf-8-sig")
assert "Datum;Typ;Konto" in body.splitlines()[0]
assert "Testladen" in body
def test_export_transactions_json(admin):
_setup(admin)
r = admin.get("/api/export/transactions.json")
assert r.status_code == 200
data = json.loads(r.content)
assert any(t["payee"] == "Testladen" for t in data)
def test_export_all_json(admin):
_setup(admin)
r = admin.get("/api/export/all.json")
assert r.status_code == 200
data = json.loads(r.content)
assert data["app"] == "Certain Finance"
assert len(data["accounts"]) >= 1
assert len(data["transactions"]) >= 1
def test_db_backup_admin_only(admin):
# Admin darf.
r = admin.get("/api/backup/db")
assert r.status_code == 200
assert r.content[:16].startswith(b"SQLite format 3")
# Normaler Nutzer darf nicht.
other, _ = login_ready(*new_user(admin))
assert other.get("/api/backup/db").status_code == 403
# ...aber seinen eigenen Export schon (leer).
assert other.get("/api/export/all.json").status_code == 200
def test_jede_nutzertabelle_ist_im_backup_beruecksichtigt():
"""Neue Tabellen dürfen nicht stillschweigend aus dem Backup fallen.
Sieben Tabellen sind genau so entstanden: angelegt, migriert, benutzt – und
in ``backup_data`` nie eingetragen. Aufgefallen wäre das erst beim
Wiedereinspielen, also im schlechtesten Moment. Dieser Test zwingt jede
künftige Tabelle mit ``owner_id`` zu einer bewussten Entscheidung: entweder
sichern oder hier begründet ausnehmen.
"""
from app.models import Base
from app.services import backup_data
gesichert = {
spec.model.__tablename__
for liste in (backup_data.USER_TABLES, backup_data.SYSTEM_USER_TABLES,
backup_data.GLOBAL_TABLES)
for spec in liste
}
# Bewusst ausgenommen – die Begründung steht in backup_data.py.
ausgenommen = {"mail_sources", "mail_senders", "pending_uploads"}
fehlend = sorted(
m.class_.__tablename__
for m in Base.registry.mappers
if "owner_id" in m.class_.__table__.columns
and m.class_.__tablename__ not in gesichert
and m.class_.__tablename__ not in ausgenommen
)
assert not fehlend, (
f"Diese Tabellen gehören dem Nutzer, stehen aber in keiner Backup-Liste: "
f"{fehlend}. Entweder in USER_TABLES aufnehmen oder oben begründet ausnehmen."
)