2026-04-21 21:50:26 +02:00
|
|
|
|
"""sn_verfahrensgebiet/tests/test_flurstueck_alkis.py
|
|
|
|
|
|
|
|
|
|
|
|
Unit-Tests für das flurstueck_alkis-Modul.
|
|
|
|
|
|
|
|
|
|
|
|
Getestet werden:
|
|
|
|
|
|
- Nutzerabbruch bei ask_text → (None, LoadStatus.NONE)
|
|
|
|
|
|
- Ungültiger WFS-Layer → (None, LoadStatus.NONE)
|
|
|
|
|
|
- Erfolgreicher Ladevorgang → gültiger Layer + LoadStatus.FIRST
|
2026-09-17 12:11:13 +02:00
|
|
|
|
- Bestehender Layer, Drei-Knopf-Dialog: "Überschreiben" → LoadStatus.RELOAD,
|
|
|
|
|
|
"Abbrechen" → (None, LoadStatus.NONE), "Anhängen" siehe
|
|
|
|
|
|
TestFlurstueckAlkisAnhaengenDedup
|
|
|
|
|
|
- vorgabe_schluessel/vorgabe_namen füllen den Eingabedialog vor
|
|
|
|
|
|
- ohne_schluessel_abfrage=True überspringt die ask_text-Abfrage
|
2026-04-21 21:50:26 +02:00
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
import sys
|
|
|
|
|
|
import types
|
|
|
|
|
|
import unittest
|
|
|
|
|
|
from unittest.mock import patch, MagicMock
|
|
|
|
|
|
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
# Minimales QGIS-Mock-Setup
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
if "qgis" not in sys.modules:
|
|
|
|
|
|
qgis_mod = types.ModuleType("qgis")
|
|
|
|
|
|
qgis_core = types.ModuleType("qgis.core")
|
|
|
|
|
|
qgis_utils = types.ModuleType("qgis.utils")
|
|
|
|
|
|
qgis_utils.iface = types.SimpleNamespace()
|
|
|
|
|
|
qgis_utils.plugins = {}
|
|
|
|
|
|
qgis_mod.utils = qgis_utils
|
|
|
|
|
|
qgis_mod.core = qgis_core
|
|
|
|
|
|
sys.modules["qgis"] = qgis_mod
|
|
|
|
|
|
sys.modules["qgis.core"] = qgis_core
|
|
|
|
|
|
sys.modules["qgis.utils"] = qgis_utils
|
|
|
|
|
|
|
2026-09-15 08:48:14 +02:00
|
|
|
|
from sn_verfahrensgebiet.functions.flurstueck_alkis import (
|
|
|
|
|
|
flurstueck_alkis,
|
2026-09-17 12:11:13 +02:00
|
|
|
|
lade_flurstuecke_per_gemarkung,
|
2026-09-15 08:48:14 +02:00
|
|
|
|
FLST_LAYER_NAME,
|
|
|
|
|
|
FELD_FLURSTUECKSKENNZEICHEN,
|
|
|
|
|
|
)
|
2026-04-21 21:50:26 +02:00
|
|
|
|
from sn_verfahrensgebiet.functions.verfahrensgebiet_alkis import LoadStatus
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
# Dummies
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
|
|
class _DummyLayer:
|
|
|
|
|
|
def __init__(self, valid: bool, layer_id="L1"):
|
|
|
|
|
|
self._valid = valid
|
|
|
|
|
|
self._id = layer_id
|
|
|
|
|
|
|
|
|
|
|
|
def isValid(self):
|
|
|
|
|
|
return self._valid
|
|
|
|
|
|
|
|
|
|
|
|
def id(self):
|
|
|
|
|
|
return self._id
|
|
|
|
|
|
|
2026-09-17 12:11:13 +02:00
|
|
|
|
def featureCount(self):
|
|
|
|
|
|
return 1
|
2026-04-21 21:50:26 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class _DummyProject:
|
|
|
|
|
|
def __init__(self, named_layers=None):
|
|
|
|
|
|
self._named_layers = named_layers or []
|
|
|
|
|
|
self.removed = []
|
|
|
|
|
|
|
|
|
|
|
|
def mapLayersByName(self, name):
|
|
|
|
|
|
return self._named_layers
|
|
|
|
|
|
|
|
|
|
|
|
def removeMapLayer(self, lid):
|
|
|
|
|
|
self.removed.append(lid)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
# Tests
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
|
|
class TestFlurstueckAlkis(unittest.TestCase):
|
|
|
|
|
|
|
|
|
|
|
|
# ------------------------------------------------------------------ #
|
|
|
|
|
|
# 1. Nutzerabbruch (ask_text liefert ok=False / None) #
|
|
|
|
|
|
# ------------------------------------------------------------------ #
|
|
|
|
|
|
@patch("sn_verfahrensgebiet.functions.flurstueck_alkis.QMessageBox")
|
|
|
|
|
|
@patch("sn_verfahrensgebiet.functions.flurstueck_alkis.QgsProject.instance")
|
|
|
|
|
|
@patch("sn_verfahrensgebiet.functions.flurstueck_alkis.ask_text")
|
|
|
|
|
|
def test_user_cancel_returns_none(self, mock_ask_text, mock_project, mock_msgbox):
|
2026-09-17 12:11:13 +02:00
|
|
|
|
"""Prüft, dass ein Abbruch im Eingabedialog (ok=False) (None, LoadStatus.NONE) liefert."""
|
2026-04-21 21:50:26 +02:00
|
|
|
|
mock_ask_text.return_value = ("", False)
|
|
|
|
|
|
mock_project.return_value = _DummyProject()
|
|
|
|
|
|
|
|
|
|
|
|
layer, status = flurstueck_alkis(None)
|
|
|
|
|
|
|
|
|
|
|
|
self.assertIsNone(layer)
|
|
|
|
|
|
self.assertEqual(status, LoadStatus.NONE)
|
|
|
|
|
|
|
|
|
|
|
|
@patch("sn_verfahrensgebiet.functions.flurstueck_alkis.QMessageBox")
|
|
|
|
|
|
@patch("sn_verfahrensgebiet.functions.flurstueck_alkis.QgsProject.instance")
|
|
|
|
|
|
@patch("sn_verfahrensgebiet.functions.flurstueck_alkis.ask_text")
|
|
|
|
|
|
def test_empty_text_after_confirmation_returns_none(self, mock_ask_text, mock_project, mock_msgbox):
|
2026-09-17 12:11:13 +02:00
|
|
|
|
"""Prüft, dass ein bestätigter, aber leerer Eingabetext ebenfalls (None, LoadStatus.NONE) liefert."""
|
2026-04-21 21:50:26 +02:00
|
|
|
|
mock_ask_text.return_value = (" ", True)
|
|
|
|
|
|
mock_project.return_value = _DummyProject()
|
|
|
|
|
|
|
|
|
|
|
|
layer, status = flurstueck_alkis(None)
|
|
|
|
|
|
|
|
|
|
|
|
self.assertIsNone(layer)
|
|
|
|
|
|
self.assertEqual(status, LoadStatus.NONE)
|
|
|
|
|
|
|
|
|
|
|
|
# ------------------------------------------------------------------ #
|
|
|
|
|
|
# 2. Ungültiger WFS-Layer #
|
|
|
|
|
|
# ------------------------------------------------------------------ #
|
|
|
|
|
|
@patch("sn_verfahrensgebiet.functions.flurstueck_alkis.QMessageBox")
|
|
|
|
|
|
@patch("sn_verfahrensgebiet.functions.flurstueck_alkis.QgsVectorLayer")
|
|
|
|
|
|
@patch("sn_verfahrensgebiet.functions.flurstueck_alkis.QgsProject.instance")
|
|
|
|
|
|
@patch("sn_verfahrensgebiet.functions.flurstueck_alkis.ask_text")
|
|
|
|
|
|
def test_invalid_wfs_layer_returns_none(
|
|
|
|
|
|
self, mock_ask_text, mock_project, mock_wfs_layer_cls, mock_msgbox
|
|
|
|
|
|
):
|
2026-09-17 12:11:13 +02:00
|
|
|
|
"""Prüft, dass ein ungültiger WFS-Layer (None, LoadStatus.NONE) liefert."""
|
2026-04-21 21:50:26 +02:00
|
|
|
|
mock_ask_text.return_value = ("1234", True)
|
|
|
|
|
|
mock_project.return_value = _DummyProject()
|
|
|
|
|
|
mock_wfs_layer_cls.return_value = _DummyLayer(valid=False)
|
|
|
|
|
|
|
|
|
|
|
|
layer, status = flurstueck_alkis(None)
|
|
|
|
|
|
|
|
|
|
|
|
self.assertIsNone(layer)
|
|
|
|
|
|
self.assertEqual(status, LoadStatus.NONE)
|
|
|
|
|
|
|
|
|
|
|
|
# ------------------------------------------------------------------ #
|
|
|
|
|
|
# 3. Erfolgreicher Erstladevorgang #
|
|
|
|
|
|
# ------------------------------------------------------------------ #
|
2026-09-17 12:11:13 +02:00
|
|
|
|
@patch("sn_verfahrensgebiet.functions.flurstueck_alkis.lade_flurstuecke_per_gemarkung")
|
2026-04-21 21:50:26 +02:00
|
|
|
|
@patch("sn_verfahrensgebiet.functions.flurstueck_alkis.QgsProject.instance")
|
|
|
|
|
|
@patch("sn_verfahrensgebiet.functions.flurstueck_alkis.ask_text")
|
|
|
|
|
|
def test_success_returns_layer_and_first_status(
|
2026-09-17 12:11:13 +02:00
|
|
|
|
self, mock_ask_text, mock_project, mock_lade
|
2026-04-21 21:50:26 +02:00
|
|
|
|
):
|
2026-09-17 12:11:13 +02:00
|
|
|
|
"""Prüft, dass ohne bestehenden Layer der von lade_flurstuecke_per_gemarkung gelieferte Layer mit LoadStatus.FIRST zurückgegeben wird."""
|
2026-04-21 21:50:26 +02:00
|
|
|
|
mock_ask_text.return_value = ("1234, 5678", True)
|
|
|
|
|
|
mock_project.return_value = _DummyProject()
|
|
|
|
|
|
valid_layer = _DummyLayer(valid=True, layer_id="wfs_ok")
|
2026-09-17 12:11:13 +02:00
|
|
|
|
mock_lade.return_value = valid_layer
|
2026-04-21 21:50:26 +02:00
|
|
|
|
|
|
|
|
|
|
layer, status = flurstueck_alkis(None)
|
|
|
|
|
|
|
|
|
|
|
|
self.assertIs(layer, valid_layer)
|
|
|
|
|
|
self.assertEqual(status, LoadStatus.FIRST)
|
2026-09-17 12:11:13 +02:00
|
|
|
|
mock_lade.assert_called_once_with(["1234", "5678"])
|
2026-04-21 21:50:26 +02:00
|
|
|
|
|
|
|
|
|
|
@patch("sn_verfahrensgebiet.functions.flurstueck_alkis.QgsVectorLayer")
|
2026-09-17 12:11:13 +02:00
|
|
|
|
def test_sql_filter_contains_all_schluessel(self, mock_wfs_layer_cls):
|
|
|
|
|
|
"""Prüft, dass lade_flurstuecke_per_gemarkung für jeden Gemarkungsschlüssel eine eigene WFS-Anfrage mit dessen Schlüssel stellt."""
|
|
|
|
|
|
# Ungültig genügt: nur die aufgebaute URI je Aufruf wird geprüft, das
|
|
|
|
|
|
# tatsächliche Zusammenführen der Features ist nicht Teil dieses Tests.
|
|
|
|
|
|
mock_wfs_layer_cls.return_value = _DummyLayer(valid=False)
|
2026-04-21 21:50:26 +02:00
|
|
|
|
|
2026-09-17 12:11:13 +02:00
|
|
|
|
lade_flurstuecke_per_gemarkung(["1111", "2222", "3333"])
|
2026-04-21 21:50:26 +02:00
|
|
|
|
|
2026-09-17 12:11:13 +02:00
|
|
|
|
uris = [call.args[0] for call in mock_wfs_layer_cls.call_args_list]
|
|
|
|
|
|
self.assertEqual(len(uris), 3)
|
|
|
|
|
|
self.assertIn("1111", uris[0])
|
|
|
|
|
|
self.assertIn("2222", uris[1])
|
|
|
|
|
|
self.assertIn("3333", uris[2])
|
2026-04-21 21:50:26 +02:00
|
|
|
|
|
|
|
|
|
|
# ------------------------------------------------------------------ #
|
2026-09-17 12:11:13 +02:00
|
|
|
|
# 4. Bestehender Layer – Nutzer klickt "Überschreiben" #
|
2026-04-21 21:50:26 +02:00
|
|
|
|
# ------------------------------------------------------------------ #
|
2026-09-17 12:11:13 +02:00
|
|
|
|
@patch("sn_verfahrensgebiet.functions.flurstueck_alkis.lade_flurstuecke_per_gemarkung")
|
2026-04-21 21:50:26 +02:00
|
|
|
|
@patch("sn_verfahrensgebiet.functions.flurstueck_alkis.QMessageBox")
|
|
|
|
|
|
@patch("sn_verfahrensgebiet.functions.flurstueck_alkis.QgsProject.instance")
|
|
|
|
|
|
@patch("sn_verfahrensgebiet.functions.flurstueck_alkis.ask_text")
|
|
|
|
|
|
def test_existing_layer_reload_yes(
|
2026-09-17 12:11:13 +02:00
|
|
|
|
self, mock_ask_text, mock_project, mock_msgbox_cls, mock_lade
|
2026-04-21 21:50:26 +02:00
|
|
|
|
):
|
2026-09-17 12:11:13 +02:00
|
|
|
|
"""Prüft, dass bei bestehendem Layer und Klick auf 'Überschreiben' der alte Layer entfernt und LoadStatus.RELOAD geliefert wird."""
|
2026-04-21 21:50:26 +02:00
|
|
|
|
existing = _DummyLayer(valid=True, layer_id="old_flst")
|
|
|
|
|
|
project = _DummyProject(named_layers=[existing])
|
|
|
|
|
|
mock_ask_text.return_value = ("1234", True)
|
|
|
|
|
|
mock_project.return_value = project
|
|
|
|
|
|
|
2026-09-17 12:11:13 +02:00
|
|
|
|
# Drei-Knopf-Dialog: addButton() liefert je Aufruf einen eigenen
|
|
|
|
|
|
# Sentinel, clickedButton() simuliert den Klick auf "Überschreiben".
|
|
|
|
|
|
btn_ueberschreiben, btn_anhaengen, btn_abbrechen = object(), object(), object()
|
|
|
|
|
|
msg_instance = mock_msgbox_cls.return_value
|
|
|
|
|
|
msg_instance.addButton.side_effect = [btn_ueberschreiben, btn_anhaengen, btn_abbrechen]
|
|
|
|
|
|
msg_instance.clickedButton.return_value = btn_ueberschreiben
|
2026-04-21 21:50:26 +02:00
|
|
|
|
|
|
|
|
|
|
new_layer = _DummyLayer(valid=True, layer_id="new_flst")
|
2026-09-17 12:11:13 +02:00
|
|
|
|
mock_lade.return_value = new_layer
|
2026-04-21 21:50:26 +02:00
|
|
|
|
|
|
|
|
|
|
layer, status = flurstueck_alkis(None)
|
|
|
|
|
|
|
|
|
|
|
|
self.assertIn(existing.id(), project.removed)
|
|
|
|
|
|
self.assertIs(layer, new_layer)
|
|
|
|
|
|
self.assertEqual(status, LoadStatus.RELOAD)
|
|
|
|
|
|
|
|
|
|
|
|
# ------------------------------------------------------------------ #
|
2026-09-17 12:11:13 +02:00
|
|
|
|
# 5. Bestehender Layer – Nutzer klickt "Abbrechen" #
|
2026-04-21 21:50:26 +02:00
|
|
|
|
# ------------------------------------------------------------------ #
|
|
|
|
|
|
@patch("sn_verfahrensgebiet.functions.flurstueck_alkis.QMessageBox")
|
|
|
|
|
|
@patch("sn_verfahrensgebiet.functions.flurstueck_alkis.QgsProject.instance")
|
|
|
|
|
|
@patch("sn_verfahrensgebiet.functions.flurstueck_alkis.ask_text")
|
2026-09-17 12:11:13 +02:00
|
|
|
|
def test_existing_layer_abbrechen_returns_none(
|
2026-04-21 21:50:26 +02:00
|
|
|
|
self, mock_ask_text, mock_project, mock_msgbox_cls
|
|
|
|
|
|
):
|
2026-09-17 12:11:13 +02:00
|
|
|
|
"""Prüft, dass bei bestehendem Layer und Klick auf 'Abbrechen' nichts verändert wird und (None, LoadStatus.NONE) geliefert wird."""
|
2026-04-21 21:50:26 +02:00
|
|
|
|
existing = _DummyLayer(valid=True, layer_id="keep_flst")
|
|
|
|
|
|
project = _DummyProject(named_layers=[existing])
|
|
|
|
|
|
mock_ask_text.return_value = ("1234", True)
|
|
|
|
|
|
mock_project.return_value = project
|
|
|
|
|
|
|
2026-09-17 12:11:13 +02:00
|
|
|
|
btn_ueberschreiben, btn_anhaengen, btn_abbrechen = object(), object(), object()
|
|
|
|
|
|
msg_instance = mock_msgbox_cls.return_value
|
|
|
|
|
|
msg_instance.addButton.side_effect = [btn_ueberschreiben, btn_anhaengen, btn_abbrechen]
|
|
|
|
|
|
msg_instance.clickedButton.return_value = btn_abbrechen
|
2026-04-21 21:50:26 +02:00
|
|
|
|
|
|
|
|
|
|
layer, status = flurstueck_alkis(None)
|
|
|
|
|
|
|
2026-09-17 12:11:13 +02:00
|
|
|
|
self.assertIsNone(layer)
|
|
|
|
|
|
self.assertEqual(status, LoadStatus.NONE)
|
2026-04-21 21:50:26 +02:00
|
|
|
|
self.assertEqual(project.removed, []) # nichts gelöscht
|
2026-09-17 12:11:13 +02:00
|
|
|
|
mock_ask_text.assert_not_called() # Abbruch schon vor der Schlüsselabfrage
|
2026-04-21 21:50:26 +02:00
|
|
|
|
|
|
|
|
|
|
|
2026-04-22 14:53:45 +02:00
|
|
|
|
# ------------------------------------------------------------------ #
|
|
|
|
|
|
# 6. vorgabe_schluessel füllt ask_text-Default vor #
|
|
|
|
|
|
# ------------------------------------------------------------------ #
|
2026-09-17 12:11:13 +02:00
|
|
|
|
@patch("sn_verfahrensgebiet.functions.flurstueck_alkis.lade_flurstuecke_per_gemarkung")
|
2026-04-22 14:53:45 +02:00
|
|
|
|
@patch("sn_verfahrensgebiet.functions.flurstueck_alkis.QgsProject.instance")
|
|
|
|
|
|
@patch("sn_verfahrensgebiet.functions.flurstueck_alkis.ask_text")
|
|
|
|
|
|
def test_vorgabe_schluessel_prefills_default_text(
|
2026-09-17 12:11:13 +02:00
|
|
|
|
self, mock_ask_text, mock_project, mock_lade
|
2026-04-22 14:53:45 +02:00
|
|
|
|
):
|
2026-09-17 12:11:13 +02:00
|
|
|
|
"""Prüft, dass vorgabe_schluessel als vorbelegter default_text im Eingabedialog erscheint."""
|
2026-04-22 14:53:45 +02:00
|
|
|
|
mock_ask_text.return_value = ("1234, 5678", True)
|
|
|
|
|
|
mock_project.return_value = _DummyProject()
|
2026-09-17 12:11:13 +02:00
|
|
|
|
mock_lade.return_value = _DummyLayer(valid=True)
|
2026-04-22 14:53:45 +02:00
|
|
|
|
|
|
|
|
|
|
flurstueck_alkis(None, vorgabe_schluessel=["1234", "5678"])
|
|
|
|
|
|
|
|
|
|
|
|
args, kwargs = mock_ask_text.call_args
|
|
|
|
|
|
# ask_text("title", label, default_text=..., parent=...)
|
|
|
|
|
|
# default_text wird als Keyword-Arg übergeben
|
|
|
|
|
|
default_text_arg = kwargs.get("default_text", args[2] if len(args) > 2 else "")
|
|
|
|
|
|
self.assertIn("1234", default_text_arg)
|
|
|
|
|
|
self.assertIn("5678", default_text_arg)
|
|
|
|
|
|
|
|
|
|
|
|
# ------------------------------------------------------------------ #
|
|
|
|
|
|
# 7. vorgabe_namen erscheint im Label-Text #
|
|
|
|
|
|
# ------------------------------------------------------------------ #
|
2026-09-17 12:11:13 +02:00
|
|
|
|
@patch("sn_verfahrensgebiet.functions.flurstueck_alkis.lade_flurstuecke_per_gemarkung")
|
2026-04-22 14:53:45 +02:00
|
|
|
|
@patch("sn_verfahrensgebiet.functions.flurstueck_alkis.QgsProject.instance")
|
|
|
|
|
|
@patch("sn_verfahrensgebiet.functions.flurstueck_alkis.ask_text")
|
|
|
|
|
|
def test_vorgabe_namen_appears_in_label(
|
2026-09-17 12:11:13 +02:00
|
|
|
|
self, mock_ask_text, mock_project, mock_lade
|
2026-04-22 14:53:45 +02:00
|
|
|
|
):
|
2026-09-17 12:11:13 +02:00
|
|
|
|
"""Prüft, dass vorgabe_namen im Beschriftungstext des Eingabedialogs erscheint."""
|
2026-04-22 14:53:45 +02:00
|
|
|
|
mock_ask_text.return_value = ("9999", True)
|
|
|
|
|
|
mock_project.return_value = _DummyProject()
|
2026-09-17 12:11:13 +02:00
|
|
|
|
mock_lade.return_value = _DummyLayer(valid=True)
|
2026-04-22 14:53:45 +02:00
|
|
|
|
|
|
|
|
|
|
flurstueck_alkis(None, vorgabe_namen=["Musterstadt"])
|
|
|
|
|
|
|
|
|
|
|
|
args, kwargs = mock_ask_text.call_args
|
|
|
|
|
|
# label ist das 2. Positional-Argument
|
|
|
|
|
|
label_arg = args[1] if len(args) > 1 else kwargs.get("label", "")
|
|
|
|
|
|
self.assertIn("Musterstadt", label_arg)
|
|
|
|
|
|
|
|
|
|
|
|
# ------------------------------------------------------------------ #
|
2026-09-17 12:11:13 +02:00
|
|
|
|
# 8. Rückwärtskompatibilität: alle neuen Params optional #
|
2026-04-22 14:53:45 +02:00
|
|
|
|
# ------------------------------------------------------------------ #
|
2026-09-17 12:11:13 +02:00
|
|
|
|
@patch("sn_verfahrensgebiet.functions.flurstueck_alkis.lade_flurstuecke_per_gemarkung")
|
2026-04-22 14:53:45 +02:00
|
|
|
|
@patch("sn_verfahrensgebiet.functions.flurstueck_alkis.QgsProject.instance")
|
|
|
|
|
|
@patch("sn_verfahrensgebiet.functions.flurstueck_alkis.ask_text")
|
|
|
|
|
|
def test_backwards_compatible_no_new_params(
|
2026-09-17 12:11:13 +02:00
|
|
|
|
self, mock_ask_text, mock_project, mock_lade
|
2026-04-22 14:53:45 +02:00
|
|
|
|
):
|
2026-09-17 12:11:13 +02:00
|
|
|
|
"""Prüft, dass flurstueck_alkis() ohne die neuen optionalen Parameter weiterhin fehlerfrei einen Layer mit LoadStatus.FIRST liefert."""
|
2026-04-22 14:53:45 +02:00
|
|
|
|
mock_ask_text.return_value = ("0000", True)
|
|
|
|
|
|
mock_project.return_value = _DummyProject()
|
2026-09-17 12:11:13 +02:00
|
|
|
|
mock_lade.return_value = _DummyLayer(valid=True)
|
2026-04-22 14:53:45 +02:00
|
|
|
|
|
|
|
|
|
|
layer, status = flurstueck_alkis(None)
|
|
|
|
|
|
|
|
|
|
|
|
self.assertIsNotNone(layer)
|
|
|
|
|
|
self.assertEqual(status, LoadStatus.FIRST)
|
|
|
|
|
|
|
2026-09-15 10:26:25 +02:00
|
|
|
|
# ------------------------------------------------------------------ #
|
2026-09-17 12:11:13 +02:00
|
|
|
|
# 9. ohne_schluessel_abfrage: vorgabe_schluessel direkt, kein ask_text (#47)
|
2026-09-15 10:26:25 +02:00
|
|
|
|
# ------------------------------------------------------------------ #
|
|
|
|
|
|
@patch("sn_verfahrensgebiet.functions.flurstueck_alkis.lade_flurstuecke_per_gemarkung")
|
|
|
|
|
|
@patch("sn_verfahrensgebiet.functions.flurstueck_alkis.QgsProject.instance")
|
|
|
|
|
|
@patch("sn_verfahrensgebiet.functions.flurstueck_alkis.ask_text")
|
|
|
|
|
|
def test_ohne_schluessel_abfrage_ueberspringt_ask_text(
|
|
|
|
|
|
self, mock_ask_text, mock_project, mock_lade
|
|
|
|
|
|
):
|
2026-09-17 12:11:13 +02:00
|
|
|
|
"""Prüft, dass ohne_schluessel_abfrage=True vorgabe_schluessel direkt verwendet und ask_text nicht aufgerufen wird."""
|
2026-09-15 10:26:25 +02:00
|
|
|
|
mock_project.return_value = _DummyProject()
|
|
|
|
|
|
geladen = _DummyFlstLayer([_DummyFlstFeature("110001_0001__00001")])
|
|
|
|
|
|
mock_lade.return_value = geladen
|
|
|
|
|
|
|
|
|
|
|
|
layer, status = flurstueck_alkis(
|
|
|
|
|
|
None, vorgabe_schluessel=["1234", "5678"], ohne_schluessel_abfrage=True
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
mock_ask_text.assert_not_called()
|
|
|
|
|
|
self.assertIs(layer, geladen)
|
|
|
|
|
|
self.assertEqual(status, LoadStatus.FIRST)
|
|
|
|
|
|
mock_lade.assert_called_once_with(["1234", "5678"])
|
2026-04-22 14:53:45 +02:00
|
|
|
|
|
2026-09-15 08:48:14 +02:00
|
|
|
|
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
# Dummies für den Anhängen-Deduplizierungstest (#28)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
|
|
class _DummyFlstFeature:
|
|
|
|
|
|
def __init__(self, kennzeichen):
|
|
|
|
|
|
self._kennzeichen = kennzeichen
|
|
|
|
|
|
|
|
|
|
|
|
def __getitem__(self, key):
|
|
|
|
|
|
if key == FELD_FLURSTUECKSKENNZEICHEN:
|
|
|
|
|
|
return self._kennzeichen
|
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class _DummyFlstFields:
|
|
|
|
|
|
def __init__(self, hat_kennzeichen_feld: bool):
|
|
|
|
|
|
self._hat_feld = hat_kennzeichen_feld
|
|
|
|
|
|
|
|
|
|
|
|
def indexFromName(self, name):
|
|
|
|
|
|
if name == FELD_FLURSTUECKSKENNZEICHEN and self._hat_feld:
|
|
|
|
|
|
return 0
|
|
|
|
|
|
return -1
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class _DummyFlstLayer:
|
|
|
|
|
|
"""Flurstück-Layer-Dummy mit Feature-Liste und aufzeichnendem ``dataProvider``."""
|
|
|
|
|
|
|
|
|
|
|
|
def __init__(self, features, hat_kennzeichen_feld: bool = True):
|
|
|
|
|
|
self._features = list(features)
|
|
|
|
|
|
self._hat_feld = hat_kennzeichen_feld
|
|
|
|
|
|
self.added = None
|
|
|
|
|
|
|
|
|
|
|
|
def getFeatures(self):
|
|
|
|
|
|
return list(self._features)
|
|
|
|
|
|
|
|
|
|
|
|
def fields(self):
|
|
|
|
|
|
return _DummyFlstFields(self._hat_feld)
|
|
|
|
|
|
|
|
|
|
|
|
def dataProvider(self):
|
|
|
|
|
|
layer = self
|
|
|
|
|
|
|
|
|
|
|
|
class _Provider:
|
|
|
|
|
|
def addFeatures(self, feats):
|
|
|
|
|
|
layer.added = list(feats)
|
|
|
|
|
|
layer._features.extend(feats)
|
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
|
|
return _Provider()
|
|
|
|
|
|
|
|
|
|
|
|
def updateExtents(self):
|
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
def triggerRepaint(self):
|
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
def isValid(self):
|
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
|
|
def featureCount(self):
|
|
|
|
|
|
return len(self._features)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class TestFlurstueckAlkisAnhaengenDedup(unittest.TestCase):
|
|
|
|
|
|
"""Anhängen an einen bestehenden Flurstück-Layer (Issue #28)."""
|
|
|
|
|
|
|
|
|
|
|
|
@patch("sn_verfahrensgebiet.functions.flurstueck_alkis.lade_flurstuecke_per_gemarkung")
|
|
|
|
|
|
@patch("sn_verfahrensgebiet.functions.flurstueck_alkis._check_existing_flurstueck_layer")
|
|
|
|
|
|
@patch("sn_verfahrensgebiet.functions.flurstueck_alkis.ask_text")
|
|
|
|
|
|
def test_append_ueberspringt_bereits_vorhandene_flurstuecke(
|
|
|
|
|
|
self, mock_ask_text, mock_check, mock_lade
|
|
|
|
|
|
):
|
|
|
|
|
|
"""Prüft, dass beim Anhängen bereits vorhandene flurstueckskennzeichen nicht erneut hinzugefügt werden."""
|
|
|
|
|
|
mock_ask_text.return_value = ("0001, 0002", True)
|
|
|
|
|
|
bestehend = _DummyFlstLayer([_DummyFlstFeature("110001_0001__00001")])
|
|
|
|
|
|
neu_geladen = _DummyFlstLayer([
|
|
|
|
|
|
_DummyFlstFeature("110001_0001__00001"), # bereits im Layer vorhanden
|
|
|
|
|
|
_DummyFlstFeature("110001_0002__00007"), # noch nicht vorhanden
|
|
|
|
|
|
])
|
|
|
|
|
|
mock_check.return_value = (bestehend, LoadStatus.APPEND)
|
|
|
|
|
|
mock_lade.return_value = neu_geladen
|
|
|
|
|
|
|
|
|
|
|
|
layer, status = flurstueck_alkis(None)
|
|
|
|
|
|
|
|
|
|
|
|
self.assertEqual(status, LoadStatus.APPEND)
|
|
|
|
|
|
self.assertIs(layer, bestehend)
|
|
|
|
|
|
self.assertEqual(len(bestehend.added), 1)
|
|
|
|
|
|
self.assertEqual(bestehend.added[0][FELD_FLURSTUECKSKENNZEICHEN], "110001_0002__00007")
|
|
|
|
|
|
|
|
|
|
|
|
@patch("sn_verfahrensgebiet.functions.flurstueck_alkis.lade_flurstuecke_per_gemarkung")
|
|
|
|
|
|
@patch("sn_verfahrensgebiet.functions.flurstueck_alkis._check_existing_flurstueck_layer")
|
|
|
|
|
|
@patch("sn_verfahrensgebiet.functions.flurstueck_alkis.ask_text")
|
|
|
|
|
|
def test_append_ohne_kennzeichen_feld_haengt_alles_an(
|
|
|
|
|
|
self, mock_ask_text, mock_check, mock_lade
|
|
|
|
|
|
):
|
|
|
|
|
|
"""Prüft, dass ohne flurstueckskennzeichen-Feld weiterhin alle Features angehängt werden (Rückfallverhalten)."""
|
|
|
|
|
|
mock_ask_text.return_value = ("0001", True)
|
|
|
|
|
|
bestehend = _DummyFlstLayer([_DummyFlstFeature("110001_0001__00001")], hat_kennzeichen_feld=False)
|
|
|
|
|
|
neu_geladen = _DummyFlstLayer(
|
|
|
|
|
|
[_DummyFlstFeature("110001_0001__00001"), _DummyFlstFeature("110001_0002__00007")],
|
|
|
|
|
|
hat_kennzeichen_feld=False,
|
|
|
|
|
|
)
|
|
|
|
|
|
mock_check.return_value = (bestehend, LoadStatus.APPEND)
|
|
|
|
|
|
mock_lade.return_value = neu_geladen
|
|
|
|
|
|
|
|
|
|
|
|
layer, status = flurstueck_alkis(None)
|
|
|
|
|
|
|
|
|
|
|
|
self.assertEqual(status, LoadStatus.APPEND)
|
|
|
|
|
|
self.assertEqual(len(bestehend.added), 2)
|
|
|
|
|
|
|
2026-09-15 10:26:25 +02:00
|
|
|
|
@patch("sn_verfahrensgebiet.functions.flurstueck_alkis.lade_flurstuecke_per_gemarkung")
|
|
|
|
|
|
@patch("sn_verfahrensgebiet.functions.flurstueck_alkis._check_existing_flurstueck_layer")
|
|
|
|
|
|
@patch("sn_verfahrensgebiet.functions.flurstueck_alkis.ask_text")
|
|
|
|
|
|
def test_ohne_schluessel_abfrage_append_dedup(self, mock_ask_text, mock_check, mock_lade):
|
|
|
|
|
|
"""ohne_schluessel_abfrage=True: Anhängen dedupliziert trotzdem, ohne nach Schlüsseln zu fragen (#47)."""
|
|
|
|
|
|
bestehend = _DummyFlstLayer([_DummyFlstFeature("110001_0001__00001")])
|
|
|
|
|
|
neu_geladen = _DummyFlstLayer([
|
|
|
|
|
|
_DummyFlstFeature("110001_0001__00001"), # bereits vorhanden
|
|
|
|
|
|
_DummyFlstFeature("110001_0002__00007"), # neu
|
|
|
|
|
|
])
|
|
|
|
|
|
mock_check.return_value = (bestehend, LoadStatus.APPEND)
|
|
|
|
|
|
mock_lade.return_value = neu_geladen
|
|
|
|
|
|
|
|
|
|
|
|
layer, status = flurstueck_alkis(
|
|
|
|
|
|
None, vorgabe_schluessel=["0001", "0002"], ohne_schluessel_abfrage=True
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
mock_ask_text.assert_not_called()
|
|
|
|
|
|
self.assertEqual(status, LoadStatus.APPEND)
|
|
|
|
|
|
self.assertEqual(len(bestehend.added), 1)
|
|
|
|
|
|
self.assertEqual(bestehend.added[0][FELD_FLURSTUECKSKENNZEICHEN], "110001_0002__00007")
|
|
|
|
|
|
|
2026-09-15 08:48:14 +02:00
|
|
|
|
|
2026-04-21 21:50:26 +02:00
|
|
|
|
if __name__ == "__main__":
|
|
|
|
|
|
unittest.main()
|