verfahrensgebiet aus Flst
This commit is contained in:
@@ -0,0 +1,360 @@
|
||||
"""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()
|
||||
Reference in New Issue
Block a user