2026-10-26 12:40:20 +01:00
|
|
|
"""Tests run against a throwaway home: progress, config and caches are module-level
|
|
|
|
|
paths from platformdirs, read at import time, so the environment is set before anything
|
|
|
|
|
from diora_tui is imported."""
|
|
|
|
|
import os
|
|
|
|
|
import tempfile
|
|
|
|
|
import zipfile
|
|
|
|
|
from pathlib import Path
|
|
|
|
|
|
|
|
|
|
_HOME = tempfile.mkdtemp(prefix="diora-tui-test-")
|
|
|
|
|
for var in ("LOCALAPPDATA", "APPDATA", "XDG_DATA_HOME", "XDG_CONFIG_HOME", "XDG_CACHE_HOME"):
|
|
|
|
|
os.environ[var] = _HOME
|
|
|
|
|
os.environ["HOME"] = _HOME
|
TUI: Lesestand-Vertrag (zuletzt gelesen gewinnt), Login wie diora_client, Bücher beim Öffnen laden
progress.py setzt docs/progress-sync.md um (read_at in Serverzeit mit Uhrversatz, sent_at beim
Senden, force, Warteschlange je Buch, Neuanwendung nach dem Abgleich; dieselben Regeln wie
Ops.kt/ops.js). Der Reader speichert nur, was der Leser bewegt hat; wiederherstellen,
Umbruch und Sprünge schreiben nichts. Neuere Stellen anderer Geräte werden angeboten
("Auf X weitergelesen – springen?"), nie von allein angewendet; "w" springt zur weitesten
Stelle. Fortschritt als Textanteil in Zeichen statt 0.0.
Abgleich über GET /books/ (ohne Markierungs-Blobs), Bücher erst beim Öffnen geladen
(☁ in der Liste). `diora-tui login` meldet wie die Apps an, liest den vorhandenen Token,
leitet den Schlüssel aus dem Passwort ab und speichert beides geschützt (DPAPI).
DIORA_TUI_HOME legt alle Dateien in einen Ordner (platformdirs folgt unter Windows den
Umgebungsvariablen nicht). 35 Tests, tools/smoke_server.py gegen einen lokalen Server.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-26 13:08:59 +01:00
|
|
|
os.environ["DIORA_TUI_HOME"] = _HOME # platformdirs ignores the variables above on Windows
|
2026-10-26 12:40:20 +01:00
|
|
|
|
|
|
|
|
import pytest # noqa: E402
|
|
|
|
|
|
|
|
|
|
CONTAINER = """<?xml version="1.0"?>
|
|
|
|
|
<container version="1.0" xmlns="urn:oasis:names:tc:opendocument:xmlns:container">
|
|
|
|
|
<rootfiles><rootfile full-path="OEBPS/content.opf" media-type="application/oebps-package+xml"/></rootfiles>
|
|
|
|
|
</container>"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def make_epub(path: Path, chapters: list[str], title: str = "Testbuch") -> Path:
|
|
|
|
|
"""A minimal EPUB whose spine is `chapters` (each one the full XHTML of a document)."""
|
|
|
|
|
items = "".join(f'<item id="c{i}" href="c{i}.xhtml" media-type="application/xhtml+xml"/>' for i in range(len(chapters)))
|
|
|
|
|
spine = "".join(f'<itemref idref="c{i}"/>' for i in range(len(chapters)))
|
|
|
|
|
opf = f"""<?xml version="1.0"?>
|
|
|
|
|
<package xmlns="http://www.idpf.org/2007/opf" version="2.0" unique-identifier="id">
|
|
|
|
|
<metadata xmlns:dc="http://purl.org/dc/elements/1.1/"><dc:title>{title}</dc:title><dc:creator>Autor</dc:creator>
|
|
|
|
|
<dc:identifier id="id">test-{title}</dc:identifier><dc:language>de</dc:language></metadata>
|
|
|
|
|
<manifest>{items}</manifest><spine>{spine}</spine></package>"""
|
|
|
|
|
with zipfile.ZipFile(path, "w") as z:
|
|
|
|
|
z.writestr("mimetype", "application/epub+zip")
|
|
|
|
|
z.writestr("META-INF/container.xml", CONTAINER)
|
|
|
|
|
z.writestr("OEBPS/content.opf", opf)
|
|
|
|
|
for i, body in enumerate(chapters):
|
|
|
|
|
z.writestr(f"OEBPS/c{i}.xhtml", f'<?xml version="1.0"?><html xmlns="http://www.w3.org/1999/xhtml"><head><title>x</title></head><body>{body}</body></html>')
|
|
|
|
|
return path
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture
|
|
|
|
|
def epub(tmp_path):
|
|
|
|
|
def build(chapters, title="Testbuch"):
|
|
|
|
|
return make_epub(tmp_path / f"{title}.epub", chapters, title)
|
|
|
|
|
return build
|
TUI: Bibliothek wie im Web (Weiterlesen-Karte, Status- und Ordner-Chips, Suche, Sortierung, Länge)
library.py hält die Regeln der Web-Bibliothek (53-library.js) als reine Funktionen:
Status Angefangen/Ungelesen/Gelesen, Ordner (mehrere je Buch, Reihenfolge aus den
verschlüsselten Bibliothekseinstellungen, "Ohne Ordner"), Suche ohne Akzente, sechs
Sortierungen mit natürlicher Zahlenordnung, Weiterlesen-Karte aus den zuletzt gelesenen,
Bücher/Zeitschriften. library_screen.py zeigt sie an (Tasten 1-4, o, /, s, m, c; Chips und
Karte klickbar). Metadaten (Ordner, Seitenzahl, Zeitschrift) und die Ordnerreihenfolge
kommen beim Abgleich; fehlt die Länge, rechnet die TUI sie für geladene Bücher aus dem Text
(1800 Zeichen je Seite). crypto.encrypt für Stufe 3. 54 Tests, Smoke prüft Ordner/Länge/Prefs
gegen den lokalen Server.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-26 13:18:49 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
|
|
|
def clean_library_meta():
|
|
|
|
|
"""library.json is one file in the shared test home: every test starts without it."""
|
|
|
|
|
from diora_tui import library_meta
|
|
|
|
|
|
|
|
|
|
library_meta._META_FILE.unlink(missing_ok=True)
|
|
|
|
|
yield
|
|
|
|
|
library_meta._META_FILE.unlink(missing_ok=True)
|