aboutsummaryrefslogtreecommitdiff
path: root/src/test/java
diff options
context:
space:
mode:
authorMatthias Andreas Benkard <code@mail.matthias.benkard.de>2026-08-27 07:10:40 +0200
committerMatthias Andreas Benkard <code@mail.matthias.benkard.de>2026-08-27 07:10:40 +0200
commit036fb101bbc92e88e2b0e99d04ed827b96afd927 (patch)
treeaa1cb8ca4515d61c587136e5e2be03e25b0a674e /src/test/java
parent17c87d1cddd6968161d28aa0365a1a1964dc6409 (diff)
Bremen öffnet seine Fassungen, und zwei Zitate bleiben offen
Bremen tritt als fünfzehntes Land hinzu. Sein Transparenzportal ist nach BRAVORS und recht.nrw.de das dritte, das einer Skriptabfrage gewöhnliches HTML liefert, und das erste außerhalb Brandenburgs, das jede frühere Gesamtausgabe einzeln adressierbar führt; Vor- und Nachfassung stammen daher aus derselben Quelle. Das Heft (Brem.GBl. 2026 Nr. 87) versäumt es zweimal, ein Zitat zu schließen. Daran zeigte sich, dass die Grenze am Aufzählungspunkt zu eng gefasst war: Sie verlangte, dass der Rest des Abschnitts ausbalanciert zurückbleibe, und konnte deshalb nur den letzten Mangel heilen. Nunmehr prüft sie, dass kein schließendes Anführungszeichen ohne öffnendes zurückbleibt — ein Abschnitt darf mehrere offene Zitate tragen, und jedes wird an seiner eigenen Grenze geschlossen. Das Heft gab vorher 22 Befehle her, nun 41. Die bremische Befehlssprache brachte sechs Nebenformen (Name der Verordnung, bloße Aufzählungsmarke als Stelle, Marke mit Schlusspunkt, „geändert“ statt „ersetzt“, „um Satz 2 ergänzt“, benanntes Satzzeichen) und fünf Anwendungsfunde: Eine unvollendet endende Neufassung lässt die Untergliederung stehen; das Zitat des ersten Satzes trägt die Absatzbezeichnung mit; ein Umbruch mitten im Satz ist Satzspiegel; ein Platzhalter hält nur im eigenen Block Platz; und eine angefügte Einheit, die ihre Nummer nennt, tritt an den genannten Platz. Dabei kam ein stiller Mangel ans Licht: „hinter dem Wort“ war in den Mustern zugelassen, wurde aber nirgends geprüft — die Wörter traten vor den Anker. Geprüft: mvnw verify (428 Testfälle) und reuse lint (204/204) gehen durch; der neue Akzeptanzfall pinnt 41 gelesene, 37 angewandte Befehle und den Gleichlauf von 26 der 29 Normen mit der amtlichen Nachfassung. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Change-Id: If83a1f3204cd4599168888f2701db71d560b7bc7
Diffstat (limited to 'src/test/java')
-rw-r--r--src/test/java/eu/mulk/aendggner/EndToEndTest.java73
1 files changed, 73 insertions, 0 deletions
diff --git a/src/test/java/eu/mulk/aendggner/EndToEndTest.java b/src/test/java/eu/mulk/aendggner/EndToEndTest.java
index bb67978..6bb82eb 100644
--- a/src/test/java/eu/mulk/aendggner/EndToEndTest.java
+++ b/src/test/java/eu/mulk/aendggner/EndToEndTest.java
@@ -1778,6 +1778,79 @@ class EndToEndTest {
.containsExactly("§ 3", "§ 4", "§ 6", "§ 9");
}
+ /**
+ * Bremen: Die Änderung der Verordnung über die Berufsfachschule für technische und kaufmännische
+ * Assistentinnen und Assistenten (Brem.GBl. 2026 Nr. 87, S. 573).
+ *
+ * <p>Der Fall ist der erste, dessen Vor- <em>und</em> Nachfassung aus demselben offenen Portal
+ * stammen (siehe {@code Bremen/SOURCES}); er bringt sechs Idiome, die das übrige Landesrecht
+ * nicht führt: die Neufassung des <em>Namens</em> der Verordnung, die bloße Aufzählungsmarke als
+ * Stellenangabe („In e) …“, „f) wird gestrichen“), die Marke mit Schlusspunkt („Nummer 1. wird
+ * wie folgt gefasst“), „durch die Worte … <em>geändert</em>“ statt „ersetzt“, die Anfügung als
+ * „am Ende um Satz 2 ergänzt“ und das <em>benannte</em> Satzzeichen („das Satzzeichen „Punkt“
+ * wird aufgehoben und folgendes angefügt“).
+ *
+ * <p>Zwei Zitate des Heftes sind nicht geschlossen (Nummern 8 a und 13). Beide Satzfehler
+ * zusammen sind der Prüfstein der Aufzählungs-Zitatgrenze: Sie muss auch dann greifen, wenn der
+ * Rest des Abschnitts seinerseits noch ein Zitat offen lässt.
+ */
+ @Test
+ void berufsfachschulVOBremen() throws Exception {
+ var alt = SAMPLEDATA.resolve("Bremen/AssBerFSchulV-BR-alt.txt");
+ var pdf = SAMPLEDATA.resolve("Bremen/BremGBl-2026-87_BerufsfachschulVO-AendVO.pdf");
+ var neu = SAMPLEDATA.resolve("Bremen/AssBerFSchulV-BR-neu.txt");
+ assumeTrue(Files.exists(alt) && Files.exists(pdf), "Bremer Beispieldaten fehlen");
+
+ var gesetz = new eu.mulk.aendggner.gesetz.land.LandesRechtLoader().load(alt);
+ assertThat(gesetz.jurabk()).isEqualTo("AssBerFSchulV BR 2019");
+ assertThat(gesetz.normen()).hasSize(28);
+
+ var text = TextBereiniger.bereinige(new PatchTextExtraktor().extrahiere(pdf));
+ // Der Kolumnentitel steht im Inhaltsstrom und klebt am Seitenumbruch hinter dem letzten Satz
+ // der Vorseite; bliebe er stehen, zerfiele die Gliederung des Heftes an ihm.
+ assertThat(text).doesNotContain("Gesetzblatt der Freien Hansestadt Bremen vom 20. August 2026");
+
+ var parseErgebnis = new AenderungsgesetzParser().parse(text, gesetz, null);
+ assertThat(parseErgebnis.artikel()).containsExactly("1");
+ assertThat(parseErgebnis.befehle()).hasSize(41);
+
+ // Beide offenen Zitate werden am folgenden Aufzählungspunkt geschlossen — das erste vor
+ // „b)“, das zweite vor „14.“. Vor dieser Welle schloss nur das letzte, und die Nummern 9 bis
+ // 13 verschwanden im Zitat der Nummer 8 a).
+ assertThat(parseErgebnis.warnungen())
+ .anyMatch(w -> w.contains("vor dem Aufzählungspunkt „b)“"))
+ .anyMatch(w -> w.contains("vor dem Aufzählungspunkt „14.“"));
+
+ var anwendung = BefehlAnwender.anwenden(gesetz, parseErgebnis.befehle());
+ assertThat(anwendung.anzahlAngewandt()).isEqualTo(37);
+
+ // Die Neufassung eines Vordersatzes lässt die Aufzählung stehen, die ihm folgt — und der
+ // gestrichene letzte Buchstabe der Nummer 1 hält keinen Platz für die Buchstaben der Nummer 2.
+ var dritter = anwendung.neu().norm("§ 3").orElseThrow().absaetze().get(1).text();
+ assertThat(dritter)
+ .startsWith("Mit Genehmigung der Senatorin oder des Senators für Kinder und Bildung")
+ .contains("Künstliche Intelligenz und Wirtschaftsinformatik")
+ .doesNotContain("(weggefallen)");
+
+ // „hinter dem Wort“ setzt die Wörter dahinter, nicht davor.
+ assertThat(anwendung.neu().norm("§ 23").orElseThrow().absaetze().get(0).text())
+ .contains("Die Senatorin oder der Senator für Kinder und Bildung kann auf Antrag");
+
+ assumeTrue(Files.exists(neu), "Bremer Nachfassung fehlt");
+ var soll = new eu.mulk.aendggner.gesetz.land.LandesRechtLoader().load(neu);
+ var abgleich = Nachfassungsabgleich.vergleiche(soll, anwendung.neu());
+ // § 26 fehlt, weil die Nummer 18 des Heftes ihr Zitat gar nicht erst öffnet: Sie verbindet
+ // eine Umnummerierung mit einer Neufassung, deren Wortlaut ohne Anführungszeichen dasteht.
+ // Geraten wird nicht.
+ assertThat(abgleich.fehlende()).containsExactly("§ 26");
+ assertThat(abgleich.ueberzaehlige()).isEmpty();
+ // § 22: „Im letzten Satz“ nennt keinen Absatz, und die Norm hat deren drei — der Befehl bleibt
+ // zu Recht mehrdeutig. § 29: die Nummer 21 verteilt den neuen Wortlaut auf zwei Unterpunkte,
+ // die je nur aus einem Zitat bestehen.
+ assertThat(abgleich.abweichungen().stream().map(a -> a.enbez()).toList())
+ .containsExactly("§ 22", "§ 29");
+ }
+
private static Aenderungsbefehl befehlZu(
AenderungsgesetzParser.ParseErgebnis ergebnis, String gliederungsPfad) {
return ergebnis.befehle().stream()