diff options
| author | Matthias Andreas Benkard <code@mail.matthias.benkard.de> | 2026-07-25 10:00:36 +0200 |
|---|---|---|
| committer | Matthias Andreas Benkard <code@mail.matthias.benkard.de> | 2026-07-25 10:00:36 +0200 |
| commit | 35e90f4ab0f9b40d4752c9b8cba57c9452a8189f (patch) | |
| tree | 271b8ce1f0537bc8546e3939d99df6e47d79369b /src/test/java | |
| parent | 519c1e7b769c37769d1b5d1ffd6e13566b26c86d (diff) | |
Add North Rhine-Westphalia TMZ-Gesetz acceptance case
Article 3 of the 22. Rundfunkänderungsgesetz (GV. NRW. 2026 S. 202) amends
the Telemedienzuständigkeitsgesetz. All four commands apply cleanly, and
the result matches the official consolidated version of 1 April 2026.
recht.nrw.de turned out to be the one remaining state portal that serves
consolidated laws as server-rendered HTML, with the version history
encoded in the URL date prefix — so the pre-amendment stem (27 Apr 2022)
was directly obtainable.
Two fixes were needed:
* LandesRechtTextParser read the parenthesised title suffix wholly as
the abbreviation. State laws routinely carry both short title and
abbreviation there ("(Telemedienzuständigkeitsgesetz – TMZ-Gesetz)"),
and the amendment act cites the short title, so article selection
never matched. The parser now splits at the dash.
* ZitatExtraktor swallowed the rest of the document when a closing
quotation mark is missing in the official text (Article 2 no. 11
here), which hid Articles 3 to 5 entirely. An amendment act never
quotes across an article heading, so an open quotation is now closed
before a bare "Artikel N" line, with a warning. Quoted paragraphs are
unaffected: Bavarian norm heads read "Art. N".
223 tests green; all previously pinned figures unchanged.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Change-Id: I78470075d9be2433966b821b6fe1e21808996c46
Diffstat (limited to 'src/test/java')
3 files changed, 129 insertions, 0 deletions
diff --git a/src/test/java/eu/mulk/aendggner/EndToEndTest.java b/src/test/java/eu/mulk/aendggner/EndToEndTest.java index d36341d..b89a367 100644 --- a/src/test/java/eu/mulk/aendggner/EndToEndTest.java +++ b/src/test/java/eu/mulk/aendggner/EndToEndTest.java @@ -482,4 +482,68 @@ class EndToEndTest { assertThat(kro.befehle()).hasSize(1); assertThat(kro.befehle().get(0).stelle().anzeigeText()).isEqualTo("§ 29a Absatz 1"); } + + /** + * Nordrhein-Westfalen: Artikel 3 des 22. Rundfunkänderungsgesetzes (GV. NRW. 2026 S. 202) ändert + * das Telemedienzuständigkeitsgesetz. Voller Akzeptanztest — alle vier Befehle werden angewandt; + * das Ergebnis stimmt mit der amtlichen Fassung vom 1. April 2026 (recht.nrw.de) überein. + */ + @Test + void tmzGesetzAcceptance() throws Exception { + var alt = SAMPLEDATA.resolve("NRW/TMZ-Gesetz-alt.txt"); + var pdf = SAMPLEDATA.resolve("NRW/GV-NRW-2026-S202_22-Rundfunkaenderungsgesetz.pdf"); + assumeTrue(Files.exists(alt) && Files.exists(pdf), "TMZ-Gesetz-Beispieldaten fehlen"); + + var gesetz = new eu.mulk.aendggner.gesetz.land.LandesRechtLoader().load(alt); + // Der Klammerzusatz führt Kurztitel und Abkürzung nebeneinander; das Änderungsgesetz zitiert + // den Kurztitel („Das Telemedienzuständigkeitsgesetz vom 29. März 2007 …“). + assertThat(gesetz.jurabk()).isEqualTo("TMZ-Gesetz"); + assertThat(gesetz.kurzue()).isEqualTo("Telemedienzuständigkeitsgesetz"); + assertThat(gesetz.normen()).hasSize(3); + assertThat(gesetz.norm("§ 1").orElseThrow().absaetze()).hasSize(3); + + var text = TextBereiniger.bereinige(new PatchTextExtraktor().extrahiere(pdf)); + var parseErgebnis = new AenderungsgesetzParser().parse(text, gesetz, null); + // Das Heft ändert vier Gesetze; nur Artikel 3 trifft das TMZ-Gesetz. + assertThat(parseErgebnis.artikel()).containsExactly("3"); + assertThat(parseErgebnis.befehle()).hasSize(4); + assertThat(parseErgebnis.befehle()).noneMatch(b -> b instanceof UnbekannterBefehl); + + // Im amtlichen Satz fehlt bei Artikel 2 Nr. 11 das schließende Anführungszeichen. Ohne die + // Artikel-Grenze im ZitatExtraktor verschlänge dieses Zitat die Artikel 3 bis 5. + assertThat(parseErgebnis.warnungen()) + .anyMatch(w -> w.startsWith("Zitat vor einer Artikel-Überschrift nicht geschlossen")); + + var anwendung = BefehlAnwender.anwenden(gesetz, parseErgebnis.befehle()); + assertThat(anwendung.anzahlAngewandt()).isEqualTo(4); + assertThat(anwendung.protokoll()) + .noneMatch(a -> a.status() == BefehlAnwender.Status.MANUELL_PRUEFEN); + + // Abgleich mit der amtlichen Fassung vom 01.04.2026: + var neu = anwendung.neu(); + // 1. Die Gesetzesüberschrift ist neu gefasst. + assertThat(neu.langue()) + .startsWith("Gesetz zur Regelung der Zuständigkeit für die Überwachung von Telemedien im") + .contains("Anwendungsbereich des Digitale-Dienste-Gesetzes"); + // 2. § 1 Absatz 2 nennt nun die Verordnung (EU) 2016/679 statt der Datenschutz-Grundverordnung + // und das Telekommunikation-Digitale-Dienste-Datenschutz-Gesetz. + var absatz2 = neu.norm("§ 1").orElseThrow().absaetze().get(1).text(); + assertThat(absatz2) + .contains("Verordnung (EU) 2016/679") + .contains("Telekommunikation-Digitale-Dienste-Datenschutz-Gesetzes vom 23. Juni 2021 (BGBl." + + " I S. 1982; 2022 I S. 1045)") + .doesNotContain("WDR-Rundfunkdatenschutzbeauftragte"); + // 3./4. § 2: Fundstellen-Ersetzung im Satzteil vor Nummer 1 und Neufassung der Nummern 1 und 2. + var paragraph2 = neu.norm("§ 2").orElseThrow().gesamtText(); + assertThat(paragraph2) + .contains("Artikel 4 des Gesetzes vom 17. Juli 2025 (BGBl. 2025 I Nr. 163)") + .contains("§ 33 Absatz 1 und 2 Nummer 1 und 2 des Digitale-Dienste-Gesetzes") + .doesNotContain("§ 11 Absatz 1 und 2 des Telemediengesetzes"); + // § 3 bleibt unberührt. + assertThat(neu.norm("§ 3").orElseThrow().gesamtText()) + .isEqualTo(gesetz.norm("§ 3").orElseThrow().gesamtText()); + + var synopse = SynopseBuilder.baue(gesetz, anwendung, parseErgebnis.warnungen(), false); + assertThat(HtmlRenderer.rendere(synopse, "E2E-Test TMZ-Gesetz")).contains("§ 2"); + } } diff --git a/src/test/java/eu/mulk/aendggner/aenderung/parse/ZitatExtraktorTest.java b/src/test/java/eu/mulk/aendggner/aenderung/parse/ZitatExtraktorTest.java index 8194f6b..618d078 100644 --- a/src/test/java/eu/mulk/aendggner/aenderung/parse/ZitatExtraktorTest.java +++ b/src/test/java/eu/mulk/aendggner/aenderung/parse/ZitatExtraktorTest.java @@ -61,6 +61,39 @@ class ZitatExtraktorTest { } @Test + void schliesstOffenesZitatVorArtikelUeberschrift() { + // Fehlt im amtlichen Satz das schließende Anführungszeichen (GV. NRW. 2026 S. 202 Artikel 2 + // Nr. 11), verschlänge das Zitat sonst alle folgenden Artikel und der Parser fände keinen + // einzigen Änderungsbefehl mehr. + var ergebnis = + ZitatExtraktor.extrahiere( + "12. Dem § 128 wird folgender Absatz angefügt:\n" + + "„(3) § 14 gilt nicht.\n" + + "Artikel 3\n" + + "§ 1 wird wie folgt gefasst: „(1) Neu.“"); + + assertThat(ergebnis.text()) + .isEqualTo( + "12. Dem § 128 wird folgender Absatz angefügt:\n«0»\nArtikel 3\n" + + "§ 1 wird wie folgt gefasst: «1»"); + assertThat(ergebnis.zitat(0)).isEqualTo("(3) § 14 gilt nicht."); + assertThat(ergebnis.zitat(1)).isEqualTo("(1) Neu."); + assertThat(ergebnis.warnungen()).hasSize(1); + assertThat(ergebnis.warnungen().get(0)).startsWith("Zitat vor einer Artikel-Überschrift"); + } + + @Test + void zitiertUeberParagraphenUeberschriftenHinweg() { + // Nur „Artikel N“ ist Strukturgrenze: Ein zitierter Paragraph („§ 19“ + Überschrift auf der + // Folgezeile) gehört vollständig ins Zitat. + var ergebnis = ZitatExtraktor.extrahiere("Es wird gefasst: „§ 19\nAußerkrafttreten\nText.“"); + + assertThat(ergebnis.text()).isEqualTo("Es wird gefasst: «0»"); + assertThat(ergebnis.zitat(0)).isEqualTo("§ 19\nAußerkrafttreten\nText."); + assertThat(ergebnis.warnungen()).isEmpty(); + } + + @Test void stelltZitateWiederHer() { var original = "Das Wort „alt“ wird durch das Wort „neu“ ersetzt."; var ergebnis = ZitatExtraktor.extrahiere(original); diff --git a/src/test/java/eu/mulk/aendggner/gesetz/land/LandesRechtTextParserTest.java b/src/test/java/eu/mulk/aendggner/gesetz/land/LandesRechtTextParserTest.java index 3354255..b170c5f 100644 --- a/src/test/java/eu/mulk/aendggner/gesetz/land/LandesRechtTextParserTest.java +++ b/src/test/java/eu/mulk/aendggner/gesetz/land/LandesRechtTextParserTest.java @@ -161,4 +161,36 @@ class LandesRechtTextParserTest { assertThat(gesetz.norm("§ 3").orElseThrow().weggefallen()).isTrue(); assertThat(gesetz.gliederungen()).extracting(Gliederung::anzeigeText).containsExactly("I. Teil — Grundlagen der Gemeindeverfassung"); } + + @Test + void trenntKurztitelUndAbkuerzungImKlammerzusatz() { + // Landesgesetze führen im Klammerzusatz oft beides („Telemedienzuständigkeitsgesetz – + // TMZ-Gesetz“); das Änderungsgesetz zitiert den Kurztitel, nicht die Abkürzung. + var gesetz = + LandesRechtTextParser.parse( + """ + Gesetz zur Regelung der Zuständigkeit für die Überwachung von Telemedien + (Telemedienzuständigkeitsgesetz – TMZ-Gesetz) + Vom 29. März 2007 + § 1 Aufsicht bei Telemedien + (1) Die Landesanstalt für Medien ist zuständig. + """); + + assertThat(gesetz.jurabk()).isEqualTo("TMZ-Gesetz"); + assertThat(gesetz.kurzue()).isEqualTo("Telemedienzuständigkeitsgesetz"); + + // Ohne Trenner bleibt der Klammerzusatz die Abkürzung; ein nachgestellter Gedankenstrich + // („Gemeindeordnung – GO –“) gehört nicht zur Abkürzung. + var ohneKurztitel = + LandesRechtTextParser.parse( + "Gemeindeordnung für Schleswig-Holstein\n(GO)\n§ 1 Wesen\n(1) Text.\n"); + assertThat(ohneKurztitel.jurabk()).isEqualTo("GO"); + assertThat(ohneKurztitel.kurzue()).isNull(); + + var mitGedankenstrich = + LandesRechtTextParser.parse( + "Gemeindeordnung für Schleswig-Holstein\n(Gemeindeordnung – GO –)\n§ 1 Wesen\n(1) Text.\n"); + assertThat(mitGedankenstrich.jurabk()).isEqualTo("GO"); + assertThat(mitGedankenstrich.kurzue()).isEqualTo("Gemeindeordnung"); + } } |
