"""Tests für die Eigentümersuche im Serienbriefexport (#56). Alle Namen und Anschriften sind erfunden. """ import contextlib import io import types import unittest from unittest.mock import patch from sn_verfahrensgebiet.functions import eigentuemer_suche as es from sn_verfahrensgebiet.functions.eigentuemer_suche import Eigentuemer KOPF = [ "EIG_KONTO", "PER_ID", "PER_VERSTORBEN", "PER_NR", "Anrede", "Titel", "Vorname", "Nachname", "OT", "STRASSE", "PLZ", "WOHNORT", "Email", "PER_KONTOINHABER", "PER_RECHTSPERSON", "EIG_GEMARKUNG", "EIG_BLATT", "LISTE_ZGBBL", "ANZAHL_PERSONEN", "EIG_ID", ] class _Null: """Nachbildung von QGIS-NULL.""" def isNull(self): return True class _Feature(dict): def __init__(self, fid, werte): super().__init__(werte) self._fid = fid def id(self): return self._fid class _Layer: def __init__(self, zeilen, kopf=KOPF, name="Serienbrief", layer_id="csv1"): self._kopf = list(kopf) self._name = name self._id = layer_id self._features = [ _Feature(i + 1, {spalte: zeile.get(spalte.lstrip(""), "") for spalte in self._kopf}) for i, zeile in enumerate(zeilen) ] def name(self): return self._name def id(self): return self._id def fields(self): return types.SimpleNamespace(names=lambda: list(self._kopf)) def getFeatures(self): return iter(self._features) def _zeile(nachname, vorname, strasse="Hauptstraße 1", plz="01234", ort="Musterstadt", **weitere): zeile = {"Nachname": nachname, "Vorname": vorname, "STRASSE": strasse, "PLZ": plz, "WOHNORT": ort} zeile.update(weitere) return zeile class TestLayererkennung(unittest.TestCase): def test_layer_mit_name_und_vorname_ist_eigentuemerlayer(self): """Prüft, dass ein Layer mit den Spalten Nachname und Vorname als Eigentümerdatei erkannt wird, auch mit BOM und anderer Schreibweise.""" self.assertTrue(es.ist_eigentuemerlayer(_Layer([]))) self.assertTrue(es.ist_eigentuemerlayer(_Layer([], kopf=["NACHNAME", "vorname"]))) self.assertFalse(es.ist_eigentuemerlayer(_Layer([], kopf=["flurstueckskennzeichen"]))) self.assertFalse(es.ist_eigentuemerlayer(object())) def test_eigentuemerlayer_im_projekt(self): """Prüft, dass aus dem Projekt nur passende Layer kommen, nach Namen sortiert.""" b = _Layer([], name="b-Export", layer_id="b") a = _Layer([], name="A-Export", layer_id="a") flst = _Layer([], kopf=["flurstueckskennzeichen"], name="Flurstücke", layer_id="f") projekt = types.SimpleNamespace(mapLayers=lambda: {"b": b, "f": flst, "a": a}) with patch.object(es, "QgsProject", types.SimpleNamespace(instance=lambda: projekt)): self.assertEqual(es.eigentuemerlayer_im_projekt(), [a, b]) class TestSuche(unittest.TestCase): def test_ohne_eingabe_fehler(self): """Prüft, dass ohne Name und Vorname nicht die ganze Datei gelistet, sondern um eine Eingabe gebeten wird.""" with self.assertRaisesRegex(es.EigentuemerSucheFehler, "Namen"): es.suche_eigentuemer(_Layer([_zeile("Beispiel", "Anna")]), " ", "") def test_layer_ohne_pflichtspalten(self): """Prüft, dass ein Layer ohne Nachname/Vorname mit Hinweis auf den Serienbriefexport abgelehnt wird.""" with self.assertRaisesRegex(es.EigentuemerSucheFehler, "Serienbriefexport"): es.suche_eigentuemer(_Layer([], kopf=["flurstueckskennzeichen"]), "Beispiel", "") def test_teilstring_ohne_gross_kleinschreibung(self): """Prüft, dass Name und Vorname als Teilstring ohne Rücksicht auf Groß-/Kleinschreibung gesucht werden.""" layer = _Layer([ _zeile("Beispiel", "Anna"), _zeile("Beispiel-Muster", "Bernd"), _zeile("Anders", "Anna"), ]) self.assertEqual([e.nachname for e in es.suche_eigentuemer(layer, "beispiel", "")], ["Beispiel", "Beispiel-Muster"]) self.assertEqual([e.nachname for e in es.suche_eigentuemer(layer, "", "ANNA")], ["Anders", "Beispiel"]) self.assertEqual([e.vorname for e in es.suche_eigentuemer(layer, "beisp", "an")], ["Anna"]) def test_umlaute_und_eszett(self): """Prüft, dass „Müller“ auch „Mueller“ findet und „Strauss“ auch „Strauß“.""" layer = _Layer([_zeile("Mueller", "Jörg"), _zeile("Strauß", "Erika", ort="Anderswo")]) self.assertEqual(len(es.suche_eigentuemer(layer, "Müller", "Joerg")), 1) self.assertEqual(len(es.suche_eigentuemer(layer, "Strauss", "")), 1) def test_gleiche_person_gleiche_anschrift_zusammengefasst(self): """Prüft, dass Zeilen mit gleichem Namen und gleicher Anschrift ein Treffer werden und Konten, Personen-IDs und E-Mails gesammelt werden.""" layer = _Layer([ _zeile("Beispiel", "Anna", EIG_KONTO="100", PER_ID="7", Email="anna@example.org"), _zeile("beispiel ", " Anna", strasse="Hauptstraße 1", EIG_KONTO="200", PER_ID="7", Titel="Dr.", Email="anna@example.org"), _zeile("Beispiel", "Anna", EIG_KONTO="300", PER_ID="8", Email="a.b@example.org"), ]) treffer = es.suche_eigentuemer(layer, "Beispiel", "Anna") self.assertEqual(len(treffer), 1) e = treffer[0] self.assertEqual(e.konten, ("100", "200", "300")) self.assertEqual(e.per_ids, ("7", "8")) self.assertEqual(e.emails, ("anna@example.org", "a.b@example.org")) self.assertEqual(e.feature_ids, (1, 2, 3)) self.assertEqual(e.titel, "Dr.") def test_andere_anschrift_eigener_treffer(self): """Prüft, dass dieselbe Person mit anderer Anschrift ein eigener Treffer bleibt, sortiert nach Wohnort.""" layer = _Layer([ _zeile("Beispiel", "Anna", ort="Zettlitz"), _zeile("Beispiel", "Anna", ort="Altdorf", plz="09999"), ]) treffer = es.suche_eigentuemer(layer, "Beispiel", "Anna") self.assertEqual([e.wohnort for e in treffer], ["Altdorf", "Zettlitz"]) def test_kennzeichen_verstorben(self): """Prüft, dass „verstorben“ beim Exportwert 1 (auch als Zahl gelesen) gesetzt ist und bei 0, Leerem und NULL nicht.""" ja = ["1", 1] nein = ["0", 0, "", None, _Null()] zeilen = [_zeile(f"Ja{i}", "Test", PER_VERSTORBEN=w) for i, w in enumerate(ja)] zeilen += [_zeile(f"Nein{i}", "Test", PER_VERSTORBEN=w) for i, w in enumerate(nein)] for e in es.suche_eigentuemer(_Layer(zeilen), "", "Test"): erwartet = e.nachname.startswith("Ja") with self.subTest(name=e.nachname): self.assertEqual(e.verstorben, erwartet) self.assertEqual(e.hinweise, "verstorben" if erwartet else "") def test_rechtsperson_als_rohwert(self): """Prüft, dass PER_RECHTSPERSON unverändert angezeigt wird (0, 1, 5, 9; noch nicht entschlüsselt) und abweichende Werte einer Person gesammelt werden.""" layer = _Layer([ _zeile("Beispiel", "Anna", PER_RECHTSPERSON="0"), _zeile("Gemeinde Musterstadt", "", strasse="Markt 1", PER_RECHTSPERSON=5), _zeile("Muster", "Bernd", PER_RECHTSPERSON="1"), _zeile("Muster", "Bernd", PER_RECHTSPERSON="9"), _zeile("Ohnewert", "Clara", PER_RECHTSPERSON=_Null()), ]) werte = {e.nachname: e.rechtsperson_text for e in es.suche_eigentuemer(layer, "e", "")} self.assertEqual(werte, {"Beispiel": "0", "Gemeinde Musterstadt": "5", "Muster": "1, 9", "Ohnewert": ""}) def test_plz_mit_fuehrender_null(self): """Prüft, dass eine von QGIS als Zahl gelesene PLZ wieder fünfstellig mit führender Null erscheint und Zeilen dadurch zusammenfinden.""" layer = _Layer([ _zeile("Beispiel", "Anna", plz=1067), _zeile("Beispiel", "Anna", plz="01067"), _zeile("Muster", "Bernd", plz="1067.0"), ]) treffer = es.suche_eigentuemer(layer, "e", "") self.assertEqual([(e.nachname, e.plz) for e in treffer], [("Beispiel", "01067"), ("Muster", "01067")]) def test_suche_gibt_nichts_aus(self): """Prüft, dass die Suche keine Personendaten auf die Konsole schreibt (Datenschutz).""" ausgabe = io.StringIO() with contextlib.redirect_stdout(ausgabe), contextlib.redirect_stderr(ausgabe): es.suche_eigentuemer(_Layer([_zeile("Beispiel", "Anna")]), "Beispiel", "") self.assertEqual(ausgabe.getvalue(), "") class TestAnzeigeformen(unittest.TestCase): def test_name_und_anschrift(self): """Prüft, dass Name mit Titel und einzeilige Anschrift mit Ortsteil lesbar zusammengesetzt werden.""" e = Eigentuemer(titel="Dr.", vorname="Anna", nachname="Beispiel", strasse="Hauptstraße 1", plz="01234", wohnort="Musterstadt", ortsteil="Unterdorf") self.assertEqual(e.name, "Beispiel, Dr. Anna") self.assertEqual(e.anschrift, "Hauptstraße 1, 01234 Musterstadt, OT Unterdorf") self.assertEqual(Eigentuemer(nachname="Beispiel", ortsteil="OT Unterdorf").anschrift, "OT Unterdorf") self.assertEqual(Eigentuemer(nachname="Gemeinde Musterstadt").name, "Gemeinde Musterstadt") if __name__ == "__main__": unittest.main()