diff options
Diffstat (limited to 'src/test')
| -rw-r--r-- | src/test/java/eu/mulk/aendggner/EndToEndTest.java | 98 | ||||
| -rw-r--r-- | src/test/java/eu/mulk/aendggner/aenderung/parse/InkrafttretensLeserTest.java | 143 |
2 files changed, 241 insertions, 0 deletions
diff --git a/src/test/java/eu/mulk/aendggner/EndToEndTest.java b/src/test/java/eu/mulk/aendggner/EndToEndTest.java index d9f36d7..10cfb8d 100644 --- a/src/test/java/eu/mulk/aendggner/EndToEndTest.java +++ b/src/test/java/eu/mulk/aendggner/EndToEndTest.java @@ -24,6 +24,7 @@ import eu.mulk.aendggner.synopse.HtmlRenderer; import eu.mulk.aendggner.synopse.SynopseBuilder; import java.nio.file.Files; import java.nio.file.Path; +import java.time.LocalDate; import java.util.List; import org.junit.jupiter.api.Test; @@ -1650,4 +1651,101 @@ class EndToEndTest { .findFirst() .orElseThrow(); } + + /** + * Die Fassung eines bestimmten Tages. Das 3. UWGÄndG tritt gestaffelt in Kraft: Artikel 1 Nummer + * 2 Buchstabe c am 19. Juni 2026, alles Übrige erst am 27. September 2026. Wer alle Befehle auf + * einen Schlag anwendet, erhält eine Fassung, die an keinem einzigen Tag gegolten hat — deshalb + * warnt die Synopse davor, und mit einem Stichtag ergibt sich die wirklich geltende Fassung. + */ + @Test + void uwgTrittGestaffeltInKraft() throws Exception { + var xml = SAMPLEDATA.resolve("UWG/BJNR141400004.xml"); + var pdf = SAMPLEDATA.resolve("UWG/bgbl126s0043_regelungstext.pdf"); + assumeTrue(Files.exists(xml) && Files.exists(pdf), "UWG-Beispieldaten fehlen"); + + // Ohne Stichtag bleibt es beim vollen Bestand — aber nicht stillschweigend. + var ohne = Pipeline.erzeugeSynopse(xml, List.of(pdf), null, false); + assertThat(ohne.anzahlAngewandt()).isEqualTo(19); + assertThat(ohne.html()) + .contains("Das Änderungsgesetz tritt gestaffelt in Kraft") + .contains("Artikel 1 Nummer 2 Buchstabe c tritt am 19. Juni 2026 in Kraft.") + .contains("<dt>Inkrafttreten</dt><dd>27. September 2026 (gestaffelt, siehe unten)</dd>"); + + // Am 19. Juni 2026 galt genau ein Befehl: der Buchstabe c der Nummer 2. + var frueh = Pipeline.erzeugeSynopse(xml, List.of(pdf), null, false, LocalDate.of(2026, 6, 19)); + assertThat(frueh.anzahlAngewandt()).isEqualTo(1); + assertThat(frueh.anzahlManuell()).isZero(); + assertThat(frueh.html()) + .contains("<dt>Stichtag</dt><dd>19. Juni 2026</dd>") + .contains("Am Stichtag noch nicht in Kraft") + .contains("Tritt erst am 27. September 2026 in Kraft"); + // Der Tag davor ändert noch gar nichts. + var davor = Pipeline.erzeugeSynopse(xml, List.of(pdf), null, false, LocalDate.of(2026, 6, 18)); + assertThat(davor.anzahlAngewandt()).isZero(); + + // Am 27. September 2026 ist das Gesetz vollständig in Kraft; dann deckt sich die Fassung mit + // der ungefilterten, und der Abschnitt „noch nicht in Kraft“ entfällt. + var spaet = Pipeline.erzeugeSynopse(xml, List.of(pdf), null, false, LocalDate.of(2026, 9, 27)); + assertThat(spaet.anzahlAngewandt()).isEqualTo(19); + assertThat(spaet.html()).doesNotContain("Am Stichtag noch nicht in Kraft"); + } + + /** + * Dasselbe am großen Fall, und zugleich die Probe auf die Schritt-Ordnung: Im GEG bleibt am 1. + * Januar 2024 allein der Befehl der Nummer 22 zurück. Er steht in keiner Umnummerierungs-Kaskade, + * sodass die übrigen 115 unverändert durchlaufen — die drei liegengebliebenen Befehle sind + * dieselben wie ohne Stichtag und werden von der Auswahl nicht vermehrt. + */ + @Test + void gegStichtagLaesstDieUebrigenBefehleUnberuehrt() throws Exception { + var xml = SAMPLEDATA.resolve("GEG/BJNR172810020-2023.xml"); + var pdf = SAMPLEDATA.resolve("GEG/bgbl123s0280_regelungstext.pdf"); + assumeTrue(Files.exists(xml) && Files.exists(pdf), "GEG-Beispieldaten fehlen"); + + var vollstaendig = Pipeline.erzeugeSynopse(xml, List.of(pdf), null, false); + assertThat(vollstaendig.anzahlAngewandt()).isEqualTo(116); + assertThat(vollstaendig.anzahlManuell()).isEqualTo(3); + + var anfang2024 = + Pipeline.erzeugeSynopse(xml, List.of(pdf), null, false, LocalDate.of(2024, 1, 1)); + assertThat(anfang2024.anzahlAngewandt()).isEqualTo(115); + assertThat(anfang2024.anzahlManuell()).isEqualTo(3); + assertThat(anfang2024.html()) + .contains("Am Stichtag noch nicht in Kraft") + .contains("Tritt erst am 1. Oktober 2024 in Kraft"); + + var oktober2024 = + Pipeline.erzeugeSynopse(xml, List.of(pdf), null, false, LocalDate.of(2024, 10, 1)); + assertThat(oktober2024.anzahlAngewandt()).isEqualTo(116); + } + + /** + * Das IfSG-Gesetz von 2020 ändert dasselbe Stammgesetz in zwei Artikeln zu zwei Zeitpunkten: + * Artikel 1 sogleich, Artikel 2 erst am 1. April 2021. Genau dort ist die auf einen Schlag + * gerechnete Fassung eine, die es nie gab. + * + * <p>Zugleich der Fall der unbestimmten Grundregel: „am Tag nach der Verkündung“ nennt kein + * Datum, das im Gesetzestext stünde. Erfunden wird keines; die betroffenen Befehle gelten als am + * Stichtag bereits wirksam, und das wird gesagt. + */ + @Test + void ifsgZweiArtikelZuZweiZeitpunkten() throws Exception { + var xml = SAMPLEDATA.resolve("IfSG/BJNR104510000-2020.xml"); + var pdf = SAMPLEDATA.resolve("IfSG/bgbl120s2397_78991.pdf"); + assumeTrue(Files.exists(xml) && Files.exists(pdf), "IfSG-Beispieldaten fehlen"); + + var novemberFassung = + Pipeline.erzeugeSynopse(xml, List.of(pdf), null, false, LocalDate.of(2020, 11, 19)); + assertThat(novemberFassung.anzahlAngewandt()).isEqualTo(65); + assertThat(novemberFassung.html()) + .contains("Am Stichtag noch nicht in Kraft") + .contains("Eine Inkrafttretens-Anordnung nennt kein bestimmtes Datum"); + + // Am 1. April 2021 kommen die zehn Befehle des Artikels 2 und der Doppelbuchstabe hinzu. + var aprilFassung = + Pipeline.erzeugeSynopse(xml, List.of(pdf), null, false, LocalDate.of(2021, 4, 1)); + assertThat(aprilFassung.anzahlAngewandt()).isEqualTo(75); + assertThat(aprilFassung.html()).doesNotContain("Am Stichtag noch nicht in Kraft"); + } } diff --git a/src/test/java/eu/mulk/aendggner/aenderung/parse/InkrafttretensLeserTest.java b/src/test/java/eu/mulk/aendggner/aenderung/parse/InkrafttretensLeserTest.java new file mode 100644 index 0000000..819cd95 --- /dev/null +++ b/src/test/java/eu/mulk/aendggner/aenderung/parse/InkrafttretensLeserTest.java @@ -0,0 +1,143 @@ +// SPDX-FileCopyrightText: 2020 Matthias Andreas Benkard <code@mail.matthias.benkard.de> +// SPDX-License-Identifier: AGPL-3.0-or-later +package eu.mulk.aendggner.aenderung.parse; + +import static org.assertj.core.api.Assertions.assertThat; + +import eu.mulk.aendggner.aenderung.Provenienz; +import java.time.LocalDate; +import org.junit.jupiter.api.Test; + +/** Der Schlussartikel, gelesen — die Wortlaute stammen sämtlich aus den Beispieldaten. */ +class InkrafttretensLeserTest { + + private static Provenienz punkt(String artikel, String pfad) { + return new Provenienz(artikel, pfad, ""); + } + + @Test + void schlichtesInkrafttretenOhneStaffelung() { + var inkrafttreten = + InkrafttretensLeser.lies("Dieses Gesetz tritt am 12. Juni 2026 in Kraft.\nBerlin, den 3."); + + assertThat(inkrafttreten).isNotNull(); + assertThat(inkrafttreten.gestaffelt()).isFalse(); + assertThat(inkrafttreten.grundregel().orElseThrow().datum()) + .isEqualTo(LocalDate.of(2026, 6, 12)); + // Ohne eigenen Bezug gilt für jeden Punkt die Grundregel. + assertThat(inkrafttreten.fuer(punkt("1", "3. a)")).orElseThrow().datum()) + .isEqualTo(LocalDate.of(2026, 6, 12)); + } + + /** UWG 2026: Ein einzelner Buchstabe tritt drei Monate vor dem übrigen Gesetz in Kraft. */ + @Test + void gestaffeltMitBuchstabenBezug() { + var inkrafttreten = + InkrafttretensLeser.lies( + """ + (1) Dieses Gesetz tritt vorbehaltlich des Absatzes 2 am 27. September 2026 in Kraft. + (2) Artikel 1 Nummer 2 Buchstabe c tritt am 19. Juni 2026 in Kraft. + Die verfassungsmäßigen Rechte des Bundesrates sind gewahrt. + """); + + assertThat(inkrafttreten).isNotNull(); + assertThat(inkrafttreten.gestaffelt()).isTrue(); + // Der Vorbehalt („vorbehaltlich des Absatzes 2“) verweist auf den Schlussartikel selbst; er + // ist kein Punktbezug und macht die Grundregel nicht zur Sonderregel. + assertThat(inkrafttreten.grundregel().orElseThrow().datum()) + .isEqualTo(LocalDate.of(2026, 9, 27)); + assertThat(inkrafttreten.fuer(punkt("1", "2. c)")).orElseThrow().datum()) + .isEqualTo(LocalDate.of(2026, 6, 19)); + // Der Nachbarbuchstabe ist nicht gemeint, und die Nummer selbst auch nicht. + assertThat(inkrafttreten.fuer(punkt("1", "2. d)")).orElseThrow().datum()) + .isEqualTo(LocalDate.of(2026, 9, 27)); + assertThat(inkrafttreten.fuer(punkt("1", "2.")).orElseThrow().datum()) + .isEqualTo(LocalDate.of(2026, 9, 27)); + } + + /** + * GEG 2023: „Abweichend von Absatz 1 treten Artikel 1 Nummer 22 sowie Artikel 3 am … in Kraft.“ — + * zwei Bezüge in einem Satz, davon einer auf einen ganzen Artikel. Der Schlusssatz über die + * Rechte des Bundesrates klebt im Gesetzblatt unmittelbar am Anordnungssatz. + */ + @Test + void zweiBezuegeInEinemSatzUndAngeklebterSchlusssatz() { + var inkrafttreten = + InkrafttretensLeser.lies( + """ + (1) Dieses Gesetz tritt vorbehaltlich des Absatzes 2 am 1. Januar 2024 in Kraft. + (2) Abweichend von Absatz 1 treten Artikel 1 Nummer 22 sowie Artikel 3 am 1. Oktober \ + 2024 in Kraft. Die verfassungsmäßigen Rechte des Bundesrates sind gewahrt. + """); + + assertThat(inkrafttreten).isNotNull(); + var sonder = inkrafttreten.sonderregeln(); + assertThat(sonder).hasSize(1); + assertThat(sonder.get(0).wortlaut()).endsWith("am 1. Oktober 2024 in Kraft."); + assertThat(inkrafttreten.fuer(punkt("1", "22.")).orElseThrow().datum()) + .isEqualTo(LocalDate.of(2024, 10, 1)); + // Ein ganzer Artikel als Bezug erfasst jeden seiner Punkte. + assertThat(inkrafttreten.fuer(punkt("3", "5. b)")).orElseThrow().datum()) + .isEqualTo(LocalDate.of(2024, 10, 1)); + assertThat(inkrafttreten.fuer(punkt("1", "21.")).orElseThrow().datum()) + .isEqualTo(LocalDate.of(2024, 1, 1)); + } + + /** + * IfSG 2020: Rückwirkendes Inkrafttreten („mit Wirkung vom“), ein Doppelbuchstabe als Bezug und + * eine Grundregel, die kein Datum nennt — der Verkündungstag steht nicht im Gesetzestext, also + * wird auch keiner erfunden. + */ + @Test + void rueckwirkungDoppelbuchstabeUndUnbestimmteGrundregel() { + var inkrafttreten = + InkrafttretensLeser.lies( + """ + (1) Dieses Gesetz tritt vorbehaltlich der Absätze 2 und 3 am Tag nach der Verkündung \ + in Kraft. + (2) Artikel 4a Nummer 1 tritt mit Wirkung vom + 18. November 2020 in Kraft. + (3) Artikel 1 Nummer 11 Buchstabe a Doppelbuchstabe bb und Artikel 2 treten am \ + 1. April 2021 in Kraft. + """); + + assertThat(inkrafttreten).isNotNull(); + var grundregel = inkrafttreten.grundregel().orElseThrow(); + assertThat(grundregel.datum()).isNull(); + assertThat(grundregel.anzeige()).isEqualTo(grundregel.wortlaut()); + // Der Zeilenumbruch mitten im Datum („mit Wirkung vom\n18. November 2020“) stört nicht. + assertThat(inkrafttreten.fuer(punkt("4a", "1.")).orElseThrow().datum()) + .isEqualTo(LocalDate.of(2020, 11, 18)); + assertThat(inkrafttreten.fuer(punkt("1", "11. a) bb)")).orElseThrow().datum()) + .isEqualTo(LocalDate.of(2021, 4, 1)); + assertThat(inkrafttreten.fuer(punkt("1", "11. a) aa)")).orElseThrow().datum()).isNull(); + assertThat(inkrafttreten.fuer(punkt("2", "6. b)")).orElseThrow().datum()) + .isEqualTo(LocalDate.of(2021, 4, 1)); + } + + /** Das Außerkrafttreten sagt nichts darüber, wann eine Änderung wirkt — es bleibt außer Acht. */ + @Test + void ausserkrafttretenIstKeineAnordnungDesInkrafttretens() { + assertThat(InkrafttretensLeser.lies("Diese Verordnung tritt am 31. Dezember 2027 außer Kraft.")) + .isNull(); + } + + /** + * Ein Gesetzblatt trägt mehrere Gesetze mit je eigener Schlussvorschrift. Maßgeblich ist die, die + * auf den eigenen ändernden Artikel folgt — nicht die eines fremden Gesetzes davor. + */ + @Test + void waehltDenSchlussartikelHinterDemEigenenGesetz() { + var bloecke = + java.util.List.of( + "Das Erste Gesetz wird wie folgt geändert: …", + "Dieses Gesetz tritt am 1. März 2026 in Kraft.", + "Das Zweite Gesetz wird wie folgt geändert: …", + "Dieses Gesetz tritt am 1. Juli 2026 in Kraft."); + + assertThat(InkrafttretensLeser.waehle(bloecke, 1).grundregel().orElseThrow().datum()) + .isEqualTo(LocalDate.of(2026, 3, 1)); + assertThat(InkrafttretensLeser.waehle(bloecke, 3).grundregel().orElseThrow().datum()) + .isEqualTo(LocalDate.of(2026, 7, 1)); + } +} |
