import sys import types import unittest from unittest.mock import patch if "qgis" not in sys.modules: qgis_mod = types.ModuleType("qgis") qgis_utils = types.ModuleType("qgis.utils") qgis_utils.iface = types.SimpleNamespace() qgis_utils.plugins = {} qgis_mod.utils = qgis_utils sys.modules["qgis"] = qgis_mod sys.modules["qgis.utils"] = qgis_utils from sn_verfahrensgebiet.functions import verfahrensgebiet_alkis as vg class _DummyMessageBox: class StandardButton: Yes = 1 No = 2 last_critical = None next_reply = StandardButton.No @staticmethod def question(_parent, _title, _message, _buttons, _default): return _DummyMessageBox.next_reply @staticmethod def critical(parent, title, message): _DummyMessageBox.last_critical = (parent, title, message) class _DummyProject: def __init__(self, layers=None): self._layers = layers or [] self.removed = [] def mapLayersByName(self, _name): return self._layers def removeMapLayer(self, layer_id): self.removed.append(layer_id) class _DummyExistingLayer: def __init__(self, layer_id="existing"): self._id = layer_id def id(self): return self._id class _DummyGeom: def __init__(self, empty=False): self._empty = empty def isEmpty(self): return self._empty class _DummyFeature: def __init__(self, geom=None, attrs=None): self._geom = geom or _DummyGeom() self._attrs = attrs or {} def geometry(self): return self._geom def attribute(self, name): return self._attrs.get(name) class _DummyTempLayer: def __init__(self, valid=True, features=None): self._valid = valid self._features = features or [] def isValid(self): return self._valid def getFeatures(self): return self._features def crs(self): class _Crs: @staticmethod def authid(): return "EPSG:25833" return _Crs() class _DummyProvider: def addAttributes(self, _attrs): return True def addFeatures(self, _features): return True class _DummyMemoryLayer: def __init__(self): self._provider = _DummyProvider() def dataProvider(self): return self._provider def updateFields(self): return None def updateExtents(self): return None class TestVerfahrensgebietAlkis(unittest.TestCase): @patch("sn_verfahrensgebiet.functions.verfahrensgebiet_alkis.QgsProject.instance") @patch("sn_verfahrensgebiet.functions.verfahrensgebiet_alkis.QMessageBox", new=_DummyMessageBox) def test_check_existing_layer_first_load(self, mock_project_instance): mock_project_instance.return_value = _DummyProject(layers=[]) layer, status = vg._check_existing_layer(tab_widget=None) self.assertIsNone(layer) self.assertEqual(status, vg.LoadStatus.FIRST) @patch("sn_verfahrensgebiet.functions.verfahrensgebiet_alkis.QgsProject.instance") @patch("sn_verfahrensgebiet.functions.verfahrensgebiet_alkis.QMessageBox", new=_DummyMessageBox) def test_check_existing_layer_keep_existing(self, mock_project_instance): existing = _DummyExistingLayer("L1") mock_project_instance.return_value = _DummyProject(layers=[existing]) _DummyMessageBox.next_reply = _DummyMessageBox.StandardButton.No layer, status = vg._check_existing_layer(tab_widget=None) self.assertIs(layer, existing) self.assertEqual(status, vg.LoadStatus.KEEP) @patch("sn_verfahrensgebiet.functions.verfahrensgebiet_alkis.QMessageBox", new=_DummyMessageBox) @patch("sn_verfahrensgebiet.functions.verfahrensgebiet_alkis.get_variable", return_value="") def test_verfahrensgebiet_alkis_fails_when_verfahrensnummer_missing(self, _mock_get_var): _DummyMessageBox.last_critical = None layer, status, feats = vg.verfahrensgebiet_alkis(tab_widget=None) self.assertIsNone(layer) self.assertEqual(status, vg.LoadStatus.NONE) self.assertIsNotNone(_DummyMessageBox.last_critical) @patch("sn_verfahrensgebiet.functions.verfahrensgebiet_alkis._check_existing_layer", return_value=(None, vg.LoadStatus.FIRST)) @patch("sn_verfahrensgebiet.functions.verfahrensgebiet_alkis.QMessageBox", new=_DummyMessageBox) @patch("sn_verfahrensgebiet.functions.verfahrensgebiet_alkis.get_variable", return_value="VKZ123") @patch("sn_verfahrensgebiet.functions.verfahrensgebiet_alkis.QgsVectorLayer") def test_verfahrensgebiet_alkis_fails_on_invalid_wfs_layer(self, mock_qgsvectorlayer, _mock_get_var, _mock_check): _DummyMessageBox.last_critical = None mock_qgsvectorlayer.return_value = _DummyTempLayer(valid=False) layer, status, feats = vg.verfahrensgebiet_alkis(tab_widget=None) self.assertIsNone(layer) self.assertEqual(status, vg.LoadStatus.NONE) self.assertIsNotNone(_DummyMessageBox.last_critical) @patch("sn_verfahrensgebiet.functions.verfahrensgebiet_alkis.QgsFeature") @patch("sn_verfahrensgebiet.functions.verfahrensgebiet_alkis.QgsGeometry.unaryUnion", return_value=_DummyGeom(empty=False)) @patch("sn_verfahrensgebiet.functions.verfahrensgebiet_alkis._check_existing_layer", return_value=(None, vg.LoadStatus.FIRST)) @patch("sn_verfahrensgebiet.functions.verfahrensgebiet_alkis.get_variable", return_value="VKZ123") @patch("sn_verfahrensgebiet.functions.verfahrensgebiet_alkis.QgsVectorLayer") @patch("sn_verfahrensgebiet.functions.verfahrensgebiet_alkis.QMessageBox", new=_DummyMessageBox) def test_verfahrensgebiet_alkis_success_single_kombination(self, mock_qgsvectorlayer, _mock_get_var, _mock_check, _mock_union, mock_feature_cls): """Einzelne (adv_name, bezeichnung)-Kombination → kein Dialog, Memory-Layer zurück.""" feat = _DummyFeature(attrs={"adv_name": "Bodensanierung", "bezeichnung": "VKZ12/001"}) temp_layer = _DummyTempLayer(valid=True, features=[feat]) memory_layer = _DummyMemoryLayer() mock_qgsvectorlayer.side_effect = [temp_layer, memory_layer] mock_feature = mock_feature_cls.return_value mock_feature.setGeometry.return_value = None mock_feature.setAttributes.return_value = None layer, status, feats = vg.verfahrensgebiet_alkis(tab_widget=None) self.assertIs(layer, memory_layer) self.assertEqual(status, vg.LoadStatus.FIRST) self.assertEqual(len(feats), 1) @patch("sn_verfahrensgebiet.functions.verfahrensgebiet_alkis.ask_bror_kombinationen") @patch("sn_verfahrensgebiet.functions.verfahrensgebiet_alkis.QgsFeature") @patch("sn_verfahrensgebiet.functions.verfahrensgebiet_alkis.QgsGeometry.unaryUnion", return_value=_DummyGeom(empty=False)) @patch("sn_verfahrensgebiet.functions.verfahrensgebiet_alkis._check_existing_layer", return_value=(None, vg.LoadStatus.FIRST)) @patch("sn_verfahrensgebiet.functions.verfahrensgebiet_alkis.get_variable", return_value="VKZ123") @patch("sn_verfahrensgebiet.functions.verfahrensgebiet_alkis.QgsVectorLayer") @patch("sn_verfahrensgebiet.functions.verfahrensgebiet_alkis.QMessageBox", new=_DummyMessageBox) def test_verfahrensgebiet_alkis_multi_kombination_dialog_auswahl( self, mock_qgsvectorlayer, _mock_get_var, _mock_check, _mock_union, mock_feature_cls, mock_dialog ): """Zwei verschiedene Kombinationen → Dialog wird gezeigt, gewählte Features genutzt.""" feat1 = _DummyFeature(attrs={"adv_name": "ADV-A", "bezeichnung": "VKZ12/001"}) feat2 = _DummyFeature(attrs={"adv_name": "ADV-B", "bezeichnung": "VKZ12/002"}) temp_layer = _DummyTempLayer(valid=True, features=[feat1, feat2]) memory_layer = _DummyMemoryLayer() mock_qgsvectorlayer.side_effect = [temp_layer, memory_layer] mock_dialog.return_value = [("ADV-A", "VKZ12/001")] mock_feature = mock_feature_cls.return_value mock_feature.setGeometry.return_value = None mock_feature.setAttributes.return_value = None layer, status, feats = vg.verfahrensgebiet_alkis(tab_widget=None) mock_dialog.assert_called_once() self.assertIs(layer, memory_layer) self.assertEqual(status, vg.LoadStatus.FIRST) # Nur feat1 gehört zur gewählten Kombination self.assertEqual(feats, [feat1]) @patch("sn_verfahrensgebiet.functions.verfahrensgebiet_alkis.ask_bror_kombinationen") @patch("sn_verfahrensgebiet.functions.verfahrensgebiet_alkis._check_existing_layer", return_value=(None, vg.LoadStatus.FIRST)) @patch("sn_verfahrensgebiet.functions.verfahrensgebiet_alkis.get_variable", return_value="VKZ123") @patch("sn_verfahrensgebiet.functions.verfahrensgebiet_alkis.QgsVectorLayer") @patch("sn_verfahrensgebiet.functions.verfahrensgebiet_alkis.QMessageBox", new=_DummyMessageBox) def test_verfahrensgebiet_alkis_multi_kombination_dialog_abbruch( self, mock_qgsvectorlayer, _mock_get_var, _mock_check, mock_dialog ): """Zwei verschiedene Kombinationen → Nutzer bricht Dialog ab → NONE.""" feat1 = _DummyFeature(attrs={"adv_name": "ADV-A", "bezeichnung": "VKZ12/001"}) feat2 = _DummyFeature(attrs={"adv_name": "ADV-B", "bezeichnung": "VKZ12/002"}) temp_layer = _DummyTempLayer(valid=True, features=[feat1, feat2]) mock_qgsvectorlayer.return_value = temp_layer mock_dialog.return_value = [] # Nutzer bricht ab layer, status, feats = vg.verfahrensgebiet_alkis(tab_widget=None) mock_dialog.assert_called_once() self.assertIsNone(layer) self.assertEqual(status, vg.LoadStatus.NONE) self.assertEqual(feats, []) @patch("sn_verfahrensgebiet.functions.verfahrensgebiet_alkis.ask_bror_kombinationen") @patch("sn_verfahrensgebiet.functions.verfahrensgebiet_alkis.QgsFeature") @patch("sn_verfahrensgebiet.functions.verfahrensgebiet_alkis.QgsGeometry.unaryUnion", return_value=_DummyGeom(empty=False)) @patch("sn_verfahrensgebiet.functions.verfahrensgebiet_alkis._check_existing_layer", return_value=(None, vg.LoadStatus.FIRST)) @patch("sn_verfahrensgebiet.functions.verfahrensgebiet_alkis.get_variable", return_value="VKZ123") @patch("sn_verfahrensgebiet.functions.verfahrensgebiet_alkis.QgsVectorLayer") @patch("sn_verfahrensgebiet.functions.verfahrensgebiet_alkis.QMessageBox", new=_DummyMessageBox) def test_verfahrensgebiet_alkis_multi_kombination_mehrfachauswahl( self, mock_qgsvectorlayer, _mock_get_var, _mock_check, _mock_union, mock_feature_cls, mock_dialog ): """Zwei verschiedene Kombinationen → beide ausgewählt → beide Feature-Gruppen genutzt.""" feat1 = _DummyFeature(attrs={"adv_name": "ADV-A", "bezeichnung": "VKZ12/001"}) feat2 = _DummyFeature(attrs={"adv_name": "ADV-B", "bezeichnung": "VKZ12/002"}) temp_layer = _DummyTempLayer(valid=True, features=[feat1, feat2]) memory_layer = _DummyMemoryLayer() mock_qgsvectorlayer.side_effect = [temp_layer, memory_layer] mock_dialog.return_value = [("ADV-A", "VKZ12/001"), ("ADV-B", "VKZ12/002")] mock_feature = mock_feature_cls.return_value mock_feature.setGeometry.return_value = None mock_feature.setAttributes.return_value = None layer, status, feats = vg.verfahrensgebiet_alkis(tab_widget=None) mock_dialog.assert_called_once() self.assertIs(layer, memory_layer) self.assertEqual(status, vg.LoadStatus.FIRST) self.assertEqual(feats, [feat1, feat2]) if __name__ == "__main__": unittest.main()