aboutsummaryrefslogtreecommitdiff
path: root/src/test/java
diff options
context:
space:
mode:
Diffstat (limited to 'src/test/java')
-rw-r--r--src/test/java/eu/mulk/aendggner/EndToEndTest.java98
-rw-r--r--src/test/java/eu/mulk/aendggner/aenderung/parse/InkrafttretensLeserTest.java143
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));
+ }
+}