"""sn_verfahrensgebiet/tests/test_verfahrensgebiet_alkis_komplett.py Unit-Tests für :mod:`sn_verfahrensgebiet.functions.verfahrensgebiet_alkis_komplett`. Getestete Szenarien: Erfolgsfall (Flächenübereinstimmung): - Vollständiger Workflow → enriched Layer + flst_layer + bror_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, NONE) - flurstueck_alkis gibt LoadStatus.NONE → (None, None, None, NONE) - Keine Flurstücke selektiert → (None, flst_layer, None, NONE) + Warnung - erstelle_verfahrensgebiet_aus_auswahl liefert ungültigen Layer → (None, flst_layer, None, NONE) - Progress-Abbruch nach Schritt 1 → sofortiger sauberer Rückgabestatus Flächenvergleich: - Übereinstimmung → show_info_dialog, keine Detailprüfung - Abweichung + abbrechen → kein Detailprüfungs-Aufruf - Abweichung + als_vg_laden → BROR umbenannt, als enriched_layer zurückgegeben - Abweichung + detailpruefung → lade_punktort_ta + knickpunkt_pruefung aufgerufen Cleanup: - BROR-Layer wird bei Erfolg NICHT entfernt - BROR-Layer wird bei Fehler entfernt (wenn in diesem Lauf hinzugefügt) - TempVerfahrensgebiet wird stets entfernt Hilfsfunktionen: - _select_by_location: processing-Fallback liefert 0 → keine Exception - _fuege_vkz_feld_hinzu: leeres VKZ wird ignoriert - _vergleiche_flaechen: korrekte Toleranzlogik - _lese_flaechen_toleranz: Default 0.0, parst Projektvariable """ 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.verfahrensgebiet_alkis import LoadStatus from sn_verfahrensgebiet.functions.verfahrensgebiet_alkis_komplett import ( verfahrensgebiet_alkis_komplett, _select_by_location, _fuege_vkz_feld_hinzu, _vergleiche_flaechen, _lese_flaechen_toleranz, ) # --------------------------------------------------------------------------- # 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, area=1000.0): """Erzeugt einen konfigurierten Mock-Layer mit getFeatures-Support.""" 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 mock_geom = MagicMock() mock_geom.isEmpty.return_value = False mock_geom.area.return_value = area mock_geom.boundary.return_value = MagicMock(isEmpty=lambda: True) mock_feat = MagicMock() mock_feat.geometry.return_value = mock_geom layer.getFeatures.return_value = [mock_feat] mock_crs = MagicMock() mock_crs.authid.return_value = "EPSG:25833" layer.crs.return_value = mock_crs 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 def _make_progress_mock(canceled=False): prog = MagicMock() prog.is_canceled.return_value = canceled return prog # --------------------------------------------------------------------------- # Patch-Konstanten # --------------------------------------------------------------------------- _MOD = "sn_verfahrensgebiet.functions.verfahrensgebiet_alkis_komplett" _PATCH_VG_ALKIS = f"{_MOD}._suche_verfahrensgebiet_features" _PATCH_GEM = f"{_MOD}.gemarkung_schluessel_und_namen_von_vg_geometrie" _PATCH_FLST = f"{_MOD}.flurstueck_alkis" _PATCH_SELECT = f"{_MOD}._select_by_location" _PATCH_ERSTELLE = f"{_MOD}.erstelle_verfahrensgebiet_aus_auswahl" _PATCH_VKZ = f"{_MOD}._fuege_vkz_feld_hinzu" _PATCH_PROJECT = f"{_MOD}.QgsProject" _PATCH_MSGBOX = f"{_MOD}.QMessageBox" _PATCH_WARNING = f"{_MOD}.warning" _PATCH_GET_VAR = f"{_MOD}.get_variable" _PATCH_PROGRESS = f"{_MOD}.create_progress_dialog" _PATCH_VERGLEICHE = f"{_MOD}._vergleiche_flaechen" _PATCH_SHOW_INFO = f"{_MOD}.show_info_dialog" _PATCH_ASK_DIALOG = f"{_MOD}.ask_detailpruefung_oder_vg_laden" _PATCH_GP_ALKIS = f"{_MOD}.lade_punktort_ta" _PATCH_KNICK = f"{_MOD}.knickpunkt_pruefung" _PATCH_EXTRAHIERE = f"{_MOD}._extrahiere_knickpunkte" _PATCH_LANDKREIS_SUCHE = f"{_MOD}._suche_landkreis_kombinationen" _PATCH_LANDKREIS_BAU = f"{_MOD}._baue_layer_aus_landkreis_auswahl" _PATCH_ASK_LANDKREIS = f"{_MOD}.ask_landkreis_auswahl" # --------------------------------------------------------------------------- # Tests: _select_by_location # --------------------------------------------------------------------------- class TestSelectByLocation(unittest.TestCase): def test_processing_not_available_returns_zero(self): flst, vg = MagicMock(), 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): flst = MagicMock() flst.selectedFeatureCount.return_value = 7 vg = MagicMock() mock_proc = MagicMock() with patch.dict("sys.modules", {"processing": mock_proc}): result = _select_by_location(flst, vg) self.assertEqual(result, 7) def test_processing_exception_returns_zero(self): flst, vg = MagicMock(), MagicMock() mock_proc = MagicMock() mock_proc.run.side_effect = RuntimeError("Kein QGIS") with patch.dict("sys.modules", {"processing": mock_proc}): 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): layer = MagicMock() _fuege_vkz_feld_hinzu(layer, "") layer.dataProvider.assert_not_called() def test_none_vkz_does_nothing(self): layer = MagicMock() _fuege_vkz_feld_hinzu(layer, None) layer.dataProvider.assert_not_called() def test_vkz_field_added(self): 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: _vergleiche_flaechen / _lese_flaechen_toleranz # --------------------------------------------------------------------------- class TestFlaechenvergleich(unittest.TestCase): def _layer(self, area: float): layer = MagicMock() geom = MagicMock() geom.isEmpty.return_value = False geom.area.return_value = area feat = MagicMock() feat.geometry.return_value = geom layer.getFeatures.return_value = [feat] return layer def test_gleiche_flaeche_stimmt_ueberein(self): self.assertTrue(_vergleiche_flaechen(self._layer(1234.5), self._layer(1234.5), 0.0)[2]) def test_verschiedene_flaeche_stimmt_nicht_ueberein(self): self.assertFalse(_vergleiche_flaechen(self._layer(1234.5), self._layer(1200.0), 0.0)[2]) def test_differenz_innerhalb_toleranz(self): self.assertTrue(_vergleiche_flaechen(self._layer(1234.5), self._layer(1234.8), 1.0)[2]) def test_differenz_ausserhalb_toleranz(self): self.assertFalse(_vergleiche_flaechen(self._layer(1234.5), self._layer(1236.0), 1.0)[2]) def test_flaechen_werte_korrekt(self): bror_f, vg_f, _ = _vergleiche_flaechen(self._layer(800.0), self._layer(750.0)) self.assertAlmostEqual(bror_f, 800.0) self.assertAlmostEqual(vg_f, 750.0) @patch(f"{_MOD}.get_variable", return_value=None) def test_lese_toleranz_default_ist_null(self, _): self.assertEqual(_lese_flaechen_toleranz(), 0.0) @patch(f"{_MOD}.get_variable", return_value="2.5") def test_lese_toleranz_parst_string(self, _): self.assertAlmostEqual(_lese_flaechen_toleranz(), 2.5) @patch(f"{_MOD}.get_variable", return_value="ungueltig") def test_lese_toleranz_ungueltig_fallback_null(self, _): self.assertEqual(_lese_flaechen_toleranz(), 0.0) # --------------------------------------------------------------------------- # Tests: verfahrensgebiet_alkis_komplett (Hauptworkflow) # --------------------------------------------------------------------------- class TestVerfahrensgebietAlkisKomplett(unittest.TestCase): # ------------------------------------------------------------------ # # Abbruchpfade # ------------------------------------------------------------------ # @patch(_PATCH_PROGRESS) @patch(_PATCH_PROJECT) @patch(_PATCH_VG_ALKIS) @patch(_PATCH_GET_VAR, return_value="VF-2024-001") def test_vg_alkis_none_aborts(self, mock_get_var, mock_vg, mock_proj, mock_prog): mock_proj.instance.return_value = _DummyProject() mock_vg.return_value = (None, LoadStatus.NONE, {}, None) mock_prog.return_value = _make_progress_mock() enriched, flst, bror, status = verfahrensgebiet_alkis_komplett(None) self.assertIsNone(enriched) self.assertIsNone(flst) self.assertIsNone(bror) self.assertEqual(status, LoadStatus.NONE) @patch(_PATCH_PROGRESS) @patch(_PATCH_PROJECT) @patch(_PATCH_GEM) @patch(_PATCH_VG_ALKIS) @patch(_PATCH_GET_VAR, return_value="VF-2024-001") def test_flst_none_aborts(self, mock_get_var, mock_vg, mock_gem, mock_proj, mock_prog): mock_proj.instance.return_value = _DummyProject() vg_layer = _make_layer(name="BauRaumOrdnungsrecht (ALKIS)") mock_vg.return_value = (vg_layer, LoadStatus.FIRST, {}, None) mock_gem.return_value = [("1234", "Muster")] mock_prog.return_value = _make_progress_mock() with patch(_PATCH_FLST, return_value=(None, LoadStatus.NONE)): enriched, flst, bror, status = verfahrensgebiet_alkis_komplett(None) self.assertIsNone(enriched) self.assertIsNone(flst) self.assertEqual(status, LoadStatus.NONE) @patch(_PATCH_PROGRESS) @patch(_PATCH_MSGBOX) @patch(_PATCH_SELECT) @patch(_PATCH_PROJECT) @patch(_PATCH_GEM) @patch(_PATCH_FLST) @patch(_PATCH_VG_ALKIS) @patch(_PATCH_GET_VAR, return_value="VF-2024-001") def test_no_selection_returns_none_enriched( self, mock_get_var, mock_vg, mock_flst, mock_gem, mock_proj, mock_select, mock_msgbox, mock_prog ): mock_proj.instance.return_value = _DummyProject() vg_layer = _make_layer(name="BauRaumOrdnungsrecht (ALKIS)") flst_layer = _make_layer(name="Flurstücke") mock_vg.return_value = (vg_layer, LoadStatus.FIRST, {}, None) mock_gem.return_value = [] mock_flst.return_value = (flst_layer, LoadStatus.FIRST) mock_select.return_value = 0 mock_prog.return_value = _make_progress_mock() enriched, flst, bror, status = verfahrensgebiet_alkis_komplett(None) self.assertIsNone(enriched) self.assertIs(flst, flst_layer) self.assertIsNone(bror) self.assertEqual(status, LoadStatus.NONE) mock_msgbox.warning.assert_called_once() @patch(_PATCH_PROGRESS) @patch(_PATCH_MSGBOX) @patch(_PATCH_ERSTELLE) @patch(_PATCH_SELECT) @patch(_PATCH_PROJECT) @patch(_PATCH_GEM) @patch(_PATCH_FLST) @patch(_PATCH_VG_ALKIS) @patch(_PATCH_GET_VAR, return_value="VF-2024-001") def test_invalid_enriched_layer_returns_none( self, mock_get_var, mock_vg, mock_flst, mock_gem, mock_proj, mock_select, mock_erstelle, mock_msgbox, mock_prog ): mock_proj.instance.return_value = _DummyProject() vg_layer = _make_layer(name="BauRaumOrdnungsrecht (ALKIS)") flst_layer = _make_layer(name="Flurstücke") bad_enriched = _make_layer(valid=False) mock_vg.return_value = (vg_layer, LoadStatus.FIRST, {}, None) 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) mock_prog.return_value = _make_progress_mock() enriched, flst, bror, status = verfahrensgebiet_alkis_komplett(None) self.assertIsNone(enriched) self.assertIs(flst, flst_layer) self.assertIsNone(bror) self.assertEqual(status, LoadStatus.NONE) mock_msgbox.critical.assert_called_once() @patch(_PATCH_PROGRESS) @patch(_PATCH_PROJECT) @patch(_PATCH_VG_ALKIS) @patch(_PATCH_GET_VAR, return_value="VF-2024-001") def test_progress_cancel_after_step1_returns_none(self, mock_get_var, mock_vg, mock_proj, mock_prog): """Abbruch nach Schritt 1 → sofortiger sauberer Rückgabestatus.""" mock_proj.instance.return_value = _DummyProject() vg_layer = _make_layer(name="BauRaumOrdnungsrecht (ALKIS)") mock_vg.return_value = (vg_layer, LoadStatus.FIRST, {}, None) prog = MagicMock() prog.is_canceled.side_effect = [False, True] # 1. Aufruf False, 2. True mock_prog.return_value = prog enriched, flst, bror, status = verfahrensgebiet_alkis_komplett(None) self.assertIsNone(enriched) self.assertEqual(status, LoadStatus.NONE) # ------------------------------------------------------------------ # # Flächenvergleich – Übereinstimmung # ------------------------------------------------------------------ # @patch(_PATCH_PROGRESS) @patch(_PATCH_SHOW_INFO) @patch(_PATCH_VERGLEICHE) @patch(_PATCH_VKZ) @patch(_PATCH_GET_VAR) @patch(_PATCH_ERSTELLE) @patch(_PATCH_SELECT) @patch(_PATCH_PROJECT) @patch(_PATCH_GEM) @patch(_PATCH_FLST) @patch(_PATCH_VG_ALKIS) def test_success_flaechen_match_zeigt_info( self, mock_vg, mock_flst, mock_gem, mock_proj, mock_select, mock_erstelle, mock_get_var, mock_vkz, mock_vergl, mock_info, mock_prog, ): """Flächenübereinstimmung → show_info_dialog mit erwartetem Text.""" proj_inst = _DummyProject() mock_proj.instance.return_value = proj_inst vg_layer = _make_layer(name="BauRaumOrdnungsrecht (ALKIS)") 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, {}, None) 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" mock_vergl.return_value = (1234.5, 1234.5, True) mock_prog.return_value = _make_progress_mock() result_enriched, result_flst, result_bror, status = verfahrensgebiet_alkis_komplett(None) self.assertIs(result_enriched, enriched) self.assertIs(result_flst, flst_layer) self.assertIs(result_bror, vg_layer) self.assertEqual(status, LoadStatus.FIRST) mock_vkz.assert_called_once_with(enriched, "VF-2024-001") mock_info.assert_called_once() self.assertIn("erwarteter Geometrie", mock_info.call_args[0][1]) @patch(_PATCH_PROGRESS) @patch(_PATCH_SHOW_INFO) @patch(_PATCH_VERGLEICHE) @patch(_PATCH_VKZ) @patch(_PATCH_GET_VAR) @patch(_PATCH_ERSTELLE) @patch(_PATCH_SELECT) @patch(_PATCH_PROJECT) @patch(_PATCH_GEM) @patch(_PATCH_FLST) @patch(_PATCH_VG_ALKIS) def test_no_vkz_variable_passes_empty_string( self, mock_vg, mock_flst, mock_gem, mock_proj, mock_select, mock_erstelle, mock_get_var, mock_vkz, mock_vergl, mock_info, mock_prog, ): mock_proj.instance.return_value = _DummyProject() vg_layer = _make_layer(name="BauRaumOrdnungsrecht (ALKIS)") flst_layer = _make_layer(name="Flurstücke") enriched = _make_layer(name="Verfahrensgebiet", valid=True) mock_vg.return_value = (vg_layer, LoadStatus.FIRST, {}, None) 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 = "VF-2024-001" mock_vergl.return_value = (1000.0, 1000.0, True) mock_prog.return_value = _make_progress_mock() verfahrensgebiet_alkis_komplett(None) mock_vkz.assert_called_once_with(enriched, "VF-2024-001") # ------------------------------------------------------------------ # # Flächenvergleich – Abweichung + Abbrechen # ------------------------------------------------------------------ # @patch(_PATCH_PROGRESS) @patch(_PATCH_KNICK) @patch(_PATCH_ASK_DIALOG) @patch(_PATCH_VERGLEICHE) @patch(_PATCH_VKZ) @patch(_PATCH_GET_VAR) @patch(_PATCH_ERSTELLE) @patch(_PATCH_SELECT) @patch(_PATCH_PROJECT) @patch(_PATCH_GEM) @patch(_PATCH_FLST) @patch(_PATCH_VG_ALKIS) def test_flaechen_abweichung_abbrechen_kein_detailcheck( self, mock_vg, mock_flst, mock_gem, mock_proj, mock_select, mock_erstelle, mock_get_var, mock_vkz, mock_vergl, mock_ask, mock_knick, mock_prog, ): """Abweichung + Abbrechen → knickpunkt_pruefung wird NICHT aufgerufen.""" mock_proj.instance.return_value = _DummyProject() vg_layer = _make_layer(name="BauRaumOrdnungsrecht (ALKIS)") flst_layer = _make_layer(name="Flurstücke") enriched = _make_layer(name="Verfahrensgebiet", valid=True) mock_vg.return_value = (vg_layer, LoadStatus.FIRST, {}, None) mock_gem.return_value = [] 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" mock_vergl.return_value = (1000.0, 800.0, False) mock_ask.return_value = "abbrechen" mock_prog.return_value = _make_progress_mock() result_enriched, _, _, status = verfahrensgebiet_alkis_komplett(None) mock_knick.assert_not_called() self.assertIs(result_enriched, enriched) self.assertEqual(status, LoadStatus.FIRST) # ------------------------------------------------------------------ # # Flächenvergleich – Abweichung + Als VG laden # ------------------------------------------------------------------ # @patch(_PATCH_PROGRESS) @patch(_PATCH_KNICK) @patch(_PATCH_ASK_DIALOG) @patch(_PATCH_VERGLEICHE) @patch(_PATCH_VKZ) @patch(_PATCH_GET_VAR) @patch(_PATCH_ERSTELLE) @patch(_PATCH_SELECT) @patch(_PATCH_PROJECT) @patch(_PATCH_GEM) @patch(_PATCH_FLST) @patch(_PATCH_VG_ALKIS) def test_flaechen_abweichung_als_vg_laden( self, mock_vg, mock_flst, mock_gem, mock_proj, mock_select, mock_erstelle, mock_get_var, mock_vkz, mock_vergl, mock_ask, mock_knick, mock_prog, ): """Abweichung + Als VG laden → BROR-Layer wird umbenannt und als enriched_layer zurückgegeben; flst_layer und bror_layer sind None.""" proj_inst = _DummyProject() mock_proj.instance.return_value = proj_inst vg_layer = _make_layer(name="BauRaumOrdnungsrecht (ALKIS)", lid="BROR_VG") flst_layer = _make_layer(name="Flurstücke") enriched = _make_layer(name="Verfahrensgebiet", valid=True) mock_vg.return_value = (vg_layer, LoadStatus.FIRST, {}, None) mock_gem.return_value = [] 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" mock_vergl.return_value = (1000.0, 800.0, False) mock_ask.return_value = "als_vg_laden" mock_prog.return_value = _make_progress_mock() result_enriched, result_flst, result_bror, status = verfahrensgebiet_alkis_komplett(None) mock_knick.assert_not_called() # BROR-Layer wird als enriched_layer zurückgegeben self.assertIs(result_enriched, vg_layer) self.assertIsNone(result_flst) self.assertIsNone(result_bror) self.assertEqual(status, LoadStatus.FIRST) # Name wurde auf VERF_LAYER_NAME geändert vg_layer.setName.assert_called() # ------------------------------------------------------------------ # # Flächenvergleich – Abweichung + Detailprüfung # ------------------------------------------------------------------ # @patch(_PATCH_PROGRESS) @patch(_PATCH_SHOW_INFO) @patch(_PATCH_EXTRAHIERE) @patch(_PATCH_KNICK) @patch(_PATCH_GP_ALKIS) @patch(_PATCH_ASK_DIALOG) @patch(_PATCH_VERGLEICHE) @patch(_PATCH_VKZ) @patch(_PATCH_GET_VAR) @patch(_PATCH_ERSTELLE) @patch(_PATCH_SELECT) @patch(_PATCH_PROJECT) @patch(_PATCH_GEM) @patch(_PATCH_FLST) @patch(_PATCH_VG_ALKIS) def test_flaechen_abweichung_detailpruefung_startet_knickpruefung( self, mock_vg, mock_flst, mock_gem, mock_proj, mock_select, mock_erstelle, mock_get_var, mock_vkz, mock_vergl, mock_ask, mock_gp, mock_knick, mock_extrahiere, mock_info, mock_prog, ): """Abweichung + Detailprüfung → lade_punktort_ta + knickpunkt_pruefung aufgerufen, Ergebnislayer ins Projekt, Abschlussmeldung mit Zählung.""" proj_inst = _DummyProject() mock_proj.instance.return_value = proj_inst vg_layer = _make_layer(name="BauRaumOrdnungsrecht (ALKIS)") flst_layer = _make_layer(name="Flurstücke") enriched = _make_layer(name="Verfahrensgebiet", valid=True) punktort_layer = _make_layer(name="PunktortTA", lid="PTA_L") ergebnis_layer = _make_layer(name="Knickpunktprüfung", lid="KNICK_L") mock_vg.return_value = (vg_layer, LoadStatus.FIRST, {}, None) mock_gem.return_value = [] 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" mock_vergl.return_value = (1000.0, 800.0, False) mock_ask.return_value = "detailpruefung" mock_extrahiere.return_value = [MagicMock(), MagicMock(), MagicMock()] # 3 Knickpunkte mock_gp.return_value = punktort_layer mock_knick.return_value = (ergebnis_layer, 2, 1, 0) mock_prog.return_value = _make_progress_mock() result_enriched, _, _, status = verfahrensgebiet_alkis_komplett(None) mock_gp.assert_called_once() mock_knick.assert_called_once() self.assertIn(ergebnis_layer, proj_inst.added) mock_info.assert_called_once() # Abschlussmeldung muss Zahlen enthalten info_text = mock_info.call_args[0][1] self.assertIn("3", info_text) # Anzahl Knickpunkte self.assertIn("2", info_text) # identisch self.assertIn("1", info_text) # kein_pta self.assertEqual(status, LoadStatus.FIRST) # ------------------------------------------------------------------ # # Cleanup-Verhalten # ------------------------------------------------------------------ # @patch(_PATCH_PROGRESS) @patch(_PATCH_SHOW_INFO) @patch(_PATCH_VERGLEICHE) @patch(_PATCH_VKZ) @patch(_PATCH_GET_VAR) @patch(_PATCH_ERSTELLE) @patch(_PATCH_SELECT) @patch(_PATCH_PROJECT) @patch(_PATCH_GEM) @patch(_PATCH_FLST) @patch(_PATCH_VG_ALKIS) def test_bror_layer_bleibt_bei_erfolg_im_projekt( self, mock_vg, mock_flst, mock_gem, mock_proj, mock_select, mock_erstelle, mock_get_var, mock_vkz, mock_vergl, mock_info, mock_prog, ): """Bei Erfolg darf der BROR-Layer NICHT entfernt werden.""" proj_inst = _DummyProject() mock_proj.instance.return_value = proj_inst vg_layer = _make_layer(name="BauRaumOrdnungsrecht (ALKIS)", lid="BROR_ID") flst_layer = _make_layer(name="Flurstücke") enriched = _make_layer(name="Verfahrensgebiet", valid=True) mock_vg.return_value = (vg_layer, LoadStatus.FIRST, {}, None) mock_gem.return_value = [] 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" mock_vergl.return_value = (1000.0, 1000.0, True) mock_prog.return_value = _make_progress_mock() verfahrensgebiet_alkis_komplett(None) self.assertNotIn("BROR_ID", proj_inst.removed) @patch(_PATCH_PROGRESS) @patch(_PATCH_PROJECT) @patch(_PATCH_GEM) @patch(_PATCH_VG_ALKIS) @patch(_PATCH_GET_VAR, return_value="VF-2024-001") def test_bror_layer_wird_bei_fehler_entfernt( self, mock_get_var, mock_vg, mock_gem, mock_proj, mock_prog ): """Bei Fehler (flst_alkis NONE) wird der BROR-Layer aus dem Projekt entfernt.""" proj_inst = _DummyProject() mock_proj.instance.return_value = proj_inst vg_layer = _make_layer(name="BauRaumOrdnungsrecht (ALKIS)", lid="BROR_ERR") mock_vg.return_value = (vg_layer, LoadStatus.FIRST, {}, None) mock_gem.return_value = [] mock_prog.return_value = _make_progress_mock() with patch(_PATCH_FLST, return_value=(None, LoadStatus.NONE)): verfahrensgebiet_alkis_komplett(None) self.assertIn("BROR_ERR", proj_inst.removed) # ------------------------------------------------------------------ # # Landkreis-Fallback (keine Verfahrensnummer) # ------------------------------------------------------------------ # @patch(_PATCH_PROGRESS) @patch(_PATCH_MSGBOX) @patch(_PATCH_ASK_LANDKREIS, return_value=None) @patch(_PATCH_GET_VAR, return_value="") def test_kein_vkz_kein_landkreis_dialog_abgebrochen( self, mock_get_var, mock_ask_landkreis, mock_msgbox, mock_prog ): """Weder Verfahrensnummer noch Landkreis gesetzt, Nutzer bricht den Landkreis-Auswahldialog ab → stiller Abbruch, kein Layer, KEINE Fehlermeldung (bewusster Abbruch, kein Fehlerzustand).""" enriched, flst, bror, status = verfahrensgebiet_alkis_komplett(None) self.assertIsNone(enriched) self.assertIsNone(flst) self.assertIsNone(bror) self.assertEqual(status, LoadStatus.NONE) mock_msgbox.critical.assert_not_called() @patch(_PATCH_PROGRESS) @patch(_PATCH_PROJECT) @patch(_PATCH_LANDKREIS_BAU) @patch(_PATCH_LANDKREIS_SUCHE) @patch(_PATCH_ASK_LANDKREIS, return_value="22") @patch(_PATCH_GET_VAR, return_value="") def test_kein_vkz_kein_landkreis_dialog_liefert_landkreis( self, mock_get_var, mock_ask_landkreis, mock_such, mock_bau, mock_proj, mock_prog ): """Weder Verfahrensnummer noch Landkreis gesetzt, Nutzer wählt einen Landkreis im Dialog → Landkreis-Suchpfad wird mit dieser Kennzahl verwendet (wie im Fall 'Landkreis aus Projekteigenschaften').""" proj_inst = _DummyProject() mock_proj.instance.return_value = proj_inst vg_layer = _make_layer(name="BauRaumOrdnungsrecht (ALKIS)") mock_such.return_value = (None, LoadStatus.FIRST, {("A", "22015"): [MagicMock()]}, "EPSG:25833") mock_bau.return_value = (vg_layer, LoadStatus.FIRST, True) # nur_laden=True mock_prog.return_value = _make_progress_mock() enriched, flst, bror, status = verfahrensgebiet_alkis_komplett(None) mock_ask_landkreis.assert_called_once() mock_such.assert_called_once_with(None, "22") mock_bau.assert_called_once() self.assertIs(enriched, vg_layer) self.assertEqual(status, LoadStatus.FIRST) @patch(_PATCH_PROGRESS) @patch(_PATCH_PROJECT) @patch(_PATCH_LANDKREIS_BAU) @patch(_PATCH_LANDKREIS_SUCHE) def test_landkreis_modus_wird_bei_fehlender_vkz_verwendet( self, mock_such, mock_bau, mock_proj, mock_prog ): """Keine Verfahrensnummer, aber Landkreis gesetzt → Landkreis-Suchpfad statt VKZ-Pfad wird verwendet.""" def _get_var_side_effect(key, scope="project"): return {"verfahrensnummer": "", "landkreis_kennzahl": "22"}.get(key, "") proj_inst = _DummyProject() mock_proj.instance.return_value = proj_inst vg_layer = _make_layer(name="BauRaumOrdnungsrecht (ALKIS)") mock_such.return_value = (None, LoadStatus.FIRST, {("A", "22015"): [MagicMock()]}, "EPSG:25833") mock_bau.return_value = (vg_layer, LoadStatus.FIRST, True) # nur_laden=True mock_prog.return_value = _make_progress_mock() with patch(_PATCH_GET_VAR, side_effect=_get_var_side_effect): enriched, flst, bror, status = verfahrensgebiet_alkis_komplett(None) mock_such.assert_called_once() mock_bau.assert_called_once() self.assertIs(enriched, vg_layer) self.assertIsNone(flst) self.assertIsNone(bror) self.assertEqual(status, LoadStatus.FIRST) @patch(_PATCH_PROGRESS) @patch(_PATCH_PROJECT) @patch(_PATCH_LANDKREIS_BAU) @patch(_PATCH_LANDKREIS_SUCHE) def test_landkreis_modus_nur_laden_ueberspringt_dialog2( self, mock_such, mock_bau, mock_proj, mock_prog ): """nur_laden=True → Funktion kehrt sofort zurück, Dialog 2 (Gemarkung etc.) wird nicht gestartet.""" def _get_var_side_effect(key, scope="project"): return {"verfahrensnummer": "", "landkreis_kennzahl": "22"}.get(key, "") proj_inst = _DummyProject() mock_proj.instance.return_value = proj_inst vg_layer = _make_layer(name="BauRaumOrdnungsrecht (ALKIS)") mock_such.return_value = (None, LoadStatus.FIRST, {("A", "22015"): [MagicMock()]}, "EPSG:25833") mock_bau.return_value = (vg_layer, LoadStatus.FIRST, True) mock_prog.return_value = _make_progress_mock() with patch(_PATCH_GET_VAR, side_effect=_get_var_side_effect), \ patch(_PATCH_GEM) as mock_gem: verfahrensgebiet_alkis_komplett(None) mock_gem.assert_not_called() if __name__ == "__main__": unittest.main()