aboutsummaryrefslogtreecommitdiff
path: root/src/test
diff options
context:
space:
mode:
authorMatthias Andreas Benkard <code@mail.matthias.benkard.de>2026-08-22 19:02:46 +0200
committerMatthias Andreas Benkard <code@mail.matthias.benkard.de>2026-08-22 19:02:46 +0200
commit08c2747e37a421232c728d038118dfcde54376dc (patch)
tree5f3145e9f66034356a5f07bc80050f11caf8f0e6 /src/test
parent14ec52a1070285a12a6bc5a0f4859996d468b59e (diff)
Das Gesetzblatt bestimmt, was ein Anführungszeichen bedeutet
Von den im Juli beschafften Gesetzblättern der Länder war eines nie angefasst worden: das Gesetz- und Verordnungsblatt für den Freistaat Thüringen. Es hat sich als lohnend erwiesen, denn seine Eigenheiten sind keine thüringischen. Das Blatt führt kein einziges öffnendes Anführungszeichen, sondern setzt das gerade beidseitig. Bislang galt ein gerades Zeichen ausnahmslos als schließendes — für das Bundesgesetzblatt und die Drucksachen trifft das zu, dort öffnet stets das deutsche Zeichen, und ein gerades ist ein Satz- oder Extraktionsfehler. In einem so gesetzten Blatt aber fand der Zitatextraktor kein einziges Zitat, und sämtliche Befehle des Heftes gingen verloren. Fortan entscheidet der Text als ganzer: Führt er kein deutsches öffnendes Zeichen, so werden die geraden paarweise gelesen, das erste öffnend, das zweite schließend. Bei ungerader Anzahl ist er nachweislich unbalanciert; dann bleibt es bei der schließenden Lesart, und es ergeht eine Warnung. Eine Regel nach der Stellung des Zeichens wäre untauglich gewesen: Das Blatt führt Zitate, die mit einem Leerzeichen beginnen, und schließende Zeichen, die am Folgewort kleben. Der Befehl „Die bisherigen §§ 9 bis 12 werden die §§ 8 bis 10“ nennt vier Ausgangs- und drei Zielbezeichnungen. Das ist kein Widerspruch: Der Bereich nennt Bezeichnungen, gezählt werden aber Einheiten, und der aufgehobene Platzhalter § 10 ist keine Einheit, denn er trägt keinen Inhalt. Welche Einheiten ein Bereich trägt, weiß erst das Gesetz. Ein solcher Befehl bleibt deshalb bis zur Anwendung ungeteilt und wird dort entfaltet; geht die Zählung auch dann nicht auf, bleibt er manuell zu prüfen, und der Grund wird genannt. Die Neufassung eines Paragraphenbereichs wurde bisher an jedem Paragraphenzeichen des Zitats geteilt. Der neue Wortlaut zitiert jedoch reihenweise andere Vorschriften; an diesen zu schneiden erfand Normen, die es nicht gibt — aus drei neu gefassten Paragraphen wurden zehn. Geteilt wird nunmehr allein an den Bezeichnungen des angekündigten Bereichs. Hinzu treten die Verweisung als Gegenstand einer Wortoperation, der Rahmen, der sich auf eine Untereinheit der umnummerierten Einheit verengt, die mehrzeilige Überschrift einer zitierten Neufassung und die Erkenntnis, dass über einen Trennstrich vor einem Großbuchstaben der Wortbestand des Dokuments entscheidet: Trägt es das Wort auch ohne Strich, so war es eine Trennung. Die Stammfassung ist aus vier Gesetzblättern zusammengesetzt. Thüringens Landesrechtportal ist dieselbe anmeldepflichtige Anwendung wie die Portale Schleswig-Holsteins, Berlins, Baden-Württembergs und Hessens, und das Archiv der Staatskanzlei antwortet Skripten mit einer Fehlerseite. Frei zugänglich ist dagegen die Parlamentsdatenbank des Landtags, der das Blatt herausgibt; über sie liegen Stammheft und Änderungshefte vor. Jeder Änderungsbefehl ist als eine geprüfte Ersetzung angewandt worden — dass keine fehlschlug, ist der Beleg der Rekonstruktion. Eine amtliche konsolidierte Nachfassung gibt es nicht; ein zeichengenauer Abgleich mit ihr steht deshalb aus, was in den Herkunftsangaben vermerkt ist. Alle zehn Befehle der Vierten Verordnung zur Änderung der Thüringer Kindergartenfinanzierungsverordnung werden angewandt, keiner bleibt manuell. Dreihundertfünfundzwanzig Testfälle bestehen, und keine der gepinnten Zahlen der übrigen Belegfälle hat sich geändert. Die Browserfassung ist übersetzt worden und liefert dieselben Zahlen wie die Befehlszeile. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Change-Id: Ifca501ed6ffb42165ee4ab4ca036d7db186bbec0
Diffstat (limited to 'src/test')
-rw-r--r--src/test/java/eu/mulk/aendggner/EndToEndTest.java115
-rw-r--r--src/test/java/eu/mulk/aendggner/aenderung/parse/BefehlErkennerTest.java67
-rw-r--r--src/test/java/eu/mulk/aendggner/aenderung/parse/TextBereinigerTest.java34
-rw-r--r--src/test/java/eu/mulk/aendggner/anwendung/BefehlAnwenderTest.java56
4 files changed, 271 insertions, 1 deletions
diff --git a/src/test/java/eu/mulk/aendggner/EndToEndTest.java b/src/test/java/eu/mulk/aendggner/EndToEndTest.java
index fe0ab21..22f1662 100644
--- a/src/test/java/eu/mulk/aendggner/EndToEndTest.java
+++ b/src/test/java/eu/mulk/aendggner/EndToEndTest.java
@@ -960,6 +960,121 @@ class EndToEndTest {
}
/**
+ * Thüringen: Vierte Verordnung zur Änderung der Thüringer Kindergartenfinanzierungsverordnung
+ * (GVBl. für den Freistaat Thüringen Nr. 2 vom 13.02.2026, S. 77). Neun der zehn Befehle des
+ * Artikels 1 werden erkannt.
+ *
+ * <p>Das Heft ist ein Sammelheft: Es trägt vier Verkündungen, von denen nur diese eine ein
+ * Änderungsdokument ist. Die übrigen — ein Zuständigkeitsbeschluss mit eigener Nummernfolge, eine
+ * Stammverordnung und eine Inkrafttretens-Bekanntmachung — bleiben folgenlos.
+ *
+ * <p>Der unterscheidende Satzbefund: Das Heft führt <em>kein einziges</em> {@code „}, sondern
+ * setzt gerade Anführungszeichen beidseitig. Würden sie wie im BGBl durchweg schließend gelesen,
+ * fände der Zitatextraktor kein Zitat und keinen einzigen Befehl.
+ *
+ * <p>Kein voller Akzeptanztest: {@code landesrecht.thueringen.de} ist dieselbe anmeldepflichtige
+ * juris-Anwendung wie die Portale Schleswig-Holsteins, Berlins, Baden-Württembergs und Hessens.
+ */
+ @Test
+ void thuerKiGaFinVOAendVO() throws Exception {
+ var pdf = SAMPLEDATA.resolve("Thueringen/GVBl-TH-2026-02_KiGaFinanzVO-AendVO-ua.pdf");
+ assumeTrue(Files.exists(pdf), "GVBl-Thüringen-Beispiel-PDF fehlt");
+
+ var text = TextBereiniger.bereinige(new PatchTextExtraktor().extrahiere(pdf));
+
+ // Gerade Anführungszeichen paarweise gelesen: Das Heft führt beide Sorten in gleicher Zahl.
+ assertThat(text).doesNotContain("\"");
+ assertThat(text.chars().filter(c -> c == '„').count())
+ .isEqualTo(text.chars().filter(c -> c == '“').count());
+
+ // Laufender Kolumnentitel und Seitenfuß herausgeschnitten. Der Kolumnentitel stand mitten in
+ // einem getrennten Wort — nach dem Schnitt ist die Silbentrennung geheilt.
+ assertThat(text).doesNotContain("Tag der Ausgabe");
+ assertThat(text).doesNotContain("Verordnungsblatt für den Freistaat");
+ assertThat(text).contains("Frauenhäusern und Frauenschutzwohnungen");
+
+ var alt = SAMPLEDATA.resolve("Thueringen/ThuerKigaFinVO-alt.txt");
+ var verordnung = new eu.mulk.aendggner.gesetz.land.LandesRechtLoader().load(alt);
+ assertThat(verordnung.jurabk()).isEqualTo("ThürKigaFinVO");
+ // Stand vor der Vierten ÄndVO: §§ 1 bis 12, darunter die aufgehobenen Platzhalter § 7 und
+ // § 10 und der 2023 eingefügte § 7a.
+ assertThat(verordnung.normen()).hasSize(13);
+ assertThat(verordnung.norm("§ 7").orElseThrow().weggefallen()).isTrue();
+ assertThat(verordnung.norm("§ 10").orElseThrow().weggefallen()).isTrue();
+ var ergebnis = new AenderungsgesetzParser().parse(text, verordnung, null);
+ // Artikel 2 regelt nur das Inkrafttreten.
+ assertThat(ergebnis.artikel()).containsExactly("1");
+ assertThat(ergebnis.befehle()).hasSize(10);
+ assertThat(ergebnis.befehle()).noneMatch(b -> b instanceof UnbekannterBefehl);
+
+ // „Die §§ 1 bis 3 erhalten folgende Fassung:“ — ein Zitat, drei Normen.
+ var bereichsNeufassung = (Aenderungsbefehl.Sammelbefehl) befehlZu(ergebnis, "1.");
+ assertThat(bereichsNeufassung.teilbefehle())
+ .extracting(b -> b.stelle().anzeigeText())
+ .containsExactly("§ 1", "§ 2", "§ 3");
+
+ // „die Verweisung „X“ durch die Verweisung „Y“ ersetzt“ — dasselbe Muster wie „die Angabe“.
+ var verweisung = (Aenderungsbefehl.Ersetzung) befehlZu(ergebnis, "2. b)");
+ assertThat(verweisung.stelle().anzeigeText()).isEqualTo("§ 4 Satz 2");
+ assertThat(verweisung.alt()).isEqualTo("§ 27 Abs. 1, 3 und 5");
+ assertThat(verweisung.neu()).isEqualTo("§ 27 Abs. 1");
+
+ // „Der bisherige § 8 wird § 7 und Absatz 1 wird wie folgt geändert:“ — der Rahmen verengt sich
+ // auf eine Untereinheit der umnummerierten Norm; die Unterpunkte meinen den NEUEN § 7.
+ var rahmenUmnummerierung = befehlZu(ergebnis, "6.");
+ assertThat(rahmenUmnummerierung).isInstanceOf(Aenderungsbefehl.Umnummerierung.class);
+ assertThat(rahmenUmnummerierung.stelle().anzeigeText()).isEqualTo("§ 8");
+ assertThat(befehlZu(ergebnis, "6. a)").stelle().anzeigeText()).isEqualTo("§ 7 Absatz 1 Satz 1");
+ assertThat(befehlZu(ergebnis, "6. b)").stelle().anzeigeText()).isEqualTo("§ 7 Absatz 1 Satz 2");
+
+ // „Die bisherigen §§ 9 bis 12 werden die §§ 8 bis 10.“ nennt vier Ausgangs-, aber drei
+ // Zielbezeichnungen — kein Widerspruch, sondern eine Lücke: § 10 ist aufgehoben und zählt nicht
+ // mit. Welche Einheiten der Bereich trägt, weiß erst das Gesetz; der Befehl bleibt deshalb bis
+ // zur Anwendung ungeteilt.
+ var bereich = befehlZu(ergebnis, "7.");
+ assertThat(bereich).isInstanceOf(Aenderungsbefehl.BereichsUmnummerierung.class);
+
+ // Anwendung auf die konsolidierte Fassung vom Stand der Dritten ÄndVO (siehe SOURCES).
+ var anwendung = BefehlAnwender.anwenden(verordnung, ergebnis.befehle());
+ assertThat(anwendung.anzahlAngewandt()).isEqualTo(10);
+ assertThat(anwendung.protokoll())
+ .noneMatch(a -> a.status() == BefehlAnwender.Status.MANUELL_PRUEFEN);
+
+ var neu = anwendung.neu();
+ // Die Verordnung zählt danach lückenlos §§ 1 bis 10: § 6 ist aufgehoben, § 7a wird § 6, § 8
+ // wird § 7, und aus §§ 9, 11 und 12 werden §§ 8, 9 und 10 — die aufgehobenen Platzhalter § 7
+ // und § 10 weichen den neuen Bezeichnungen.
+ assertThat(neu.normen())
+ .extracting(n -> n.enbez())
+ .containsExactly("§ 1", "§ 2", "§ 3", "§ 4", "§ 5", "§ 6", "§ 7", "§ 8", "§ 9", "§ 10");
+ assertThat(neu.norm("§ 6").orElseThrow().titel())
+ .isEqualTo("Zuschuss für die praxisintegrierte Ausbildung");
+ assertThat(neu.norm("§ 10").orElseThrow().titel()).isEqualTo("Inkrafttreten, Außerkrafttreten");
+
+ // Der umnummerierte § 7 (bisher § 8) trägt die beiden Verweisungs-Ersetzungen aus Punkt 6.
+ var paragraph7 = neu.norm("§ 7").orElseThrow().gesamtText();
+ assertThat(paragraph7)
+ .contains("Meldungen nach § 27 Abs. 1 ThürKigaG")
+ .contains("Meldungen nach § 30 Abs. 4 und 6 ThürKigaG")
+ .doesNotContain("§ 27 Abs. 1 und 3 ThürKigaG")
+ .doesNotContain("§ 27 Abs. 5 sowie");
+ // Die neu gefassten §§ 1 bis 3 stammen aus einem einzigen Zitat.
+ assertThat(neu.norm("§ 1").orElseThrow().gesamtText())
+ .contains("Die Zahlung der Landeszuschüsse nach § 25 Satz 1 Nr. 2 und 3 ThürKigaG");
+ // Die Überschrift des zitierten § 3 läuft über zwei Zeilen; sie gehört ganz in den Titel …
+ assertThat(neu.norm("§ 3").orElseThrow().titel())
+ .isEqualTo("Zahlungen an den örtlichen Träger der öffentlichen Jugendhilfe");
+ // … und die des § 1, die im Satz am Kopf klebt, gehört nicht zusätzlich in den Normtext.
+ assertThat(neu.norm("§ 1").orElseThrow().gesamtText())
+ .doesNotContain("Zahlungen an die Wohnsitzgemeinde");
+ // Die Silbentrennung des Spaltenumbruchs ist geheilt: „Thür-“ + „KigaG“ ist ein Wort, obwohl
+ // die Folgezeile großgeschrieben beginnt — der Wortbestand des Heftes bezeugt es.
+ assertThat(neu.norm("§ 5").orElseThrow().gesamtText())
+ .contains("nach den §§ 25 und 26 ThürKigaG")
+ .doesNotContain("Thür-KigaG");
+ }
+
+ /**
* Änderungsantrag der GRÜNEN (Ltg-Drs. 19/10365) zum Landtags-Gesetzentwurf 19/9707: Er ändert
* nicht das Stammgesetz, sondern die Drucksache — genau den Befehl § 3 Nr. 22, der die Artenliste
* des neuen § 18 AVBayJG zitiert.
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 cbf790b..02538ce 100644
--- a/src/test/java/eu/mulk/aendggner/aenderung/parse/BefehlErkennerTest.java
+++ b/src/test/java/eu/mulk/aendggner/aenderung/parse/BefehlErkennerTest.java
@@ -661,7 +661,7 @@ class BefehlErkennerTest {
var teile =
((Sammelbefehl)
erkenne(
- "Die §§ 52 bis 56 werden wie folgt gefasst: „§ 52 (weggefallen) § 53"
+ "Die §§ 52 bis 54 werden wie folgt gefasst: „§ 52 (weggefallen) § 53"
+ " (weggefallen) § 54 (weggefallen)“.",
Stelle.LEER)
.orElseThrow())
@@ -672,6 +672,71 @@ class BefehlErkennerTest {
.containsExactly("§ 52", "§ 53", "§ 54");
}
+ /**
+ * Der neue Wortlaut einer Bereichs-Neufassung zitiert reihenweise andere Vorschriften.
+ * Geschnitten wird allein an den Bezeichnungen des angekündigten Bereichs — an den Querverweisen
+ * zu schneiden erfände Normen, die es nicht gibt.
+ */
+ @Test
+ void paragraphBereichNeufassungSchneidetNichtAnQuerverweisen() {
+ var teile =
+ ((Sammelbefehl)
+ erkenne(
+ "Die §§ 1 bis 2 erhalten folgende Fassung: „§ 1 Zahlungen\nDie Zahlung"
+ + " nach § 25 Satz 1 Nr. 2 erfolgt nach § 30 Abs. 2.\n§ 2"
+ + " Auszahlung\nDie Zahlung nach § 26 Abs. 1 erfolgt jährlich.“",
+ Stelle.LEER)
+ .orElseThrow())
+ .teilbefehle();
+ assertThat(teile)
+ .hasSize(2)
+ .extracting(t -> t.stelle().anzeigeText())
+ .containsExactly("§ 1", "§ 2");
+ }
+
+ /**
+ * Nennt der Bereich mehr Paragraphen, als das Zitat trägt, so sagen Ankündigung und Wortlaut
+ * Verschiedenes; geraten wird nicht, der Befehl bleibt manuell zu prüfen.
+ */
+ @Test
+ void paragraphBereichNeufassungMitFehlendemNormkopfBleibtUnerkannt() {
+ assertThat(
+ erkenne(
+ "Die §§ 52 bis 56 werden wie folgt gefasst: „§ 52 (weggefallen) § 53"
+ + " (weggefallen) § 54 (weggefallen)“.",
+ Stelle.LEER))
+ .isEmpty();
+ }
+
+ /**
+ * „Die bisherigen §§ 9 bis 12 werden die §§ 8 bis 10.“ — ungleich viele Bezeichnungen auf beiden
+ * Seiten. Bei Bereichen ist das kein Widerspruch, sondern eine Lücke; die Zuordnung bleibt bis
+ * zur Anwendung offen.
+ */
+ @Test
+ void ungleicherParagraphenBereichBleibtUngeteilt() {
+ var befehl = erkenne("Die bisherigen §§ 9 bis 12 werden die §§ 8 bis 10.", Stelle.LEER);
+ assertThat(befehl).get().isInstanceOf(Aenderungsbefehl.BereichsUmnummerierung.class);
+ var bereich = (Aenderungsbefehl.BereichsUmnummerierung) befehl.orElseThrow();
+ assertThat(bereich.stelle().anzeigeText()).isEqualTo("§ 9");
+ assertThat(bereich.bis().anzeigeText()).isEqualTo("§ 12");
+ assertThat(bereich.neu().anzeigeText()).isEqualTo("§ 8");
+ assertThat(bereich.neuBis().anzeigeText()).isEqualTo("§ 10");
+ }
+
+ /** Geht die Zählung auf, bleibt es bei der paarweisen Zuordnung. */
+ @Test
+ void gleicherParagraphenBereichWirdSofortGeteilt() {
+ var teile =
+ ((Sammelbefehl)
+ erkenne("Die §§ 46 und 47 werden zu den §§ 34 und 35.", Stelle.LEER).orElseThrow())
+ .teilbefehle();
+ assertThat(teile)
+ .hasSize(2)
+ .extracting(t -> t.stelle().anzeigeText())
+ .containsExactly("§ 46", "§ 47");
+ }
+
@Test
void wortlautWirdAbsatz() {
var befehl =
diff --git a/src/test/java/eu/mulk/aendggner/aenderung/parse/TextBereinigerTest.java b/src/test/java/eu/mulk/aendggner/aenderung/parse/TextBereinigerTest.java
index 4d78639..334fac3 100644
--- a/src/test/java/eu/mulk/aendggner/aenderung/parse/TextBereinigerTest.java
+++ b/src/test/java/eu/mulk/aendggner/aenderung/parse/TextBereinigerTest.java
@@ -315,4 +315,38 @@ class TextBereinigerTest {
assertThat(TextBereiniger.bereinige("§ 19„\nAußerkrafttreten"))
.isEqualTo("„§ 19\nAußerkrafttreten");
}
+
+ /**
+ * Manche Gesetzblätter setzen gerade Anführungszeichen beidseitig (GVBl. für den Freistaat
+ * Thüringen). Führt ein Text kein einziges {@code „}, werden sie paarweise gelesen — sonst fände
+ * der Zitatextraktor dort kein Zitat.
+ */
+ @Test
+ void geradeAnfuehrungszeichenWerdenPaarweiseGelesen() {
+ var bereinigt =
+ TextBereiniger.bereinige(
+ "In Satz 2 wird die Verweisung \"§ 27 Abs. 1, 3 und 5\" durch die Verweisung"
+ + " \"§ 27 Abs. 1\" ersetzt.");
+ assertThat(bereinigt)
+ .isEqualTo(
+ "In Satz 2 wird die Verweisung „§ 27 Abs. 1, 3 und 5“ durch die Verweisung"
+ + " „§ 27 Abs. 1“ ersetzt.");
+ }
+
+ /**
+ * Führt der Text dagegen das deutsche öffnende Zeichen, so ist ein gerades ein Satz- oder
+ * Extraktionsfehler und schließend zu lesen — so steht es in BGBl und Drucksachen.
+ */
+ @Test
+ void geradesZeichenNebenDeutschemBleibtSchliessend() {
+ var bereinigt = TextBereiniger.bereinige("Das Wort „alt\" wird durch das Wort „neu“ ersetzt.");
+ assertThat(bereinigt).isEqualTo("Das Wort „alt“ wird durch das Wort „neu“ ersetzt.");
+ }
+
+ /** Bei ungerader Anzahl lässt sich nicht paaren; dann bleibt es bei der schließenden Lesart. */
+ @Test
+ void ungeradeAnzahlGeraderZeichenWirdNichtGepaart() {
+ var bereinigt = TextBereiniger.bereinige("Die Angabe \"X\" wird durch \"Y gestrichen.");
+ assertThat(bereinigt).doesNotContain("„").contains("“");
+ }
}
diff --git a/src/test/java/eu/mulk/aendggner/anwendung/BefehlAnwenderTest.java b/src/test/java/eu/mulk/aendggner/anwendung/BefehlAnwenderTest.java
index 212a037..094de7e 100644
--- a/src/test/java/eu/mulk/aendggner/anwendung/BefehlAnwenderTest.java
+++ b/src/test/java/eu/mulk/aendggner/anwendung/BefehlAnwenderTest.java
@@ -4,6 +4,7 @@ import static org.assertj.core.api.Assertions.assertThat;
import eu.mulk.aendggner.aenderung.Aenderungsbefehl.Anfuegung;
import eu.mulk.aendggner.aenderung.Aenderungsbefehl.Aufhebung;
+import eu.mulk.aendggner.aenderung.Aenderungsbefehl.BereichsUmnummerierung;
import eu.mulk.aendggner.aenderung.Aenderungsbefehl.Ebene;
import eu.mulk.aendggner.aenderung.Aenderungsbefehl.Ersetzung;
import eu.mulk.aendggner.aenderung.Aenderungsbefehl.GliederungsUeberschriften;
@@ -1574,4 +1575,59 @@ class BefehlAnwenderTest {
+ " 3. die schriftliche Bestätigung.")),
false)));
}
+
+ /**
+ * „Die bisherigen §§ 2 bis 4 werden die §§ 2 und 3.“ — der Bereich nennt drei Bezeichnungen, das
+ * Gesetz trägt darin aber nur zwei Einheiten: § 3 ist aufgehoben und zählt nicht mit. Erst am
+ * Gesetz steht die Zuordnung fest.
+ */
+ @Test
+ void bereichsUmnummerierungUeberspringtAufgehobeneEinheiten() {
+ var gesetz =
+ new Gesetz(
+ "TestG",
+ "Gesetz zur Erprobung",
+ "Testgesetz",
+ List.of(
+ new Norm("§ 1", "Zweck", null, List.of(new Absatz(null, "Erprobung.")), false),
+ new Norm(
+ "§ 2", "Mittel", null, List.of(new Absatz(null, "Mittel sind frei.")), false),
+ new Norm("§ 3", "(aufgehoben)", null, List.of(), true),
+ new Norm(
+ "§ 4", "Schluss", null, List.of(new Absatz(null, "Tritt in Kraft.")), false)),
+ List.of());
+
+ var befehl =
+ new BereichsUmnummerierung(
+ stelle(new Stelle.Paragraph("2")),
+ stelle(new Stelle.Paragraph("4")),
+ stelle(new Stelle.Paragraph("2")),
+ stelle(new Stelle.Paragraph("3")),
+ PROV);
+ var ergebnis = BefehlAnwender.anwenden(gesetz, List.of(befehl));
+
+ assertThat(ergebnis.anzahlManuell()).isZero();
+ assertThat(ergebnis.neu().normen())
+ .extracting(Norm::enbez)
+ .containsExactly("§ 1", "§ 2", "§ 3");
+ assertThat(ergebnis.neu().norm("§ 3").orElseThrow().titel()).isEqualTo("Schluss");
+ }
+
+ /**
+ * Trägt der Bereich mehr Einheiten, als der Befehl neue Bezeichnungen nennt, so ist er nicht
+ * auflösbar — dann wird nicht geraten, sondern gemeldet.
+ */
+ @Test
+ void unaufloesbarerBereichBleibtManuell() {
+ var befehl =
+ new BereichsUmnummerierung(
+ stelle(new Stelle.Paragraph("1")),
+ stelle(new Stelle.Paragraph("3")),
+ stelle(new Stelle.Paragraph("1")),
+ stelle(new Stelle.Paragraph("1")),
+ PROV);
+ var ergebnis = BefehlAnwender.anwenden(gesetz(), List.of(befehl));
+ assertThat(ergebnis.anzahlManuell()).isEqualTo(1);
+ assertThat(ergebnis.protokoll().get(0).begruendung()).contains("nicht so viele Einheiten");
+ }
}