diff --git a/.agents/skills/sn-tests/SKILL.md b/.agents/skills/sn-tests/SKILL.md index e9f12fb..ab4351d 100644 --- a/.agents/skills/sn-tests/SKILL.md +++ b/.agents/skills/sn-tests/SKILL.md @@ -46,12 +46,16 @@ Dialoge: `Pruefmanager(ui_modus="test")` liefert Standardantworten; `dialog_wrap ## Testkatalog ```bash -python scripts/testkatalog.py # erzeugt docs/tests/testkatalog.md -python scripts/testkatalog.py --pruefen # Exit 1, wenn die Datei veraltet ist +python scripts/testkatalog.py # erzeugt docs/tests/testkatalog.md UND testkatalog.html +python scripts/testkatalog.py --pruefen # Exit 1, wenn eine der beiden Dateien veraltet ist +python scripts/testkatalog.py --ohne-html # nur die Markdown-Datei ``` Wird aus den Testdateien per `ast` erzeugt (kein Import, läuft ohne QGIS). **Nicht von Hand bearbeiten.** -Nach jeder Änderung an Tests neu erzeugen und mitcommitten. Der Abschnitt „Lücken" listet Tests ohne -Docstring – Stand 04.09.2026 haben nur 100 von 332 Tests einen, `sn_basis` keinen einzigen. +Nach jeder Änderung an Tests neu erzeugen und mitcommitten. Der Abschnitt „Lücken" in `testkatalog.md` +listet Tests ohne Docstring – Stand 15.09.2026 haben nur 114 von 346 Tests einen, `sn_basis` fast keine +(7 von 94). `testkatalog.html` ist dasselbe als interaktives Dashboard „Prüfstand" (Baumdiagramm je +Testdatei per Mermaid, Filter, Suche) – lokal im Browser öffnen, kein Server nötig; Mermaid lädt von +`cdnjs.cloudflare.com`, ohne Internetverbindung bleiben nur die Diagramme leer. ## Testkonzept-Seiten Für fachlich wichtige Abläufe liegt in `docs/tests/.md` eine Seite nach `docs/vorlagen/testkonzept.md`: diff --git a/docs/README.md b/docs/README.md index 7cc8613..e50fd4d 100644 --- a/docs/README.md +++ b/docs/README.md @@ -16,6 +16,7 @@ | `leitfaden/05_git_arbeitsweise.md` | **verbindlich** | Branches, Fix-Einstieg, Rückführung, Tags, was der Agent darf | | `leitfaden/06_tests.md` | **verbindlich** | Testebenen, Testpflicht, Katalog, wie man Tests fachlich beurteilt | | `tests/testkatalog.md` | generiert | was die Suite prüft – erzeugt von `scripts/testkatalog.py` | +| `tests/testkatalog.html` | generiert | dieselben Daten als lokal zu öffnendes Dashboard „Prüfstand“ (Baumdiagramm je Testdatei, Filter, Suche) – vom selben Lauf erzeugt | | `tests/*.md` | lebend | Testkonzepte je Fachablauf, mit Mermaid; Vorlage in `vorlagen/` | | `konzept/07_qgis_tests_plan.md` | Vorschlag | Stufenplan für Tests gegen echtes QGIS | | `konzept/09_alkis7_umstellung.md` | Prüfliste | ALKIS 6 ↔ 7 (GID 6.0/7.1), was in `sn_verfahrensgebiet` anzupassen ist | diff --git a/docs/status/STATUS.md b/docs/status/STATUS.md index ba9c5e4..cc08474 100644 --- a/docs/status/STATUS.md +++ b/docs/status/STATUS.md @@ -38,7 +38,8 @@ spätere Phase und beginnt erst, wenn die Test-Baseline grün ist und das Projek 5. **Testbeschreibungen nachziehen.** Von 332 Testfällen haben nur 100 (30 %) einen Docstring — `sn_basis` keinen einzigen von 87, `sn_plan41` einen von 44. Ohne Beschreibung ist von außen nicht erkennbar, was ein Test prüft. Arbeitsliste: Abschnitt „Lücken" in `docs/tests/testkatalog.md`. Pflicht für neue und geänderte - Tests gilt ab sofort (ADR 0009). + Tests gilt ab sofort (ADR 0009). Seit 15.09.2026 zusätzlich als Dashboard `docs/tests/testkatalog.html` + (lokal im Browser öffnen) – zeigt Testabdeckung und -status auf einen Blick, ohne in den Code zu schauen. 6. **Testlücken beim Shape-Import schließen.** Beim Erstellen von `docs/tests/verfahrensgebiet-shape-import.md` gefunden und gegen `origin/unstable` geprüft: `_ermittle_gebietstyp_aus_fbschl` hat **keinen** Test — obwohl PR #44/#45 genau diese Funktion tolerant gemacht hat. Ebenso ungetestet: mehrere Shapefile-Sätze in einer diff --git a/docs/tests/testkatalog.html b/docs/tests/testkatalog.html new file mode 100644 index 0000000..d09fbcb --- /dev/null +++ b/docs/tests/testkatalog.html @@ -0,0 +1,1270 @@ +Prüfstand + + + + + + + +

Prüfstand

+Testkatalog der sn_*-Plugins — was die Suite heute prüft, auf einen Blick.
+
Quelle: docs/tests/testkatalog.md
Baseline: docs/status/testbaseline.json (SHA-256 aeabc946a7f3)
+
+
346Testfälle
+
301grün
+
37rot (Baseline)
+ +
33%mit Docstring
+
31Testdateien
+
+
Verteilung je Paket
+
sn_basis14 Dateien · 94 Testfälle
+
sn_verfahrensgebiet12 Dateien · 208 Testfälle
+
sn_plan415 Dateien · 44 Testfälle
+
+
+ + + + +
+
+ +
+
+
+

sn_basis

+

Plattform-Plugin · Ordner Plugin_SN_Basis · 14 Testdateien · 94 Testfälle

+
+ +
+

— (Docstring fehlt)

Keine Testfälle: Die Datei enthält keine unittest.TestCase-Klasse – sie ist eine reine Hilfsdatei, die beim Testlauf nur importiert wird.

— (Docstring fehlt)

TestDataGrabber 6
flowchart TD
+  root(["test_datagrabber.py"])
+  class root rootnode
+  root --> g1["TestDataGrabber (6)"]
+  class g1 groupnode
+  g1 --> t1("analyze_source_type_detects_excel")
+  g1 --> t2("analyze_source_type_returns_unknown_filetype")
+  g1 --> t3("analyze_source_type_detects_service_when_file_check_fails")
+  g1 --> t4("analyze_source_type_returns_unbekannte_quelle_on_full_failure")
+  g1 --> t5("run_returns_success_summary_with_valid_rows")
+  g1 --> t6("run_returns_failure_summary_without_rows")
+  class t1 ok
+  class t2 ok
+  class t3 ok
+  class t4 ok
+  class t5 ok
+  class t6 ok
+  classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
+  classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
+  classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
+  classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestDataGrabber
test_analyze_source_type_detects_excel
— (Docstring fehlt)grün
TestDataGrabber
test_analyze_source_type_returns_unknown_filetype
— (Docstring fehlt)grün
TestDataGrabber
test_analyze_source_type_detects_service_when_file_check_fails
— (Docstring fehlt)grün
TestDataGrabber
test_analyze_source_type_returns_unbekannte_quelle_on_full_failure
— (Docstring fehlt)grün
TestDataGrabber
test_run_returns_success_summary_with_valid_rows
— (Docstring fehlt)grün
TestDataGrabber
test_run_returns_failure_summary_without_rows
— (Docstring fehlt)grün

— (Docstring fehlt)

TestDateipruefer 6
flowchart TD
+  root(["test_dateipruefer.py"])
+  class root rootnode
+  root --> g1["TestDateipruefer (6)"]
+  class g1 groupnode
+  g1 --> t1("leereingabe_erlaubt")
+  g1 --> t2("leereingabe_nicht_erlaubt")
+  g1 --> t3("standarddatei_vorschlagen")
+  g1 --> t4("temporaer_erlaubt")
+  g1 --> t5("datei_nicht_gefunden")
+  g1 --> t6("datei_ok")
+  class t1 ok
+  class t2 ok
+  class t3 ok
+  class t4 ok
+  class t5 ok
+  class t6 ok
+  classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
+  classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
+  classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
+  classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestDateipruefer
test_leereingabe_erlaubt
— (Docstring fehlt)grün
TestDateipruefer
test_leereingabe_nicht_erlaubt
— (Docstring fehlt)grün
TestDateipruefer
test_standarddatei_vorschlagen
— (Docstring fehlt)grün
TestDateipruefer
test_temporaer_erlaubt
— (Docstring fehlt)grün
TestDateipruefer
test_datei_nicht_gefunden
— (Docstring fehlt)grün
TestDateipruefer
test_datei_ok
— (Docstring fehlt)grün

Unit-Tests für `sn_basis.functions.laenderkennung`.

TestGetLaenderkennung 5
flowchart TD
+  root(["test_laenderkennung.py"])
+  class root rootnode
+  root --> g1["TestGetLaenderkennung (5)"]
+  class g1 groupnode
+  g1 --> t1("bekannter_praefix_gibt_kennung_zurueck")
+  g1 --> t2("praefix_gross_klein_unabhaengig")
+  g1 --> t3("unbekannter_praefix_wirft_keyerror")
+  g1 --> t4("ohne_argument_wird_aus_aufrufendem_modul_abgeleitet")
+  g1 --> t5("mapping_enthaelt_sachsen")
+  class t1 ok
+  class t2 ok
+  class t3 ok
+  class t4 ok
+  class t5 ok
+  classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
+  classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
+  classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
+  classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestGetLaenderkennung
test_bekannter_praefix_gibt_kennung_zurueck
— (Docstring fehlt)grün
TestGetLaenderkennung
test_praefix_gross_klein_unabhaengig
— (Docstring fehlt)grün
TestGetLaenderkennung
test_unbekannter_praefix_wirft_keyerror
— (Docstring fehlt)grün
TestGetLaenderkennung
test_ohne_argument_wird_aus_aufrufendem_modul_abgeleitet
— (Docstring fehlt)grün
TestGetLaenderkennung
test_mapping_enthaelt_sachsen
— (Docstring fehlt)grün

— (Docstring fehlt)

TestLayerpruefer 10
flowchart TD
+  root(["test_layerpruefer.py"])
+  class root rootnode
+  root --> g1["TestLayerpruefer (10)"]
+  class g1 groupnode
+  g1 --> t1("layer_exists")
+  g1 --> t2("layer_unsichtbar")
+  g1 --> t3("falscher_layertyp")
+  g1 --> t4("falscher_geotyp")
+  g1 --> t5("layer_leer")
+  g1 --> t6("falsches_crs")
+  g1 --> t7("felder_fehlen")
+  g1 --> t8("datenquelle_unerwartet")
+  g1 --> t9("layer_nicht_editierbar")
+  g1 --> t10("layer_ok")
+  class t1 ok
+  class t2 ok
+  class t3 ok
+  class t4 ok
+  class t5 ok
+  class t6 ok
+  class t7 ok
+  class t8 ok
+  class t9 ok
+  class t10 ok
+  classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
+  classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
+  classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
+  classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestLayerpruefer
test_layer_exists
— (Docstring fehlt)grün
TestLayerpruefer
test_layer_unsichtbar
— (Docstring fehlt)grün
TestLayerpruefer
test_falscher_layertyp
— (Docstring fehlt)grün
TestLayerpruefer
test_falscher_geotyp
— (Docstring fehlt)grün
TestLayerpruefer
test_layer_leer
— (Docstring fehlt)grün
TestLayerpruefer
test_falsches_crs
— (Docstring fehlt)grün
TestLayerpruefer
test_felder_fehlen
— (Docstring fehlt)grün
TestLayerpruefer
test_datenquelle_unerwartet
— (Docstring fehlt)grün
TestLayerpruefer
test_layer_nicht_editierbar
— (Docstring fehlt)grün
TestLayerpruefer
test_layer_ok
— (Docstring fehlt)grün

— (Docstring fehlt)

TestLinkpruefer 5
flowchart TD
+  root(["test_linkpruefer.py"])
+  class root rootnode
+  root --> g1["TestLinkpruefer (5)"]
+  class g1 groupnode
+  g1 --> t1("remote_link_ok")
+  g1 --> t2("remote_link_error")
+  g1 --> t3("remote_link_network_error")
+  g1 --> t4("local_link_not_found")
+  g1 --> t5("local_link_ok")
+  class t1 ok
+  class t2 ok
+  class t3 ok
+  class t4 ok
+  class t5 ok
+  classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
+  classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
+  classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
+  classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestLinkpruefer
test_remote_link_ok
— (Docstring fehlt)grün
TestLinkpruefer
test_remote_link_error
— (Docstring fehlt)grün
TestLinkpruefer
test_remote_link_network_error
— (Docstring fehlt)grün
TestLinkpruefer
test_local_link_not_found
— (Docstring fehlt)grün
TestLinkpruefer
test_local_link_ok
— (Docstring fehlt)grün

— (Docstring fehlt)

TestPrintLayout 5
flowchart TD
+  root(["test_print_layout.py"])
+  class root rootnode
+  root --> g1["TestPrintLayout (5)"]
+  class g1 groupnode
+  g1 --> t1("create_legend_item_links_map_and_enables_map_filtering")
+  g1 --> t2("create_legend_item_does_not_copy_project_layers_into_legend_model")
+  g1 --> t3("configure_legend_size_uses_content_adaptive_size")
+  g1 --> t4("configure_hauptkarte_grids_uses_wrapper_constants")
+  g1 --> t5("create_single_page_layout_keeps_legend_linked_to_hauptkarte_and_theme")
+  class t1 ok
+  class t2 ok
+  class t3 ok
+  class t4 ok
+  class t5 ok
+  classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
+  classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
+  classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
+  classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestPrintLayout
test_create_legend_item_links_map_and_enables_map_filtering
— (Docstring fehlt)grün
TestPrintLayout
test_create_legend_item_does_not_copy_project_layers_into_legend_model
— (Docstring fehlt)grün
TestPrintLayout
test_configure_legend_size_uses_content_adaptive_size
— (Docstring fehlt)grün
TestPrintLayout
test_configure_hauptkarte_grids_uses_wrapper_constants
— (Docstring fehlt)grün
TestPrintLayout
test_create_single_page_layout_keeps_legend_linked_to_hauptkarte_and_theme
— (Docstring fehlt)grün

— (Docstring fehlt)

TestPrintLogic 17
flowchart TD
+  root(["test_print_logic.py"])
+  class root rootnode
+  root --> g1["TestPrintLogic (17)"]
+  class g1 groupnode
+  g1 --> t1("get_baufreigabe_page_dimensions_prefers_a4_for_small_extent")
+  g1 --> t2("get_baufreigabe_page_dimensions_escalates_to_a3")
+  g1 --> t3("get_baufreigabe_page_dimensions_respects_a4_as_maximum_with_fallback")
+  g1 --> t4("get_baufreigabe_page_dimensions_falls_back_to_a3_for_oversize_extent")
+  g1 --> t5("select_standard_page_layout_uses_a4_as_minimum_sheet")
+  g1 --> t6("select_standard_page_layout_limits_hauptkarte_to_a3")
+  g1 --> t7("select_standard_page_layout_returns_none_if_a3_is_too_small")
+  g1 --> t8("get_standard_layout_dimensions_uses_a4_and_forces_fit_for_small_extent")
+  g1 --> t9("get_standard_layout_dimensions_caps_endlosrolle_width_at_2000_mm")
+  g1 --> t10("apply_standard_minimum_a4_fallback_for_small_page")
+  g1 --> t11("apply_standard_minimum_a4_fallback_keeps_large_page")
+  g1 --> t12("does_plot_fit_page_true_for_standard_a4")
+  g1 --> t13("does_plot_fit_page_false_if_objects_exceed_page")
+  g1 --> t14("set_druckbereich_accepts_wie_kartenfenster")
+  g1 --> t15("get_canvas_extent_returns_valid_extent")
+  g1 --> t16("resolve_extent_uses_canvas_extent_without_buffer")
+  g1 --> t17("resolve_extent_uses_rectangle_extent_without_buffer")
+  class t1 ok
+  class t2 ok
+  class t3 ok
+  class t4 ok
+  class t5 ok
+  class t6 ok
+  class t7 ok
+  class t8 ok
+  class t9 ok
+  class t10 ok
+  class t11 ok
+  class t12 ok
+  class t13 ok
+  class t14 ok
+  class t15 ok
+  class t16 ok
+  class t17 ok
+  classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
+  classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
+  classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
+  classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestPrintLogic
test_get_baufreigabe_page_dimensions_prefers_a4_for_small_extent
— (Docstring fehlt)grün
TestPrintLogic
test_get_baufreigabe_page_dimensions_escalates_to_a3
— (Docstring fehlt)grün
TestPrintLogic
test_get_baufreigabe_page_dimensions_respects_a4_as_maximum_with_fallback
— (Docstring fehlt)grün
TestPrintLogic
test_get_baufreigabe_page_dimensions_falls_back_to_a3_for_oversize_extent
— (Docstring fehlt)grün
TestPrintLogic
test_select_standard_page_layout_uses_a4_as_minimum_sheet
— (Docstring fehlt)grün
TestPrintLogic
test_select_standard_page_layout_limits_hauptkarte_to_a3
— (Docstring fehlt)grün
TestPrintLogic
test_select_standard_page_layout_returns_none_if_a3_is_too_small
— (Docstring fehlt)grün
TestPrintLogic
test_get_standard_layout_dimensions_uses_a4_and_forces_fit_for_small_extent
— (Docstring fehlt)grün
TestPrintLogic
test_get_standard_layout_dimensions_caps_endlosrolle_width_at_2000_mm
— (Docstring fehlt)grün
TestPrintLogic
test_apply_standard_minimum_a4_fallback_for_small_page
— (Docstring fehlt)grün
TestPrintLogic
test_apply_standard_minimum_a4_fallback_keeps_large_page
— (Docstring fehlt)grün
TestPrintLogic
test_does_plot_fit_page_true_for_standard_a4
— (Docstring fehlt)grün
TestPrintLogic
test_does_plot_fit_page_false_if_objects_exceed_page
— (Docstring fehlt)grün
TestPrintLogic
test_set_druckbereich_accepts_wie_kartenfenster
— (Docstring fehlt)grün
TestPrintLogic
test_get_canvas_extent_returns_valid_extent
— (Docstring fehlt)grün
TestPrintLogic
test_resolve_extent_uses_canvas_extent_without_buffer
— (Docstring fehlt)grün
TestPrintLogic
test_resolve_extent_uses_rectangle_extent_without_buffer
— (Docstring fehlt)grün

— (Docstring fehlt)

TestPruefmanager 14
flowchart TD
+  root(["test_pruefmanager.py"])
+  class root rootnode
+  root --> g1["TestPruefmanager (14)"]
+  class g1 groupnode
+  g1 --> t1("ok")
+  g1 --> t2("leereingabe_erlaubt_ja")
+  g1 --> t3("leereingabe_erlaubt_nein")
+  g1 --> t4("standarddatei_vorschlagen_ja")
+  g1 --> t5("standarddatei_vorschlagen_nein")
+  g1 --> t6("temporaer_erlaubt_ja")
+  g1 --> t7("temporaer_erlaubt_nein")
+  g1 --> t8("layer_unsichtbar_ja")
+  g1 --> t9("layer_unsichtbar_nein")
+  g1 --> t10("unbekannte_aktion")
+  g1 --> t11("datei_existiert_overwrite")
+  g1 --> t12("datei_existiert_append")
+  g1 --> t13("datei_existiert_cancel")
+  g1 --> t14("datei_existiert_in_non_qgis_mode_returns_input")
+  class t1 ok
+  class t2 ok
+  class t3 ok
+  class t4 ok
+  class t5 ok
+  class t6 ok
+  class t7 ok
+  class t8 ok
+  class t9 ok
+  class t10 ok
+  class t11 ok
+  class t12 ok
+  class t13 ok
+  class t14 ok
+  classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
+  classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
+  classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
+  classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestPruefmanager
test_ok
— (Docstring fehlt)grün
TestPruefmanager
test_leereingabe_erlaubt_ja
— (Docstring fehlt)grün
TestPruefmanager
test_leereingabe_erlaubt_nein
— (Docstring fehlt)grün
TestPruefmanager
test_standarddatei_vorschlagen_ja
— (Docstring fehlt)grün
TestPruefmanager
test_standarddatei_vorschlagen_nein
— (Docstring fehlt)grün
TestPruefmanager
test_temporaer_erlaubt_ja
— (Docstring fehlt)grün
TestPruefmanager
test_temporaer_erlaubt_nein
— (Docstring fehlt)grün
TestPruefmanager
test_layer_unsichtbar_ja
— (Docstring fehlt)grün
TestPruefmanager
test_layer_unsichtbar_nein
— (Docstring fehlt)grün
TestPruefmanager
test_unbekannte_aktion
— (Docstring fehlt)grün
TestPruefmanager
test_datei_existiert_overwrite
— (Docstring fehlt)grün
TestPruefmanager
test_datei_existiert_append
— (Docstring fehlt)grün
TestPruefmanager
test_datei_existiert_cancel
— (Docstring fehlt)grün
TestPruefmanager
test_datei_existiert_in_non_qgis_mode_returns_input
— (Docstring fehlt)grün

— (Docstring fehlt)

TestSettingsLogic 4
flowchart TD
+  root(["test_settings_logic.py"])
+  class root rootnode
+  root --> g1["TestSettingsLogic (4)"]
+  class g1 groupnode
+  g1 --> t1("load")
+  g1 --> t2("save")
+  g1 --> t3("load_handles_partial_values_with_empty_fallback")
+  g1 --> t4("save_empty_string_value_is_persisted")
+  class t1 ok
+  class t2 ok
+  class t3 ok
+  class t4 ok
+  classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
+  classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
+  classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
+  classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestSettingsLogic
test_load
— (Docstring fehlt)grün
TestSettingsLogic
test_save
— (Docstring fehlt)grün
TestSettingsLogic
test_load_handles_partial_values_with_empty_fallback
— (Docstring fehlt)grün
TestSettingsLogic
test_save_empty_string_value_is_persisted
— (Docstring fehlt)grün

— (Docstring fehlt)

TestStilpruefer 4
flowchart TD
+  root(["test_stilpruefer.py"])
+  class root rootnode
+  root --> g1["TestStilpruefer (4)"]
+  class g1 groupnode
+  g1 --> t1("keine_datei_angegeben")
+  g1 --> t2("datei_existiert_mit_qml")
+  g1 --> t3("datei_existiert_falsche_endung")
+  g1 --> t4("datei_existiert_nicht")
+  class t1 ok
+  class t2 ok
+  class t3 ok
+  class t4 ok
+  classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
+  classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
+  classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
+  classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestStilpruefer
test_keine_datei_angegeben
— (Docstring fehlt)grün
TestStilpruefer
test_datei_existiert_mit_qml
— (Docstring fehlt)grün
TestStilpruefer
test_datei_existiert_falsche_endung
— (Docstring fehlt)grün
TestStilpruefer
test_datei_existiert_nicht
— (Docstring fehlt)grün

Unit-Tests für `sn_basis.functions.text_wrapper`.

TestSanitizeBezeichner 7
flowchart TD
+  root(["test_text_wrapper.py"])
+  class root rootnode
+  root --> g1["TestSanitizeBezeichner (7)"]
+  class g1 groupnode
+  g1 --> t1("umlaute_bleiben_erhalten")
+  g1 --> t2("bindestrich_bleibt_erhalten")
+  g1 --> t3("unterstrich_bleibt_erhalten")
+  g1 --> t4("leerzeichen_wird_durch_ersatz_zusammengefasst")
+  g1 --> t5("fuehrender_und_folgender_ersatz_wird_entfernt")
+  g1 --> t6("eigenes_ersatzzeichen_wird_verwendet")
+  g1 --> t7("nur_unzulaessige_zeichen_ergibt_leeren_string")
+  class t1 ok
+  class t2 ok
+  class t3 ok
+  class t4 ok
+  class t5 ok
+  class t6 ok
+  class t7 ok
+  classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
+  classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
+  classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
+  classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestSanitizeBezeichner
test_umlaute_bleiben_erhalten
Prüft, dass Umlaute und ß nicht durch den Ersatz entfernt werden.grün
TestSanitizeBezeichner
test_bindestrich_bleibt_erhalten
Prüft, dass ein Bindestrich im Bezeichner erhalten bleibt (Issue #42).grün
TestSanitizeBezeichner
test_unterstrich_bleibt_erhalten
Prüft, dass ein Unterstrich im Bezeichner erhalten bleibt.grün
TestSanitizeBezeichner
test_leerzeichen_wird_durch_ersatz_zusammengefasst
Prüft, dass Leerzeichen (kein zulässiges Zeichen) zu einem Ersatzzeichen werden.grün
TestSanitizeBezeichner
test_fuehrender_und_folgender_ersatz_wird_entfernt
Prüft, dass ein Ersatzzeichen am Anfang oder Ende des Ergebnisses entfernt wird.grün
TestSanitizeBezeichner
test_eigenes_ersatzzeichen_wird_verwendet
Prüft, dass ein abweichendes Ersatzzeichen anstelle von Unterstrich genutzt wird.grün
TestSanitizeBezeichner
test_nur_unzulaessige_zeichen_ergibt_leeren_string
Prüft, dass ein Bezeichner ganz aus unzulässigen Zeichen zu einem leeren String wird.grün

— (Docstring fehlt)

TestVariableWrapper 6
flowchart TD
+  root(["test_variable_wrapper.py"])
+  class root rootnode
+  root --> g1["TestVariableWrapper (6)"]
+  class g1 groupnode
+  g1 --> t1("set_variable_project_noop_write_is_skipped")
+  g1 --> t2("set_variable_project_changed_value_is_written")
+  g1 --> t3("set_variable_global_writes_prefixed_name")
+  g1 --> t4("get_variable_global_returns_empty_string_fallback")
+  g1 --> t5("get_variable_invalid_scope_raises")
+  g1 --> t6("set_variable_invalid_scope_raises")
+  class t1 ok
+  class t2 ok
+  class t3 ok
+  class t4 ok
+  class t5 ok
+  class t6 ok
+  classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
+  classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
+  classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
+  classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestVariableWrapper
test_set_variable_project_noop_write_is_skipped
— (Docstring fehlt)grün
TestVariableWrapper
test_set_variable_project_changed_value_is_written
— (Docstring fehlt)grün
TestVariableWrapper
test_set_variable_global_writes_prefixed_name
— (Docstring fehlt)grün
TestVariableWrapper
test_get_variable_global_returns_empty_string_fallback
— (Docstring fehlt)grün
TestVariableWrapper
test_get_variable_invalid_scope_raises
— (Docstring fehlt)grün
TestVariableWrapper
test_set_variable_invalid_scope_raises
— (Docstring fehlt)grün

— (Docstring fehlt)

TestVerfahrensgebietManager 5
flowchart TD
+  root(["test_verfahrensgebiet_manager.py"])
+  class root rootnode
+  root --> g1["TestVerfahrensgebietManager (5)"]
+  class g1 groupnode
+  g1 --> t1("get_verfahrensgebiet_layer_id_returns_value")
+  g1 --> t2("get_verfahrensgebiet_layer_id_returns_none_for_empty_value")
+  g1 --> t3("set_verfahrensgebiet_layer_id_writes_project_variable")
+  g1 --> t4("update_without_layer_does_not_write_when_already_empty")
+  g1 --> t5("update_with_layer_writes_variable")
+  class t1 ok
+  class t2 ok
+  class t3 ok
+  class t4 ok
+  class t5 ok
+  classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
+  classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
+  classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
+  classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestVerfahrensgebietManager
test_get_verfahrensgebiet_layer_id_returns_value
— (Docstring fehlt)grün
TestVerfahrensgebietManager
test_get_verfahrensgebiet_layer_id_returns_none_for_empty_value
— (Docstring fehlt)grün
TestVerfahrensgebietManager
test_set_verfahrensgebiet_layer_id_writes_project_variable
— (Docstring fehlt)grün
TestVerfahrensgebietManager
test_update_without_layer_does_not_write_when_already_empty
— (Docstring fehlt)grün
TestVerfahrensgebietManager
test_update_with_layer_writes_variable
— (Docstring fehlt)grün
+
+ + +
+
+
+

sn_verfahrensgebiet

+

Fachplugin · Ordner Plugin_SN_Verfahrensgebiet · 12 Testdateien · 208 Testfälle

+
+ +
+

Unit-Tests für `sn_verfahrensgebiet.functions.beteiligung_pruefung`.

TestBaueFlurstueckCsv 12TestBaueFlurstueckCsvListe 1TestHoleVerfahrensgebietLayer 3TestFordereManuellesLadenAn 2TestTry3Bootstrap 1TestBeteiligungPruefenDispatch 2TestLadeBeteiligungCsv 4
flowchart TD
+  root(["test_beteiligung_pruefung.py"])
+  class root rootnode
+  root --> g1["TestBaueFlurstueckCsv (12)"]
+  class g1 groupnode
+  g1 --> t1("normalfall_mit_fuehrenden_nullen")
+  g1 --> t2("nenner_null_wird_zu_unterstrichen")
+  g1 --> t3("nenner_leer_wird_zu_unterstrichen")
+  g1 --> t4("gemarkung_ohne_fuehrende_nullen_wird_aufgefuellt")
+  g1 --> t5("ungueltiges_format_gibt_none")
+  g1 --> t6("leerer_wert_gibt_none")
+  g1 --> t7("nenner_9001_wird_zu_buchstabe_a")
+  g1 --> t8("nenner_9002_wird_zu_buchstabe_b")
+  g1 --> t9("nenner_9026_wird_zu_buchstabe_z")
+  g1 --> t10("nenner_ausserhalb_buchstaben_bereich_bleibt_numerisch")
+  g1 --> t11("nenner_9001_mit_fuehrenden_nullen_wird_erkannt")
+  g1 --> t12("einstellige_folge_wird_auf_2_stellen_aufgefuellt")
+  root --> g2["TestBaueFlurstueckCsvListe (1)"]
+  class g2 groupnode
+  g2 --> t13("nur_gueltige_zeilen_in_liste")
+  root --> g3["TestHoleVerfahrensgebietLayer (3)"]
+  class g3 groupnode
+  g3 --> t14("layer_per_name_hat_vorrang_vor_combo_auswahl")
+  g3 --> t15("combo_auswahl_als_fallback_ohne_namens_layer")
+  g3 --> t16("kein_layer_gibt_none")
+  root --> g4["TestFordereManuellesLadenAn (2)"]
+  class g4 groupnode
+  g4 --> t17("ohne_vef_vkzf_nennt_fehlende_kennzeichen")
+  g4 --> t18("mit_vef_vkzf_nennt_verfahrensnummer")
+  root --> g5["TestTry3Bootstrap (1)"]
+  class g5 groupnode
+  g5 --> t19("setzt_verfahrensnummer_und_fordert_manuelles_laden_an")
+  root --> g6["TestBeteiligungPruefenDispatch (2)"]
+  class g6 groupnode
+  g6 --> t20("layer_ohne_kennzeichen_fordert_manuelles_laden_an")
+  g6 --> t21("kein_layer_setzt_verfahrensnummer_und_fordert_laden_an")
+  root --> g7["TestLadeBeteiligungCsv (4)"]
+  class g7 groupnode
+  g7 --> t22("dialog_abgebrochen_gibt_none")
+  g7 --> t23("fehlende_flurstueck_spalte_wirft_fehler")
+  g7 --> t24("leere_datei_wirft_fehler")
+  g7 --> t25("erfolgsfall_gibt_zeilen_und_spalten_zurueck")
+  class t1 ok
+  class t2 ok
+  class t3 ok
+  class t4 ok
+  class t5 ok
+  class t6 ok
+  class t7 ok
+  class t8 ok
+  class t9 ok
+  class t10 ok
+  class t11 ok
+  class t12 ok
+  class t13 ok
+  class t14 ok
+  class t15 ok
+  class t16 ok
+  class t17 ok
+  class t18 ok
+  class t19 ok
+  class t20 ok
+  class t21 ok
+  class t22 ok
+  class t23 ok
+  class t24 ok
+  class t25 ok
+  classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
+  classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
+  classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
+  classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestBaueFlurstueckCsv
test_normalfall_mit_fuehrenden_nullen
— (Docstring fehlt)grün
TestBaueFlurstueckCsv
test_nenner_null_wird_zu_unterstrichen
— (Docstring fehlt)grün
TestBaueFlurstueckCsv
test_nenner_leer_wird_zu_unterstrichen
— (Docstring fehlt)grün
TestBaueFlurstueckCsv
test_gemarkung_ohne_fuehrende_nullen_wird_aufgefuellt
— (Docstring fehlt)grün
TestBaueFlurstueckCsv
test_ungueltiges_format_gibt_none
— (Docstring fehlt)grün
TestBaueFlurstueckCsv
test_leerer_wert_gibt_none
— (Docstring fehlt)grün
TestBaueFlurstueckCsv
test_nenner_9001_wird_zu_buchstabe_a
— (Docstring fehlt)grün
TestBaueFlurstueckCsv
test_nenner_9002_wird_zu_buchstabe_b
— (Docstring fehlt)grün
TestBaueFlurstueckCsv
test_nenner_9026_wird_zu_buchstabe_z
— (Docstring fehlt)grün
TestBaueFlurstueckCsv
test_nenner_ausserhalb_buchstaben_bereich_bleibt_numerisch
— (Docstring fehlt)grün
TestBaueFlurstueckCsv
test_nenner_9001_mit_fuehrenden_nullen_wird_erkannt
— (Docstring fehlt)grün
TestBaueFlurstueckCsv
test_einstellige_folge_wird_auf_2_stellen_aufgefuellt
— (Docstring fehlt)grün
TestBaueFlurstueckCsvListe
test_nur_gueltige_zeilen_in_liste
— (Docstring fehlt)grün
TestHoleVerfahrensgebietLayer
test_layer_per_name_hat_vorrang_vor_combo_auswahl
— (Docstring fehlt)grün
TestHoleVerfahrensgebietLayer
test_combo_auswahl_als_fallback_ohne_namens_layer
— (Docstring fehlt)grün
TestHoleVerfahrensgebietLayer
test_kein_layer_gibt_none
— (Docstring fehlt)grün
TestFordereManuellesLadenAn
test_ohne_vef_vkzf_nennt_fehlende_kennzeichen
— (Docstring fehlt)grün
TestFordereManuellesLadenAn
test_mit_vef_vkzf_nennt_verfahrensnummer
— (Docstring fehlt)grün
TestTry3Bootstrap
test_setzt_verfahrensnummer_und_fordert_manuelles_laden_an
— (Docstring fehlt)grün
TestBeteiligungPruefenDispatch
test_layer_ohne_kennzeichen_fordert_manuelles_laden_an
— (Docstring fehlt)grün
TestBeteiligungPruefenDispatch
test_kein_layer_setzt_verfahrensnummer_und_fordert_laden_an
— (Docstring fehlt)grün
TestLadeBeteiligungCsv
test_dialog_abgebrochen_gibt_none
— (Docstring fehlt)grün
TestLadeBeteiligungCsv
test_fehlende_flurstueck_spalte_wirft_fehler
— (Docstring fehlt)grün
TestLadeBeteiligungCsv
test_leere_datei_wirft_fehler
— (Docstring fehlt)grün
TestLadeBeteiligungCsv
test_erfolgsfall_gibt_zeilen_und_spalten_zurueck
— (Docstring fehlt)grün

— (Docstring fehlt)

TestDockWidget 2
flowchart TD
+  root(["test_dockwidget.py"])
+  class root rootnode
+  root --> g1["TestDockWidget (2)"]
+  class g1 groupnode
+  g1 --> t1("init_tabs_with_services_injects_available_services")
+  g1 --> t2("init_tabs_with_services_reports_errors")
+  class t1 ok
+  class t2 ok
+  classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
+  classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
+  classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
+  classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestDockWidget
test_init_tabs_with_services_injects_available_services
— (Docstring fehlt)grün
TestDockWidget
test_init_tabs_with_services_reports_errors
— (Docstring fehlt)grün

Unit-Tests für das flurstueck_alkis-Modul.

TestFlurstueckAlkis 12TestFlurstueckAlkisAnhaengenDedup 3
flowchart TD
+  root(["test_flurstueck_alkis.py"])
+  class root rootnode
+  root --> g1["TestFlurstueckAlkis (12)"]
+  class g1 groupnode
+  g1 --> t1("user_cancel_returns_none")
+  g1 --> t2("empty_text_after_confirmation_returns_none")
+  g1 --> t3("invalid_wfs_layer_returns_none")
+  g1 --> t4("success_returns_layer_and_first_status")
+  g1 --> t5("sql_filter_contains_all_schluessel")
+  g1 --> t6("existing_layer_reload_yes")
+  g1 --> t7("existing_layer_keep_no")
+  g1 --> t8("vorgabe_schluessel_prefills_default_text")
+  g1 --> t9("vorgabe_namen_appears_in_label")
+  g1 --> t10("bbox_embedded_in_wfs_uri")
+  g1 --> t11("backwards_compatible_no_new_params")
+  g1 --> t12("ohne_schluessel_abfrage_ueberspringt_ask_text")
+  root --> g2["TestFlurstueckAlkisAnhaengenDedup (3)"]
+  class g2 groupnode
+  g2 --> t13("append_ueberspringt_bereits_vorhandene_flurstuecke")
+  g2 --> t14("append_ohne_kennzeichen_feld_haengt_alles_an")
+  g2 --> t15("ohne_schluessel_abfrage_append_dedup")
+  class t1 ok
+  class t2 ok
+  class t3 ok
+  class t4 warn
+  class t5 warn
+  class t6 warn
+  class t7 warn
+  class t8 warn
+  class t9 warn
+  class t10 warn
+  class t11 warn
+  class t12 ok
+  class t13 ok
+  class t14 ok
+  class t15 ok
+  classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
+  classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
+  classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
+  classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestFlurstueckAlkis
test_user_cancel_returns_none
ask_text → ok=False, text="" → (None, NONE).grün
TestFlurstueckAlkis
test_empty_text_after_confirmation_returns_none
ask_text → ok=True, aber Text leer → (None, NONE).grün
TestFlurstueckAlkis
test_invalid_wfs_layer_returns_none
WFS-Layer nicht valide → (None, NONE).grün
TestFlurstueckAlkis
test_success_returns_layer_and_first_status
Kein bestehender Layer, WFS valide → (layer, FIRST).rot
TestFlurstueckAlkis
test_sql_filter_contains_all_schluessel
Der WFS-URI enthält alle eingegebenen Gemarkungsschlüssel.rot
TestFlurstueckAlkis
test_existing_layer_reload_yes
Bestehender Layer + Nutzer: 'Ja, neu laden' → LoadStatus.RELOAD.rot
TestFlurstueckAlkis
test_existing_layer_keep_no
Bestehender Layer + Nutzer: 'Nein, behalten' → LoadStatus.KEEP.rot
TestFlurstueckAlkis
test_vorgabe_schluessel_prefills_default_text
vorgabe_schluessel=['1234','5678'] → ask_text erhält '1234, 5678' als default_text.rot
TestFlurstueckAlkis
test_vorgabe_namen_appears_in_label
vorgabe_namen=['Musterstadt'] → ask_text-label enthält den Namen.rot
TestFlurstueckAlkis
test_bbox_embedded_in_wfs_uri
bbox-Parameter → WFS-URI enthält 'bbox=' Substring.rot
TestFlurstueckAlkis
test_backwards_compatible_no_new_params
flurstueck_alkis(tab) ohne neue Parameter → kein Fehler.rot
TestFlurstueckAlkis
test_ohne_schluessel_abfrage_ueberspringt_ask_text
ohne_schluessel_abfrage=True verwendet vorgabe_schluessel direkt, ohne ask_text aufzurufen.grün
TestFlurstueckAlkisAnhaengenDedup
test_append_ueberspringt_bereits_vorhandene_flurstuecke
Prüft, dass beim Anhängen bereits vorhandene flurstueckskennzeichen nicht erneut hinzugefügt werden.grün
TestFlurstueckAlkisAnhaengenDedup
test_append_ohne_kennzeichen_feld_haengt_alles_an
Prüft, dass ohne flurstueckskennzeichen-Feld weiterhin alle Features angehängt werden (Rückfallverhalten).grün
TestFlurstueckAlkisAnhaengenDedup
test_ohne_schluessel_abfrage_append_dedup
ohne_schluessel_abfrage=True: Anhängen dedupliziert trotzdem, ohne nach Schlüsseln zu fragen (#47).grün

Unit-Tests für `sn_verfahrensgebiet.functions.gemarkung_alkis`.

TestExtractSchluesselUndName 4TestGemarkungSchluesselbbox 6TestGemarkungSchluesselnurNummern 2TestLadeGemarkungenlayer 2
flowchart TD
+  root(["test_gemarkung_alkis.py"])
+  class root rootnode
+  root --> g1["TestExtractSchluesselUndName (4)"]
+  class g1 groupnode
+  g1 --> t1("standard_attribute")
+  g1 --> t2("fallback_attribute")
+  g1 --> t3("missing_attribute_returns_empty")
+  g1 --> t4("none_value_returns_empty")
+  root --> g2["TestGemarkungSchluesselbbox (6)"]
+  class g2 groupnode
+  g2 --> t5("success_bbox_filter")
+  g2 --> t6("fallback_when_bbox_fails")
+  g2 --> t7("both_layers_fail_returns_empty")
+  g2 --> t8("deduplication_and_sorting")
+  g2 --> t9("feature_without_schluessel_skipped")
+  g2 --> t10("exception_returns_empty")
+  root --> g3["TestGemarkungSchluesselnurNummern (2)"]
+  class g3 groupnode
+  g3 --> t11("returns_only_nummern")
+  g3 --> t12("empty_input_returns_empty")
+  root --> g4["TestLadeGemarkungenlayer (2)"]
+  class g4 groupnode
+  g4 --> t13("returns_layer_when_service_reachable")
+  g4 --> t14("returns_none_when_service_unreachable")
+  class t1 warn
+  class t2 warn
+  class t3 ok
+  class t4 ok
+  class t5 skip
+  class t6 skip
+  class t7 skip
+  class t8 skip
+  class t9 skip
+  class t10 skip
+  class t11 skip
+  class t12 skip
+  class t13 ok
+  class t14 ok
+  classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
+  classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
+  classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
+  classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestExtractSchluesselUndName
test_standard_attribute
— (Docstring fehlt)rot
TestExtractSchluesselUndName
test_fallback_attribute
— (Docstring fehlt)rot
TestExtractSchluesselUndName
test_missing_attribute_returns_empty
— (Docstring fehlt)grün
TestExtractSchluesselUndName
test_none_value_returns_empty
— (Docstring fehlt)grün
TestGemarkungSchluesselbbox
test_success_bbox_filter
WFS-BBox-Filter liefert Features → korrekte Tupel-Liste.übersp.
TestGemarkungSchluesselbbox
test_fallback_when_bbox_fails
BBox-Filter schlägt fehl → Fallback auf Volllade + client-seitige Filterung.übersp.
TestGemarkungSchluesselbbox
test_both_layers_fail_returns_empty
Beide Lade-Strategien scheitern → leere Liste.übersp.
TestGemarkungSchluesselbbox
test_deduplication_and_sorting
Doppelte Schlüssel werden dedupliziert, Ergebnis ist sortiert.übersp.
TestGemarkungSchluesselbbox
test_feature_without_schluessel_skipped
Feature ohne Schlüssel-Attribut wird übersprungen.übersp.
TestGemarkungSchluesselbbox
test_exception_returns_empty
Exception im Ladevorgang → leere Liste (kein Crash).übersp.
TestGemarkungSchluesselnurNummern
test_returns_only_nummern
— (Docstring fehlt)übersp.
TestGemarkungSchluesselnurNummern
test_empty_input_returns_empty
— (Docstring fehlt)übersp.
TestLadeGemarkungenlayer
test_returns_layer_when_service_reachable
Dienst erreichbar (isValid=True) → Layer-Objekt zurückgegeben.grün
TestLadeGemarkungenlayer
test_returns_none_when_service_unreachable
Dienst nicht erreichbar (isValid=False) → None zurückgegeben.grün

Unit-Tests für `sn_verfahrensgebiet.functions.grenzpunkt_alkis`.

TestExtrahiereGmlIds 5TestGrenzpunktAlkis 7
flowchart TD
+  root(["test_grenzpunkt_alkis.py"])
+  class root rootnode
+  root --> g1["TestExtrahiereGmlIds (5)"]
+  class g1 groupnode
+  g1 --> t1("xlink_href_format")
+  g1 --> t2("plain_gml_id")
+  g1 --> t3("empty_value_skipped")
+  g1 --> t4("deduplication")
+  g1 --> t5("sorted_output")
+  root --> g2["TestGrenzpunktAlkis (7)"]
+  class g2 groupnode
+  g2 --> t6("no_vg_layer_returns_none")
+  g2 --> t7("invalid_punktort_layer")
+  g2 --> t8("empty_punktort_layer")
+  g2 --> t9("no_gml_ids_returns_pta_only")
+  g2 --> t10("grenzpunkt_batch_empty")
+  g2 --> t11("success_returns_both_layers")
+  g2 --> t12("extent_derived_from_vg_layer")
+  class t1 ok
+  class t2 ok
+  class t3 ok
+  class t4 ok
+  class t5 ok
+  class t6 ok
+  class t7 ok
+  class t8 ok
+  class t9 warn
+  class t10 warn
+  class t11 warn
+  class t12 warn
+  classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
+  classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
+  classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
+  classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestExtrahiereGmlIds
test_xlink_href_format
URN-Format 'urn:adv:oid:DEABC123' → nur 'DEABC123' extrahiert.grün
TestExtrahiereGmlIds
test_plain_gml_id
Plain-GML-ID ohne Doppelpunkt → direkt übernommen.grün
TestExtrahiereGmlIds
test_empty_value_skipped
Leerer gehoertZu-Wert wird übersprungen.grün
TestExtrahiereGmlIds
test_deduplication
Doppelte Referenzen werden dedupliziert.grün
TestExtrahiereGmlIds
test_sorted_output
Rückgabe ist aufsteigend sortiert.grün
TestGrenzpunktAlkis
test_no_vg_layer_returns_none
— (Docstring fehlt)grün
TestGrenzpunktAlkis
test_invalid_punktort_layer
— (Docstring fehlt)grün
TestGrenzpunktAlkis
test_empty_punktort_layer
— (Docstring fehlt)grün
TestGrenzpunktAlkis
test_no_gml_ids_returns_pta_only
— (Docstring fehlt)rot
TestGrenzpunktAlkis
test_grenzpunkt_batch_empty
— (Docstring fehlt)rot
TestGrenzpunktAlkis
test_success_returns_both_layers
— (Docstring fehlt)rot
TestGrenzpunktAlkis
test_extent_derived_from_vg_layer
Wenn kein extent übergeben → Extent aus VG-Layer holen.rot

Unit-Tests für `sn_verfahrensgebiet.functions.knickpunkt_pruefung`.

TestExtrahiereKnickpunkte 4TestKnickpunktPruefungInputvalidierung 3TestKnickpunktPruefungKlassifizierung 2TestFehlendKnickpunkteViaVerbindungslinien 4
flowchart TD
+  root(["test_knickpunkt_pruefung.py"])
+  class root rootnode
+  root --> g1["TestExtrahiereKnickpunkte (4)"]
+  class g1 groupnode
+  g1 --> t1("leerer_layer_gibt_leere_liste")
+  g1 --> t2("keine_gueltige_geometrie_gibt_leere_liste")
+  g1 --> t3("feature_mit_leerer_geometrie_gibt_leere_liste")
+  g1 --> t4("qgis_nicht_verfuegbar_gibt_leere_liste")
+  root --> g2["TestKnickpunktPruefungInputvalidierung (3)"]
+  class g2 groupnode
+  g2 --> t5("keine_knickpunkte_gibt_none_zurueck")
+  g2 --> t6("kein_punktort_layer_gibt_none_zurueck")
+  g2 --> t7("abbruch_via_progress_gibt_none_zurueck")
+  root --> g3["TestKnickpunktPruefungKlassifizierung (2)"]
+  class g3 groupnode
+  g3 --> t8("identischer_punkt_wird_gruen_gezaehlt")
+  g3 --> t9("kein_pta_an_knickpunkt_wird_rot_gezaehlt")
+  root --> g4["TestFehlendKnickpunkteViaVerbindungslinien (4)"]
+  class g4 groupnode
+  g4 --> t10("leere_union_gibt_null_zurueck")
+  g4 --> t11("weniger_als_zwei_pta_auf_ring_kein_orange")
+  g4 --> t12("ungematchter_pta_im_puffer_wird_orange")
+  g4 --> t13("ungematchter_pta_ausserhalb_puffer_kein_orange")
+  class t1 ok
+  class t2 ok
+  class t3 ok
+  class t4 ok
+  class t5 ok
+  class t6 ok
+  class t7 ok
+  class t8 ok
+  class t9 ok
+  class t10 ok
+  class t11 ok
+  class t12 ok
+  class t13 ok
+  classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
+  classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
+  classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
+  classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestExtrahiereKnickpunkte
test_leerer_layer_gibt_leere_liste
— (Docstring fehlt)grün
TestExtrahiereKnickpunkte
test_keine_gueltige_geometrie_gibt_leere_liste
— (Docstring fehlt)grün
TestExtrahiereKnickpunkte
test_feature_mit_leerer_geometrie_gibt_leere_liste
— (Docstring fehlt)grün
TestExtrahiereKnickpunkte
test_qgis_nicht_verfuegbar_gibt_leere_liste
Wenn qgis.core kein QgsPointXY hat (ImportError in Funktion), leere Liste.grün
TestKnickpunktPruefungInputvalidierung
test_keine_knickpunkte_gibt_none_zurueck
— (Docstring fehlt)grün
TestKnickpunktPruefungInputvalidierung
test_kein_punktort_layer_gibt_none_zurueck
— (Docstring fehlt)grün
TestKnickpunktPruefungInputvalidierung
test_abbruch_via_progress_gibt_none_zurueck
— (Docstring fehlt)grün
TestKnickpunktPruefungKlassifizierung
test_identischer_punkt_wird_gruen_gezaehlt
PunktortTA exakt am Knickpunkt → identisch_count == 1.grün
TestKnickpunktPruefungKlassifizierung
test_kein_pta_an_knickpunkt_wird_rot_gezaehlt
Kein PunktortTA am Knickpunkt → kein_pta_count == 1.grün
TestFehlendKnickpunkteViaVerbindungslinien
test_leere_union_gibt_null_zurueck
bror_union.isEmpty() == True → (0, []).grün
TestFehlendKnickpunkteViaVerbindungslinien
test_weniger_als_zwei_pta_auf_ring_kein_orange
Nur 1 gematchter PunktortTA auf Ring → keine fehlenden Knickpunkte.grün
TestFehlendKnickpunkteViaVerbindungslinien
test_ungematchter_pta_im_puffer_wird_orange
Nicht-gematchter PunktortTA innerhalb 3-cm-Puffer → orange gezählt.grün
TestFehlendKnickpunkteViaVerbindungslinien
test_ungematchter_pta_ausserhalb_puffer_kein_orange
Nicht-gematchter PunktortTA außerhalb Puffer → nicht orange gezählt.grün

— (Docstring fehlt)

TestPluginMain 4
flowchart TD
+  root(["test_main.py"])
+  class root rootnode
+  root --> g1["TestPluginMain (4)"]
+  class g1 groupnode
+  g1 --> t1("init_sets_services_and_names")
+  g1 --> t2("initGui_registers_action_when_basis_ui_exists")
+  g1 --> t3("run_creates_dock_and_marks_toolbar_active")
+  g1 --> t4("unload_removes_dock_and_action")
+  class t1 ok
+  class t2 ok
+  class t3 ok
+  class t4 ok
+  classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
+  classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
+  classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
+  classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestPluginMain
test_init_sets_services_and_names
— (Docstring fehlt)grün
TestPluginMain
test_initGui_registers_action_when_basis_ui_exists
— (Docstring fehlt)grün
TestPluginMain
test_run_creates_dock_and_marks_toolbar_active
— (Docstring fehlt)grün
TestPluginMain
test_unload_removes_dock_and_action
— (Docstring fehlt)grün

— (Docstring fehlt)

TestVerfahrensgebietAlkis 8
flowchart TD
+  root(["test_verfahrensgebiet_alkis.py"])
+  class root rootnode
+  root --> g1["TestVerfahrensgebietAlkis (8)"]
+  class g1 groupnode
+  g1 --> t1("check_existing_layer_first_load")
+  g1 --> t2("check_existing_layer_keep_existing")
+  g1 --> t3("verfahrensgebiet_alkis_fails_when_verfahrensnummer_missing")
+  g1 --> t4("verfahrensgebiet_alkis_fails_on_invalid_wfs_layer")
+  g1 --> t5("verfahrensgebiet_alkis_success_single_kombination")
+  g1 --> t6("verfahrensgebiet_alkis_multi_kombination_dialog_auswahl")
+  g1 --> t7("verfahrensgebiet_alkis_multi_kombination_dialog_abbruch")
+  g1 --> t8("verfahrensgebiet_alkis_multi_kombination_mehrfachauswahl")
+  class t1 ok
+  class t2 ok
+  class t3 ok
+  class t4 ok
+  class t5 ok
+  class t6 ok
+  class t7 ok
+  class t8 ok
+  classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
+  classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
+  classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
+  classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestVerfahrensgebietAlkis
test_check_existing_layer_first_load
— (Docstring fehlt)grün
TestVerfahrensgebietAlkis
test_check_existing_layer_keep_existing
— (Docstring fehlt)grün
TestVerfahrensgebietAlkis
test_verfahrensgebiet_alkis_fails_when_verfahrensnummer_missing
— (Docstring fehlt)grün
TestVerfahrensgebietAlkis
test_verfahrensgebiet_alkis_fails_on_invalid_wfs_layer
— (Docstring fehlt)grün
TestVerfahrensgebietAlkis
test_verfahrensgebiet_alkis_success_single_kombination
Einzelne (adv_name, bezeichnung)-Kombination → kein Dialog, Memory-Layer zurück.grün
TestVerfahrensgebietAlkis
test_verfahrensgebiet_alkis_multi_kombination_dialog_auswahl
Zwei verschiedene Kombinationen → Dialog wird gezeigt, gewählte Features genutzt.grün
TestVerfahrensgebietAlkis
test_verfahrensgebiet_alkis_multi_kombination_dialog_abbruch
Zwei verschiedene Kombinationen → Nutzer bricht Dialog ab → NONE.grün
TestVerfahrensgebietAlkis
test_verfahrensgebiet_alkis_multi_kombination_mehrfachauswahl
Zwei verschiedene Kombinationen → beide ausgewählt → beide Feature-Gruppen genutzt.grün

Unit-Tests für `sn_verfahrensgebiet.functions.verfahrensgebiet_alkis_komplett`.

TestSelectByLocation 3TestFuegeVkzFeldHinzu 3TestFlaechenvergleich 8TestVerfahrensgebietAlkisKomplett 16
flowchart TD
+  root(["test_verfahrensgebiet_alkis_komplett.py"])
+  class root rootnode
+  root --> g1["TestSelectByLocation (3)"]
+  class g1 groupnode
+  g1 --> t1("processing_not_available_returns_zero")
+  g1 --> t2("processing_success_returns_count")
+  g1 --> t3("processing_exception_returns_zero")
+  root --> g2["TestFuegeVkzFeldHinzu (3)"]
+  class g2 groupnode
+  g2 --> t4("empty_vkz_does_nothing")
+  g2 --> t5("none_vkz_does_nothing")
+  g2 --> t6("vkz_field_added")
+  root --> g3["TestFlaechenvergleich (8)"]
+  class g3 groupnode
+  g3 --> t7("gleiche_flaeche_stimmt_ueberein")
+  g3 --> t8("verschiedene_flaeche_stimmt_nicht_ueberein")
+  g3 --> t9("differenz_innerhalb_toleranz")
+  g3 --> t10("differenz_ausserhalb_toleranz")
+  g3 --> t11("flaechen_werte_korrekt")
+  g3 --> t12("lese_toleranz_default_ist_null")
+  g3 --> t13("lese_toleranz_parst_string")
+  g3 --> t14("lese_toleranz_ungueltig_fallback_null")
+  root --> g4["TestVerfahrensgebietAlkisKomplett (16)"]
+  class g4 groupnode
+  g4 --> t15("vg_alkis_none_aborts")
+  g4 --> t16("flst_none_aborts")
+  g4 --> t17("no_selection_returns_none_enriched")
+  g4 --> t18("invalid_enriched_layer_returns_none")
+  g4 --> t19("progress_cancel_after_step1_returns_none")
+  g4 --> t20("success_flaechen_match_zeigt_info")
+  g4 --> t21("no_vkz_variable_passes_empty_string")
+  g4 --> t22("flaechen_abweichung_abbrechen_kein_detailcheck")
+  g4 --> t23("flaechen_abweichung_als_vg_laden")
+  g4 --> t24("flaechen_abweichung_detailpruefung_startet_knickpruefung")
+  g4 --> t25("bror_layer_bleibt_bei_erfolg_im_projekt")
+  g4 --> t26("bror_layer_wird_bei_fehler_entfernt")
+  g4 --> t27("kein_vkz_kein_landkreis_dialog_abgebrochen")
+  g4 --> t28("kein_vkz_kein_landkreis_dialog_liefert_landkreis")
+  g4 --> t29("landkreis_modus_wird_bei_fehlender_vkz_verwendet")
+  g4 --> t30("landkreis_modus_nur_laden_ueberspringt_dialog2")
+  class t1 ok
+  class t2 ok
+  class t3 ok
+  class t4 ok
+  class t5 ok
+  class t6 ok
+  class t7 ok
+  class t8 ok
+  class t9 ok
+  class t10 ok
+  class t11 ok
+  class t12 ok
+  class t13 ok
+  class t14 ok
+  class t15 ok
+  class t16 ok
+  class t17 ok
+  class t18 ok
+  class t19 ok
+  class t20 ok
+  class t21 ok
+  class t22 ok
+  class t23 ok
+  class t24 ok
+  class t25 ok
+  class t26 ok
+  class t27 ok
+  class t28 ok
+  class t29 ok
+  class t30 ok
+  classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
+  classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
+  classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
+  classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestSelectByLocation
test_processing_not_available_returns_zero
— (Docstring fehlt)grün
TestSelectByLocation
test_processing_success_returns_count
— (Docstring fehlt)grün
TestSelectByLocation
test_processing_exception_returns_zero
— (Docstring fehlt)grün
TestFuegeVkzFeldHinzu
test_empty_vkz_does_nothing
— (Docstring fehlt)grün
TestFuegeVkzFeldHinzu
test_none_vkz_does_nothing
— (Docstring fehlt)grün
TestFuegeVkzFeldHinzu
test_vkz_field_added
— (Docstring fehlt)grün
TestFlaechenvergleich
test_gleiche_flaeche_stimmt_ueberein
— (Docstring fehlt)grün
TestFlaechenvergleich
test_verschiedene_flaeche_stimmt_nicht_ueberein
— (Docstring fehlt)grün
TestFlaechenvergleich
test_differenz_innerhalb_toleranz
— (Docstring fehlt)grün
TestFlaechenvergleich
test_differenz_ausserhalb_toleranz
— (Docstring fehlt)grün
TestFlaechenvergleich
test_flaechen_werte_korrekt
— (Docstring fehlt)grün
TestFlaechenvergleich
test_lese_toleranz_default_ist_null
— (Docstring fehlt)grün
TestFlaechenvergleich
test_lese_toleranz_parst_string
— (Docstring fehlt)grün
TestFlaechenvergleich
test_lese_toleranz_ungueltig_fallback_null
— (Docstring fehlt)grün
TestVerfahrensgebietAlkisKomplett
test_vg_alkis_none_aborts
— (Docstring fehlt)grün
TestVerfahrensgebietAlkisKomplett
test_flst_none_aborts
— (Docstring fehlt)grün
TestVerfahrensgebietAlkisKomplett
test_no_selection_returns_none_enriched
— (Docstring fehlt)grün
TestVerfahrensgebietAlkisKomplett
test_invalid_enriched_layer_returns_none
— (Docstring fehlt)grün
TestVerfahrensgebietAlkisKomplett
test_progress_cancel_after_step1_returns_none
Abbruch nach Schritt 1 → sofortiger sauberer Rückgabestatus.grün
TestVerfahrensgebietAlkisKomplett
test_success_flaechen_match_zeigt_info
Flächenübereinstimmung → show_info_dialog mit erwartetem Text.grün
TestVerfahrensgebietAlkisKomplett
test_no_vkz_variable_passes_empty_string
— (Docstring fehlt)grün
TestVerfahrensgebietAlkisKomplett
test_flaechen_abweichung_abbrechen_kein_detailcheck
Abweichung + Abbrechen → knickpunkt_pruefung wird NICHT aufgerufen.grün
TestVerfahrensgebietAlkisKomplett
test_flaechen_abweichung_als_vg_laden
Abweichung + Als VG laden → BROR-Layer wird umbenannt und alsgrün
TestVerfahrensgebietAlkisKomplett
test_flaechen_abweichung_detailpruefung_startet_knickpruefung
Abweichung + Detailprüfung → lade_punktort_ta + knickpunkt_pruefunggrün
TestVerfahrensgebietAlkisKomplett
test_bror_layer_bleibt_bei_erfolg_im_projekt
Bei Erfolg darf der BROR-Layer NICHT entfernt werden.grün
TestVerfahrensgebietAlkisKomplett
test_bror_layer_wird_bei_fehler_entfernt
Bei Fehler (flst_alkis NONE) wird der BROR-Layer aus dem Projekt entfernt.grün
TestVerfahrensgebietAlkisKomplett
test_kein_vkz_kein_landkreis_dialog_abgebrochen
Weder Verfahrensnummer noch Landkreis gesetzt, Nutzer bricht dengrün
TestVerfahrensgebietAlkisKomplett
test_kein_vkz_kein_landkreis_dialog_liefert_landkreis
Weder Verfahrensnummer noch Landkreis gesetzt, Nutzer wählt einengrün
TestVerfahrensgebietAlkisKomplett
test_landkreis_modus_wird_bei_fehlender_vkz_verwendet
Keine Verfahrensnummer, aber Landkreis gesetzt → Landkreis-Suchpfadgrün
TestVerfahrensgebietAlkisKomplett
test_landkreis_modus_nur_laden_ueberspringt_dialog2
nur_laden=True → Funktion kehrt sofort zurück, Dialog 2 (Gemarkunggrün

Unit-Tests für das Verfahrensgebiet-Edit-Service-Modul.

TestPruefeFlurstueckLayer 3TestPruefeAuswahl 3TestGetKennzeichenMenge 3TestErstelle 3TestErgaenzeVerfahrensgebiet 3TestEntferneAusVerfahrensgebiet 5TestErsetze 1
flowchart TD
+  root(["test_verfahrensgebiet_edit_service.py"])
+  class root rootnode
+  root --> g1["TestPruefeFlurstueckLayer (3)"]
+  class g1 groupnode
+  g1 --> t1("layer_ok_passes")
+  g1 --> t2("layer_missing_field_fails")
+  g1 --> t3("layer_wrong_geometry_fails")
+  root --> g2["TestPruefeAuswahl (3)"]
+  class g2 groupnode
+  g2 --> t4("no_selection_fails")
+  g2 --> t5("with_selection_passes")
+  g2 --> t6("selectedFeatureCount_exception_treated_as_zero")
+  root --> g3["TestGetKennzeichenMenge (3)"]
+  class g3 groupnode
+  g3 --> t7("returns_set_of_ids")
+  g3 --> t8("empty_layer_returns_empty_set")
+  g3 --> t9("single_kennzeichen_without_comma")
+  root --> g4["TestErstelle (3)"]
+  class g4 groupnode
+  g4 --> t10("success_counts")
+  g4 --> t11("partial_failure_on_empty_geometry")
+  g4 --> t12("all_fail_on_empty_geometries")
+  root --> g5["TestErgaenzeVerfahrensgebiet (3)"]
+  class g5 groupnode
+  g5 --> t13("adds_only_new_ids")
+  g5 --> t14("skip_empty_geometry_counts_as_failed")
+  g5 --> t15("all_already_present")
+  root --> g6["TestEntferneAusVerfahrensgebiet (5)"]
+  class g6 groupnode
+  g6 --> t16("removes_present_ids")
+  g6 --> t17("counts_ids_not_in_layer")
+  g6 --> t18("none_present_in_layer")
+  g6 --> t19("empty_selection_removes_nothing")
+  g6 --> t20("removes_last_id_deletes_feature")
+  root --> g7["TestErsetze (1)"]
+  class g7 groupnode
+  g7 --> t21("ersetze_delegates_to_erstelle")
+  class t1 ok
+  class t2 ok
+  class t3 ok
+  class t4 ok
+  class t5 ok
+  class t6 ok
+  class t7 ok
+  class t8 ok
+  class t9 ok
+  class t10 ok
+  class t11 ok
+  class t12 ok
+  class t13 ok
+  class t14 ok
+  class t15 ok
+  class t16 ok
+  class t17 ok
+  class t18 ok
+  class t19 ok
+  class t20 ok
+  class t21 ok
+  classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
+  classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
+  classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
+  classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestPruefeFlurstueckLayer
test_layer_ok_passes
Gültiger Polygon-Vektor-Layer mit Pflichtfeldern → ok=True.grün
TestPruefeFlurstueckLayer
test_layer_missing_field_fails
Layer ohne Pflichtfelder → ok=False, aktion=felder_fehlen.grün
TestPruefeFlurstueckLayer
test_layer_wrong_geometry_fails
Layer mit falschem Geometrietyp → ok=False, aktion=falscher_geotyp.grün
TestPruefeAuswahl
test_no_selection_fails
— (Docstring fehlt)grün
TestPruefeAuswahl
test_with_selection_passes
— (Docstring fehlt)grün
TestPruefeAuswahl
test_selectedFeatureCount_exception_treated_as_zero
— (Docstring fehlt)grün
TestGetKennzeichenMenge
test_returns_set_of_ids
Kommagetrennte Kennzeichen im Single-Feature werden korrekt geparst.grün
TestGetKennzeichenMenge
test_empty_layer_returns_empty_set
— (Docstring fehlt)grün
TestGetKennzeichenMenge
test_single_kennzeichen_without_comma
Einzelnes Kennzeichen ohne Komma wird korrekt erkannt.grün
TestErstelle
test_success_counts
3 gültige Features → erfolgreich=3, fehlgeschlagen=0, 1 Union-Feature im Layer.grün
TestErstelle
test_partial_failure_on_empty_geometry
2 ok + 1 leere Geometrie → erfolgreich=2, fehlgeschlagen=1.grün
TestErstelle
test_all_fail_on_empty_geometries
Alle Features haben leere Geometrie → erfolgreich=0, kein Feature im Layer.grün
TestErgaenzeVerfahrensgebiet
test_adds_only_new_ids
Auswahl enthält 1 neue + 1 bereits vorhandene ID → hinzugefügt=1.grün
TestErgaenzeVerfahrensgebiet
test_skip_empty_geometry_counts_as_failed
Neue ID mit leerer Geometrie → fehlgeschlagen=1, hinzugefügt=0.grün
TestErgaenzeVerfahrensgebiet
test_all_already_present
Alle ausgewählten IDs bereits vorhanden → bereits_vorhanden=n, hinzugefügt=0.grün
TestEntferneAusVerfahrensgebiet
test_removes_present_ids
Auswahl enthält 1 vorhandene ID → entfernt=1, nicht_enthalten=0.grün
TestEntferneAusVerfahrensgebiet
test_counts_ids_not_in_layer
Auswahl enthält ID, die nicht im Verfahrensgebiet ist → nicht_enthalten=1.grün
TestEntferneAusVerfahrensgebiet
test_none_present_in_layer
Auswahl hat IDs, die alle nicht im Layer sind → entfernt=0, nicht_enthalten=n.grün
TestEntferneAusVerfahrensgebiet
test_empty_selection_removes_nothing
Leere Auswahl → nichts entfernt, nichts nicht_enthalten.grün
TestEntferneAusVerfahrensgebiet
test_removes_last_id_deletes_feature
Wird das letzte Kennzeichen entfernt, wird das Feature gelöscht.grün
TestErsetze
test_ersetze_delegates_to_erstelle
ersetze_verfahrensgebiet gibt dasselbe Ergebnis wie erstelle zurück.grün

Unit-Tests für `sn_verfahrensgebiet.functions.verfahrensgebiet_shape_komplett`.

TestFindShp 4TestErstelleMemoryLayerAusShp 3TestLadeVgAusZip 6TestVerfahrensgebietShapeKomplett 18
flowchart TD
+  root(["test_verfahrensgebiet_shape_komplett.py"])
+  class root rootnode
+  root --> g1["TestFindShp (4)"]
+  class g1 groupnode
+  g1 --> t1("findet_shp_in_root")
+  g1 --> t2("findet_shp_in_unterordner")
+  g1 --> t3("kein_shp_gibt_none_zurueck")
+  g1 --> t4("case_insensitive_erkennung")
+  root --> g2["TestErstelleMemoryLayerAusShp (3)"]
+  class g2 groupnode
+  g2 --> t5("leere_geometrien_geben_none")
+  g2 --> t6("keine_features_gibt_none")
+  g2 --> t7("attribute_von_erstem_feature_werden_gesetzt")
+  root --> g3["TestLadeVgAusZip (6)"]
+  class g3 groupnode
+  g3 --> t8("dialog_abgebrochen_gibt_none_zurueck")
+  g3 --> t9("zip_fehler_gibt_none_und_warning")
+  g3 --> t10("kein_shp_in_zip_gibt_none_und_warning")
+  g3 --> t11("invalider_qgslayer_gibt_none_und_warning")
+  g3 --> t12("leere_geometrien_geben_none_und_warning")
+  g3 --> t13("erfolg_gibt_memory_layer_zurueck")
+  root --> g4["TestVerfahrensgebietShapeKomplett (18)"]
+  class g4 groupnode
+  g4 --> t14("lade_zip_none_aborts")
+  g4 --> t15("shape_vg_sofort_ins_projekt_nach_laden")
+  g4 --> t16("shape_vg_wird_bei_abbruch_aus_projekt_entfernt")
+  g4 --> t17("shape_vg_bleibt_im_projekt_bei_erfolg")
+  g4 --> t18("flst_none_aborts")
+  g4 --> t19("keine_flurstuecke_selektiert_gibt_warning")
+  g4 --> t20("flaechen_uebereinstimmung_gibt_info_und_kein_dialog")
+  g4 --> t21("abweichung_abbrechen_gibt_shape_vg_zurueck")
+  g4 --> t22("abweichung_detailpruefung_ruft_knickpunkt_pruefung_auf")
+  g4 --> t23("punktort_ta_none_gibt_shape_vg_zurueck_und_zeigt_fehler")
+  g4 --> t24("temp_layer_wird_stets_entfernt")
+  g4 --> t25("bror_layer_ist_stets_none")
+  g4 --> t26("bror_geladen_und_zurueckgegeben_wenn_vkz_gesetzt")
+  g4 --> t27("bror_wird_vor_shape_vg_ins_projekt_eingefuegt")
+  g4 --> t28("bror_wird_bei_abbruch_aus_projekt_entfernt")
+  g4 --> t29("verfahrensgebiet_alkis_nicht_aufgerufen_wenn_vkz_leer")
+  g4 --> t30("bror_als_erstes_argument_beim_flaechenvergleich")
+  g4 --> t31("fallback_auf_temp_enriched_wenn_bror_ladeversagen")
+  class t1 ok
+  class t2 ok
+  class t3 ok
+  class t4 ok
+  class t5 ok
+  class t6 ok
+  class t7 ok
+  class t8 warn
+  class t9 warn
+  class t10 warn
+  class t11 warn
+  class t12 warn
+  class t13 warn
+  class t14 ok
+  class t15 warn
+  class t16 warn
+  class t17 ok
+  class t18 ok
+  class t19 ok
+  class t20 warn
+  class t21 warn
+  class t22 warn
+  class t23 warn
+  class t24 ok
+  class t25 ok
+  class t26 warn
+  class t27 warn
+  class t28 warn
+  class t29 ok
+  class t30 warn
+  class t31 warn
+  classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
+  classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
+  classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
+  classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestFindShp
test_findet_shp_in_root
— (Docstring fehlt)grün
TestFindShp
test_findet_shp_in_unterordner
— (Docstring fehlt)grün
TestFindShp
test_kein_shp_gibt_none_zurueck
— (Docstring fehlt)grün
TestFindShp
test_case_insensitive_erkennung
— (Docstring fehlt)grün
TestErstelleMemoryLayerAusShp
test_leere_geometrien_geben_none
— (Docstring fehlt)grün
TestErstelleMemoryLayerAusShp
test_keine_features_gibt_none
— (Docstring fehlt)grün
TestErstelleMemoryLayerAusShp
test_attribute_von_erstem_feature_werden_gesetzt
Attributwerte des ersten Features werden auf das Union-Feature übertragen.grün
TestLadeVgAusZip
test_dialog_abgebrochen_gibt_none_zurueck
— (Docstring fehlt)rot
TestLadeVgAusZip
test_zip_fehler_gibt_none_und_warning
— (Docstring fehlt)rot
TestLadeVgAusZip
test_kein_shp_in_zip_gibt_none_und_warning
— (Docstring fehlt)rot
TestLadeVgAusZip
test_invalider_qgslayer_gibt_none_und_warning
— (Docstring fehlt)rot
TestLadeVgAusZip
test_leere_geometrien_geben_none_und_warning
— (Docstring fehlt)rot
TestLadeVgAusZip
test_erfolg_gibt_memory_layer_zurueck
— (Docstring fehlt)rot
TestVerfahrensgebietShapeKomplett
test_lade_zip_none_aborts
— (Docstring fehlt)grün
TestVerfahrensgebietShapeKomplett
test_shape_vg_sofort_ins_projekt_nach_laden
Shape-VG wird unmittelbar nach dem Laden ins Projekt eingefügt.rot
TestVerfahrensgebietShapeKomplett
test_shape_vg_wird_bei_abbruch_aus_projekt_entfernt
Shape-VG wird bei Fehler/Abbruch aus dem Projekt entfernt.rot
TestVerfahrensgebietShapeKomplett
test_shape_vg_bleibt_im_projekt_bei_erfolg
Bei erfolgreichem Abschluss wird shape_vg NICHT aus dem Projekt entfernt.grün
TestVerfahrensgebietShapeKomplett
test_flst_none_aborts
— (Docstring fehlt)grün
TestVerfahrensgebietShapeKomplett
test_keine_flurstuecke_selektiert_gibt_warning
— (Docstring fehlt)grün
TestVerfahrensgebietShapeKomplett
test_flaechen_uebereinstimmung_gibt_info_und_kein_dialog
— (Docstring fehlt)rot
TestVerfahrensgebietShapeKomplett
test_abweichung_abbrechen_gibt_shape_vg_zurueck
— (Docstring fehlt)rot
TestVerfahrensgebietShapeKomplett
test_abweichung_detailpruefung_ruft_knickpunkt_pruefung_auf
— (Docstring fehlt)rot
TestVerfahrensgebietShapeKomplett
test_punktort_ta_none_gibt_shape_vg_zurueck_und_zeigt_fehler
— (Docstring fehlt)rot
TestVerfahrensgebietShapeKomplett
test_temp_layer_wird_stets_entfernt
— (Docstring fehlt)grün
TestVerfahrensgebietShapeKomplett
test_bror_layer_ist_stets_none
Dritter Rückgabewert ist im Shape-Workflow immer None (kein BROR).grün
TestVerfahrensgebietShapeKomplett
test_bror_geladen_und_zurueckgegeben_wenn_vkz_gesetzt
VKZ gesetzt → verfahrensgebiet_alkis aufgerufen, bror_layer zurückgegeben.rot
TestVerfahrensgebietShapeKomplett
test_bror_wird_vor_shape_vg_ins_projekt_eingefuegt
BROR wird VOR shape_vg ins Projekt eingefügt (kein Namenskonflikt).rot
TestVerfahrensgebietShapeKomplett
test_bror_wird_bei_abbruch_aus_projekt_entfernt
BROR und shape_vg werden bei Fehler aus dem Projekt entfernt.rot
TestVerfahrensgebietShapeKomplett
test_verfahrensgebiet_alkis_nicht_aufgerufen_wenn_vkz_leer
Leere VKZ → verfahrensgebiet_alkis wird NICHT aufgerufen.grün
TestVerfahrensgebietShapeKomplett
test_bror_als_erstes_argument_beim_flaechenvergleich
Mit BROR: _vergleiche_flaechen wird (bror, shape_vg, ...) aufgerufen.rot
TestVerfahrensgebietShapeKomplett
test_fallback_auf_temp_enriched_wenn_bror_ladeversagen
BROR-Ladeversagen → Fallback: _vergleiche_flaechen mit shape_vg vs temp_enriched.rot

— (Docstring fehlt)

TestWorkingTab 24TestHandleVerfShape 9
flowchart TD
+  root(["test_working_tab.py"])
+  class root rootnode
+  root --> g1["TestWorkingTab (24)"]
+  class g1 groupnode
+  g1 --> t1("set_busy_updates_button_state")
+  g1 --> t2("on_verf_layer_changed_valid_sets_variable")
+  g1 --> t3("on_verf_layer_changed_none_clears_variable")
+  g1 --> t4("handle_verf_alkis_success_path")
+  g1 --> t5("handle_verf_alkis_failure_clears_layer_var")
+  g1 --> t6("handle_flurst_alkis_first_load_uebernimmt_layer")
+  g1 --> t7("handle_flurst_aus_gemarkungen_ruft_flurstueck_alkis_ohne_schluessel_abfrage")
+  g1 --> t8("handle_flurst_aus_gemarkungen_abbruch_zeigt_keine_zusaetzliche_meldung")
+  g1 --> t9("handle_grenz_alkis_success_sets_haken")
+  g1 --> t10("handle_grenz_alkis_none_result_sets_no_haken")
+  g1 --> t11("handle_flurst_alkis_success_sets_haken_and_style")
+  g1 --> t12("handle_flurst_alkis_existing_layer_keep_path")
+  g1 --> t13("handle_flurst_alkis_invalid_input_returns_early")
+  g1 --> t14("handle_verfahrensgebiet_from_selection_requires_active_layer")
+  g1 --> t15("handle_verfahrensgebiet_from_selection_requires_required_fields")
+  g1 --> t16("handle_verfahrensgebiet_from_selection_requires_selection")
+  g1 --> t17("handle_verfahrensgebiet_from_selection_new_layer_success")
+  g1 --> t18("handle_verfahrensgebiet_from_selection_existing_layer_ergaenzen_dispatch")
+  g1 --> t19("handle_verfahrensgebiet_from_selection_existing_layer_entfernen_dispatch")
+  g1 --> t20("handle_verfahrensgebiet_from_selection_existing_layer_ersetzen_dispatch")
+  g1 --> t21("aus_liste_button_shows_not_implemented_hint")
+  g1 --> t22("handle_gem_alkis_service_reachable_adds_layer")
+  g1 --> t23("handle_gem_alkis_service_unreachable_shows_warning")
+  g1 --> t24("handle_gem_alkis_layer_already_exists_shows_info")
+  root --> g2["TestHandleVerfShape (9)"]
+  class g2 groupnode
+  g2 --> t25("status_none_clears_layer_variable")
+  g2 --> t26("button_always_re_enabled_on_failure")
+  g2 --> t27("verfahrensnummer_nicht_gesetzt_nein_bricht_ab")
+  g2 --> t28("verfahrensnummer_nicht_gesetzt_ja_fuehrt_fort")
+  g2 --> t29("verfahrensnummer_gesetzt_kein_rueckfrage_dialog")
+  g2 --> t30("success_path_applies_style_and_sets_haken")
+  g2 --> t31("bror_layer_erhaelt_qml_stil")
+  g2 --> t32("enriched_layer_already_in_project_not_added_again")
+  g2 --> t33("busy_state_set_during_execution")
+  class t1 ok
+  class t2 ok
+  class t3 ok
+  class t4 warn
+  class t5 ok
+  class t6 ok
+  class t7 ok
+  class t8 ok
+  class t9 warn
+  class t10 warn
+  class t11 ok
+  class t12 ok
+  class t13 ok
+  class t14 ok
+  class t15 ok
+  class t16 ok
+  class t17 ok
+  class t18 ok
+  class t19 ok
+  class t20 ok
+  class t21 ok
+  class t22 ok
+  class t23 ok
+  class t24 ok
+  class t25 ok
+  class t26 ok
+  class t27 warn
+  class t28 warn
+  class t29 ok
+  class t30 ok
+  class t31 ok
+  class t32 ok
+  class t33 ok
+  classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
+  classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
+  classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
+  classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestWorkingTab
test_set_busy_updates_button_state
— (Docstring fehlt)grün
TestWorkingTab
test_on_verf_layer_changed_valid_sets_variable
— (Docstring fehlt)grün
TestWorkingTab
test_on_verf_layer_changed_none_clears_variable
— (Docstring fehlt)grün
TestWorkingTab
test_handle_verf_alkis_success_path
verfahrensgebiet_alkis_komplett → 4-Tupel (enriched, flst, bror, FIRST).rot
TestWorkingTab
test_handle_verf_alkis_failure_clears_layer_var
Status NONE → Layer-Variable wird auf '' gesetzt.grün
TestWorkingTab
test_handle_flurst_alkis_first_load_uebernimmt_layer
Erstmaliges Laden über 'Flurstücke laden' fügt den Layer dem Projekt hinzu.grün
TestWorkingTab
test_handle_flurst_aus_gemarkungen_ruft_flurstueck_alkis_ohne_schluessel_abfrage
Der Gemarkungen-Auswahl-Button ruft dieselbe Ladefunktion mit ohne_schluessel_abfrage=True auf (#47).grün
TestWorkingTab
test_handle_flurst_aus_gemarkungen_abbruch_zeigt_keine_zusaetzliche_meldung
Bricht flurstueck_alkis() ab (None, NONE), zeigt der Button keine eigene Meldung – flurstueck_alkis() hat das schon getan (#47).grün
TestWorkingTab
test_handle_grenz_alkis_success_sets_haken
Grenzpunkte erfolgreich geladen → haken_grenz wird gesetzt.rot
TestWorkingTab
test_handle_grenz_alkis_none_result_sets_no_haken
grenzpunkt_alkis liefert (None, None) → kein Haken.rot
TestWorkingTab
test_handle_flurst_alkis_success_sets_haken_and_style
— (Docstring fehlt)grün
TestWorkingTab
test_handle_flurst_alkis_existing_layer_keep_path
— (Docstring fehlt)grün
TestWorkingTab
test_handle_flurst_alkis_invalid_input_returns_early
Status NONE (z. B. Nutzerabbruch) → kein Layer, kein Haken.grün
TestWorkingTab
test_handle_verfahrensgebiet_from_selection_requires_active_layer
Kein aktiver Layer → warning, kein Layer wird erstellt.grün
TestWorkingTab
test_handle_verfahrensgebiet_from_selection_requires_required_fields
Layer ohne Pflichtfelder → warning, kein Layer wird erstellt.grün
TestWorkingTab
test_handle_verfahrensgebiet_from_selection_requires_selection
Keine Flurstück-Auswahl → warning, kein Layer erstellt.grün
TestWorkingTab
test_handle_verfahrensgebiet_from_selection_new_layer_success
Erstlauf ohne bestehenden Layer → Layer wird erstellt und hinzugefügt.grün
TestWorkingTab
test_handle_verfahrensgebiet_from_selection_existing_layer_ergaenzen_dispatch
Entscheidung 'ergaenzen' → ergaenze_verfahrensgebiet wird aufgerufen.grün
TestWorkingTab
test_handle_verfahrensgebiet_from_selection_existing_layer_entfernen_dispatch
Entscheidung 'entfernen' → entferne_aus_verfahrensgebiet wird aufgerufen.grün
TestWorkingTab
test_handle_verfahrensgebiet_from_selection_existing_layer_ersetzen_dispatch
Entscheidung 'ersetzen' → alter Layer entfernt, neuer hinzugefügt.grün
TestWorkingTab
test_aus_liste_button_shows_not_implemented_hint
— (Docstring fehlt)grün
TestWorkingTab
test_handle_gem_alkis_service_reachable_adds_layer
Dienst erreichbar → Layer ins Projekt, Stil angewendet, Haken gesetzt.grün
TestWorkingTab
test_handle_gem_alkis_service_unreachable_shows_warning
Dienst nicht erreichbar (None) → warning, kein addMapLayer.grün
TestWorkingTab
test_handle_gem_alkis_layer_already_exists_shows_info
Layer 'Gemarkungen' bereits vorhanden → info, kein erneutes Laden.grün
TestHandleVerfShape
test_status_none_clears_layer_variable
Status NONE → Layer-Variable wird auf '' gesetzt.grün
TestHandleVerfShape
test_button_always_re_enabled_on_failure
Button muss nach Status NONE wieder freigegeben sein (finally).grün
TestHandleVerfShape
test_verfahrensnummer_nicht_gesetzt_nein_bricht_ab
Verfahrensnummer leer + Nutzer wählt 'Nein' → Vorgang abgebrochen.rot
TestHandleVerfShape
test_verfahrensnummer_nicht_gesetzt_ja_fuehrt_fort
Verfahrensnummer leer + Nutzer wählt 'Ja' → Pipeline wird aufgerufen.rot
TestHandleVerfShape
test_verfahrensnummer_gesetzt_kein_rueckfrage_dialog
Verfahrensnummer gesetzt → kein ask_yes_no-Dialog.grün
TestHandleVerfShape
test_success_path_applies_style_and_sets_haken
Erfolg → verfahrensgebiet.qml angewendet, haken_verf gesetzt, success aufgerufen.grün
TestHandleVerfShape
test_bror_layer_erhaelt_qml_stil
Wenn bror_layer zurückgegeben → BauRaumOderBodenordnungsrecht.qml angewendet.grün
TestHandleVerfShape
test_enriched_layer_already_in_project_not_added_again
Layer bereits im Projekt → kein erneutes addMapLayer für VG-Layer.grün
TestHandleVerfShape
test_busy_state_set_during_execution
Button wird während der Ausführung auf busy gesetzt.grün
+
+ + +
+
+
+

sn_plan41

+

Fachplugin · Ordner Plugin_SN_Plan41 · 5 Testdateien · 44 Testfälle

+
+ +
+

— (Docstring fehlt)

TestListenauswerter 4
flowchart TD
+  root(["test_listenauswerter.py"])
+  class root rootnode
+  root --> g1["TestListenauswerter (4)"]
+  class g1 groupnode
+  g1 --> t1("validate_rows_accepts_valid_row_and_normalizes_provider")
+  g1 --> t2("validate_rows_rejects_missing_required_fields")
+  g1 --> t3("validate_rows_sets_stildatei_none_on_style_failure")
+  g1 --> t4("validate_rows_handles_non_mapping_rows_edge_case")
+  class t1 ok
+  class t2 ok
+  class t3 ok
+  class t4 ok
+  classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
+  classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
+  classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
+  classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestListenauswerter
test_validate_rows_accepts_valid_row_and_normalizes_provider
— (Docstring fehlt)grün
TestListenauswerter
test_validate_rows_rejects_missing_required_fields
— (Docstring fehlt)grün
TestListenauswerter
test_validate_rows_sets_stildatei_none_on_style_failure
— (Docstring fehlt)grün
TestListenauswerter
test_validate_rows_handles_non_mapping_rows_edge_case
— (Docstring fehlt)grün

— (Docstring fehlt)

TestTabALogic 9
flowchart TD
+  root(["test_tab_a_logic.py"])
+  class root rootnode
+  root --> g1["TestTabALogic (9)"]
+  class g1 groupnode
+  g1 --> t1("verfahrens_db_set_and_load_roundtrip")
+  g1 --> t2("verfahrens_db_clear")
+  g1 --> t3("linkliste_set_and_load_existing_path")
+  g1 --> t4("linkliste_load_returns_none_when_file_missing")
+  g1 --> t5("resolve_linkliste_empty_uses_standard_and_persists")
+  g1 --> t6("resolve_linkliste_returns_none_when_pruefmanager_rejects")
+  g1 --> t7("resolve_raumfilter_without_filter_returns_none")
+  g1 --> t8("resolve_raumfilter_verfahrensgebiet_uses_layer_lookup")
+  g1 --> t9("extract_datenabruf_error_maps_merges_reason_maps")
+  class t1 ok
+  class t2 ok
+  class t3 ok
+  class t4 ok
+  class t5 ok
+  class t6 ok
+  class t7 ok
+  class t8 ok
+  class t9 ok
+  classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
+  classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
+  classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
+  classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestTabALogic
test_verfahrens_db_set_and_load_roundtrip
— (Docstring fehlt)grün
TestTabALogic
test_verfahrens_db_clear
— (Docstring fehlt)grün
TestTabALogic
test_linkliste_set_and_load_existing_path
— (Docstring fehlt)grün
TestTabALogic
test_linkliste_load_returns_none_when_file_missing
— (Docstring fehlt)grün
TestTabALogic
test_resolve_linkliste_empty_uses_standard_and_persists
— (Docstring fehlt)grün
TestTabALogic
test_resolve_linkliste_returns_none_when_pruefmanager_rejects
— (Docstring fehlt)grün
TestTabALogic
test_resolve_raumfilter_without_filter_returns_none
— (Docstring fehlt)grün
TestTabALogic
test_resolve_raumfilter_verfahrensgebiet_uses_layer_lookup
— (Docstring fehlt)grün
TestTabALogic
test_extract_datenabruf_error_maps_merges_reason_maps
— (Docstring fehlt)grün

— (Docstring fehlt)

TestTabAPreconditions 3
flowchart TD
+  root(["test_tab_a_preconditions.py"])
+  class root rootnode
+  root --> g1["TestTabAPreconditions (3)"]
+  class g1 groupnode
+  g1 --> t1("get_verfahrensgebiet_layer_shows_hint_when_layer_id_missing")
+  g1 --> t2("get_verfahrensgebiet_layer_shows_hint_when_layer_not_in_project")
+  g1 --> t3("pipeline_aborts_and_shows_hint_when_verfahrensgebiet_missing")
+  class t1 ok
+  class t2 ok
+  class t3 ok
+  classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
+  classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
+  classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
+  classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestTabAPreconditions
test_get_verfahrensgebiet_layer_shows_hint_when_layer_id_missing
— (Docstring fehlt)grün
TestTabAPreconditions
test_get_verfahrensgebiet_layer_shows_hint_when_layer_not_in_project
— (Docstring fehlt)grün
TestTabAPreconditions
test_pipeline_aborts_and_shows_hint_when_verfahrensgebiet_missing
— (Docstring fehlt)grün

— (Docstring fehlt)

TestTabAUI 3
flowchart TD
+  root(["test_tab_a_ui.py"])
+  class root rootnode
+  root --> g1["TestTabAUI (3)"]
+  class g1 groupnode
+  g1 --> t1("tab_a_callbacks_can_be_called_without_crash")
+  g1 --> t2("tab_a_raumfilter_callback_persists_value")
+  g1 --> t3("set_services_initializes_logic_and_injects_data_grabber")
+  class t1 ok
+  class t2 ok
+  class t3 warn
+  classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
+  classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
+  classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
+  classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestTabAUI
test_tab_a_callbacks_can_be_called_without_crash
— (Docstring fehlt)grün
TestTabAUI
test_tab_a_raumfilter_callback_persists_value
— (Docstring fehlt)grün
TestTabAUI
test_set_services_initializes_logic_and_injects_data_grabber
— (Docstring fehlt)rot

Tests für sn_plan41/modules/vln_api_logic.py

TestVlnApiLogicAvailability 2TestVlnApiLogicCredentials 4TestVlnApiLogicLogin 6TestVlnApiLogicVerfahren 2TestVlnApiLogicVkz 4TestVlnApiLogicLoadP41 3TestVlnApiLogicUpload 4
flowchart TD
+  root(["test_vln_api_logic.py"])
+  class root rootnode
+  root --> g1["TestVlnApiLogicAvailability (2)"]
+  class g1 groupnode
+  g1 --> t1("is_available_reflects_import_state")
+  g1 --> t2("is_available_false_when_vln_karten_missing")
+  root --> g2["TestVlnApiLogicCredentials (4)"]
+  class g2 groupnode
+  g2 --> t3("save_api_key_calls_qsettings")
+  g2 --> t4("clear_api_key_calls_remove")
+  g2 --> t5("load_stored_credentials_returns_api_key_and_mail")
+  g2 --> t6("load_stored_credentials_returns_none_for_empty")
+  root --> g3["TestVlnApiLogicLogin (6)"]
+  class g3 groupnode
+  g3 --> t7("login_sets_client_and_saves_key")
+  g3 --> t8("login_raises_runtime_error_without_qgis_modules")
+  g3 --> t9("login_propagates_api_error")
+  g3 --> t10("logout_clears_client")
+  g3 --> t11("is_authenticated_false_after_logout")
+  g3 --> t12("handle_session_expired_clears_key_and_client")
+  root --> g4["TestVlnApiLogicVerfahren (2)"]
+  class g4 groupnode
+  g4 --> t13("get_verfahren_raises_when_not_authenticated")
+  g4 --> t14("get_verfahren_delegates_to_client")
+  root --> g5["TestVlnApiLogicVkz (4)"]
+  class g5 groupnode
+  g5 --> t15("load_stored_vkz_reads_from_project")
+  g5 --> t16("load_stored_vkz_returns_none_for_empty")
+  g5 --> t17("save_vkz_writes_to_project")
+  g5 --> t18("save_vkz_does_nothing_for_none")
+  root --> g6["TestVlnApiLogicLoadP41 (3)"]
+  class g6 groupnode
+  g6 --> t19("load_p41_raises_without_vln_karten")
+  g6 --> t20("load_p41_applies_styles_to_polygon_and_line_layers")
+  g6 --> t21("load_p41_no_style_for_point_layer")
+  root --> g7["TestVlnApiLogicUpload (4)"]
+  class g7 groupnode
+  g7 --> t22("upload_returns_error_for_layer_without_api_path")
+  g7 --> t23("upload_returns_error_for_none_layer")
+  g7 --> t24("upload_returns_false_without_qgis_modules")
+  g7 --> t25("upload_calls_save_feature_collection")
+  class t1 ok
+  class t2 ok
+  class t3 ok
+  class t4 ok
+  class t5 ok
+  class t6 ok
+  class t7 ok
+  class t8 ok
+  class t9 ok
+  class t10 ok
+  class t11 ok
+  class t12 ok
+  class t13 ok
+  class t14 ok
+  class t15 ok
+  class t16 ok
+  class t17 ok
+  class t18 ok
+  class t19 ok
+  class t20 ok
+  class t21 ok
+  class t22 ok
+  class t23 ok
+  class t24 ok
+  class t25 ok
+  classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;
+  classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;
+  classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;
+  classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;
+  classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;
TestfallprüftStatus
TestVlnApiLogicAvailability
test_is_available_reflects_import_state
— (Docstring fehlt)grün
TestVlnApiLogicAvailability
test_is_available_false_when_vln_karten_missing
Wenn VLN_KARTEN_AVAILABLE=False, liefert is_available() False.grün
TestVlnApiLogicCredentials
test_save_api_key_calls_qsettings
— (Docstring fehlt)grün
TestVlnApiLogicCredentials
test_clear_api_key_calls_remove
— (Docstring fehlt)grün
TestVlnApiLogicCredentials
test_load_stored_credentials_returns_api_key_and_mail
— (Docstring fehlt)grün
TestVlnApiLogicCredentials
test_load_stored_credentials_returns_none_for_empty
— (Docstring fehlt)grün
TestVlnApiLogicLogin
test_login_sets_client_and_saves_key
— (Docstring fehlt)grün
TestVlnApiLogicLogin
test_login_raises_runtime_error_without_qgis_modules
— (Docstring fehlt)grün
TestVlnApiLogicLogin
test_login_propagates_api_error
— (Docstring fehlt)grün
TestVlnApiLogicLogin
test_logout_clears_client
— (Docstring fehlt)grün
TestVlnApiLogicLogin
test_is_authenticated_false_after_logout
— (Docstring fehlt)grün
TestVlnApiLogicLogin
test_handle_session_expired_clears_key_and_client
— (Docstring fehlt)grün
TestVlnApiLogicVerfahren
test_get_verfahren_raises_when_not_authenticated
— (Docstring fehlt)grün
TestVlnApiLogicVerfahren
test_get_verfahren_delegates_to_client
— (Docstring fehlt)grün
TestVlnApiLogicVkz
test_load_stored_vkz_reads_from_project
— (Docstring fehlt)grün
TestVlnApiLogicVkz
test_load_stored_vkz_returns_none_for_empty
— (Docstring fehlt)grün
TestVlnApiLogicVkz
test_save_vkz_writes_to_project
— (Docstring fehlt)grün
TestVlnApiLogicVkz
test_save_vkz_does_nothing_for_none
— (Docstring fehlt)grün
TestVlnApiLogicLoadP41
test_load_p41_raises_without_vln_karten
— (Docstring fehlt)grün
TestVlnApiLogicLoadP41
test_load_p41_applies_styles_to_polygon_and_line_layers
— (Docstring fehlt)grün
TestVlnApiLogicLoadP41
test_load_p41_no_style_for_point_layer
— (Docstring fehlt)grün
TestVlnApiLogicUpload
test_upload_returns_error_for_layer_without_api_path
— (Docstring fehlt)grün
TestVlnApiLogicUpload
test_upload_returns_error_for_none_layer
— (Docstring fehlt)grün
TestVlnApiLogicUpload
test_upload_returns_false_without_qgis_modules
— (Docstring fehlt)grün
TestVlnApiLogicUpload
test_upload_calls_save_feature_collection
— (Docstring fehlt)grün
+
+ + + + + + diff --git a/docs/wissen/fallstricke.md b/docs/wissen/fallstricke.md index 4f72e25..fa5e6db 100644 --- a/docs/wissen/fallstricke.md +++ b/docs/wissen/fallstricke.md @@ -11,6 +11,10 @@ Kurz, konkret, mit Datum und Quelle. Neue Einträge oben in der passenden Rubrik - **`openpyxl` ist eine harte Abhängigkeit** von `sn_basis` (Excel-Import) und unter Linux-QGIS oft nicht installiert (Issue Verfahrensgebiet #21). `requests` ist nur optionaler Fallback. Es gibt keine `requirements.txt`. (09/2026) - **QGIS lädt Plugins in nicht garantierter Reihenfolge.** Fachplugins dürfen in `initGui()` nicht davon ausgehen, dass `sn_basis` fertig ist; `sn_basis` ruft `initGui()` aller `sn_*` nach. (Code `main.py`) - **Exit-Crash unter QGIS 3.40** entstand durch nicht getrennte Projektsignale beim Unload. Regel: jeder Tab/Manager, der Signale verbindet, bietet `cleanup()`/`teardown_...()` und wird beim Schließen aufgerufen. (Basis, `assets/plan-qgis340ExitCrashFix.prompt.md`) +- **`docs/tests/testkatalog.html` (Dashboard „Prüfstand") lädt Mermaid von `cdnjs.cloudflare.com`.** Ohne + Internetverbindung beim Öffnen bleiben die Baumdiagramme leer, Text/Tabellen/Filter funktionieren trotzdem + (kein Server nötig, einfach lokal im Browser öffnen). Erzeugt zusammen mit `testkatalog.md` durch + `python scripts/testkatalog.py`; beide Dateien müssen zueinander passen (`--pruefen` prüft beide). (09/2026) - **`.vscode/` gehört nicht ins Repository.** Im Projektwissen wurde er am 04.09.2026 entfernt; brauchbare Einstellungen stehen jetzt in `docs/leitfaden/04_werkzeuge_einrichten.md` Abschnitt 4.2 zum Kopieren in die Benutzereinstellungen. In `Plugin_SN_Basis`, `Plugin_SN_Plan41`, `Plugin_SN_Verfahrensgebiet` und `Plugin_SN_Widmung` liegt er noch und enthält Pfade eines einzelnen Arbeitsplatzes (OSGeo-Installationspfad, persönliches Benutzerprofil) – nicht als Vorgabe verstehen. (09/2026) ## QGIS-Tests (geplant, siehe `docs/konzept/07_qgis_tests_plan.md`) diff --git a/scripts/testkatalog.py b/scripts/testkatalog.py index 1042c34..772fdb5 100644 --- a/scripts/testkatalog.py +++ b/scripts/testkatalog.py @@ -26,14 +26,22 @@ Eingang ändert. Sortiert wird nach der Reihenfolge in workspace.toml (Pakete), nach Dateinamen (Testdateien) und nach der Reihenfolge im Quelltext (Klassen und Testfälle). -Aufrufe (aus dem Workspace-Root): - python scripts/testkatalog.py # docs/tests/testkatalog.md neu schreiben - python scripts/testkatalog.py sn_basis # nur ein Paket - python scripts/testkatalog.py --pruefen # nur prüfen, ob die Datei aktuell ist (CI, vor dem Commit) - python scripts/testkatalog.py --ausgabe /tmp/k.md # anderes Ziel +Zusätzlich zum Markdown-Katalog schreibt der Lauf ein eigenständiges HTML-Dashboard +(docs/tests/testkatalog.html): dieselben Daten als Baumdiagramm je Testdatei +(Mermaid, geladen von cdnjs – ohne Internetverbindung bleiben die Diagramme leer, +Text und Tabellen bleiben lesbar), mit Filtern und Suche. Im Browser lokal öffnen, +kein Server nötig. Siehe docs/wissen/fallstricke.md. -Exit-Code: 0 = erfolgreich (mit --pruefen: Datei ist aktuell), 1 = Datei veraltet -oder fehlt, 2 = Aufruffehler. +Aufrufe (aus dem Workspace-Root): + python scripts/testkatalog.py # .md und .html neu schreiben + python scripts/testkatalog.py sn_basis # nur ein Paket + python scripts/testkatalog.py --pruefen # nur prüfen, ob beide Dateien aktuell sind (CI, vor dem Commit) + python scripts/testkatalog.py --ausgabe /tmp/k.md # anderes Ziel für den Markdown-Katalog + python scripts/testkatalog.py --html-ausgabe /tmp/k.html + python scripts/testkatalog.py --ohne-html # nur den Markdown-Katalog erzeugen/prüfen + +Exit-Code: 0 = erfolgreich (mit --pruefen: Dateien sind aktuell), 1 = mindestens +eine Datei veraltet oder fehlt, 2 = Aufruffehler. """ from __future__ import annotations @@ -42,6 +50,7 @@ import argparse import ast import difflib import hashlib +import html import json import re import tomllib @@ -52,6 +61,7 @@ ROOT = Path(__file__).resolve().parents[1] MANIFEST = ROOT / "workspace.toml" BASELINE_DEFAULT = ROOT / "docs" / "status" / "testbaseline.json" AUSGABE_DEFAULT = ROOT / "docs" / "tests" / "testkatalog.md" +HTML_AUSGABE_DEFAULT = ROOT / "docs" / "tests" / "testkatalog.html" FEHLT = "— (Docstring fehlt)" SPHINX_ROLLE = re.compile(r":(?:mod|func|class|meth|attr|data|obj|ref):`~?([^`]+)`") @@ -423,6 +433,405 @@ def erzeuge_markdown(kataloge: list[Paketkatalog], baseline_pfad: Path, baseline return "\n".join(z) + "\n" +STATUS_KLASSE = { + "grün": "ok", + "rot (Baseline)": "warn", + "übersprungen": "skip", + "erwarteter Fehlschlag": "skip", +} + + +def _slug(text: str) -> str: + return re.sub(r"[^a-zA-Z0-9]+", "-", text).strip("-").lower() + + +def _mermaid_fuer_datei(datei: Testdatei) -> str: + """Baumdiagramm Datei -> Testgruppe -> Testfall, Farbe = Baseline-Status.""" + zeilen = ["flowchart TD", f' root(["{datei.pfad.name}"])', " class root rootnode"] + klassenzeilen: list[str] = [] + gi = 0 + ti = 0 + for gruppe in datei.gruppen: + gi += 1 + gid = f"g{gi}" + zeilen.append(f' root --> {gid}["{gruppe.name} ({len(gruppe.faelle)})"]') + zeilen.append(f" class {gid} groupnode") + for fall in gruppe.faelle: + ti += 1 + tid = f"t{ti}" + name = fall.name[5:] if fall.name.startswith("test_") else fall.name + zeilen.append(f' {gid} --> {tid}("{name}")') + klassenzeilen.append(f" class {tid} {STATUS_KLASSE.get(fall.status, 'ok')}") + zeilen.extend(klassenzeilen) + zeilen.append(" classDef rootnode fill:#1f4038,stroke:#16302a,stroke-width:1px,color:#f4f1e8,font-weight:600;") + zeilen.append(" classDef groupnode fill:#cfe0da,stroke:#5c8a7d,stroke-width:1px,color:#152420;") + zeilen.append(" classDef ok fill:#dcefdc,stroke:#3f7d4a,stroke-width:1px,color:#1c3a20;") + zeilen.append(" classDef warn fill:#f6ead9,stroke:#b5722a,stroke-width:1px,color:#4a2c08;") + zeilen.append(" classDef skip fill:#e6e6ee,stroke:#6f7285,stroke-width:1px,color:#2b2d38;") + return "\n".join(zeilen) + + +def _html_karte_fuer_datei(datei: Testdatei) -> str: + fid = _slug(datei.anzeigepfad) + faelle = datei.faelle + gesamt = len(faelle) + zaehler = {"ok": 0, "warn": 0, "skip": 0} + for fall in faelle: + zaehler[STATUS_KLASSE.get(fall.status, "ok")] += 1 + mit_doc = sum(1 for f in faelle if f.beschreibung) + doc_pct = round(100 * mit_doc / gesamt) if gesamt else 0 + + if not datei.gruppen: + koerper = ( + f'

{html.escape(datei.beschreibung) if datei.beschreibung else "— (Docstring fehlt)"}

' + '

Keine Testfälle: Die Datei enthält keine unittest.TestCase' + "-Klasse – sie ist eine reine Hilfsdatei, die beim Testlauf nur importiert wird.

" + ) + pills = "" + else: + pills = "".join( + f'{v}' for k, v in zaehler.items() if v + ) + gchips = "".join( + f'{html.escape(g.name)} ' + f"{len(g.faelle)}" + for g in datei.gruppen + ) + zeilen = [] + kurz = {"grün": "grün", "rot (Baseline)": "rot", "übersprungen": "übersp.", "erwarteter Fehlschlag": "übersp."} + for fall in faelle: + klasse = STATUS_KLASSE.get(fall.status, "ok") + pruft = ( + f'— (Docstring fehlt)' + if not fall.beschreibung + else html.escape(fall.beschreibung) + ) + titel = f' title="{html.escape(fall.grund)}"' if fall.grund else "" + zeilen.append( + f'{html.escape(fall.klasse)}
' + f'{html.escape(fall.name)}' + f"{pruft}" + f'{kurz.get(fall.status, fall.status)}' + ) + tabelle = ( + '' + f"{''.join(zeilen)}
TestfallprüftStatus
" + ) + diagramm = ( + '
'
+            + html.escape(_mermaid_fuer_datei(datei))
+            + "
" + ) + koerper = ( + f'

{html.escape(datei.beschreibung) if datei.beschreibung else "— (Docstring fehlt)"}

' + f'
{gchips}
{diagramm}{tabelle}' + ) + + suchtext = html.escape( + (datei.anzeigepfad + " " + " ".join(f"{f.klasse} {f.name}" for f in faelle)).lower() + ) + header = ( + f'' + ) + return ( + f'
' + f"{header}" + f'
{koerper}
' + "
" + ) + + +def _html_abschnitt_fuer_paket(katalog: Paketkatalog) -> str: + k = zaehle(katalog.faelle) + rolle = ROLLENTEXT.get(katalog.rolle, katalog.rolle or "Plugin") + karten = "".join(_html_karte_fuer_datei(d) for d in katalog.dateien) or "

Keine Testdateien gefunden.

" + gesamt = k["gesamt"] or 1 + ok_pct = round(100 * k["gruen"] / gesamt) + warn_pct = round(100 * k["rot"] / gesamt) + skip_pct = max(0, 100 - ok_pct - warn_pct) + return f""" +
+
+
+

{html.escape(katalog.paket)}

+

{html.escape(rolle)} · Ordner {html.escape(katalog.ordner)} · {len(katalog.dateien)} Testdateien · {k['gesamt']} Testfälle

+
+ +
+
{karten}
+
+""" + + +_HTML_KOPF = """Prüfstand + + + + + + +""" + +_HTML_SKRIPT = """ + + +""" + + +def erzeuge_html(kataloge: list[Paketkatalog], baseline_pfad: Path, baseline_kurz: str) -> str: + """Erzeugt das HTML-Dashboard 'Prüfstand' – dieselben Daten wie erzeuge_markdown, als + interaktive Seite mit Baumdiagramm (Mermaid) je Testdatei. Läuft lokal ohne Server.""" + alle = [f for k in kataloge for f in k.faelle] + summe = zaehle(alle) + baseline_anzeige = ( + baseline_pfad.relative_to(ROOT).as_posix() if baseline_pfad.is_relative_to(ROOT) else str(baseline_pfad) + ) + meta_zeile = ( + f'Baseline: {html.escape(baseline_anzeige)} (SHA-256 {baseline_kurz})' + if baseline_kurz + else f'Baseline {html.escape(baseline_anzeige)} nicht gefunden' + ) + + nav_links = "\n ".join( + f'{html.escape(k.paket)} ({len(k.faelle)})' + for k in kataloge + ) + + bar_zeilen = [] + for k in kataloge: + z = zaehle(k.faelle) + gesamt = z["gesamt"] or 1 + ok_pct = round(100 * z["gruen"] / gesamt) + warn_pct = round(100 * z["rot"] / gesamt) + skip_pct = max(0, 100 - ok_pct - warn_pct) + bar_zeilen.append( + f'
{html.escape(k.paket)}' + f'' + f'' + f'' + f'' + f'{len(k.dateien)} Dateien · {z["gesamt"]} Testfälle
' + ) + + sections = "\n".join(_html_abschnitt_fuer_paket(k) for k in kataloge) + + teile = [_HTML_KOPF] + teile.append('

Prüfstand

') + teile.append( + 'Testkatalog der sn_*-Plugins — was die Suite heute prüft, auf einen Blick.
' + ) + teile.append(f'
Quelle: docs/tests/testkatalog.md
{meta_zeile}
') + + teile.append('
') + teile.append(f'
{summe["gesamt"]}Testfälle
') + teile.append(f'
{summe["gruen"]}grün
') + teile.append(f'
{summe["rot"]}rot (Baseline)
') + teile.append(f'') + doc_pct = round(100 * summe["mit_docstring"] / summe["gesamt"]) if summe["gesamt"] else 0 + teile.append(f'
{doc_pct}%mit Docstring
') + teile.append(f'
{sum(len(k.dateien) for k in kataloge)}Testdateien
') + teile.append("
") + + teile.append('
Verteilung je Paket
') + teile.append("\n ".join(bar_zeilen)) + teile.append("
") + + teile.append('
') + teile.append('') + teile.append('') + teile.append('') + teile.append('') + teile.append("
") + teile.append( + '
' + ) + + teile.append(sections) + + teile.append( + "" + ) + teile.append(_HTML_SKRIPT) + return "\n".join(teile) + "\n" + + def melde_unterschied(vorhanden: bytes, erzeugt: str, ziel: str) -> None: """Kurzmeldung, worin sich Datei und erzeugter Stand unterscheiden.""" alt = vorhanden.decode("utf-8", errors="replace").splitlines() @@ -450,6 +859,9 @@ def main() -> int: ap.add_argument("--pruefen", action="store_true", help="nichts schreiben; Exit-Code 1, wenn die vorhandene Datei nicht dem erzeugten Stand entspricht") ap.add_argument("--ausgabe", type=Path, default=AUSGABE_DEFAULT, help="Zieldatei (Standard: docs/tests/testkatalog.md)") + ap.add_argument("--html-ausgabe", type=Path, default=HTML_AUSGABE_DEFAULT, + help="Zieldatei für das HTML-Dashboard (Standard: docs/tests/testkatalog.html)") + ap.add_argument("--ohne-html", action="store_true", help="kein HTML-Dashboard erzeugen/prüfen") ap.add_argument("--baseline-datei", type=Path, default=BASELINE_DEFAULT) args = ap.parse_args() @@ -490,24 +902,28 @@ def main() -> int: print("Keine Pakete mit Testdateien gefunden – nichts zu tun.") return 2 - text = erzeuge_markdown(kataloge, args.baseline_datei, baseline_kurz) - ziel = args.ausgabe - anzeige = ziel.relative_to(ROOT).as_posix() if ziel.is_relative_to(ROOT) else str(ziel) + ausgaben = [(args.ausgabe, erzeuge_markdown(kataloge, args.baseline_datei, baseline_kurz))] + if not args.ohne_html: + ausgaben.append((args.html_ausgabe, erzeuge_html(kataloge, args.baseline_datei, baseline_kurz))) - if args.pruefen: - if not ziel.exists(): - print(f"{anzeige} fehlt. Neu erzeugen mit: python scripts/testkatalog.py") - return 1 - if ziel.read_bytes() == text.encode("utf-8"): - print(f"{anzeige} ist aktuell.") - return 0 - melde_unterschied(ziel.read_bytes(), text, anzeige) - return 1 - - ziel.parent.mkdir(parents=True, exist_ok=True) - ziel.write_text(text, encoding="utf-8", newline="\n") - print(f"Testkatalog geschrieben: {anzeige} ({len(text.splitlines())} Zeilen)") - return 0 + ergebnis = 0 + for ziel, text in ausgaben: + anzeige = ziel.relative_to(ROOT).as_posix() if ziel.is_relative_to(ROOT) else str(ziel) + if args.pruefen: + if not ziel.exists(): + print(f"{anzeige} fehlt. Neu erzeugen mit: python scripts/testkatalog.py") + ergebnis = 1 + continue + if ziel.read_bytes() == text.encode("utf-8"): + print(f"{anzeige} ist aktuell.") + continue + melde_unterschied(ziel.read_bytes(), text, anzeige) + ergebnis = 1 + continue + ziel.parent.mkdir(parents=True, exist_ok=True) + ziel.write_text(text, encoding="utf-8", newline="\n") + print(f"Testkatalog geschrieben: {anzeige} ({len(text.splitlines())} Zeilen)") + return ergebnis if __name__ == "__main__":