Files
desktop-planer/backend/tests/test_sync.py
2026-09-22 13:39:16 +02:00

125 lines
5.0 KiB
Python

import importlib
import os
import pytest
from fastapi.testclient import TestClient
@pytest.fixture
def client(tmp_path):
os.environ["DB_PATH"] = str(tmp_path / "test.db")
os.environ["SYNC_TOKEN"] = "geheim"
os.environ["FILES_PAGE_BYTES"] = "150"
from app import config, db, main
importlib.reload(config)
importlib.reload(db)
importlib.reload(main)
with TestClient(main.app) as c:
yield c
AUTH = {"Authorization": "Bearer geheim"}
def sync(client, since=0, **collections):
body = {"since": since, **collections}
r = client.post("/api/sync", json=body, headers=AUTH)
assert r.status_code == 200, r.text
return r.json()
def test_health_ohne_token(client):
assert client.get("/api/health").json() == {"ok": True}
def test_falscher_token(client):
assert client.post("/api/sync", json={}).status_code == 401
assert client.post("/api/sync", json={}, headers={"Authorization": "Bearer falsch"}).status_code == 401
assert client.get("/api/events").status_code == 401
def test_push_und_pull(client):
a = sync(client, events=[{"id": "e1", "updated": "2026-09-19T10:00:00.000Z", "title": "Zahnarzt"}])
assert a["cursor"] == 1
assert [e["id"] for e in a["events"]] == ["e1"]
# Anderes Gerät ohne Stand bekommt alles
b = sync(client)
assert [e["title"] for e in b["events"]] == ["Zahnarzt"]
assert b["cursor"] == 1
# Mit aktuellem Stand kommt nichts Neues
c = sync(client, since=1)
assert c["events"] == [] and c["cursor"] == 1
def test_neuester_gewinnt(client):
sync(client, todos=[{"id": "t1", "updated": "2026-09-19T10:00:00.000Z", "title": "alt"}])
sync(client, todos=[{"id": "t1", "updated": "2026-09-19T09:00:00.000Z", "title": "älter"}])
assert client.get("/api/todos", headers=AUTH).json()[0]["title"] == "alt"
r = sync(client, since=0, todos=[{"id": "t1", "updated": "2026-09-19T11:00:00.000Z", "title": "neu"}])
assert r["todos"][0]["title"] == "neu"
assert r["cursor"] == 2
def test_abgelehnte_aenderung_bekommt_serverstand(client):
sync(client, todos=[{"id": "t1", "updated": "2026-09-19T10:00:00.000Z", "title": "server"}])
r = sync(client, since=1, todos=[{"id": "t1", "updated": "2026-09-19T09:00:00.000Z", "title": "client"}])
assert [t["title"] for t in r["todos"]] == ["server"]
assert r["cursor"] == 1
def test_tombstone(client):
sync(client, notes=[{"id": "Plan.md", "updated": "2026-09-19T10:00:00.000Z", "content": "x"}])
r = sync(client, since=1, notes=[{"id": "Plan.md", "updated": "2026-09-19T10:05:00.000Z", "deleted": True}])
assert r["notes"][0]["deleted"] is True
assert client.get("/api/notes", headers=AUTH).json() == []
# Ein verspäteter älterer Stand belebt die Notiz nicht wieder
sync(client, notes=[{"id": "Plan.md", "updated": "2026-09-19T10:01:00.000Z", "content": "y"}])
assert client.get("/api/notes", headers=AUTH).json() == []
def test_ordner_laufen_mit(client):
r = sync(client, folders=[{"id": "Projekte/Alt", "updated": "2026-09-19T10:00:00.000Z", "deleted": False}])
assert r["folders"][0]["id"] == "Projekte/Alt"
r = sync(client, since=1, folders=[{"id": "Projekte/Alt", "updated": "2026-09-19T10:05:00.000Z", "deleted": True}])
assert r["folders"][0]["deleted"] is True
def test_seit_cursor_liefert_nur_neues(client):
sync(client, events=[{"id": "e1", "updated": "2026-09-19T10:00:00.000Z", "title": "a"}])
sync(client, events=[{"id": "e2", "updated": "2026-09-19T10:00:00.000Z", "title": "b"}])
r = sync(client, since=1)
assert [e["id"] for e in r["events"]] == ["e2"]
def test_dateien_nur_auf_anfrage(client):
sync(client, files=[{"id": "-home-x/s1.jsonl", "updated": "2026-09-22T10:00:00.000Z", "content": "zeile"}])
# Handy und Extension fragen ohne files: nichts davon in der Antwort
r = sync(client, events=[], todos=[], notes=[], folders=[])
assert "files" not in r and r["events"] == []
assert "files" not in sync(client)
# Nur Dateien angefragt: nur Dateien zurück, Stand zählt trotzdem
r = sync(client, files=[])
assert [f["id"] for f in r["files"]] == ["-home-x/s1.jsonl"] and "events" not in r
assert r["cursor"] == 1
# Tombstone gewinnt mit neuerem updated
r = sync(client, files=[{"id": "-home-x/s1.jsonl", "updated": "2026-09-22T11:00:00.000Z", "deleted": True}])
assert r["files"][-1]["deleted"] is True
def test_dateien_seitenweise(client):
for i in range(3):
sync(client, files=[{"id": f"s{i}.jsonl", "updated": "2026-09-22T10:00:00.000Z", "content": "x" * 60}])
# Jede Antwort höchstens 150 Bytes: ein Eintrag pro Seite, more zeigt an, dass es weitergeht
seen, since = [], 0
for _ in range(5):
r = sync(client, since=since, files=[])
seen += [f["id"] for f in r["files"]]
since = r["cursor"]
if not r.get("more"):
break
assert seen == ["s0.jsonl", "s1.jsonl", "s2.jsonl"] and since == 3
# Basis-Sammlungen bleiben ungeteilt
assert "more" not in sync(client)