Files
Plugin_SN_Verfahrensgebiet/tests/test_eigentuemer_korrektur.py
T
DanielandClaude Opus 5.5 e547cb90c5 Anzeige für den Eigentümer und Korrekturen im Tab „Eigentümer“ (#56)
„Daten anzeigen“ in der Trefferliste zeigt Name, Anschrift und E-Mail der
gewählten Einträge in einem eigenen Fenster: auf einem zweiten Bildschirm
im Vollbild, auf dem QGIS-Bildschirm als normales Fenster. Die Sitzung
wird von Hand begonnen und beendet; „Anzeige leeren“ entfernt die Daten,
ohne das Fenster zu schließen. Konten, IDs und Kennzeichen erscheinen dort
nicht, alle Werte werden maskiert.

„Korrigieren …“ ändert angezeigte Daten. Die Eigentümerdatei bleibt
unverändert; jede Änderung wird mit Zeitpunkt, Konto, Personen-ID, altem
und neuem Wert an <Eigentümerdatei>_korrekturen.csv angehängt und sofort
angezeigt. Ohne beschreibbaren Ordner wird nach dem Speicherort gefragt.

In test_eigentuemer_tab.py sind die beim letzten Mal zusammengezogenen
with-Zeilen wieder umbrochen; die Trefferliste bekommt jetzt den
Anzeige-Rückruf, der Test dazu ist angepasst.

Dritter und letzter Schritt von #56.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 13:27:15 +02:00

135 lines
6.4 KiB
Python

"""Tests für die Korrekturen beim Eigentümer-Datenabgleich (#56).
Alle Namen und Anschriften sind erfunden.
"""
import contextlib
import csv
import io
import os
import tempfile
import unittest
from datetime import datetime
from sn_verfahrensgebiet.functions import eigentuemer_korrektur as ek
from sn_verfahrensgebiet.functions.eigentuemer_suche import Eigentuemer
ANNA = Eigentuemer(anrede="Frau", vorname="Anna", nachname="Beispiel", strasse="Hauptstraße 1", plz="01234",
wohnort="Musterstadt", emails=("anna@example.org",), per_ids=("7",), konten=("100", "200"))
class TestAenderungen(unittest.TestCase):
def test_feldwerte(self):
"""Prüft, dass alle korrigierbaren Felder vorbelegt werden, mehrere E-Mails durch Komma getrennt."""
werte = ek.feldwerte(Eigentuemer(nachname="Beispiel", emails=("a@example.org", "b@example.org")))
self.assertEqual(set(werte), {k for k, _ in ek.FELDER})
self.assertEqual(werte["email"], "a@example.org, b@example.org")
self.assertEqual(werte["nachname"], "Beispiel")
def test_nur_echte_aenderungen(self):
"""Prüft, dass nur geänderte Felder zählen und Leerraum am Rand oder doppelte Leerzeichen keine Änderung sind."""
werte = ek.feldwerte(ANNA)
werte.update(strasse=" Hauptstraße 1 ", wohnort="Neustadt", email="anna.neu@example.org")
self.assertEqual(ek.ermittle_aenderungen(ANNA, werte), [
("wohnort", "Musterstadt", "Neustadt"),
("email", "anna@example.org", "anna.neu@example.org"),
])
self.assertEqual(ek.ermittle_aenderungen(ANNA, ek.feldwerte(ANNA)), [])
def test_feld_leeren_ist_aenderung(self):
"""Prüft, dass das Leeren eines Feldes als Änderung zählt."""
self.assertEqual(ek.ermittle_aenderungen(ANNA, {"email": ""}), [("email", "anna@example.org", "")])
def test_wende_an(self):
"""Prüft, dass die Änderungen im angezeigten Eintrag landen und E-Mails wieder getrennt werden; Konten bleiben."""
neu = ek.wende_an(ANNA, [("wohnort", "Musterstadt", "Neustadt"),
("email", "", "a@example.org, b@example.org")])
self.assertEqual(neu.wohnort, "Neustadt")
self.assertEqual(neu.emails, ("a@example.org", "b@example.org"))
self.assertEqual(neu.konten, ANNA.konten)
self.assertEqual(ANNA.wohnort, "Musterstadt")
class TestKorrekturPfad(unittest.TestCase):
def test_textdatei_provider(self):
"""Prüft, dass aus der Quelle eines Textdatei-Layers (file-URI, Leerzeichen kodiert) der Pfad neben der CSV wird."""
quelle = "file:///D:/Verfahren/Eigent%C3%BCmer%20Export.csv?type=csv&detectTypes=yes&geomType=none"
self.assertEqual(ek.korrektur_pfad(quelle), "D:/Verfahren/Eigentümer Export_korrekturen.csv")
self.assertEqual(ek.korrektur_pfad("file:///home/test/export.CSV?type=csv"),
"/home/test/export_korrekturen.csv")
def test_ogr_provider(self):
"""Prüft, dass auch eine OGR-Quelle mit Layernamen den Pfad neben der CSV ergibt."""
self.assertEqual(ek.korrektur_pfad("D:/Verfahren/export.csv|layername=export"),
"D:/Verfahren/export_korrekturen.csv")
def test_keine_csv(self):
"""Prüft, dass für Quellen ohne CSV-Datei kein Pfad vorgeschlagen wird."""
self.assertIsNone(ek.korrektur_pfad(""))
self.assertIsNone(ek.korrektur_pfad("D:/Verfahren/daten.gpkg|layername=export"))
self.assertIsNone(ek.korrektur_pfad("memory?geometry=None"))
class TestSchreiben(unittest.TestCase):
def setUp(self):
self._ordner = tempfile.TemporaryDirectory()
self.pfad = os.path.join(self._ordner.name, "export_korrekturen.csv")
def tearDown(self):
self._ordner.cleanup()
def _zeilen(self):
with open(self.pfad, encoding="utf-8-sig", newline="") as datei:
return list(csv.reader(datei))
def test_neue_datei_mit_kopfzeile_und_bom(self):
"""Prüft, dass eine neue Korrektur-CSV mit BOM, Kopfzeile und je Änderung einer Zeile mit Exportspaltennamen entsteht."""
anzahl = ek.schreibe_korrekturen(self.pfad, ANNA, [
("strasse", "Hauptstraße 1", "Am Anger 3, Haus B"),
("email", "anna@example.org", ""),
], zeitpunkt=datetime(2026, 10, 6, 14, 30, 5))
self.assertEqual(anzahl, 2)
with open(self.pfad, "rb") as datei:
self.assertTrue(datei.read().startswith(b"\xef\xbb\xbf"))
self.assertEqual(self._zeilen(), [
ek.KOPFZEILE,
["2026-10-06 14:30:05", "100 200", "7", "STRASSE", "Hauptstraße 1", "Am Anger 3, Haus B"],
["2026-10-06 14:30:05", "100 200", "7", "Email", "anna@example.org", ""],
])
def test_anhaengen_ohne_zweite_kopfzeile(self):
"""Prüft, dass weitere Korrekturen angehängt werden, ohne Kopfzeile oder BOM zu wiederholen."""
ek.schreibe_korrekturen(self.pfad, ANNA, [("wohnort", "Musterstadt", "Neustadt")])
ek.schreibe_korrekturen(self.pfad, ANNA, [("plz", "01234", "01235")])
zeilen = self._zeilen()
self.assertEqual(len(zeilen), 3)
self.assertEqual(zeilen[0], ek.KOPFZEILE)
self.assertEqual([z[3] for z in zeilen[1:]], ["WOHNORT", "PLZ"])
with open(self.pfad, "rb") as datei:
self.assertEqual(datei.read().count(b"\xef\xbb\xbf"), 1)
def test_ohne_aenderung_keine_datei(self):
"""Prüft, dass ohne Änderung keine Datei angelegt wird."""
self.assertEqual(ek.schreibe_korrekturen(self.pfad, ANNA, []), 0)
self.assertFalse(os.path.exists(self.pfad))
def test_schreibfehler_wird_weitergereicht(self):
"""Prüft, dass ein nicht schreibbarer Pfad als OSError beim Aufrufer ankommt."""
with self.assertRaises(OSError):
ek.schreibe_korrekturen(os.path.join(self._ordner.name, "fehlt", "x.csv"), ANNA,
[("wohnort", "Musterstadt", "Neustadt")])
def test_schreibt_nichts_auf_die_konsole(self):
"""Prüft, dass das Festhalten von Korrekturen keine Personendaten auf die Konsole schreibt (Datenschutz)."""
ausgabe = io.StringIO()
with contextlib.redirect_stdout(ausgabe), contextlib.redirect_stderr(ausgabe):
ek.schreibe_korrekturen(self.pfad, ANNA, [("wohnort", "Musterstadt", "Neustadt")])
self.assertEqual(ausgabe.getvalue(), "")
if __name__ == "__main__":
unittest.main()