"""sn_verfahrensgebiet/tests/test_verfahrensgebiet_alkis_komplett.py Unit-Tests für :mod:`sn_verfahrensgebiet.functions.verfahrensgebiet_alkis_komplett`. Getestete Szenarien: Erfolgsfall: - Vollständiger Workflow → enriched Layer + flst_layer + LoadStatus.FIRST - VKZ aus Projektvariable wird als Feld gesetzt (wenn vorhanden) - Kein VKZ in Projektvariable → Feld wird nicht gesetzt (kein Fehler) Abbruchpfade: - verfahrensgebiet_alkis gibt LoadStatus.NONE → (None, None, NONE) - flurstueck_alkis gibt LoadStatus.NONE → (None, None, NONE) - Keine Flurstücke selektiert → (None, flst_layer, NONE) + Warnung - erstelle_verfahrensgebiet_aus_auswahl liefert ungültigen Layer → (None, flst_layer, NONE) - Cleanup: ALKIS-VG-Hilfs-Layer wird in allen Fällen aus Projekt entfernt Hilfsfunktionen: - _select_by_location: processing-Fallback liefert 0 → keine Exception - _fuege_vkz_feld_hinzu: leeres VKZ wird ignoriert """ import sys import types import unittest from unittest.mock import patch, MagicMock, call # --------------------------------------------------------------------------- # 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.verfahrensgebiet_alkis import LoadStatus from sn_verfahrensgebiet.functions.verfahrensgebiet_alkis_komplett import ( verfahrensgebiet_alkis_komplett, _select_by_location, _fuege_vkz_feld_hinzu, ) # --------------------------------------------------------------------------- # Dummies # --------------------------------------------------------------------------- class _DummyRect: def xMinimum(self): return 414000.0 def yMinimum(self): return 5657000.0 def xMaximum(self): return 415000.0 def yMaximum(self): return 5658000.0 def _make_layer(valid=True, name="Layer", lid="L1", feature_count=1): layer = MagicMock() layer.isValid.return_value = valid layer.id.return_value = lid layer.name.return_value = name layer.extent.return_value = _DummyRect() layer.featureCount.return_value = feature_count layer.selectedFeatureCount.return_value = feature_count return layer class _DummyProject: def __init__(self): self.added = [] self.removed = [] self._layers_by_name: dict = {} def addMapLayer(self, layer): self.added.append(layer) def removeMapLayer(self, lid): self.removed.append(lid) def mapLayersByName(self, name): return self._layers_by_name.get(name, []) def instance(self): return self # --------------------------------------------------------------------------- # Tests: _select_by_location # --------------------------------------------------------------------------- class TestSelectByLocation(unittest.TestCase): def test_processing_not_available_returns_zero(self): """Wenn processing nicht importierbar ist → 0 ohne Exception.""" flst = MagicMock() vg = MagicMock() with patch.dict("sys.modules", {"processing": None}): result = _select_by_location(flst, vg) self.assertEqual(result, 0) def test_processing_success_returns_count(self): """processing.run erfolgreich → selectedFeatureCount des Layers.""" flst = MagicMock() flst.selectedFeatureCount.return_value = 7 vg = MagicMock() mock_processing = MagicMock() with patch.dict("sys.modules", {"processing": mock_processing}): result = _select_by_location(flst, vg) self.assertEqual(result, 7) def test_processing_exception_returns_zero(self): """Exception in processing.run → 0 ohne Propagation.""" flst = MagicMock() vg = MagicMock() mock_processing = MagicMock() mock_processing.run.side_effect = RuntimeError("Kein QGIS") with patch.dict("sys.modules", {"processing": mock_processing}): result = _select_by_location(flst, vg) self.assertEqual(result, 0) # --------------------------------------------------------------------------- # Tests: _fuege_vkz_feld_hinzu # --------------------------------------------------------------------------- class TestFuegeVkzFeldHinzu(unittest.TestCase): def test_empty_vkz_does_nothing(self): """Leeres VKZ → kein Zugriff auf Layer.""" layer = MagicMock() _fuege_vkz_feld_hinzu(layer, "") layer.dataProvider.assert_not_called() def test_none_vkz_does_nothing(self): """None-VKZ → kein Zugriff auf Layer.""" layer = MagicMock() _fuege_vkz_feld_hinzu(layer, None) layer.dataProvider.assert_not_called() def test_vkz_field_added(self): """Nicht-leeres VKZ → Provider.addAttributes wird aufgerufen.""" pr = MagicMock() pr.addAttributes.return_value = True pr.changeAttributeValues.return_value = True layer = MagicMock() layer.dataProvider.return_value = pr layer.fields.return_value.indexFromName.return_value = 2 layer.getFeatures.return_value = [] _fuege_vkz_feld_hinzu(layer, "VF-2024-001") pr.addAttributes.assert_called_once() # --------------------------------------------------------------------------- # Tests: verfahrensgebiet_alkis_komplett (Hauptworkflow) # --------------------------------------------------------------------------- _PATCH_VG_ALKIS = "sn_verfahrensgebiet.functions.verfahrensgebiet_alkis_komplett.verfahrensgebiet_alkis" _PATCH_GEM_BY_BBOX = "sn_verfahrensgebiet.functions.verfahrensgebiet_alkis_komplett.gemarkung_schluessel_und_namen_by_bbox" _PATCH_FLST = "sn_verfahrensgebiet.functions.verfahrensgebiet_alkis_komplett.flurstueck_alkis" _PATCH_SELECT = "sn_verfahrensgebiet.functions.verfahrensgebiet_alkis_komplett._select_by_location" _PATCH_ERSTELLE = "sn_verfahrensgebiet.functions.verfahrensgebiet_alkis_komplett.erstelle_verfahrensgebiet_aus_auswahl" _PATCH_VKZ = "sn_verfahrensgebiet.functions.verfahrensgebiet_alkis_komplett._fuege_vkz_feld_hinzu" _PATCH_PROJECT = "sn_verfahrensgebiet.functions.verfahrensgebiet_alkis_komplett.QgsProject" _PATCH_MSGBOX = "sn_verfahrensgebiet.functions.verfahrensgebiet_alkis_komplett.QMessageBox" _PATCH_INFO = "sn_verfahrensgebiet.functions.verfahrensgebiet_alkis_komplett.info" _PATCH_WARNING = "sn_verfahrensgebiet.functions.verfahrensgebiet_alkis_komplett.warning" _PATCH_GET_VAR = "sn_verfahrensgebiet.functions.verfahrensgebiet_alkis_komplett.get_variable" class TestVerfahrensgebietAlkisKomplett(unittest.TestCase): def _make_project(self): proj = _DummyProject() mock = MagicMock() mock.instance.return_value = proj # Leere mapLayersByName für Cleanup proj._layers_by_name = {"Verfahrensgebiet": [], "TempVerfahrensgebiet": []} return mock, proj # ------------------------------------------------------------------ # # 1. VG-ALKIS liefert NONE → sofortiger Abbruch # # ------------------------------------------------------------------ # @patch(_PATCH_PROJECT) @patch(_PATCH_VG_ALKIS) def test_vg_alkis_none_aborts(self, mock_vg, mock_proj): mock_proj.instance.return_value = _DummyProject() mock_vg.return_value = (None, LoadStatus.NONE) enriched, flst, status = verfahrensgebiet_alkis_komplett(None) self.assertIsNone(enriched) self.assertIsNone(flst) self.assertEqual(status, LoadStatus.NONE) # ------------------------------------------------------------------ # # 2. flurstueck_alkis liefert NONE → Abbruch nach Gemarkungsabfrage # # ------------------------------------------------------------------ # @patch(_PATCH_PROJECT) @patch(_PATCH_FLST) @patch(_PATCH_GEM_BY_BBOX) @patch(_PATCH_VG_ALKIS) def test_flst_none_aborts(self, mock_vg, mock_gem, mock_flst, mock_proj): mock_proj.instance.return_value = _DummyProject() vg_layer = _make_layer(name="Verfahrensgebiet") mock_vg.return_value = (vg_layer, LoadStatus.FIRST) mock_gem.return_value = [("1234", "Muster")] mock_flst.return_value = (None, LoadStatus.NONE) enriched, flst, status = verfahrensgebiet_alkis_komplett(None) self.assertIsNone(enriched) self.assertIsNone(flst) self.assertEqual(status, LoadStatus.NONE) # ------------------------------------------------------------------ # # 3. Keine Selektion → Warnung + (None, flst_layer, NONE) # # ------------------------------------------------------------------ # @patch(_PATCH_MSGBOX) @patch(_PATCH_SELECT) @patch(_PATCH_PROJECT) @patch(_PATCH_FLST) @patch(_PATCH_GEM_BY_BBOX) @patch(_PATCH_VG_ALKIS) def test_no_selection_returns_none_enriched( self, mock_vg, mock_gem, mock_flst, mock_proj, mock_select, mock_msgbox ): mock_proj.instance.return_value = _DummyProject() vg_layer = _make_layer(name="Verfahrensgebiet") flst_layer = _make_layer(name="Flurstücke") mock_vg.return_value = (vg_layer, LoadStatus.FIRST) mock_gem.return_value = [] mock_flst.return_value = (flst_layer, LoadStatus.FIRST) mock_select.return_value = 0 # keine Selektion enriched, flst, status = verfahrensgebiet_alkis_komplett(None) self.assertIsNone(enriched) self.assertIs(flst, flst_layer) self.assertEqual(status, LoadStatus.NONE) mock_msgbox.warning.assert_called_once() # ------------------------------------------------------------------ # # 4. Vollständiger Erfolgsfall # # ------------------------------------------------------------------ # @patch(_PATCH_VKZ) @patch(_PATCH_GET_VAR) @patch(_PATCH_ERSTELLE) @patch(_PATCH_SELECT) @patch(_PATCH_PROJECT) @patch(_PATCH_FLST) @patch(_PATCH_GEM_BY_BBOX) @patch(_PATCH_VG_ALKIS) def test_success_full_workflow( self, mock_vg, mock_gem, mock_flst, mock_proj, mock_select, mock_erstelle, mock_get_var, mock_vkz_fn, ): """Vollständiger Workflow → enriched Layer + flst Layer + FIRST.""" proj_instance = _DummyProject() mock_proj.instance.return_value = proj_instance vg_layer = _make_layer(name="Verfahrensgebiet") flst_layer = _make_layer(name="Flurstücke", lid="FLST1") enriched = _make_layer(name="Verfahrensgebiet", lid="VG_ENRICHED", valid=True) mock_vg.return_value = (vg_layer, LoadStatus.FIRST) mock_gem.return_value = [("1234", "Muster")] mock_flst.return_value = (flst_layer, LoadStatus.FIRST) mock_select.return_value = 3 mock_erstelle.return_value = (enriched, 3, 0) mock_get_var.return_value = "VF-2024-001" result_enriched, result_flst, status = verfahrensgebiet_alkis_komplett(None) self.assertIs(result_enriched, enriched) self.assertIs(result_flst, flst_layer) self.assertEqual(status, LoadStatus.FIRST) mock_vkz_fn.assert_called_once_with(enriched, "VF-2024-001") # ------------------------------------------------------------------ # # 5. Kein VKZ → _fuege_vkz_feld_hinzu mit leerem String # # ------------------------------------------------------------------ # @patch(_PATCH_VKZ) @patch(_PATCH_GET_VAR) @patch(_PATCH_ERSTELLE) @patch(_PATCH_SELECT) @patch(_PATCH_PROJECT) @patch(_PATCH_FLST) @patch(_PATCH_GEM_BY_BBOX) @patch(_PATCH_VG_ALKIS) def test_no_vkz_variable_passes_empty_string( self, mock_vg, mock_gem, mock_flst, mock_proj, mock_select, mock_erstelle, mock_get_var, mock_vkz_fn, ): proj_instance = _DummyProject() mock_proj.instance.return_value = proj_instance vg_layer = _make_layer(name="Verfahrensgebiet") flst_layer = _make_layer(name="Flurstücke") enriched = _make_layer(name="Verfahrensgebiet", valid=True) mock_vg.return_value = (vg_layer, LoadStatus.FIRST) mock_gem.return_value = [] mock_flst.return_value = (flst_layer, LoadStatus.FIRST) mock_select.return_value = 2 mock_erstelle.return_value = (enriched, 2, 0) mock_get_var.return_value = "" # keine Projektvariable verfahrensgebiet_alkis_komplett(None) mock_vkz_fn.assert_called_once_with(enriched, "") # ------------------------------------------------------------------ # # 6. erstelle_verfahrensgebiet liefert ungültigen Layer # # ------------------------------------------------------------------ # @patch(_PATCH_MSGBOX) @patch(_PATCH_ERSTELLE) @patch(_PATCH_SELECT) @patch(_PATCH_PROJECT) @patch(_PATCH_FLST) @patch(_PATCH_GEM_BY_BBOX) @patch(_PATCH_VG_ALKIS) def test_invalid_enriched_layer_returns_none( self, mock_vg, mock_gem, mock_flst, mock_proj, mock_select, mock_erstelle, mock_msgbox ): mock_proj.instance.return_value = _DummyProject() vg_layer = _make_layer() flst_layer = _make_layer(name="Flurstücke") bad_enriched = _make_layer(valid=False) mock_vg.return_value = (vg_layer, LoadStatus.FIRST) mock_gem.return_value = [] mock_flst.return_value = (flst_layer, LoadStatus.FIRST) mock_select.return_value = 5 mock_erstelle.return_value = (bad_enriched, 0, 5) enriched, flst, status = verfahrensgebiet_alkis_komplett(None) self.assertIsNone(enriched) self.assertIs(flst, flst_layer) self.assertEqual(status, LoadStatus.NONE) mock_msgbox.critical.assert_called_once() if __name__ == "__main__": unittest.main()