diff options
Diffstat (limited to 'src/test/java')
| -rw-r--r-- | src/test/java/eu/mulk/aendggner/EndToEndTest.java | 78 | ||||
| -rw-r--r-- | src/test/java/eu/mulk/aendggner/aenderung/parse/BefehlErkennerTest.java | 88 |
2 files changed, 166 insertions, 0 deletions
diff --git a/src/test/java/eu/mulk/aendggner/EndToEndTest.java b/src/test/java/eu/mulk/aendggner/EndToEndTest.java index 8c9743d..bb67978 100644 --- a/src/test/java/eu/mulk/aendggner/EndToEndTest.java +++ b/src/test/java/eu/mulk/aendggner/EndToEndTest.java @@ -1700,6 +1700,84 @@ class EndToEndTest { .containsOnlyOnce("Fortgeschrieben durch: Änderungsgesetz vom 12. Februar 2026"); } + /** + * Hamburg: Die Verordnung zur Änderung der Verordnung über den Gutachterausschuss für + * Grundstückswerte (HmbGVBl. Nr. 17/2026, S. 147). + * + * <p>Der Fall bringt eine Gliederung, die im übrigen Landesrecht nicht vorkommt: Das + * Änderungsdokument selbst teilt sich in <em>Paragraphen</em> (§ 1 Änderung, § 2 Inkrafttreten), + * obgleich dasselbe Heft davor eine Verkündung führt, die sich in Artikel teilt; und seine + * Befehle sind <em>dezimal</em> gegliedert (6.1, 7.1.1, 7.2.3) statt a)/aa)/aaa). Dazu vier + * Idiome — „die Textstelle“, „Hinter § 7 wird … eingefügt“, „Es wird folgender Absatz angefügt“, + * „§§ 6 und 7 erhalten folgende Fassung“ — und ein schmales Leerzeichen zwischen + * Paragraphenzeichen und Nummer. + * + * <p>Vier Normen weichen von der amtlichen Nachfassung ab; drei davon liegen an der Vorlage und + * nicht am Werkzeug (siehe {@code Hamburg/SOURCES}). + */ + @Test + void gutachterausschussVOHamburg() throws Exception { + var alt = SAMPLEDATA.resolve("Hamburg/GAusschV-HH-alt.txt"); + var pdf = SAMPLEDATA.resolve("Hamburg/HmbGVBl-2026-17_GutachterausschussVO-AendVO.pdf"); + var neu = SAMPLEDATA.resolve("Hamburg/GAusschV-HH-neu.txt"); + assumeTrue(Files.exists(alt) && Files.exists(pdf), "Hamburger Beispieldaten fehlen"); + + var gesetz = new eu.mulk.aendggner.gesetz.land.LandesRechtLoader().load(alt); + assertThat(gesetz.jurabk()).isEqualTo("GAusschV HA 2009"); + assertThat(gesetz.normen()).hasSize(13); + + var text = TextBereiniger.bereinige(new PatchTextExtraktor().extrahiere(pdf)); + // Das schmale Leerzeichen (U+2009) zwischen § und Nummer ist normalisiert; ohne das wäre + // „§ 1“ kein Kopf, und das ganze Heft bliebe ungelesen. + assertThat(text).contains("\n§ 1\n").doesNotContain("§\u2009"); + // Der Kolumnentitel steht im Inhaltsstrom und fiele sonst hinter das Zitat eines Befehls. + assertThat(text).doesNotContain("Dienstag, den 26. Mai 2026 149"); + + var parseErgebnis = new AenderungsgesetzParser().parse(text, gesetz, null); + // Der Artikel-Modus findet nichts, das die Verordnung beträfe; erst die §-Teilung tut es. + assertThat(parseErgebnis.artikel()).containsExactly("1"); + assertThat(parseErgebnis.befehle()).hasSize(21); + + // Die Dezimalgliederung trägt ihre Herkunft im Label; der Pfad wiederholt sie nicht. + assertThat(befehlAn(parseErgebnis, "7.1.4")) + .isInstanceOf(Aenderungsbefehl.Umnummerierung.class); + assertThat(befehlAn(parseErgebnis, "8.1")).isInstanceOf(Aenderungsbefehl.Neufassung.class); + + // Zwei Befehle bleiben unerkannt, und zwar zu Recht: Die Nummern 2 und 3 des Heftes schreiben + // „wir die Textstelle … ersetzt“ statt „wird“. Das ist ein Verkündungsfehler, und geraten wird + // nicht. + var unerkannt = + parseErgebnis.befehle().stream() + .filter(b -> b instanceof UnbekannterBefehl) + .map(b -> b.provenienz().gliederungsPfad()) + .toList(); + assertThat(unerkannt).containsExactly("2.", "3."); + + var anwendung = BefehlAnwender.anwenden(gesetz, parseErgebnis.befehle()); + assertThat(anwendung.anzahlAngewandt()).isEqualTo(17); + + // Der angefügte Schlusssatz steht hinter der Aufzählung auf eigener Zeile und nicht am Ende + // des letzten Aufzählungsgliedes. + var neunter = anwendung.neu().norm("§ 9").orElseThrow().absaetze().get(0).text(); + assertThat(neunter) + .contains("\nDie DIN EN ISO/IEC 17024 ist zur kostenfreien Einsicht") + // Das Satzzeichen, das an die Stelle eines Wortes tritt, nimmt dessen Zwischenraum mit. + .contains("zur Erfüllung ihrer Aufgaben,\n") + // Die bisherige Nummer 3 ist erhalten: Die Umnummerierung geht der Neufassung vor, die + // ihre Bezeichnung neu vergäbe — sonst ginge jener Wortlaut verloren. + .contains("4. weitere Personen und Stellen"); + + assumeTrue(Files.exists(neu), "Hamburger Nachfassung fehlt"); + var soll = new eu.mulk.aendggner.gesetz.land.LandesRechtLoader().load(neu); + var abgleich = Nachfassungsabgleich.vergleiche(soll, anwendung.neu()); + assertThat(abgleich.fehlende()).isEmpty(); + assertThat(abgleich.ueberzaehlige()).isEmpty(); + // § 3 und § 4: der Verkündungsfehler „wir“. § 6: das Gesetzblatt setzt „1. in Präsenz“ ohne + // Komma, das Portal mit. § 9: die Neufassung der soeben geräumten Nummer 3. + assertThat(abgleich.abweichungen().stream().map(a -> a.enbez()).toList()) + .containsExactly("§ 3", "§ 4", "§ 6", "§ 9"); + } + private static Aenderungsbefehl befehlZu( AenderungsgesetzParser.ParseErgebnis ergebnis, String gliederungsPfad) { return ergebnis.befehle().stream() diff --git a/src/test/java/eu/mulk/aendggner/aenderung/parse/BefehlErkennerTest.java b/src/test/java/eu/mulk/aendggner/aenderung/parse/BefehlErkennerTest.java index 327c8db..4c55154 100644 --- a/src/test/java/eu/mulk/aendggner/aenderung/parse/BefehlErkennerTest.java +++ b/src/test/java/eu/mulk/aendggner/aenderung/parse/BefehlErkennerTest.java @@ -1544,4 +1544,92 @@ class BefehlErkennerTest { Stelle.LEER); assertThat(befehl).containsInstanceOf(Ersetzung.class); } + + // --- Hamburger Idiome (HmbGVBl. Nr. 17/2026) -------------------------------------------------- + + /** „Hinter“ steht dem „nach“ gleich; das hamburgische Blatt setzt es durchweg. */ + @Test + void erkenntEinfuegungHinterEinerNorm() { + var befehl = erkenne("Hinter § 7 wird folgender § 7a eingefügt: „§ 7a Aufgaben“", Stelle.LEER); + + assertThat(befehl).containsInstanceOf(StrukturEinfuegung.class); + var einfuegung = (StrukturEinfuegung) befehl.orElseThrow(); + assertThat(einfuegung.vorher()).isFalse(); + assertThat(einfuegung.stelle().anzeigeText()).isEqualTo("§ 7"); + assertThat(einfuegung.bezeichnung()).isEqualTo("7a"); + } + + @Test + void erkenntEinfuegungHinterEinemAbsatz() { + var befehl = + erkenne( + "Hinter Absatz 3 wird folgender neuer Absatz 4 eingefügt: „(4) Nicht" + + " grundstücksbezogene Auskünfte sind zu erteilen.“", + PARAGRAPH_5); + + assertThat(befehl).containsInstanceOf(StrukturEinfuegung.class); + var einfuegung = (StrukturEinfuegung) befehl.orElseThrow(); + assertThat(einfuegung.vorher()).isFalse(); + assertThat(einfuegung.bezeichnung()).isEqualTo("4"); + } + + /** Die Anfügung ohne Subjekt: Das Ziel steht im Rahmen, der Satz nennt nur das Angefügte. */ + @Test + void erkenntAnfuegungOhneSubjekt() { + var befehl = + erkenne( + "Es wird folgender Absatz 3 angefügt: „(3) Personenbezogene Daten sind zu löschen.“", + PARAGRAPH_5); + + assertThat(befehl).isPresent(); + assertThat(befehl.orElseThrow().stelle().anzeigeText()).isEqualTo("§ 5"); + } + + @Test + void erkenntSatzAnfuegungOhneSubjekt() { + var befehl = + erkenne("Es wird folgender Satz angefügt: „Die Norm ist niedergelegt.“", PARAGRAPH_5); + + assertThat(befehl).isPresent(); + } + + /** „Textstelle“ ist die hamburgische Nebenform zu „Wörter“ und „Angabe“. */ + @Test + void erkenntTextstellenErsetzung() { + var befehl = + erkenne( + "In § 3 Absatz 2 Nummer 1 wird die Textstelle „Absatz 5“ durch die Textstelle" + + " „Absatz 8“ ersetzt.", + Stelle.LEER); + + assertThat(befehl).containsInstanceOf(Ersetzung.class); + var ersetzung = (Ersetzung) befehl.orElseThrow(); + assertThat(ersetzung.alt()).isEqualTo("Absatz 5"); + assertThat(ersetzung.neu()).isEqualTo("Absatz 8"); + } + + @Test + void erkenntEinfuegungHinterEinerTextstelle() { + var befehl = + erkenne( + "In Satz 3 wird hinter der Textstelle „Sachverständige nach Absatz 1 Nummer 2“ die" + + " Textstelle „und 3“ eingefügt.", + PARAGRAPH_5); + + assertThat(befehl).containsInstanceOf(WoerterEinfuegung.class); + var einfuegung = (WoerterEinfuegung) befehl.orElseThrow(); + assertThat(einfuegung.woerter()).isEqualTo("und 3"); + } + + /** „§§ 6 und 7 erhalten folgende Fassung“ — die Neufassung einer Paragraphenfolge. */ + @Test + void erkenntNeufassungMehrererParagraphen() { + var befehl = + erkenne( + "§§ 6 und 7 erhalten folgende Fassung: „§ 6 Verfahren (1) Beraten wird gemeinsam." + + " § 7 Aufgaben Das vorsitzende Mitglied ist verantwortlich.“", + Stelle.LEER); + + assertThat(befehl).isPresent(); + } } |
