Merge pull request 'Feature/testdatagrabber docstrings' (#61) from feature/testdatagrabber-docstrings into unstable

Reviewed-on: #61
This commit was merged in pull request #61.
This commit is contained in:
2026-09-17 13:59:16 +00:00
12 changed files with 110 additions and 5 deletions
+6
View File
@@ -18,6 +18,7 @@ class TestDataGrabber(unittest.TestCase):
@patch("sn_basis.modules.DataGrabber.Dateipruefer")
def test_analyze_source_type_detects_excel(self, mock_dateipruefer):
"""Prüft, dass eine erreichbare .xlsx-Datei als Quelltyp 'excel' erkannt wird."""
mock_dateipruefer.return_value.pruefe.return_value = pruef_ergebnis(
ok=True,
meldung="ok",
@@ -33,6 +34,7 @@ class TestDataGrabber(unittest.TestCase):
@patch("sn_basis.modules.DataGrabber.Dateipruefer")
def test_analyze_source_type_returns_unknown_filetype(self, mock_dateipruefer):
"""Prüft, dass eine erreichbare Datei mit unbekannter Endung als 'unbekannter_dateityp' eingestuft wird."""
mock_dateipruefer.return_value.pruefe.return_value = pruef_ergebnis(
ok=True,
meldung="ok",
@@ -48,6 +50,7 @@ class TestDataGrabber(unittest.TestCase):
@patch("sn_basis.modules.DataGrabber.Linkpruefer")
@patch("sn_basis.modules.DataGrabber.Dateipruefer")
def test_analyze_source_type_detects_service_when_file_check_fails(self, mock_dateipruefer, mock_linkpruefer):
"""Prüft, dass eine Quelle als 'dienst' erkannt wird, wenn die Dateiprüfung fehlschlägt, die URL aber erreichbar ist."""
mock_dateipruefer.return_value.pruefe.return_value = pruef_ergebnis(
ok=False,
meldung="missing",
@@ -70,6 +73,7 @@ class TestDataGrabber(unittest.TestCase):
@patch("sn_basis.modules.DataGrabber.Linkpruefer")
@patch("sn_basis.modules.DataGrabber.Dateipruefer")
def test_analyze_source_type_returns_unbekannte_quelle_on_full_failure(self, mock_dateipruefer, mock_linkpruefer):
"""Prüft, dass eine Quelle als 'unbekannte_quelle' eingestuft wird, wenn weder Datei- noch Linkprüfung erfolgreich sind."""
mock_dateipruefer.return_value.pruefe.return_value = pruef_ergebnis(
ok=False,
meldung="missing",
@@ -92,6 +96,7 @@ class TestDataGrabber(unittest.TestCase):
@patch.object(DataGrabber, "analyze_source_type")
@patch.object(DataGrabber, "_process_excel_source")
def test_run_returns_success_summary_with_valid_rows(self, mock_process_excel, mock_analyze):
"""Prüft, dass run() bei erfolgreichem Excel-Import eine Erfolgsmeldung mit den geladenen Zeilen liefert."""
mock_analyze.return_value = (
"excel",
pruef_ergebnis(ok=True, meldung="ok", aktion="ok", kontext=None),
@@ -110,6 +115,7 @@ class TestDataGrabber(unittest.TestCase):
@patch.object(DataGrabber, "analyze_source_type")
def test_run_returns_failure_summary_without_rows(self, mock_analyze):
"""Prüft, dass run() bei nicht erkennbarer Quelle eine Fehlermeldung ohne Zeilen liefert."""
mock_analyze.return_value = (
"unbekannte_quelle",
pruef_ergebnis(ok=False, meldung="bad", aktion="unbekannte_quelle", kontext=None),
+12
View File
@@ -13,6 +13,7 @@ class TestDateipruefer(unittest.TestCase):
# 1. Leere Eingabe erlaubt
# -----------------------------------------------------
def test_leereingabe_erlaubt(self):
"""Prüft, dass eine leere Pfadangabe bei leereingabe_erlaubt=True als 'leereingabe_erlaubt' ohne Kontext gemeldet wird."""
pruefer = Dateipruefer(
pfad="",
leereingabe_erlaubt=True
@@ -28,6 +29,7 @@ class TestDateipruefer(unittest.TestCase):
# 2. Leere Eingabe nicht erlaubt
# -----------------------------------------------------
def test_leereingabe_nicht_erlaubt(self):
"""Prüft, dass eine leere Pfadangabe ohne leereingabe_erlaubt als 'leereingabe_nicht_erlaubt' abgelehnt wird."""
pruefer = Dateipruefer(
pfad="",
leereingabe_erlaubt=False
@@ -43,6 +45,13 @@ class TestDateipruefer(unittest.TestCase):
# 3. Standarddatei vorschlagen
# -----------------------------------------------------
def test_standarddatei_vorschlagen(self):
"""Prüft, dass bei leerer Pfadangabe mit gesetzter Standarddatei 'standarddatei_vorschlagen' mit dem Standardpfad als Kontext geliefert wird.
Deckt nicht ab, ob die Standarddatei tatsächlich existiert - das wird hier
(und in Pruefmanager._handle_basic_decision() bei einer Ja-Antwort) nirgends
geprüft. Fix zurückgestellt, bis geklärt ist, ob der Standarddatei-Modus
überhaupt gebraucht wird (Basis #59, siehe STATUS.md).
"""
pruefer = Dateipruefer(
pfad="",
standarddatei="/tmp/std.txt"
@@ -58,6 +67,7 @@ class TestDateipruefer(unittest.TestCase):
# 4. Temporäre Datei erlaubt
# -----------------------------------------------------
def test_temporaer_erlaubt(self):
"""Prüft, dass eine leere Pfadangabe bei temporaer_erlaubt=True als 'temporaer_erlaubt' ohne Kontext gemeldet wird."""
pruefer = Dateipruefer(
pfad="",
temporaer_erlaubt=True
@@ -74,6 +84,7 @@ class TestDateipruefer(unittest.TestCase):
# -----------------------------------------------------
@patch("sn_basis.modules.Dateipruefer.file_exists", return_value=False)
def test_datei_nicht_gefunden(self, mock_exists):
"""Prüft, dass ein nicht existierender Dateipfad als 'datei_nicht_gefunden' mit dem Pfad als Kontext abgelehnt wird."""
pruefer = Dateipruefer(
pfad="/tmp/nichtvorhanden.txt"
)
@@ -89,6 +100,7 @@ class TestDateipruefer(unittest.TestCase):
# -----------------------------------------------------
@patch("sn_basis.modules.Dateipruefer.file_exists", return_value=True)
def test_datei_ok(self, mock_exists):
"""Prüft, dass ein existierender Dateipfad als 'ok' mit dem Pfad als Kontext bestätigt wird."""
pruefer = Dateipruefer(
pfad="/tmp/test.txt"
)
+5
View File
@@ -10,20 +10,25 @@ from sn_basis.functions.laenderkennung import get_laenderkennung, LAENDERKENNUNG
class TestGetLaenderkennung(unittest.TestCase):
def test_bekannter_praefix_gibt_kennung_zurueck(self):
"""Prüft, dass der Präfix 'sn' die ALKIS-Länderkennung '14' für Sachsen liefert."""
self.assertEqual(get_laenderkennung("sn"), "14")
def test_praefix_gross_klein_unabhaengig(self):
"""Prüft, dass die Präfixsuche groß-/kleinschreibungsunabhängig ist."""
self.assertEqual(get_laenderkennung("SN"), "14")
def test_unbekannter_praefix_wirft_keyerror(self):
"""Prüft, dass ein unbekannter Präfix einen KeyError auslöst."""
with self.assertRaises(KeyError):
get_laenderkennung("xx")
def test_ohne_argument_wird_aus_aufrufendem_modul_abgeleitet(self):
"""Prüft, dass ohne Argument der Präfix aus dem Modulnamen des Aufrufers abgeleitet wird."""
# __name__ dieses Testmoduls beginnt mit "sn_basis" -> Präfix "sn"
self.assertEqual(get_laenderkennung(), "14")
def test_mapping_enthaelt_sachsen(self):
"""Prüft, dass die Mapping-Tabelle LAENDERKENNUNGEN einen Eintrag für Sachsen ('sn' -> '14') enthält."""
self.assertEqual(LAENDERKENNUNGEN.get("sn"), "14")
+10
View File
@@ -97,6 +97,7 @@ class TestLayerpruefer(unittest.TestCase):
# -----------------------------------------------------
def test_layer_exists(self):
"""Prüft, dass ein nicht vorhandener Layer als 'layer_nicht_gefunden' abgelehnt wird."""
layer = MockLayer(exists=False)
pruefer = Layerpruefer(layer)
ergebnis = pruefer.pruefe()
@@ -104,6 +105,7 @@ class TestLayerpruefer(unittest.TestCase):
self.assertEqual(ergebnis.aktion, "layer_nicht_gefunden")
def test_layer_unsichtbar(self):
"""Prüft, dass ein unsichtbarer Layer bei geforderter Sichtbarkeit als 'layer_unsichtbar' abgelehnt wird."""
layer = MockLayer(visible=False)
pruefer = Layerpruefer(layer, muss_sichtbar_sein=True)
ergebnis = pruefer.pruefe()
@@ -111,6 +113,7 @@ class TestLayerpruefer(unittest.TestCase):
self.assertEqual(ergebnis.aktion, "layer_unsichtbar")
def test_falscher_layertyp(self):
"""Prüft, dass ein Layer mit abweichendem Layertyp (Raster statt Vektor) als 'falscher_layertyp' abgelehnt wird."""
layer = MockLayer(layer_type="raster")
pruefer = Layerpruefer(layer, erwarteter_layertyp="vector")
ergebnis = pruefer.pruefe()
@@ -118,6 +121,7 @@ class TestLayerpruefer(unittest.TestCase):
self.assertEqual(ergebnis.aktion, "falscher_layertyp")
def test_falscher_geotyp(self):
"""Prüft, dass ein Layer mit abweichendem Geometrietyp (Punkt statt Polygon) als 'falscher_geotyp' abgelehnt wird."""
layer = MockLayer(geometry_type="Point")
pruefer = Layerpruefer(layer, erwarteter_geotyp="Polygon")
ergebnis = pruefer.pruefe()
@@ -125,6 +129,7 @@ class TestLayerpruefer(unittest.TestCase):
self.assertEqual(ergebnis.aktion, "falscher_geotyp")
def test_layer_leer(self):
"""Prüft, dass ein Layer ohne Features als 'layer_leer' abgelehnt wird."""
layer = MockLayer(feature_count=0)
pruefer = Layerpruefer(layer)
ergebnis = pruefer.pruefe()
@@ -132,6 +137,7 @@ class TestLayerpruefer(unittest.TestCase):
self.assertEqual(ergebnis.aktion, "layer_leer")
def test_falsches_crs(self):
"""Prüft, dass ein Layer mit abweichendem Koordinatenbezugssystem als 'falsches_crs' abgelehnt wird."""
layer = MockLayer(crs="EPSG:4326")
pruefer = Layerpruefer(layer, erwartetes_crs="EPSG:25833")
ergebnis = pruefer.pruefe()
@@ -139,6 +145,7 @@ class TestLayerpruefer(unittest.TestCase):
self.assertEqual(ergebnis.aktion, "falsches_crs")
def test_felder_fehlen(self):
"""Prüft, dass ein Layer mit fehlenden Pflichtfeldern als 'felder_fehlen' abgelehnt wird."""
layer = MockLayer(fields=["id"])
pruefer = Layerpruefer(layer, erforderliche_felder=["id", "name"])
ergebnis = pruefer.pruefe()
@@ -146,6 +153,7 @@ class TestLayerpruefer(unittest.TestCase):
self.assertEqual(ergebnis.aktion, "felder_fehlen")
def test_datenquelle_unerwartet(self):
"""Prüft, dass eine Datenquelle außerhalb der erlaubten Liste als 'datenquelle_unerwartet' abgelehnt wird."""
layer = MockLayer(source="/tmp/test.shp")
pruefer = Layerpruefer(layer, erlaubte_datenquellen=["/tmp/allowed.shp"])
ergebnis = pruefer.pruefe()
@@ -153,6 +161,7 @@ class TestLayerpruefer(unittest.TestCase):
self.assertEqual(ergebnis.aktion, "datenquelle_unerwartet")
def test_layer_nicht_editierbar(self):
"""Prüft, dass ein nicht editierbarer Layer bei geforderter Editierbarkeit als 'layer_nicht_editierbar' abgelehnt wird."""
layer = MockLayer(editable=False)
pruefer = Layerpruefer(layer, muss_editierbar_sein=True)
ergebnis = pruefer.pruefe()
@@ -160,6 +169,7 @@ class TestLayerpruefer(unittest.TestCase):
self.assertEqual(ergebnis.aktion, "layer_nicht_editierbar")
def test_layer_ok(self):
"""Prüft, dass ein Layer, der alle Anforderungen erfüllt, als 'ok' bestätigt wird."""
layer = MockLayer()
pruefer = Layerpruefer(layer)
ergebnis = pruefer.pruefe()
+22 -5
View File
@@ -11,10 +11,23 @@ from sn_basis.functions.qgiscore_wrapper import NetworkReply
class TestLinkpruefer(unittest.TestCase):
# -----------------------------------------------------
# 1. Remote-Link erreichbar
# 1. Leere Eingabe
# -----------------------------------------------------
def test_leere_eingabe(self):
"""Prüft, dass eine leere Eingabe ohne Netzwerk-/Dateizugriff als 'leer' abgelehnt wird."""
lp = Linkpruefer()
result = lp.pruefe("")
self.assertFalse(result.ok)
self.assertEqual(result.aktion, "leer")
self.assertIsNone(result.kontext)
# -----------------------------------------------------
# 2. Remote-Link erreichbar
# -----------------------------------------------------
@patch("sn_basis.modules.linkpruefer.network_head")
def test_remote_link_ok(self, mock_head):
"""Prüft, dass eine erreichbare Remote-URL (HTTP HEAD ohne Fehler) als 'ok' bestätigt wird."""
mock_head.return_value = NetworkReply(error=0)
lp = Linkpruefer()
@@ -25,10 +38,11 @@ class TestLinkpruefer(unittest.TestCase):
self.assertEqual(result.kontext, "http://example.com")
# -----------------------------------------------------
# 2. Remote-Link nicht erreichbar
# 3. Remote-Link nicht erreichbar
# -----------------------------------------------------
@patch("sn_basis.modules.linkpruefer.network_head")
def test_remote_link_error(self, mock_head):
"""Prüft, dass eine Remote-URL mit Fehlerantwort als 'url_nicht_erreichbar' abgelehnt wird."""
mock_head.return_value = NetworkReply(error=1)
lp = Linkpruefer()
@@ -39,10 +53,11 @@ class TestLinkpruefer(unittest.TestCase):
self.assertEqual(result.kontext, "http://example.com")
# -----------------------------------------------------
# 3. Netzwerkfehler (None)
# 4. Netzwerkfehler (None)
# -----------------------------------------------------
@patch("sn_basis.modules.linkpruefer.network_head", return_value=None)
def test_remote_link_network_error(self, mock_head):
"""Prüft, dass eine ausbleibende Netzwerkantwort (None) als 'netzwerkfehler' gemeldet wird."""
lp = Linkpruefer()
result = lp.pruefe("http://example.com")
@@ -51,10 +66,11 @@ class TestLinkpruefer(unittest.TestCase):
self.assertEqual(result.kontext, "http://example.com")
# -----------------------------------------------------
# 4. Lokaler Pfad existiert nicht
# 5. Lokaler Pfad existiert nicht
# -----------------------------------------------------
@patch("sn_basis.modules.linkpruefer.file_exists", return_value=False)
def test_local_link_not_found(self, mock_exists):
"""Prüft, dass ein nicht existierender lokaler Pfad als 'pfad_nicht_gefunden' abgelehnt wird."""
lp = Linkpruefer()
result = lp.pruefe("/path/to/missing/file.shp")
@@ -63,10 +79,11 @@ class TestLinkpruefer(unittest.TestCase):
self.assertEqual(result.kontext, Path("/path/to/missing/file.shp"))
# -----------------------------------------------------
# 5. Lokaler Pfad existiert
# 6. Lokaler Pfad existiert
# -----------------------------------------------------
@patch("sn_basis.modules.linkpruefer.file_exists", return_value=True)
def test_local_link_ok(self, mock_exists):
"""Prüft, dass ein existierender lokaler Pfad als 'ok' bestätigt wird."""
lp = Linkpruefer()
result = lp.pruefe("/path/to/file.shp")
+5
View File
@@ -54,6 +54,7 @@ class _FakeLegendForResize:
class TestPrintLayout(unittest.TestCase):
def test_create_legend_item_links_map_and_enables_map_filtering(self):
"""Prüft, dass eine erzeugte Legende mit der übergebenen Karte verknüpft ist und automatisch nach deren Sichtbarkeit gefiltert wird."""
project = QgsProject.instance()
print_layout = PrintLayout(project=project)
layout = QgsPrintLayout(project)
@@ -74,6 +75,7 @@ class TestPrintLayout(unittest.TestCase):
self.assertTrue(legend.legend_filter_by_map_enabled)
def test_create_legend_item_does_not_copy_project_layers_into_legend_model(self):
"""Prüft, dass eine neu erzeugte Legende keine Projekt-Layer in ihr Legendenmodell übernimmt (leere Root-Gruppe)."""
project = QgsProject.instance()
print_layout = PrintLayout(project=project)
layout = QgsPrintLayout(project)
@@ -91,6 +93,7 @@ class TestPrintLayout(unittest.TestCase):
self.assertEqual(root_group.layers, [])
def test_configure_legend_size_uses_content_adaptive_size(self):
"""Prüft, dass die Legendengröße inhaltsadaptiv bestimmt wird (kein manuelles Resize) und die ermittelte Höhe zurückgegeben wird."""
project = QgsProject.instance()
print_layout = PrintLayout(project=project)
legend = _FakeLegendForResize()
@@ -107,6 +110,7 @@ class TestPrintLayout(unittest.TestCase):
self.assertEqual(height, 33.0)
def test_configure_hauptkarte_grids_uses_wrapper_constants(self):
"""Prüft, dass das Gitternetz der Hauptkarte mit den erwarteten Stil-, Rahmen- und Beschriftungskonstanten (nur rechts/unten beschriftet, Schriftgröße und Hintergrund) konfiguriert wird."""
project = QgsProject.instance()
print_layout = PrintLayout(project=project)
layout = QgsPrintLayout(project)
@@ -153,6 +157,7 @@ class TestPrintLayout(unittest.TestCase):
@patch("sn_basis.modules.print_layout.open_layout_designer")
def test_create_single_page_layout_keeps_legend_linked_to_hauptkarte_and_theme(self, _mock_open_designer):
"""Prüft, dass ein einseitiges Layout die Hauptkarte auf das übergebene Kartenthema festlegt und die Legende mit dieser Karte verknüpft bleibt."""
project = QgsProject.instance()
print_layout = PrintLayout(project=project)
+17
View File
@@ -38,6 +38,7 @@ class TestPrintLogic(unittest.TestCase):
self.logic = PrintLogic(pruefmanager=MagicMock())
def test_get_baufreigabe_page_dimensions_prefers_a4_for_small_extent(self):
"""Prüft, dass für einen kleinen Kartenausschnitt (Baufreigabe) das Format DIN A4 ohne Fallback gewählt wird."""
din_format, page_w, page_h, map_w, map_h, fallback = self.logic._get_baufreigabe_page_dimensions(80.0, 120.0)
self.assertEqual(din_format, "DIN A4")
@@ -46,6 +47,7 @@ class TestPrintLogic(unittest.TestCase):
self.assertFalse(fallback)
def test_get_baufreigabe_page_dimensions_escalates_to_a3(self):
"""Prüft, dass ein größerer Kartenausschnitt (Baufreigabe) auf DIN A3 eskaliert wird."""
din_format, page_w, page_h, map_w, map_h, fallback = self.logic._get_baufreigabe_page_dimensions(200.0, 180.0)
self.assertEqual(din_format, "DIN A3")
@@ -54,6 +56,7 @@ class TestPrintLogic(unittest.TestCase):
self.assertFalse(fallback)
def test_get_baufreigabe_page_dimensions_respects_a4_as_maximum_with_fallback(self):
"""Prüft, dass bei auf DIN A4 begrenztem Höchstformat ein eigentlich A3-großer Ausschnitt mit Fallback-Kennzeichnung auf A4 reduziert wird."""
din_format, page_w, page_h, map_w, map_h, fallback = self.logic._get_baufreigabe_page_dimensions(
200.0,
180.0,
@@ -66,6 +69,7 @@ class TestPrintLogic(unittest.TestCase):
self.assertTrue(fallback)
def test_get_baufreigabe_page_dimensions_falls_back_to_a3_for_oversize_extent(self):
"""Prüft, dass ein Ausschnitt, der auch DIN A3 übersteigt, mit Fallback-Kennzeichnung auf DIN A3 begrenzt wird."""
din_format, page_w, page_h, map_w, map_h, fallback = self.logic._get_baufreigabe_page_dimensions(400.0, 300.0)
self.assertEqual(din_format, "DIN A3")
@@ -74,21 +78,25 @@ class TestPrintLogic(unittest.TestCase):
self.assertTrue(fallback)
def test_select_standard_page_layout_uses_a4_as_minimum_sheet(self):
"""Prüft, dass für einen kleinen Ausschnitt trotz Höchstgrenze DIN A3 mindestens DIN A4 als Blattgröße gewählt wird."""
selected = self.logic._select_standard_page_layout(60.0, 100.0, max_format="DIN A3")
self.assertEqual(selected, (297.0, 210.0, 87.0, 190.0))
def test_select_standard_page_layout_limits_hauptkarte_to_a3(self):
"""Prüft, dass ein die Fläche voll ausnutzender Ausschnitt auf DIN A3 als größtes zulässiges Format begrenzt wird."""
selected = self.logic._select_standard_page_layout(120.0, 240.0, max_format="DIN A3")
self.assertEqual(selected, (420.0, 297.0, 210.0, 277.0))
def test_select_standard_page_layout_returns_none_if_a3_is_too_small(self):
"""Prüft, dass kein Layout gewählt wird (None), wenn der Ausschnitt selbst DIN A3 als Höchstformat übersteigt."""
selected = self.logic._select_standard_page_layout(260.0, 280.0, max_format="DIN A3")
self.assertIsNone(selected)
def test_get_standard_layout_dimensions_uses_a4_and_forces_fit_for_small_extent(self):
"""Prüft, dass für einen kleinen Ausschnitt DIN A4 gewählt und das Einpassen (force_fit) erzwungen wird."""
page_w, page_h, map_w, map_h, force_fit = self.logic._get_standard_layout_dimensions(
60.0,
100.0,
@@ -101,6 +109,7 @@ class TestPrintLogic(unittest.TestCase):
self.assertTrue(force_fit)
def test_get_standard_layout_dimensions_caps_endlosrolle_width_at_2000_mm(self):
"""Prüft, dass bei aktiviertem Formfaktor (Endlosrolle) die Seitenbreite auf die maximale Rollenbreite (2000 mm) begrenzt wird."""
page_w, page_h, map_w, map_h, force_fit = self.logic._get_standard_layout_dimensions(
2500.0,
100.0,
@@ -114,6 +123,7 @@ class TestPrintLogic(unittest.TestCase):
self.assertTrue(force_fit)
def test_apply_standard_minimum_a4_fallback_for_small_page(self):
"""Prüft, dass eine zu kleine Seite auf die A4-Mindestgröße angehoben und als erzwungen (forced) markiert wird."""
page_w, page_h, map_w, map_h, forced = self.logic._apply_standard_minimum_a4_fallback(
250.0,
180.0,
@@ -126,6 +136,7 @@ class TestPrintLogic(unittest.TestCase):
self.assertEqual((map_w, map_h), (87.0, 190.0))
def test_apply_standard_minimum_a4_fallback_keeps_large_page(self):
"""Prüft, dass eine bereits ausreichend große Seite (A3) unverändert bleibt und nicht als erzwungen markiert wird."""
page_w, page_h, map_w, map_h, forced = self.logic._apply_standard_minimum_a4_fallback(
420.0,
297.0,
@@ -138,23 +149,27 @@ class TestPrintLogic(unittest.TestCase):
self.assertEqual((map_w, map_h), (210.0, 277.0))
def test_does_plot_fit_page_true_for_standard_a4(self):
"""Prüft, dass ein Kartenobjekt, das in eine DIN A4-Seite passt, als passend erkannt wird."""
fits = self.logic._does_plot_fit_page(297.0, 210.0, 87.0, 190.0, 210.0, 20.0)
self.assertTrue(fits)
def test_does_plot_fit_page_false_if_objects_exceed_page(self):
"""Prüft, dass ein Kartenobjekt, das die Seitenbreite überschreitet, als nicht passend erkannt wird."""
fits = self.logic._does_plot_fit_page(297.0, 210.0, 120.0, 190.0, 210.0, 20.0)
self.assertFalse(fits)
@patch("sn_basis.modules.print_logic.set_variable")
def test_set_druckbereich_accepts_wie_kartenfenster(self, mock_set_variable):
"""Prüft, dass die Auswahl 'wie Kartenfenster' als Projektvariable 'druckbereich' gespeichert wird."""
self.logic.set_druckbereich_for_auswahl(DRUCKBEREICH_WIE_KARTENFENSTER)
mock_set_variable.assert_called_once_with("druckbereich", DRUCKBEREICH_WIE_KARTENFENSTER, scope="project")
@patch("sn_basis.modules.print_logic.iface")
def test_get_canvas_extent_returns_valid_extent(self, mock_iface):
"""Prüft, dass der aktuelle Kartenausschnitt des Kartenfensters unverändert zurückgegeben wird."""
extent = _FakeExtent(120.0, 90.0)
mock_iface.mapCanvas.return_value = _FakeCanvas(extent)
@@ -165,6 +180,7 @@ class TestPrintLogic(unittest.TestCase):
@patch.object(PrintLogic, "_buffer_extent")
@patch.object(PrintLogic, "_get_canvas_extent")
def test_resolve_extent_uses_canvas_extent_without_buffer(self, mock_get_canvas_extent, mock_buffer_extent):
"""Prüft, dass bei Druckbereich 'wie Kartenfenster' der Kartenausschnitt ohne Puffer und ohne verknüpften Layer übernommen wird."""
extent = _FakeExtent(120.0, 90.0)
mock_get_canvas_extent.return_value = extent
@@ -177,6 +193,7 @@ class TestPrintLogic(unittest.TestCase):
@patch.object(PrintLogic, "_buffer_extent")
@patch.object(PrintLogic, "_get_druckrechteck_extent")
def test_resolve_extent_uses_rectangle_extent_without_buffer(self, mock_get_rect_extent, mock_buffer_extent):
"""Prüft, dass bei Druckbereich 'Rechteck' der gezeichnete Ausschnitt ohne Puffer und ohne verknüpften Layer übernommen wird."""
extent = _FakeExtent(120.0, 90.0)
mock_get_rect_extent.return_value = extent
+14
View File
@@ -16,6 +16,7 @@ class TestPruefmanager(unittest.TestCase):
# 1. OK-Ergebnis → keine Interaktion
# -----------------------------------------------------
def test_ok(self):
"""Prüft, dass ein bereits erfolgreiches Prüfergebnis ohne Nutzerinteraktion als 'ok' durchgereicht wird."""
ergebnis = pruef_ergebnis(True, "Alles gut", "ok", None)
entscheidung = self.manager.verarbeite(ergebnis)
@@ -27,6 +28,7 @@ class TestPruefmanager(unittest.TestCase):
# -----------------------------------------------------
@patch("sn_basis.modules.Pruefmanager.ask_yes_no", return_value=True)
def test_leereingabe_erlaubt_ja(self, mock_ask):
"""Prüft, dass bei erlaubter leerer Eingabe eine Ja-Antwort des Nutzers die Entscheidung 'ok' liefert."""
ergebnis = pruef_ergebnis(False, "Leer?", "leereingabe_erlaubt", None)
entscheidung = self.manager.verarbeite(ergebnis)
@@ -38,6 +40,7 @@ class TestPruefmanager(unittest.TestCase):
# -----------------------------------------------------
@patch("sn_basis.modules.Pruefmanager.ask_yes_no", return_value=False)
def test_leereingabe_erlaubt_nein(self, mock_ask):
"""Prüft, dass bei erlaubter leerer Eingabe eine Nein-Antwort die Aktion 'leereingabe_erlaubt' als abgelehnt bestehen lässt."""
ergebnis = pruef_ergebnis(False, "Leer?", "leereingabe_erlaubt", None)
entscheidung = self.manager.verarbeite(ergebnis)
@@ -49,6 +52,7 @@ class TestPruefmanager(unittest.TestCase):
# -----------------------------------------------------
@patch("sn_basis.modules.Pruefmanager.ask_yes_no", return_value=True)
def test_standarddatei_vorschlagen_ja(self, mock_ask):
"""Prüft, dass eine Ja-Antwort auf den Vorschlag der Standarddatei die Entscheidung 'ok' mit dem Standardpfad als Kontext liefert."""
ergebnis = pruef_ergebnis(
False,
"Standarddatei verwenden?",
@@ -67,6 +71,7 @@ class TestPruefmanager(unittest.TestCase):
# -----------------------------------------------------
@patch("sn_basis.modules.Pruefmanager.ask_yes_no", return_value=False)
def test_standarddatei_vorschlagen_nein(self, mock_ask):
"""Prüft, dass eine Nein-Antwort auf den Vorschlag der Standarddatei die Aktion 'standarddatei_vorschlagen' als abgelehnt bestehen lässt."""
ergebnis = pruef_ergebnis(
False,
"Standarddatei verwenden?",
@@ -84,6 +89,7 @@ class TestPruefmanager(unittest.TestCase):
# -----------------------------------------------------
@patch("sn_basis.modules.Pruefmanager.ask_yes_no", return_value=True)
def test_temporaer_erlaubt_ja(self, mock_ask):
"""Prüft, dass eine Ja-Antwort auf die Rückfrage nach einer temporären Datei die Aktion 'temporaer_erzeugen' auslöst."""
ergebnis = pruef_ergebnis(False, "Temporär?", "temporaer_erlaubt", None)
entscheidung = self.manager.verarbeite(ergebnis)
@@ -95,6 +101,7 @@ class TestPruefmanager(unittest.TestCase):
# -----------------------------------------------------
@patch("sn_basis.modules.Pruefmanager.ask_yes_no", return_value=False)
def test_temporaer_erlaubt_nein(self, mock_ask):
"""Prüft, dass eine Nein-Antwort auf die Rückfrage nach einer temporären Datei die Aktion 'temporaer_erlaubt' als abgelehnt bestehen lässt."""
ergebnis = pruef_ergebnis(False, "Temporär?", "temporaer_erlaubt", None)
entscheidung = self.manager.verarbeite(ergebnis)
@@ -107,6 +114,7 @@ class TestPruefmanager(unittest.TestCase):
@patch("sn_basis.modules.Pruefmanager.ask_yes_no", return_value=True)
@patch("sn_basis.modules.Pruefmanager.set_layer_visible")
def test_layer_unsichtbar_ja(self, mock_set, mock_ask):
"""Prüft, dass eine Ja-Antwort bei unsichtbarem Layer den Layer sichtbar schaltet und die Entscheidung 'ok' liefert."""
fake_layer = object()
ergebnis = pruef_ergebnis(False, "Layer unsichtbar", "layer_unsichtbar", fake_layer)
@@ -121,6 +129,7 @@ class TestPruefmanager(unittest.TestCase):
# -----------------------------------------------------
@patch("sn_basis.modules.Pruefmanager.ask_yes_no", return_value=False)
def test_layer_unsichtbar_nein(self, mock_ask):
"""Prüft, dass eine Nein-Antwort bei unsichtbarem Layer die Aktion 'layer_unsichtbar' als abgelehnt bestehen lässt."""
fake_layer = object()
ergebnis = pruef_ergebnis(False, "Layer unsichtbar", "layer_unsichtbar", fake_layer)
@@ -134,6 +143,7 @@ class TestPruefmanager(unittest.TestCase):
# -----------------------------------------------------
@patch("sn_basis.modules.Pruefmanager.warning")
def test_unbekannte_aktion(self, mock_warn):
"""Prüft, dass eine unbekannte Aktion eine Warnung auslöst und unverarbeitet als Fehlschlag zurückgegeben wird."""
ergebnis = pruef_ergebnis(False, "???", "unbekannt", None)
entscheidung = self.manager.verarbeite(ergebnis)
@@ -143,6 +153,7 @@ class TestPruefmanager(unittest.TestCase):
@patch("sn_basis.modules.Pruefmanager.ask_overwrite_append_cancel_custom", return_value="overwrite")
def test_datei_existiert_overwrite(self, _mock_choice):
"""Prüft, dass die Nutzerwahl 'überschreiben' bei bestehender Datei zur Aktion 'datei_existiert_ueberschreiben' führt."""
ergebnis = pruef_ergebnis(False, "Datei existiert", "datei_existiert", "C:/tmp/v.gpkg")
entscheidung = self.manager.verarbeite(ergebnis)
@@ -152,6 +163,7 @@ class TestPruefmanager(unittest.TestCase):
@patch("sn_basis.modules.Pruefmanager.ask_overwrite_append_cancel_custom", return_value="append")
def test_datei_existiert_append(self, _mock_choice):
"""Prüft, dass die Nutzerwahl 'anhängen' bei bestehender Datei zur Aktion 'datei_existiert_anhaengen' führt."""
ergebnis = pruef_ergebnis(False, "Datei existiert", "datei_existiert", "C:/tmp/v.gpkg")
entscheidung = self.manager.verarbeite(ergebnis)
@@ -160,6 +172,7 @@ class TestPruefmanager(unittest.TestCase):
@patch("sn_basis.modules.Pruefmanager.ask_overwrite_append_cancel_custom", return_value="cancel")
def test_datei_existiert_cancel(self, _mock_choice):
"""Prüft, dass die Nutzerwahl 'abbrechen' bei bestehender Datei zur Aktion 'datei_existiert_ueberspringen' führt."""
ergebnis = pruef_ergebnis(False, "Datei existiert", "datei_existiert", "C:/tmp/v.gpkg")
entscheidung = self.manager.verarbeite(ergebnis)
@@ -167,6 +180,7 @@ class TestPruefmanager(unittest.TestCase):
self.assertEqual(entscheidung.aktion, "datei_existiert_ueberspringen")
def test_datei_existiert_in_non_qgis_mode_returns_input(self):
"""Prüft, dass außerhalb des QGIS-Modus bei bestehender Datei keine Rückfrage erfolgt, sondern die ursprüngliche Aktion unentschieden zurückgegeben wird."""
manager = Pruefmanager(ui_modus="test")
ergebnis = pruef_ergebnis(False, "Datei existiert", "datei_existiert", "C:/tmp/v.gpkg")
+4
View File
@@ -13,6 +13,7 @@ class TestSettingsLogic(unittest.TestCase):
# -----------------------------------------------------
@patch("sn_basis.functions.settings_logic.get_variable")
def test_load(self, mock_get):
"""Prüft, dass load() jede bekannte Projektvariable genau einmal über get_variable() liest."""
# Mock-Rückgabe für jede Variable
mock_get.side_effect = lambda key, scope="project": f"wert_{key}"
@@ -32,6 +33,7 @@ class TestSettingsLogic(unittest.TestCase):
# -----------------------------------------------------
@patch("sn_basis.functions.settings_logic.set_variable")
def test_save(self, mock_set):
"""Prüft, dass save() nur bekannte Variablen-Keys speichert und unbekannte Keys ignoriert."""
logic = SettingsLogic()
# Eingabedaten enthalten gültige und ungültige Keys
@@ -57,6 +59,7 @@ class TestSettingsLogic(unittest.TestCase):
@patch("sn_basis.functions.settings_logic.get_variable")
def test_load_handles_partial_values_with_empty_fallback(self, mock_get):
"""Prüft, dass load() für jede Variable den (auch leeren) Wert von get_variable() unverändert übernimmt, selbst wenn nicht alle Variablen im Mock konfiguriert sind."""
values = {
"amt": "A1",
"behoerde": "",
@@ -74,6 +77,7 @@ class TestSettingsLogic(unittest.TestCase):
@patch("sn_basis.functions.settings_logic.set_variable")
def test_save_empty_string_value_is_persisted(self, mock_set):
"""Prüft, dass save() einen leeren String als gültigen Wert speichert, statt ihn wie einen fehlenden Key zu behandeln."""
logic = SettingsLogic()
logic.save({"amt": "", "behoerde": "B2"})
+4
View File
@@ -18,6 +18,7 @@ class TestStilpruefer(unittest.TestCase):
# 1. Keine Datei angegeben
# -----------------------------------------------------
def test_keine_datei_angegeben(self):
"""Prüft, dass eine leere Pfadangabe als 'ok' ohne Kontext akzeptiert wird (Stil ist optional)."""
result = self.pruefer.pruefe("")
self.assertTrue(result.ok)
@@ -30,6 +31,7 @@ class TestStilpruefer(unittest.TestCase):
# -----------------------------------------------------
@patch("sn_basis.modules.stilpruefer.file_exists", return_value=True)
def test_datei_existiert_mit_qml(self, mock_exists):
"""Prüft, dass eine existierende .qml-Datei als 'stil_anwendbar' bestätigt wird."""
with tempfile.NamedTemporaryFile(suffix=".qml", delete=False) as tmp:
tmp_path = tmp.name
@@ -48,6 +50,7 @@ class TestStilpruefer(unittest.TestCase):
# -----------------------------------------------------
@patch("sn_basis.modules.stilpruefer.file_exists", return_value=True)
def test_datei_existiert_falsche_endung(self, mock_exists):
"""Prüft, dass eine existierende Datei ohne .qml-Endung als 'falsche_endung' abgelehnt wird."""
with tempfile.NamedTemporaryFile(suffix=".txt", delete=False) as tmp:
tmp_path = tmp.name
@@ -67,6 +70,7 @@ class TestStilpruefer(unittest.TestCase):
# -----------------------------------------------------
@patch("sn_basis.modules.stilpruefer.file_exists", return_value=False)
def test_datei_existiert_nicht(self, mock_exists):
"""Prüft, dass ein nicht existierender .qml-Pfad als 'datei_nicht_gefunden' abgelehnt wird."""
fake_path = "/tmp/nichtvorhanden.qml"
result = self.pruefer.pruefe(fake_path)
+6
View File
@@ -23,6 +23,7 @@ class TestVariableWrapper(unittest.TestCase):
mock_set_project_variable,
mock_project_instance,
):
"""Prüft, dass set_variable() im Projekt-Scope nicht schreibt, wenn der neue Wert dem bestehenden entspricht."""
mock_project_instance.return_value = object()
mock_project_scope.return_value = _Scope("layer_1")
@@ -39,6 +40,7 @@ class TestVariableWrapper(unittest.TestCase):
mock_set_project_variable,
mock_project_instance,
):
"""Prüft, dass set_variable() im Projekt-Scope bei geändertem Wert mit dem Präfix 'sn_' geschrieben wird."""
fake_project = object()
mock_project_instance.return_value = fake_project
mock_project_scope.return_value = _Scope("old_value")
@@ -53,12 +55,14 @@ class TestVariableWrapper(unittest.TestCase):
@patch("sn_basis.functions.variable_wrapper.QgsExpressionContextUtils.setGlobalVariable")
def test_set_variable_global_writes_prefixed_name(self, mock_set_global):
"""Prüft, dass set_variable() im globalen Scope den Variablennamen mit 'sn_' vorangestellt schreibt."""
variable_wrapper.set_variable("verfahrensnummer", "123", scope="global")
mock_set_global.assert_called_once_with("sn_verfahrensnummer", "123")
@patch("sn_basis.functions.variable_wrapper.QgsExpressionContextUtils.globalScope")
def test_get_variable_global_returns_empty_string_fallback(self, mock_global_scope):
"""Prüft, dass get_variable() im globalen Scope für eine nicht gesetzte Variable einen leeren String liefert."""
mock_global_scope.return_value = _Scope("")
value = variable_wrapper.get_variable("nicht_vorhanden", scope="global")
@@ -66,10 +70,12 @@ class TestVariableWrapper(unittest.TestCase):
self.assertEqual(value, "")
def test_get_variable_invalid_scope_raises(self):
"""Prüft, dass get_variable() bei einem unbekannten Scope-Namen einen ValueError auslöst."""
with self.assertRaises(ValueError):
variable_wrapper.get_variable("foo", scope="invalid")
def test_set_variable_invalid_scope_raises(self):
"""Prüft, dass set_variable() bei einem unbekannten Scope-Namen einen ValueError auslöst."""
with self.assertRaises(ValueError):
variable_wrapper.set_variable("foo", "bar", scope="invalid")
+5
View File
@@ -16,18 +16,21 @@ class TestVerfahrensgebietManager(unittest.TestCase):
@patch("sn_basis.functions.verfahrensgebiet_manager.get_variable", return_value="layer_123")
def test_get_verfahrensgebiet_layer_id_returns_value(self, _mock_get_variable):
"""Prüft, dass get_verfahrensgebiet_layer_id() die in der Projektvariable gespeicherte Layer-ID liefert."""
result = verfahrensgebiet_manager.get_verfahrensgebiet_layer_id()
self.assertEqual(result, "layer_123")
@patch("sn_basis.functions.verfahrensgebiet_manager.get_variable", return_value="")
def test_get_verfahrensgebiet_layer_id_returns_none_for_empty_value(self, _mock_get_variable):
"""Prüft, dass get_verfahrensgebiet_layer_id() bei leerer Projektvariable None statt eines leeren Strings liefert."""
result = verfahrensgebiet_manager.get_verfahrensgebiet_layer_id()
self.assertIsNone(result)
@patch("sn_basis.functions.verfahrensgebiet_manager.set_variable")
def test_set_verfahrensgebiet_layer_id_writes_project_variable(self, mock_set_variable):
"""Prüft, dass set_verfahrensgebiet_layer_id() die Layer-ID als Projektvariable 'verfahrensgebiet_layer' speichert."""
verfahrensgebiet_manager.set_verfahrensgebiet_layer_id("layer_abc")
mock_set_variable.assert_called_once_with("verfahrensgebiet_layer", "layer_abc", scope="project")
@@ -43,6 +46,7 @@ class TestVerfahrensgebietManager(unittest.TestCase):
mock_project_instance,
mock_find_layer,
):
"""Prüft, dass ohne gefundenen Verfahrensgebiet-Layer keine Schreibaktion erfolgt, wenn die Variable bereits leer ist."""
mock_find_layer.return_value = (None, None)
mock_project_instance.return_value = object()
mock_project_scope.return_value = _Scope("")
@@ -55,6 +59,7 @@ class TestVerfahrensgebietManager(unittest.TestCase):
@patch("sn_basis.functions.verfahrensgebiet_manager._find_verfahrensgebiet_layer")
@patch("sn_basis.functions.verfahrensgebiet_manager.set_variable")
def test_update_with_layer_writes_variable(self, mock_set_variable, mock_find_layer):
"""Prüft, dass ein gefundener Verfahrensgebiet-Layer seine ID als Projektvariable speichert."""
mock_layer = object()
mock_find_layer.return_value = (mock_layer, "layer_99")