aboutsummaryrefslogtreecommitdiff
path: root/src/test
diff options
context:
space:
mode:
authorMatthias Andreas Benkard <code@mail.matthias.benkard.de>2026-08-26 07:19:59 +0200
committerMatthias Andreas Benkard <code@mail.matthias.benkard.de>2026-08-26 07:19:59 +0200
commit63e1283230c5457666de90c9af74657b52579909 (patch)
tree901d7c16bfbd9d4c80b801cbac6a22c7667cd936 /src/test
parenta6b42bc533eb2d453ea838b90b93081de3f00c3c (diff)
Die Fassung merkt sich, welche Hefte sie schon trägt
Die Kette führte kein Gedächtnis, und der Grund lag tiefer als die Warnung des Handbuchs besagte. Der Textausgeber schrieb die Standzeile sehr wohl aus; der Textparser aber verwarf alles, was vor dem ersten Normkopf steht. Was das Erzeugnis errechnet hatte, kam mithin standlos zurück, und die Altersrüge konnte auf der eigenen Ausgabe niemals anschlagen — ein stiller Verlust, den erst der Blick auf die Kette sichtbar machte. Das Gesetz führt nunmehr seine Fortschreibungen mit: jedes Heft, das auf seinen Wortlaut angewandt worden ist, mit Bezeichnung und Ausfertigungsdatum. Der Ausgeber schreibt sie als „Fortgeschrieben durch: …“ hinter die Standzeile, der Parser liest beides zurück, und der Rundlauf prüft, dass nichts dabei verloren geht. Trifft ein Heft ein zweites Mal auf dieselbe Fassung, so wird das gerügt; abgebrochen wird nicht, denn ein Befehl bleibt anwendbar, auch wo er sinnlos ist, und verworfen wird hier nichts. Damit ein Heft wiedererkennbar ist, muss es sich nennen können. Der Erkenner erhebt dafür das Ausfertigungsdatum aus der Zeile „Vom …“ — aber nur, wenn das ganze Dokument genau eine solche führt. Ein Sammelheft stellt mehrere Verkündungen nebeneinander, und welche von ihnen es ausmacht, sagt es nicht; ein falsch bezeichnetes Heft wäre schlimmer als ein unbezeichnetes. Aus demselben Datum ergibt sich nebenher die Reihenfolge, in der mehrere Hefte eines Laufes anzuwenden sind, und die Fortwirkung der Altersrüge über die Kette hinweg. Geprüft: mvnw verify, 397 Tests (zuvor 388), reuse lint 191/191. Change-Id: Ice3af51eaec777961dafc997e34391d2fa6d6031
Diffstat (limited to 'src/test')
-rw-r--r--src/test/java/eu/mulk/aendggner/EndToEndTest.java33
-rw-r--r--src/test/java/eu/mulk/aendggner/aenderung/parse/DokumentErkennerTest.java46
-rw-r--r--src/test/java/eu/mulk/aendggner/gesetz/land/LandesRechtTextAusgeberTest.java43
3 files changed, 122 insertions, 0 deletions
diff --git a/src/test/java/eu/mulk/aendggner/EndToEndTest.java b/src/test/java/eu/mulk/aendggner/EndToEndTest.java
index 8cd867e..109bbf6 100644
--- a/src/test/java/eu/mulk/aendggner/EndToEndTest.java
+++ b/src/test/java/eu/mulk/aendggner/EndToEndTest.java
@@ -1667,6 +1667,39 @@ class EndToEndTest {
assertThat(ergebnis.html()).contains("keine Änderungsbefehle");
}
+ /**
+ * Die Kette und ihr Gedächtnis. Die eigene Ausgabe ist wieder Eingabe; sie trägt im Kopf, welche
+ * Hefte auf ihr schon vollzogen sind. Trifft dasselbe Heft ein zweites Mal auf sie, so greifen
+ * seine Befehle abermals — ein Befehl ist eine Anordnung und keine Zustandsbeschreibung —, und
+ * der angefügte Absatz stünde zweimal da. Am Wortlaut allein ist das nicht zu erkennen, wohl aber
+ * an dem, was die Fassung über sich selbst mitführt.
+ */
+ @Test
+ void ketteRuegtDasZweimalAngewandteHeft() 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");
+
+ var erste = Pipeline.erzeugeSynopse(Pipeline.Auftrag.von(xml, List.of(pdf)));
+ assertThat(erste.anzahlAngewandt()).isEqualTo(19);
+ // Das Heft nennt sich nach dem, was im Dokument steht (Ausfertigungsdatum), nicht nach seinem
+ // Dateinamen — sonst hinge die Wiedererkennung an einer Zufälligkeit des Ablageortes.
+ assertThat(erste.neufassung())
+ .containsOnlyOnce("Fortgeschrieben durch: Änderungsgesetz vom 12. Februar 2026");
+
+ var zwischenfassung =
+ new Quelle("UWG-Zwischenfassung.txt", erste.neufassung().getBytes(StandardCharsets.UTF_8));
+ var zweite =
+ Pipeline.erzeugeSynopse(Pipeline.Auftrag.von(zwischenfassung, List.of(Quelle.lies(pdf))));
+
+ assertThat(zweite.html())
+ .contains("Die Fassung trägt „Änderungsgesetz vom 12. Februar 2026“ bereits")
+ .contains("ein zweites Mal angewandt");
+ // Vermerkt bleibt das Heft einmal: Die Liste zählt Hefte, nicht Anwendungen.
+ assertThat(zweite.neufassung())
+ .containsOnlyOnce("Fortgeschrieben durch: Änderungsgesetz vom 12. Februar 2026");
+ }
+
private static Aenderungsbefehl befehlZu(
AenderungsgesetzParser.ParseErgebnis ergebnis, String gliederungsPfad) {
return ergebnis.befehle().stream()
diff --git a/src/test/java/eu/mulk/aendggner/aenderung/parse/DokumentErkennerTest.java b/src/test/java/eu/mulk/aendggner/aenderung/parse/DokumentErkennerTest.java
index aad40d5..4d756a7 100644
--- a/src/test/java/eu/mulk/aendggner/aenderung/parse/DokumentErkennerTest.java
+++ b/src/test/java/eu/mulk/aendggner/aenderung/parse/DokumentErkennerTest.java
@@ -8,6 +8,7 @@ import static org.junit.jupiter.api.Assumptions.assumeTrue;
import eu.mulk.aendggner.aenderung.DokumentArt;
import java.nio.file.Files;
import java.nio.file.Path;
+import java.time.LocalDate;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
@@ -45,6 +46,51 @@ class DokumentErkennerTest {
assertThat(kopf.art()).isEqualTo(erwartet);
}
+ /**
+ * Das Ausfertigungsdatum aus der Zeile „Vom …“. An ihm erkennt die Fassung ein Heft wieder, das
+ * auf sie schon angewandt worden ist; deshalb darf es nicht geraten sein. Ein Sammelheft stellt
+ * mehrere Verkündungen nebeneinander (so das thüringische GVBl. Nr. 2/2026 mit vieren), und
+ * welche von ihnen das Dokument ausmacht, sagt sein Kopf nicht — dann bleibt das Datum leer.
+ */
+ @ParameterizedTest
+ @CsvSource({
+ "UWG/bgbl126s0043_regelungstext.pdf, 2026-02-12",
+ "Brandenburg/GVBl-I-2026-12_FraktG-AendG.pdf, 2026-04-22",
+ "NRW/GV-NRW-2026-S202_22-Rundfunkaenderungsgesetz.pdf, 2026-03-24",
+ "Hessen/GVBl-2026-05_11-AendVO-verkehrsrechtl-Zustaendigkeiten.pdf, 2026-01-28",
+ "Thueringen/GVBl-TH-2026-02_KiGaFinanzVO-AendVO-ua.pdf, ",
+ "GEG/BT-Drs-20-6875_Regierungsentwurf.pdf, ",
+ })
+ void liestDasAusfertigungsdatum(String datei, String erwartet) throws Exception {
+ var pfad = SAMPLEDATA.resolve(datei.strip());
+ assumeTrue(Files.exists(pfad), "Beispieldatei fehlt: " + datei);
+
+ var kopf = DokumentErkenner.erkenne(new PatchTextExtraktor().extrahiere(pfad));
+
+ assertThat(kopf.ausfertigung())
+ .isEqualTo(erwartet == null ? null : LocalDate.parse(erwartet.strip()));
+ }
+
+ /**
+ * Woran ein Heft sich nennt: an seiner Drucksachennummer, sonst an seinem Ausfertigungsdatum. Der
+ * Dateiname bleibt außen vor — er ist eine Zufälligkeit des Ablageortes, und an der Bezeichnung
+ * hängt die Wiedererkennung in der Kette.
+ */
+ @Test
+ void bezeichnetSichNachDemDokumentUndNichtNachDerDatei() throws Exception {
+ var gesetzblatt = SAMPLEDATA.resolve("UWG/bgbl126s0043_regelungstext.pdf");
+ var drucksache = SAMPLEDATA.resolve("UWG/BT-Drs-21-1855_Regierungsentwurf.pdf");
+ assumeTrue(Files.exists(gesetzblatt) && Files.exists(drucksache), "UWG-Beispieldaten fehlen");
+
+ assertThat(
+ DokumentErkenner.erkenne(new PatchTextExtraktor().extrahiere(gesetzblatt))
+ .anzeigeName())
+ .isEqualTo("Änderungsgesetz vom 12. Februar 2026");
+ assertThat(
+ DokumentErkenner.erkenne(new PatchTextExtraktor().extrahiere(drucksache)).anzeigeName())
+ .isEqualTo("Gesetzentwurf Drs. 21/1855");
+ }
+
/** Die Drucksachennummern stiften die Verbindung zwischen Antrag und Entwurf. */
@Test
void liestEigeneUndBezogeneDrucksachennummern() throws Exception {
diff --git a/src/test/java/eu/mulk/aendggner/gesetz/land/LandesRechtTextAusgeberTest.java b/src/test/java/eu/mulk/aendggner/gesetz/land/LandesRechtTextAusgeberTest.java
index e620a03..6d6eab2 100644
--- a/src/test/java/eu/mulk/aendggner/gesetz/land/LandesRechtTextAusgeberTest.java
+++ b/src/test/java/eu/mulk/aendggner/gesetz/land/LandesRechtTextAusgeberTest.java
@@ -5,12 +5,16 @@ package eu.mulk.aendggner.gesetz.land;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assumptions.assumeTrue;
+import eu.mulk.aendggner.gesetz.Fortschreibung;
import eu.mulk.aendggner.gesetz.Gesetz;
+import eu.mulk.aendggner.gesetz.Stand;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
+import java.time.LocalDate;
import java.util.List;
import java.util.stream.Stream;
+import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
@@ -52,6 +56,8 @@ class LandesRechtTextAusgeberTest {
assertThat(wiederGelesen.kurzue()).isEqualTo(gelesen.kurzue());
assertThat(bezeichnungen(wiederGelesen)).isEqualTo(bezeichnungen(gelesen));
assertThat(wiederGelesen.gliederungen()).isEqualTo(gelesen.gliederungen());
+ assertThat(wiederGelesen.stand()).isEqualTo(gelesen.stand());
+ assertThat(wiederGelesen.fortschreibungen()).isEqualTo(gelesen.fortschreibungen());
for (var soll : gelesen.normen()) {
var ist = wiederGelesen.norm(soll.enbez()).orElseThrow();
@@ -63,6 +69,43 @@ class LandesRechtTextAusgeberTest {
}
}
+ /**
+ * Der Kopf trägt das Gedächtnis der Kette: Standangabe und angewandte Hefte müssen den Rundlauf
+ * überstehen, sonst wüsste die nächste Stufe nicht, was der Wortlaut schon trägt — und dasselbe
+ * Heft ließe sich unbemerkt ein zweites Mal anwenden.
+ */
+ @Test
+ void rundlaufErhaeltStandUndHefte() throws IOException {
+ var datei = SAMPLEDATA.resolve("Brandenburg/FraktG-alt.txt");
+ assumeTrue(Files.exists(datei), "Beispieldaten fehlen");
+ var gelesen =
+ new LandesRechtLoader()
+ .load(datei)
+ .mitFortschreibung(
+ new Fortschreibung("Änderungsgesetz vom 22. April 2026", LocalDate.of(2026, 4, 22)))
+ .mitFortschreibung(new Fortschreibung("Gesetzentwurf Drs. 7/4711", null));
+ var mitStand =
+ new Gesetz(
+ gelesen.jurabk(),
+ gelesen.langue(),
+ gelesen.kurzue(),
+ gelesen.normen(),
+ gelesen.gliederungen(),
+ Stand.aus("Zuletzt geändert durch Art. 2 G v. 15.12.2022 I Nr. 26"),
+ gelesen.fortschreibungen());
+
+ var wiederGelesen = LandesRechtTextParser.parse(LandesRechtTextAusgeber.ausgeben(mitStand));
+
+ assertThat(wiederGelesen.stand()).isEqualTo(mitStand.stand());
+ assertThat(wiederGelesen.stand().juengsteAenderung()).isEqualTo(LocalDate.of(2022, 12, 15));
+ assertThat(wiederGelesen.fortschreibungen()).isEqualTo(mitStand.fortschreibungen());
+ // Der Wortlautstand folgt dem jüngsten Heft, nicht der Standzeile: Wer fortschreibt, macht den
+ // Wortlaut jünger, als die Quelle ihn ausweist.
+ assertThat(wiederGelesen.wortlautStand()).isEqualTo(LocalDate.of(2026, 4, 22));
+ assertThat(wiederGelesen.traegt(new Fortschreibung("Änderungsgesetz vom 22. April 2026", null)))
+ .isTrue();
+ }
+
private static List<String> bezeichnungen(Gesetz gesetz) {
return gesetz.normen().stream().map(n -> n.enbez()).toList();
}