diff options
| author | Matthias Andreas Benkard <code@mail.matthias.benkard.de> | 2026-08-26 07:39:57 +0200 |
|---|---|---|
| committer | Matthias Andreas Benkard <code@mail.matthias.benkard.de> | 2026-08-26 07:39:57 +0200 |
| commit | e0754851af52d64d912d19828cab6044d1520cfc (patch) | |
| tree | ad8a95a76236ddca2c224286d1d26ec6747240f6 /src/test/java/eu | |
| parent | 63e1283230c5457666de90c9af74657b52579909 (diff) | |
Die Verweisung wird vollzogen, und die Übersicht wird geprüft
Der verweisende Befehl galt als Grenze: „Die Inhaltsübersicht wird entsprechend der
vorstehenden Nummer 8 Buchst. a geändert“ wurde gelesen und liegengelassen. Die
Grenze bestand nicht in der Sache. Zu übertragen ist nicht der Wortlaut jenes
Punktes, sondern sein Ergebnis — er ändert die Überschrift eines Paragraphen, und
die Angabe wird auf den Titel gesetzt, den der Paragraph danach trägt. Das trifft,
was „entsprechend“ meint, und erspart es, jede Befehlsform ein zweites Mal auf dem
Zeilenmodell der Übersicht nachzubilden. Ausgeführt wird der Befehl dort, wo die
Befehlsliste vorliegt: in der Schleife des Anwenders, nicht in der Weiche.
Hinzu tritt eine Prüfung, die es bisher nicht gab. Wer Paragraphen einfügt oder
ihre Überschriften neu fasst, muss die Angaben eigens mitändern; unterbleibt das,
so bleibt die Übersicht hinter dem Text zurück, ohne dass ein einziger Befehl
liegenbliebe. Geprüft wird allein der Unterschied, den der Lauf bewirkt hat — was
die Quelle von sich aus ungenau führt, ist ein Befund über die Quelle. Geheilt wird
nichts; die Übersicht ist eine Norm wie jede andere.
Die Probe hat sich sogleich bewährt und drei Mängel aufgedeckt, die keine Zahl des
Protokolls angezeigt hatte: Ein Zitat, das mit einer Gliederungsmarke beginnt, wurde
nicht in Zeilen zerlegt, sondern blieb eine einzige; ein Querverweis mitten im Satz
(„… nach § 71a Projektunterlagen … vorzulegen“) erzeugte einen Paragraphen mit einem
halben Satz als Überschrift, weil ein Normkopf nicht an seine Stellung gebunden war;
und ein am Spaltenrand umbrochener Titel verlor seinen zweiten Teil, weil eine Zeile,
die auf „und“ endet, als vollendet galt.
Geprüft: mvnw verify, 406 Tests (zuvor 397), reuse lint 194/194. Das GEG-Heft bleibt
bei 119 von 119 angewandten Befehlen; die Probe rügt dort nur noch, was die amtliche
Fassung selbst verschieden führt.
Change-Id: I24aabefc33406beaf7b1f1fca80d3212a7620f71
Diffstat (limited to 'src/test/java/eu')
3 files changed, 190 insertions, 7 deletions
diff --git a/src/test/java/eu/mulk/aendggner/EndToEndTest.java b/src/test/java/eu/mulk/aendggner/EndToEndTest.java index 109bbf6..8c9743d 100644 --- a/src/test/java/eu/mulk/aendggner/EndToEndTest.java +++ b/src/test/java/eu/mulk/aendggner/EndToEndTest.java @@ -1927,17 +1927,23 @@ class EndToEndTest { // Kein Befehl des Heftes bleibt mehr ungelesen. assertThat(parseErgebnis.befehle()).noneMatch(b -> b instanceof UnbekannterBefehl); - // Die Verweisung auf einen anderen Punkt desselben Artikels wird gelesen, aber bewusst nicht - // ausgeführt. Der Unterschied ist kein kosmetischer: Die Synopse sagt fortan, dass das - // Erzeugnis hier die Grenze zieht, und nicht, dass die Vorlage unverständlich sei. + // Die Verweisung auf einen anderen Punkt desselben Artikels wird gelesen und ausgeführt: Der + // verwiesene Punkt ändert die Überschrift des § 13, und die Angabe der Inhaltsübersicht wird + // ihr nachgeführt. Hier scheitert das nicht am Erzeugnis, sondern am Gegenstand — diese + // Verordnung führt gar keine Inhaltsübersicht, und die Rüge sagt genau das. var verweisung = befehlAn(parseErgebnis, "12."); assertThat(verweisung).isInstanceOf(VerweisenderBefehl.class); assertThat(((VerweisenderBefehl) verweisung).verweis()).isEqualTo("Nummer 8 Buchst. a"); + assertThat(gesetz.norm("Inhaltsübersicht")).isEmpty(); - var anwendung = BefehlAnwender.anwenden(gesetz, List.of(verweisung)); - assertThat(anwendung.protokoll().get(0).grund()).isEqualTo(Grund.NICHT_UNTERSTUETZT); - assertThat(anwendung.protokoll().get(0).begruendung()) - .contains("verweist auf „Nummer 8 Buchst. a“"); + var anwendung = BefehlAnwender.anwenden(gesetz, parseErgebnis.befehle()); + var protokoll = + anwendung.protokoll().stream() + .filter(a -> a.befehl() == verweisung) + .findFirst() + .orElseThrow(); + assertThat(protokoll.grund()).isEqualTo(Grund.BESTAND_WIDERSPRICHT); + assertThat(protokoll.begruendung()).contains("keine Inhaltsübersicht"); } private static Aenderungsbefehl befehlAn( diff --git a/src/test/java/eu/mulk/aendggner/anwendung/BefehlAnwenderTest.java b/src/test/java/eu/mulk/aendggner/anwendung/BefehlAnwenderTest.java index 39c71cc..3ed028b 100644 --- a/src/test/java/eu/mulk/aendggner/anwendung/BefehlAnwenderTest.java +++ b/src/test/java/eu/mulk/aendggner/anwendung/BefehlAnwenderTest.java @@ -18,6 +18,7 @@ import eu.mulk.aendggner.aenderung.Aenderungsbefehl.StrukturEinfuegung; import eu.mulk.aendggner.aenderung.Aenderungsbefehl.StrukturErsetzung; import eu.mulk.aendggner.aenderung.Aenderungsbefehl.Umnummerierung; import eu.mulk.aendggner.aenderung.Aenderungsbefehl.UnbekannterBefehl; +import eu.mulk.aendggner.aenderung.Aenderungsbefehl.VerweisenderBefehl; import eu.mulk.aendggner.aenderung.Aenderungsbefehl.WoerterEinfuegung; import eu.mulk.aendggner.aenderung.Aenderungsbefehl.WortAnker; import eu.mulk.aendggner.aenderung.Provenienz; @@ -1029,6 +1030,81 @@ class BefehlAnwenderTest { .contains("Teil 2 | Verfahren\n§ 3 | Ablauf"); } + // --- Der verweisende Befehl ------------------------------------------------------------------ + + /** + * „Die Inhaltsübersicht wird entsprechend der vorstehenden Nummer 8 Buchst. a geändert.“ Der + * verwiesene Punkt ändert die Überschrift des § 1; die Angabe wird auf den Titel nachgeführt, den + * der Paragraph danach trägt. Übertragen wird also das Ergebnis jenes Punktes und nicht sein + * Wortlaut — das trifft, was „entsprechend“ meint. + */ + @Test + void fuehrtDieInhaltsuebersichtDemVerwiesenenPunktNach() { + var ueberschrift = + new Ersetzung( + new Stelle(List.of(new Stelle.Paragraph("1"), new Stelle.Ueberschrift())), + "Zweck", + "Zweck und Ziel", + false, + false, + new Provenienz("1", "8. a)", "(Test)")); + var verweisung = + new VerweisenderBefehl( + iuStelle(), "Nummer 8 Buchst. a", new Provenienz("1", "12.", "(Test)")); + + var ergebnis = + BefehlAnwender.anwenden(gesetzMitInhaltsuebersicht(), List.of(ueberschrift, verweisung)); + + assertThat(ergebnis.protokoll()).allMatch(a -> a.status() == Status.ANGEWANDT); + assertThat(ergebnis.neu().norm("§ 1").orElseThrow().titel()).isEqualTo("Zweck und Ziel"); + assertThat(absatzText(ergebnis.neu(), "Inhaltsübersicht", 0)) + .contains("§ 1 | Zweck und Ziel") + .doesNotContain("§ 1 | Zweck\n"); + } + + /** + * Was der Verweis nicht trägt, bleibt liegen und wird benannt. Die Inhaltsübersicht führt allein + * Bezeichnung und Überschrift; was im Absatz eines Paragraphen geschieht, hat in ihr kein + * Gegenstück, und es zu erraten wäre schlimmer, als die Grenze zu benennen. + */ + @Test + void ruegtDenVerweisAufEinenPunktOhneGegenstueckInDerUebersicht() { + var imAbsatz = + new Ersetzung( + new Stelle(List.of(new Stelle.Paragraph("1"))), + "Text", + "Wortlaut", + false, + false, + new Provenienz("1", "8. a)", "(Test)")); + var verweisung = + new VerweisenderBefehl( + iuStelle(), "Nummer 8 Buchst. a", new Provenienz("1", "12.", "(Test)")); + + var ergebnis = + BefehlAnwender.anwenden(gesetzMitInhaltsuebersicht(), List.of(imAbsatz, verweisung)); + + var ruege = ergebnis.protokoll().get(1); + assertThat(ruege.status()).isEqualTo(Status.MANUELL_PRUEFEN); + assertThat(ruege.grund()).isEqualTo(Grund.NICHT_UNTERSTUETZT); + assertThat(ruege.begruendung()).contains("ändert keine Überschrift"); + } + + /** Ein Verweis ins Leere ist keine Grenze des Erzeugnisses, sondern ein Befund am Dokument. */ + @Test + void ruegtDenVerweisAufEinenNichtVorhandenenPunkt() { + var verweisung = + new VerweisenderBefehl( + iuStelle(), "Nummer 8 Buchst. a", new Provenienz("1", "12.", "(Test)")); + + var ergebnis = BefehlAnwender.anwenden(gesetzMitInhaltsuebersicht(), List.of(verweisung)); + + var ruege = ergebnis.protokoll().get(0); + assertThat(ruege.status()).isEqualTo(Status.MANUELL_PRUEFEN); + assertThat(ruege.grund()).isEqualTo(Grund.STELLE_NICHT_AUFLOESBAR); + assertThat(ruege.begruendung()).contains("findet sich nicht im selben Artikel"); + } + // --- Gliederungs-Überschriften --------------------------------------------------------------- private static Gesetz gesetzMitGliederungen() { diff --git a/src/test/java/eu/mulk/aendggner/anwendung/InhaltsuebersichtsProbeTest.java b/src/test/java/eu/mulk/aendggner/anwendung/InhaltsuebersichtsProbeTest.java new file mode 100644 index 0000000..742430b --- /dev/null +++ b/src/test/java/eu/mulk/aendggner/anwendung/InhaltsuebersichtsProbeTest.java @@ -0,0 +1,101 @@ +// SPDX-FileCopyrightText: 2026 Matthias Andreas Benkard <code@mail.matthias.benkard.de> +// SPDX-License-Identifier: AGPL-3.0-or-later +package eu.mulk.aendggner.anwendung; + +import static org.assertj.core.api.Assertions.assertThat; + +import eu.mulk.aendggner.gesetz.Absatz; +import eu.mulk.aendggner.gesetz.Gesetz; +import eu.mulk.aendggner.gesetz.Norm; +import java.util.ArrayList; +import java.util.List; +import org.junit.jupiter.api.Test; + +/** + * Die Probe schlägt an, wo ein Lauf den Bestand angerührt und die Inhaltsübersicht zurückgelassen + * hat — und nur dort. Was die Quelle von sich aus ungenau führt, geht sie nichts an. + */ +class InhaltsuebersichtsProbeTest { + + private static Gesetz gesetz(String uebersicht, Norm... normen) { + var alle = new ArrayList<Norm>(); + alle.add( + new Norm("Inhaltsübersicht", null, null, List.of(new Absatz(null, uebersicht)), false)); + alle.addAll(List.of(normen)); + return new Gesetz("TestG", "Testgesetz", null, alle); + } + + private static Norm norm(String enbez, String titel) { + return new Norm(enbez, titel, null, List.of(new Absatz(null, "Text.")), false); + } + + private static final String UEBERSICHT = "§ 1 | Zweck\n§ 2 | Begriffe"; + + @Test + void ruegtDieNichtNachgefuehrteAngabe() { + var alt = gesetz(UEBERSICHT, norm("§ 1", "Zweck"), norm("§ 2", "Begriffe")); + var neu = gesetz(UEBERSICHT, norm("§ 1", "Zweck und Ziel"), norm("§ 2", "Begriffe")); + + assertThat(InhaltsuebersichtsProbe.pruefe(alt, neu)) + .singleElement() + .asString() + .contains("§ 1") + .contains("gehen auseinander"); + } + + @Test + void schweigtWennDieAngabeMitgefuehrtWurde() { + var alt = gesetz(UEBERSICHT, norm("§ 1", "Zweck"), norm("§ 2", "Begriffe")); + var neu = + gesetz( + "§ 1 | Zweck und Ziel\n§ 2 | Begriffe", + norm("§ 1", "Zweck und Ziel"), + norm("§ 2", "Begriffe")); + + assertThat(InhaltsuebersichtsProbe.pruefe(alt, neu)).isEmpty(); + } + + @Test + void ruegtDenEingefuegtenParagraphenOhneAngabe() { + var alt = gesetz(UEBERSICHT, norm("§ 1", "Zweck"), norm("§ 2", "Begriffe")); + var neu = + gesetz(UEBERSICHT, norm("§ 1", "Zweck"), norm("§ 1a", "Neues"), norm("§ 2", "Begriffe")); + + assertThat(InhaltsuebersichtsProbe.pruefe(alt, neu)) + .singleElement() + .asString() + .contains("keine Angabe zu § 1a"); + } + + @Test + void ruegtDieAngabeZurBeseitigtenNorm() { + var alt = gesetz(UEBERSICHT, norm("§ 1", "Zweck"), norm("§ 2", "Begriffe")); + var neu = gesetz(UEBERSICHT, norm("§ 1", "Zweck")); + + assertThat(InhaltsuebersichtsProbe.pruefe(alt, neu)) + .singleElement() + .asString() + .contains("weiterhin eine Angabe zu § 2"); + } + + /** + * Eine Anlage steht nicht in der Inhaltsübersicht; sie dort zu vermissen hieße, ihr etwas + * abzuverlangen, was sie nie leistet. + */ + @Test + void schweigtZurGeaendertenAnlage() { + var alt = gesetz(UEBERSICHT, norm("§ 1", "Zweck"), norm("Anlage 8", "Muster")); + var neu = gesetz(UEBERSICHT, norm("§ 1", "Zweck"), norm("Anlage 8", "Anderes Muster")); + + assertThat(InhaltsuebersichtsProbe.pruefe(alt, neu)).isEmpty(); + } + + /** Ohne Inhaltsübersicht gibt es nichts zu prüfen — und nichts zu rügen. */ + @Test + void schweigtOhneInhaltsuebersicht() { + var ohne = new Gesetz("TestG", "Testgesetz", null, List.of(norm("§ 1", "Zweck"))); + var geaendert = new Gesetz("TestG", "Testgesetz", null, List.of(norm("§ 1", "Zweck und Ziel"))); + + assertThat(InhaltsuebersichtsProbe.pruefe(ohne, geaendert)).isEmpty(); + } +} |
