aboutsummaryrefslogtreecommitdiff
path: root/src/test/java
diff options
context:
space:
mode:
authorMatthias Andreas Benkard <code@mail.matthias.benkard.de>2026-08-28 08:36:29 +0200
committerMatthias Andreas Benkard <code@mail.matthias.benkard.de>2026-08-28 08:36:29 +0200
commitd51e3dad33edb14c048372c035b635313d7a1dc9 (patch)
treeed8cb2cf39e141f04ba05d99f5b913fa6ac93409 /src/test/java
parent0be6e41ca5ae1c16aab308931616f1ec84ea3eba (diff)
Die Anlage gliedert sich nach eigener Wahl
Der Korpuslauf sollte klären, woran die Anlagen-Reste liegen, und hat die Frage anders beantwortet als erwartet: Unter den 116 liegengebliebenen Befehlen des Korpus ist kein einziger ein Tabellenproblem. Die Annahme, die vier mecklenburgischen Reste scheiterten an fehlender Tabellenunterstützung, war unrichtig. Beschafft man das Ursprungsheft (GVOBl. M-V 2016 Nr. 12, über das Internet-Archiv, weil der Wirt einer Skriptabfrage nicht antwortet), so zeigt sich: Der Ausbildungsrahmenplan ist keine Tabelle, sondern gegliederter Fließtext — je Abschnitt eine Kopfzeile, die Ausbildungsstelle, die Dauer und eine Spiegelstrichliste. Er fehlte in der Stammfassung, das war alles. Eine Anlage gliedert sich nach eigener Wahl. Neben den Nummern eines Zuständigkeitskatalogs kennt das Erzeugnis deshalb fortan die benannte Einheit und führt sie wie jene als eigene Norm, deren Bezeichnung die Anlage voranstellt: „Anlage 1 Ausbildungsabschnitt 1“. Erkannt wird sie an ihrem Bau und nicht an einer Wortliste — verlangt sind Buchstaben vor dem Wortstamm —, denn jede Anlage darf ihre Einheiten nennen, wie sie will, und eine Liste wäre am nächsten Land wieder unvollständig. Leser und Ausgeber tragen sie (der Rundlauf geht auf), der Stellenparser nimmt sie als Stellenangabe, der Auflöser setzt sie mit der Anlage zur Normbezeichnung zusammen, und der Anwender benennt sie um. Dabei ist ein stiller Fehler zutage getreten: Die Umnummerierung lief für alles, was weder Paragraph noch Absatz noch Aufzählungsmarke noch Satz war, in den Auffangzweig „keine Textänderung nötig“ und meldete Erfolg, ohne etwas zu tun. Erst der Belegfall hat das gezeigt. Von den vier mecklenburgischen Resten sind zwei erledigt (22 statt 20 von 24 Befehlen). Die beiden übrigen haben benannte Gründe: Der eine fügt einen ganzen Ausbildungsabschnitt ein — eine neue Norm innerhalb einer Anlage setzt das Erzeugnis nicht —, der andere verweist auf einen Anhang des ändernden Gesetzes, der nicht vorliegt. Die Anlage 2 (Vordruck des Befähigungsberichts) bleibt aus demselben Grund fort; sie ist der einzige echte Tabellensatz des ganzen Korpus. Die Grundlinie hat sich sogleich bewährt: Die Erweiterung kostete einen Befehl, der zuvor angewandt wurde — „Die Überschrift zu Unterabschnitt 4 wird gestrichen“ des GEG-Entwurfs fiel aus dem Gliederungszweig, weil auch „Unterabschnitt“ auf den Wortstamm endet. Der Korpuslauf rügte das im selben Durchgang („angewandte Befehle 86 statt 87“), und zwar in einem anderen Land und einem anderen Dokument, wo keine Einzelprüfung es bemerkt hätte. Die Grenze ist gezogen und durch zwei Prüffälle gepinnt. Geprüft: mvnw verify (445 Testfälle) und reuse lint (214/214) gehen durch. Der Akzeptanzfall pinnt 35 statt 29 Normen und 22 statt 20 angewandte Befehle; die Grundlinie des Korpuslaufs ist auf 1289 statt 1287 angewandter Befehle fortgeschrieben. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Change-Id: I3e6046c3a91ecc45406058b367ec5a9a19c84967
Diffstat (limited to 'src/test/java')
-rw-r--r--src/test/java/eu/mulk/aendggner/EndToEndTest.java34
-rw-r--r--src/test/java/eu/mulk/aendggner/aenderung/parse/StellenParserTest.java20
2 files changed, 47 insertions, 7 deletions
diff --git a/src/test/java/eu/mulk/aendggner/EndToEndTest.java b/src/test/java/eu/mulk/aendggner/EndToEndTest.java
index 34bc8e6..ea014b0 100644
--- a/src/test/java/eu/mulk/aendggner/EndToEndTest.java
+++ b/src/test/java/eu/mulk/aendggner/EndToEndTest.java
@@ -1931,8 +1931,12 @@ class EndToEndTest {
var gesetz = new eu.mulk.aendggner.gesetz.land.LandesRechtLoader().load(alt);
assertThat(gesetz.jurabk()).isEqualTo("APOAmtsTA M-V");
- // Die Inhaltsübersicht ist eine Norm gleichen Namens; dazu die 28 Paragraphen.
- assertThat(gesetz.normen()).hasSize(29);
+ // Die Inhaltsübersicht ist eine Norm gleichen Namens; dazu die 28 Paragraphen, die Anlage 1
+ // (Ausbildungsrahmenplan) und deren fünf Ausbildungsabschnitte, die das Portal wie die Nummern
+ // eines Zuständigkeitskatalogs als eigene Einheiten führt.
+ assertThat(gesetz.normen()).hasSize(35);
+ assertThat(gesetz.norm("Anlage 1 Ausbildungsabschnitt 1")).isPresent();
+ assertThat(gesetz.norm("Anlage 1 Ausbildungsabschnitt 5")).isPresent();
var text = TextBereiniger.bereinige(new PatchTextExtraktor().extrahiere(pdf));
// Kolumnentitel und Seitenfuß stehen im Inhaltsstrom, und zwar mitten im Befehlstext.
@@ -1945,7 +1949,7 @@ class EndToEndTest {
assertThat(parseErgebnis.befehle()).hasSize(24);
var anwendung = BefehlAnwender.anwenden(gesetz, parseErgebnis.befehle());
- assertThat(anwendung.anzahlAngewandt()).isEqualTo(20);
+ assertThat(anwendung.anzahlAngewandt()).isEqualTo(22);
// Die Nummern 2 und 3 entfallen, die Nummern 4 bis 6 rücken auf 2 bis 4 — ohne Platzhalter,
// denn sie stehen am Ende ihres Blockes nicht.
@@ -1965,15 +1969,31 @@ class EndToEndTest {
assertThat(anwendung.neu().norm("Inhaltsübersicht").orElseThrow().absaetze().get(0).text())
.contains("§ 10 | Ausbildungsnachweise");
- // Übrig bleiben allein die vier Befehle, die die Anlagen betreffen: Die Stammfassung führt sie
- // nicht mit (Tabellen und Vordrucke), und der elfte Befehl verweist auf einen Anhang des
- // Änderungsgesetzes.
+ // Die benannte Einheit der Anlage trägt die Wortersetzung …
+ assertThat(
+ anwendung
+ .neu()
+ .norm("Anlage 1 Ausbildungsabschnitt 1")
+ .orElseThrow()
+ .absaetze()
+ .get(0)
+ .text())
+ .startsWith("Das für das Veterinärwesen zuständige Ministerium")
+ .doesNotContain("Ministerium für Landwirtschaft, Umwelt und Verbraucherschutz");
+ // … und die Umnummerierung trifft ihre Bezeichnung, nicht ihren Wortlaut.
+ assertThat(anwendung.neu().norm("Anlage 1 Ausbildungsabschnitt 6")).isPresent();
+ assertThat(anwendung.neu().norm("Anlage 1 Ausbildungsabschnitt 5")).isEmpty();
+
+ // Übrig bleiben zwei Befehle, beide mit benanntem Grund: Nummer 10 Buchstabe b fügt einen
+ // ganzen Ausbildungsabschnitt ein — eine neue Norm innerhalb einer Anlage, die das Erzeugnis
+ // nicht setzt —, und Nummer 11 verweist auf einen Anhang des ändernden Gesetzes, der nicht
+ // vorliegt. Was nicht vorliegt, lässt sich nicht anwenden.
var offen =
anwendung.protokoll().stream()
.filter(a -> a.status() == BefehlAnwender.Status.MANUELL_PRUEFEN)
.map(a -> a.befehl().provenienz().gliederungsPfad())
.toList();
- assertThat(offen).containsExactly("10. a)", "10. b)", "10. c)", "11.");
+ assertThat(offen).containsExactly("10. b)", "11.");
}
private static Aenderungsbefehl befehlZu(
diff --git a/src/test/java/eu/mulk/aendggner/aenderung/parse/StellenParserTest.java b/src/test/java/eu/mulk/aendggner/aenderung/parse/StellenParserTest.java
index 13e4442..6c34b8c 100644
--- a/src/test/java/eu/mulk/aendggner/aenderung/parse/StellenParserTest.java
+++ b/src/test/java/eu/mulk/aendggner/aenderung/parse/StellenParserTest.java
@@ -152,4 +152,24 @@ class StellenParserTest {
// Ein Wort, das bloß so anfängt, ist keiner.
assertThat(StellenParser.istNurChapeau("In der Einleitungsformel")).isFalse();
}
+
+ @Test
+ void benannteEinheitEinerAnlage() {
+ // „Im Ausbildungsabschnitt 1 …“ — der Ausbildungsrahmenplan gliedert sich in benannte
+ // Einheiten, die das Portal wie eigene Normen führt.
+ var stelle = StellenParser.parse("Ausbildungsabschnitt 1").orElseThrow();
+ assertThat(stelle.anzeigeText()).isEqualTo("Ausbildungsabschnitt 1");
+ assertThat(stelle.anlagenEinheit()).isPresent();
+ assertThat(stelle.betrifftEchteGliederung()).isFalse();
+ }
+
+ @Test
+ void unterabschnittBleibtGliederungDesGesetzes() {
+ // Er endet auf denselben Wortstamm, gliedert aber das Gesetz und ist keine Einheit einer
+ // Anlage. Ohne diese Grenze fiele „Die Überschrift zu Unterabschnitt 4 wird gestrichen“ aus
+ // dem Gliederungszweig heraus.
+ var stelle = StellenParser.parse("Unterabschnitt 4").orElseThrow();
+ assertThat(stelle.anlagenEinheit()).isEmpty();
+ assertThat(stelle.betrifftEchteGliederung()).isTrue();
+ }
}