Backup & Export: - routers/backup: /backup/db (Admin, konsistentes SQLite-Online-Backup), /export/transactions.csv|json und /export/all.json (owner-gescoped). - Einstellungen: Bereich „Backup & Export" mit Download-Buttons. docs/BACKUP.md (Wiederherstellung + LXC-Sicherung). pytest für Export + Admin-Schutz. Import-Verbesserungen (Nutzer-Feedback zum Ablauf): - Klarer Hinweis, dass die Zuordnungstabelle nur Beispielzeilen zeigt und „Vorschau erstellen" die vollständige Prüfliste + Buchen-Button erzeugt. - Zusammenfassung (erkannt/gebucht) und Warnung, wenn keine gültige Zeile erkannt wurde (Hinweis auf Format/Bank-Vorlage). - Fix: Raiffeisen-Preset hat has_header=true (der Export hat eine Kopfzeile). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
60 lines
1.9 KiB
Python
60 lines
1.9 KiB
Python
import json
|
|
import secrets
|
|
from datetime import date
|
|
|
|
|
|
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):
|
|
from fastapi.testclient import TestClient
|
|
from app.main import app
|
|
|
|
# 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.
|
|
u, p = "u_" + secrets.token_hex(4), "pw"
|
|
admin.post("/api/users", json={"username": u, "password": p, "role": "user"})
|
|
other = TestClient(app)
|
|
other.post("/api/auth/login", data={"username": u, "password": p})
|
|
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
|