"""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 - Bestehender Layer: Nutzer wählt "Neu laden" → LoadStatus.RELOAD - Bestehender Layer: Nutzer wählt "Behalten" → LoadStatus.KEEP """ 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 from sn_verfahrensgebiet.functions.flurstueck_alkis import flurstueck_alkis, FLST_LAYER_NAME 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 def extent(self): return object() 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): """ask_text → ok=False, text="" → (None, NONE).""" 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): """ask_text → ok=True, aber Text leer → (None, NONE).""" 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 ): """WFS-Layer nicht valide → (None, NONE).""" 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 # # ------------------------------------------------------------------ # @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_success_returns_layer_and_first_status( self, mock_ask_text, mock_project, mock_wfs_layer_cls ): """Kein bestehender Layer, WFS valide → (layer, FIRST).""" mock_ask_text.return_value = ("1234, 5678", True) mock_project.return_value = _DummyProject() valid_layer = _DummyLayer(valid=True, layer_id="wfs_ok") mock_wfs_layer_cls.return_value = valid_layer layer, status = flurstueck_alkis(None) self.assertIs(layer, valid_layer) self.assertEqual(status, LoadStatus.FIRST) @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_sql_filter_contains_all_schluessel( self, mock_ask_text, mock_project, mock_wfs_layer_cls ): """Der WFS-URI enthält alle eingegebenen Gemarkungsschlüssel.""" mock_ask_text.return_value = ("1111, 2222, 3333", True) mock_project.return_value = _DummyProject() mock_wfs_layer_cls.return_value = _DummyLayer(valid=True) flurstueck_alkis(None) # Erster Positionsarg von QgsVectorLayer ist der URI-String call_args = mock_wfs_layer_cls.call_args uri_str = call_args[0][0] if call_args[0] else call_args[1].get("path", "") self.assertIn("1111", uri_str) self.assertIn("2222", uri_str) self.assertIn("3333", uri_str) # ------------------------------------------------------------------ # # 4. Bestehender Layer – Nutzer wählt "Neu laden" # # ------------------------------------------------------------------ # @patch("sn_verfahrensgebiet.functions.flurstueck_alkis.QgsVectorLayer") @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( self, mock_ask_text, mock_project, mock_msgbox_cls, mock_wfs_layer_cls ): """Bestehender Layer + Nutzer: 'Ja, neu laden' → LoadStatus.RELOAD.""" 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 # StandardButton als int, damit | Operator funktioniert mock_msgbox_cls.StandardButton.Yes = 0x4000 mock_msgbox_cls.StandardButton.No = 0x10000 # QMessageBox.question → Yes mock_msgbox_cls.question.return_value = 0x4000 new_layer = _DummyLayer(valid=True, layer_id="new_flst") mock_wfs_layer_cls.return_value = new_layer layer, status = flurstueck_alkis(None) self.assertIn(existing.id(), project.removed) self.assertIs(layer, new_layer) self.assertEqual(status, LoadStatus.RELOAD) # ------------------------------------------------------------------ # # 5. Bestehender Layer – Nutzer wählt "Behalten" # # ------------------------------------------------------------------ # @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_keep_no( self, mock_ask_text, mock_project, mock_msgbox_cls ): """Bestehender Layer + Nutzer: 'Nein, behalten' → LoadStatus.KEEP.""" 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 # StandardButton als int, damit | Operator funktioniert mock_msgbox_cls.StandardButton.Yes = 0x4000 mock_msgbox_cls.StandardButton.No = 0x10000 # QMessageBox.question → No mock_msgbox_cls.question.return_value = 0x10000 layer, status = flurstueck_alkis(None) self.assertIs(layer, existing) self.assertEqual(status, LoadStatus.KEEP) self.assertEqual(project.removed, []) # nichts gelöscht # ------------------------------------------------------------------ # # 6. vorgabe_schluessel füllt ask_text-Default vor # # ------------------------------------------------------------------ # @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_vorgabe_schluessel_prefills_default_text( self, mock_ask_text, mock_project, mock_wfs_layer_cls ): """vorgabe_schluessel=['1234','5678'] → ask_text erhält '1234, 5678' als default_text.""" mock_ask_text.return_value = ("1234, 5678", True) mock_project.return_value = _DummyProject() mock_wfs_layer_cls.return_value = _DummyLayer(valid=True) 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 # # ------------------------------------------------------------------ # @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_vorgabe_namen_appears_in_label( self, mock_ask_text, mock_project, mock_wfs_layer_cls ): """vorgabe_namen=['Musterstadt'] → ask_text-label enthält den Namen.""" mock_ask_text.return_value = ("9999", True) mock_project.return_value = _DummyProject() mock_wfs_layer_cls.return_value = _DummyLayer(valid=True) 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) # ------------------------------------------------------------------ # # 8. bbox wird in WFS-URI eingebettet # # ------------------------------------------------------------------ # @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_bbox_embedded_in_wfs_uri( self, mock_ask_text, mock_project, mock_wfs_layer_cls ): """bbox-Parameter → WFS-URI enthält 'bbox=' Substring.""" mock_ask_text.return_value = ("1234", True) mock_project.return_value = _DummyProject() mock_wfs_layer_cls.return_value = _DummyLayer(valid=True) class _Rect: def xMinimum(self): return 414000.0 def yMinimum(self): return 5657000.0 def xMaximum(self): return 415000.0 def yMaximum(self): return 5658000.0 flurstueck_alkis(None, bbox=_Rect()) uri_str = mock_wfs_layer_cls.call_args[0][0] self.assertIn("bbox=", uri_str.lower()) # ------------------------------------------------------------------ # # 9. Rückwärtskompatibilität: alle neuen Params optional # # ------------------------------------------------------------------ # @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_backwards_compatible_no_new_params( self, mock_ask_text, mock_project, mock_wfs_layer_cls ): """flurstueck_alkis(tab) ohne neue Parameter → kein Fehler.""" mock_ask_text.return_value = ("0000", True) mock_project.return_value = _DummyProject() mock_wfs_layer_cls.return_value = _DummyLayer(valid=True) layer, status = flurstueck_alkis(None) self.assertIsNotNone(layer) self.assertEqual(status, LoadStatus.FIRST) if __name__ == "__main__": unittest.main()