diff options
| author | Matthias Andreas Benkard <code@mail.matthias.benkard.de> | 2026-07-13 07:09:19 +0200 |
|---|---|---|
| committer | Matthias Andreas Benkard <code@mail.matthias.benkard.de> | 2026-07-13 07:09:19 +0200 |
| commit | 58e19982f25ae6433d7837454bb54b8edc37589c (patch) | |
| tree | ba5f4f8712150e0a9aae195e59290393831845a4 /src | |
| parent | d1e2facd01c88dbd4758fbc2a64bf9e01abca4e7 (diff) | |
Add tests, usage documentation, and .claude/ to .gitignore.
Unit tests for the gii-norm loader, text cleanup, quote extraction,
command recognition (literal sentences from the sample amendment act),
sentence splitting, and command application on a synthetic mini law;
an HTML renderer test including escaping; and an end-to-end smoke test
on the IfSG sample data that skips itself when the (untracked) sample
files are absent. The README gains a German usage section.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Change-Id: I7f02ff349f9c3bd34fa90a2a84678b9e6fad697d
Diffstat (limited to 'src')
9 files changed, 1145 insertions, 0 deletions
diff --git a/src/test/java/eu/mulk/aendggner/EndToEndTest.java b/src/test/java/eu/mulk/aendggner/EndToEndTest.java new file mode 100644 index 0000000..57e745f --- /dev/null +++ b/src/test/java/eu/mulk/aendggner/EndToEndTest.java @@ -0,0 +1,72 @@ +package eu.mulk.aendggner; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assumptions.assumeTrue; + +import eu.mulk.aendggner.aenderung.Aenderungsbefehl.UnbekannterBefehl; +import eu.mulk.aendggner.aenderung.parse.AenderungsgesetzParser; +import eu.mulk.aendggner.aenderung.parse.PatchTextExtraktor; +import eu.mulk.aendggner.aenderung.parse.TextBereiniger; +import eu.mulk.aendggner.anwendung.BefehlAnwender; +import eu.mulk.aendggner.gesetz.gii.GiiXmlLoader; +import eu.mulk.aendggner.synopse.HtmlRenderer; +import eu.mulk.aendggner.synopse.SynopseBuilder; +import java.nio.file.Files; +import java.nio.file.Path; +import org.junit.jupiter.api.Test; + +/** + * Smoke-Test der Gesamtpipeline auf den IfSG-Beispieldaten (nicht eingecheckt; der Test wird + * übersprungen, wenn sie fehlen). + * + * <p>Achtung: Das Beispiel-XML ist bereits konsolidiert (Stand 18.11.2020, enthält die + * Art.-1-Änderungen des Beispiel-Änderungsgesetzes schon). Die Assertions prüfen deshalb + * Parse-Zahlen und sauberes Reporting, nicht die Diff-Korrektheit. + */ +class EndToEndTest { + + private static final Path STAMMGESETZ = + Path.of("src/main/resources/sampledata/IfSG/BJNR104510000.xml"); + private static final Path AENDERUNGSGESETZ = + Path.of("src/main/resources/sampledata/IfSG/bgbl120s2397_78991.pdf"); + + @Test + void gesamtePipelineAufIfSgBeispiel() throws Exception { + assumeTrue(Files.exists(STAMMGESETZ), "IfSG-Beispieldaten fehlen"); + assumeTrue(Files.exists(AENDERUNGSGESETZ), "BGBl-Beispiel-PDF fehlt"); + + // 1. Stammgesetz laden. + var gesetz = new GiiXmlLoader().load(STAMMGESETZ); + assertThat(gesetz.jurabk()).isEqualTo("IfSG"); + assertThat(gesetz.normen()).hasSize(86); + assertThat(gesetz.norm("§ 5").orElseThrow().absaetze()).hasSize(8); + + // 2. Änderungsgesetz-Text extrahieren. + var text = TextBereiniger.bereinige(new PatchTextExtraktor().extrahiere(AENDERUNGSGESETZ)); + assertThat(text).contains("wird wie folgt geändert"); + assertThat(text).contains("Nach § 28 wird folgender § 28a eingefügt"); + + // 3. Befehle parsen: Artikel 1 und 2 betreffen das IfSG. + var parseErgebnis = new AenderungsgesetzParser().parse(text, gesetz, null); + assertThat(parseErgebnis.artikel()).containsExactly("1", "2"); + assertThat(parseErgebnis.befehle().size()).isGreaterThanOrEqualTo(50); + var typisiert = + parseErgebnis.befehle().stream().filter(b -> !(b instanceof UnbekannterBefehl)).count(); + assertThat(typisiert).isGreaterThanOrEqualTo(40); + // Das Original-BGBl enthält ein überzähliges Anführungszeichen (Artikel 3) — muss als + // Warnung gemeldet werden, nicht als Abbruch. + assertThat(parseErgebnis.warnungen()).isNotEmpty(); + + // 4. Anwenden: wirft nicht; jeder Befehl erhält einen Protokolleintrag. + var anwendung = BefehlAnwender.anwenden(gesetz, parseErgebnis.befehle()); + assertThat(anwendung.protokoll()).hasSameSizeAs(parseErgebnis.befehle()); + assertThat(anwendung.anzahlAngewandt()).isGreaterThan(0); + + // 5. Synopse rendern. + var synopse = SynopseBuilder.baue(gesetz, anwendung, parseErgebnis.warnungen(), false); + var html = HtmlRenderer.rendere(synopse, "E2E-Test"); + assertThat(html).contains("Alte Fassung").contains("Neue Fassung"); + assertThat(html).contains("Manuell prüfen"); + assertThat(html).containsAnyOf("<del>", "<ins>"); + } +} diff --git a/src/test/java/eu/mulk/aendggner/aenderung/parse/BefehlErkennerTest.java b/src/test/java/eu/mulk/aendggner/aenderung/parse/BefehlErkennerTest.java new file mode 100644 index 0000000..c8f9cb8 --- /dev/null +++ b/src/test/java/eu/mulk/aendggner/aenderung/parse/BefehlErkennerTest.java @@ -0,0 +1,266 @@ +package eu.mulk.aendggner.aenderung.parse; + +import static org.assertj.core.api.Assertions.assertThat; + +import eu.mulk.aendggner.aenderung.Aenderungsbefehl; +import eu.mulk.aendggner.aenderung.Aenderungsbefehl.Anfuegung; +import eu.mulk.aendggner.aenderung.Aenderungsbefehl.Aufhebung; +import eu.mulk.aendggner.aenderung.Aenderungsbefehl.Ebene; +import eu.mulk.aendggner.aenderung.Aenderungsbefehl.Ersetzung; +import eu.mulk.aendggner.aenderung.Aenderungsbefehl.Neufassung; +import eu.mulk.aendggner.aenderung.Aenderungsbefehl.Streichung; +import eu.mulk.aendggner.aenderung.Aenderungsbefehl.StrukturEinfuegung; +import eu.mulk.aendggner.aenderung.Aenderungsbefehl.Umnummerierung; +import eu.mulk.aendggner.aenderung.Aenderungsbefehl.WoerterEinfuegung; +import eu.mulk.aendggner.aenderung.Aenderungsbefehl.WortAnker; +import eu.mulk.aendggner.aenderung.Provenienz; +import eu.mulk.aendggner.aenderung.Stelle; +import java.util.List; +import java.util.Optional; +import org.junit.jupiter.api.Test; + +class BefehlErkennerTest { + + private static final Provenienz PROV = new Provenienz("1", "1.", "(Test)"); + + /** Erkennt einen Befehl aus echtem Gesetzestext (mit „…“-Zitaten). */ + private static Optional<Aenderungsbefehl> erkenne(String befehlsSatz, Stelle kontext) { + var zitate = ZitatExtraktor.extrahiere(befehlsSatz); + var text = zitate.text().replaceAll("\\s+", " ").strip(); + return BefehlErkenner.erkenne(text, kontext, zitate, PROV); + } + + @Test + void erkenntErsetzung() { + var befehl = + erkenne( + "In § 20 Absatz 6 Satz 1 werden die Wörter „der Bevölkerung“ durch die Wörter" + + " „der Bevölkerung oder von Bevölkerungsteilen“ ersetzt.", + Stelle.LEER); + + assertThat(befehl).containsInstanceOf(Ersetzung.class); + var ersetzung = (Ersetzung) befehl.orElseThrow(); + assertThat(ersetzung.stelle().anzeigeText()).isEqualTo("§ 20 Absatz 6 Satz 1"); + assertThat(ersetzung.alt()).isEqualTo("der Bevölkerung"); + assertThat(ersetzung.neu()).isEqualTo("der Bevölkerung oder von Bevölkerungsteilen"); + assertThat(ersetzung.jeweils()).isFalse(); + } + + @Test + void erkenntJeweilsErsetzung() { + var befehl = + erkenne( + "In § 5 werden jeweils die Wörter „epidemischen Lage“ durch die Wörter" + + " „epidemischen Lage von nationaler Tragweite“ ersetzt.", + Stelle.LEER); + + assertThat(befehl).containsInstanceOf(Ersetzung.class); + assertThat(((Ersetzung) befehl.orElseThrow()).jeweils()).isTrue(); + } + + @Test + void erkenntSatzzeichenErsetzung() { + var befehl = + erkenne( + "In Nummer 16 wird der Punkt am Ende durch ein Komma ersetzt.", + new Stelle(List.of(new Stelle.Paragraph("2")))); + + assertThat(befehl).containsInstanceOf(Ersetzung.class); + var ersetzung = (Ersetzung) befehl.orElseThrow(); + assertThat(ersetzung.stelle().anzeigeText()).isEqualTo("§ 2 Nummer 16"); + assertThat(ersetzung.alt()).isEqualTo("."); + assertThat(ersetzung.neu()).isEqualTo(","); + assertThat(ersetzung.amEnde()).isTrue(); + } + + @Test + void erkenntNeufassung() { + var befehl = + erkenne( + "§ 5 Absatz 1 wird wie folgt gefasst: „(1) Der Deutsche Bundestag kann eine" + + " epidemische Lage von nationaler Tragweite feststellen.“", + Stelle.LEER); + + assertThat(befehl).containsInstanceOf(Neufassung.class); + var neufassung = (Neufassung) befehl.orElseThrow(); + assertThat(neufassung.stelle().anzeigeText()).isEqualTo("§ 5 Absatz 1"); + assertThat(neufassung.neuerText()).startsWith("(1) Der Deutsche Bundestag"); + } + + @Test + void erkenntParagraphEinfuegung() { + // Echter Befehl aus Art. 1 Nr. 17 des Dritten Bevölkerungsschutzgesetzes. + var befehl = + erkenne( + "Nach § 28 wird folgender § 28a eingefügt: „§ 28a Besondere Schutzmaßnahmen zur" + + " Verhinderung der Verbreitung der Coronavirus-Krankheit-2019 (COVID-19)" + + " (1) Notwendige Schutzmaßnahmen im Sinne des § 28 Absatz 1 Satz 1 und 2" + + " können … sein.“", + Stelle.LEER); + + assertThat(befehl).containsInstanceOf(StrukturEinfuegung.class); + var einfuegung = (StrukturEinfuegung) befehl.orElseThrow(); + assertThat(einfuegung.stelle().anzeigeText()).isEqualTo("§ 28"); + assertThat(einfuegung.vorher()).isFalse(); + assertThat(einfuegung.ebene()).isEqualTo(Ebene.PARAGRAPH); + assertThat(einfuegung.bezeichnung()).isEqualTo("28a"); + assertThat(einfuegung.text()).startsWith("§ 28a Besondere Schutzmaßnahmen"); + } + + @Test + void erkenntSatzEinfuegung() { + var befehl = + erkenne( + "Nach Satz 1 wird folgender Satz eingefügt: „Das Robert Koch-Institut ist der" + + " Verantwortliche im Sinne des Datenschutzrechts.“", + new Stelle(List.of(new Stelle.Paragraph("14"), new Stelle.AbsatzNr("1")))); + + assertThat(befehl).containsInstanceOf(StrukturEinfuegung.class); + var einfuegung = (StrukturEinfuegung) befehl.orElseThrow(); + assertThat(einfuegung.stelle().anzeigeText()).isEqualTo("§ 14 Absatz 1 Satz 1"); + assertThat(einfuegung.ebene()).isEqualTo(Ebene.SATZ); + assertThat(einfuegung.bezeichnung()).isNull(); + } + + @Test + void erkenntWoerterEinfuegung() { + var befehl = + erkenne( + "In Nummer 3 werden nach dem Wort „Kontaktdaten“ die Wörter „sowie die lebenslange" + + " Arztnummer (LANR)“ eingefügt.", + Stelle.LEER); + + assertThat(befehl).containsInstanceOf(WoerterEinfuegung.class); + var einfuegung = (WoerterEinfuegung) befehl.orElseThrow(); + assertThat(einfuegung.anker()).isEqualTo(new WortAnker.NachWoertern("Kontaktdaten")); + assertThat(einfuegung.woerter()).startsWith("sowie die lebenslange"); + } + + @Test + void erkenntWoerterEinfuegungVorKommaAmEnde() { + var befehl = + erkenne( + "In Nummer 2 werden vor dem Komma am Ende die Wörter „sowie Zahnärzte und" + + " Tierärzte“ eingefügt.", + Stelle.LEER); + + assertThat(befehl).containsInstanceOf(WoerterEinfuegung.class); + assertThat(((WoerterEinfuegung) befehl.orElseThrow()).anker()) + .isEqualTo(new WortAnker.VorKommaAmEnde()); + } + + @Test + void erkenntAnfuegung() { + var befehl = + erkenne( + "Folgender Absatz 8 wird angefügt: „(8) Aufgrund einer epidemischen" + + " Lage von nationaler Tragweite kann das Bundesministerium für Gesundheit Hilfe" + + " leisten.“", + new Stelle(List.of(new Stelle.Paragraph("5")))); + + assertThat(befehl).containsInstanceOf(Anfuegung.class); + var anfuegung = (Anfuegung) befehl.orElseThrow(); + assertThat(anfuegung.stelle().anzeigeText()).isEqualTo("§ 5"); + assertThat(anfuegung.ebene()).isEqualTo(Ebene.ABSATZ); + assertThat(anfuegung.bezeichnung()).isEqualTo("8"); + } + + @Test + void erkenntSatzAnfuegungMitDativStelle() { + var befehl = + erkenne( + "Dem Absatz 6 wird folgender Satz angefügt: „Die Kontrolle obliegt dem" + + " Bundesbeauftragten.“", + new Stelle(List.of(new Stelle.Paragraph("14")))); + + assertThat(befehl).containsInstanceOf(Anfuegung.class); + var anfuegung = (Anfuegung) befehl.orElseThrow(); + assertThat(anfuegung.stelle().anzeigeText()).isEqualTo("§ 14 Absatz 6"); + assertThat(anfuegung.ebene()).isEqualTo(Ebene.SATZ); + } + + @Test + void erkenntAufhebungMitKontext() { + var kontext = new Stelle(List.of(new Stelle.Paragraph("56"))); + var befehl = erkenne("Absatz 3 wird aufgehoben.", kontext); + + assertThat(befehl).containsInstanceOf(Aufhebung.class); + assertThat(befehl.orElseThrow().stelle().anzeigeText()).isEqualTo("§ 56 Absatz 3"); + } + + @Test + void erkenntStreichung() { + var befehl = + erkenne( + "In Satz 2 werden die Wörter „bis zum Ende“ gestrichen.", + new Stelle(List.of(new Stelle.Paragraph("5"), new Stelle.AbsatzNr("1")))); + + assertThat(befehl).containsInstanceOf(Streichung.class); + var streichung = (Streichung) befehl.orElseThrow(); + assertThat(streichung.stelle().anzeigeText()).isEqualTo("§ 5 Absatz 1 Satz 2"); + assertThat(streichung.woerter()).isEqualTo("bis zum Ende"); + } + + @Test + void erkenntUmnummerierung() { + var befehl = + erkenne("Absatz 4 wird Absatz 3.", new Stelle(List.of(new Stelle.Paragraph("10")))); + + assertThat(befehl).containsInstanceOf(Umnummerierung.class); + var umnummerierung = (Umnummerierung) befehl.orElseThrow(); + assertThat(umnummerierung.stelle().anzeigeText()).isEqualTo("§ 10 Absatz 4"); + assertThat(umnummerierung.neu().anzeigeText()).isEqualTo("§ 10 Absatz 3"); + } + + @Test + void erkenntInhaltsuebersichtEinfuegung() { + var befehl = + erkenne( + "In der Inhaltsübersicht wird nach der Angabe zu § 28 folgende Angabe eingefügt:" + + " „§ 28a Besondere Schutzmaßnahmen“.", + Stelle.LEER); + + assertThat(befehl).containsInstanceOf(WoerterEinfuegung.class); + assertThat(befehl.orElseThrow().stelle().betrifftInhaltsuebersicht()).isTrue(); + } + + @Test + void erkenntUeberschriftNeufassung() { + var befehl = + erkenne( + "Die Überschrift wird wie folgt gefasst: „§ 5 Epidemische Lage“.", + new Stelle(List.of(new Stelle.Paragraph("5")))); + + assertThat(befehl).containsInstanceOf(Neufassung.class); + assertThat(befehl.orElseThrow().stelle().betrifftUeberschrift()).isTrue(); + } + + @Test + void erkenntKontextRahmen() { + assertThat(BefehlErkenner.kontextRahmen("§ 2 wird wie folgt geändert:")) + .contains(new Stelle(List.of(new Stelle.Paragraph("2")))); + assertThat(BefehlErkenner.kontextRahmen("Absatz 1 wird wie folgt geändert:")) + .contains(new Stelle(List.of(new Stelle.AbsatzNr("1")))); + assertThat(BefehlErkenner.kontextRahmen("§ 8 Absatz 1 wird wie folgt geändert:")) + .contains(new Stelle(List.of(new Stelle.Paragraph("8"), new Stelle.AbsatzNr("1")))); + assertThat(BefehlErkenner.kontextRahmen("Absatz 3 wird aufgehoben.")).isEmpty(); + } + + @Test + void faelltBeiBereichsbefehlenAufUnbekanntZurueck() { + // Bereichs- und Mehrfachbefehle sind in v1 bewusst nicht unterstützt. + assertThat( + erkenne( + "Die Absätze 2 bis 4 werden durch die folgenden Absätze 2 bis 6 ersetzt:" + + " „(2) Text.“", + Stelle.LEER)) + .isEmpty(); + assertThat(erkenne("Die Nummern 1 bis 3 werden aufgehoben.", Stelle.LEER)).isEmpty(); + assertThat( + erkenne( + "In Absatz 1 Satz 1 und 2 werden die Wörter „alt“ durch die Wörter „neu“" + + " ersetzt.", + Stelle.LEER)) + .isEmpty(); + } +} diff --git a/src/test/java/eu/mulk/aendggner/aenderung/parse/TextBereinigerTest.java b/src/test/java/eu/mulk/aendggner/aenderung/parse/TextBereinigerTest.java new file mode 100644 index 0000000..d8c5a03 --- /dev/null +++ b/src/test/java/eu/mulk/aendggner/aenderung/parse/TextBereinigerTest.java @@ -0,0 +1,49 @@ +package eu.mulk.aendggner.aenderung.parse; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Test; + +class TextBereinigerTest { + + @Test + void entferntKolumnentitelUndSeitenzahlen() { + var roh = + """ + Erster Satz. + 2397 Bundesgesetzblatt Jahrgang 2020 Teil I Nr. 52, ausgegeben zu Bonn am 18. November 2020 + Das Bundesgesetzblatt im Internet: www.bundesgesetzblatt.de | Ein Service des Bundesanzeiger Verlag + 42 + Zweiter Satz."""; + + assertThat(TextBereiniger.bereinige(roh)).isEqualTo("Erster Satz.\nZweiter Satz."); + } + + @Test + void ziehtSilbentrennungZusammen() { + assertThat(TextBereiniger.bereinige("die Bundes-\nregierung")).isEqualTo("die Bundesregierung"); + } + + @Test + void ziehtSilbentrennungUeberLeerzeilenZusammen() { + assertThat(TextBereiniger.bereinige("verhält-\n\n\nnismäßig")).isEqualTo("verhältnismäßig"); + } + + @Test + void verbindetUmbrocheneKompositaMitBindestrich() { + assertThat(TextBereiniger.bereinige("der Coronavirus-\nKrankheit-2019")) + .isEqualTo("der Coronavirus-Krankheit-2019"); + } + + @Test + void erhaeltSuspensivstricheVorKonjunktionen() { + assertThat(TextBereiniger.bereinige("Wirk-, Ausgangs-\nund Hilfsstoffe")) + .isEqualTo("Wirk-, Ausgangs-\nund Hilfsstoffe"); + } + + @Test + void erhaeltGedankenstricheOhneVorangehendenBuchstaben() { + assertThat(TextBereiniger.bereinige("Preisbildung und -\ngestaltung")) + .isEqualTo("Preisbildung und -\ngestaltung"); + } +} diff --git a/src/test/java/eu/mulk/aendggner/aenderung/parse/ZitatExtraktorTest.java b/src/test/java/eu/mulk/aendggner/aenderung/parse/ZitatExtraktorTest.java new file mode 100644 index 0000000..8194f6b --- /dev/null +++ b/src/test/java/eu/mulk/aendggner/aenderung/parse/ZitatExtraktorTest.java @@ -0,0 +1,70 @@ +package eu.mulk.aendggner.aenderung.parse; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Test; + +class ZitatExtraktorTest { + + @Test + void ersetztZitateDurchPlatzhalter() { + var ergebnis = ZitatExtraktor.extrahiere("Das Wort „alt“ wird durch das Wort „neu“ ersetzt."); + + assertThat(ergebnis.text()).isEqualTo("Das Wort «0» wird durch das Wort «1» ersetzt."); + assertThat(ergebnis.zitat(0)).isEqualTo("alt"); + assertThat(ergebnis.zitat(1)).isEqualTo("neu"); + assertThat(ergebnis.warnungen()).isEmpty(); + } + + @Test + void schlaegtGeschachtelteZitateDemAeusserenZu() { + var ergebnis = ZitatExtraktor.extrahiere("Es wird gefasst: „Satz mit „innerem“ Zitat.“"); + + assertThat(ergebnis.text()).isEqualTo("Es wird gefasst: «0»"); + assertThat(ergebnis.zitat(0)).isEqualTo("Satz mit „innerem“ Zitat."); + } + + @Test + void laesstEinfacheZitateUnangetastet() { + var ergebnis = ZitatExtraktor.extrahiere("„Wort ‚einfach‘ zitiert“"); + + assertThat(ergebnis.zitat(0)).isEqualTo("Wort ‚einfach‘ zitiert"); + } + + @Test + void verkraftetMehrzeiligeZitate() { + var ergebnis = + ZitatExtraktor.extrahiere("Gefasst: „(1) Erster Absatz.\n\n(2) Zweiter Absatz.“ Ende."); + + assertThat(ergebnis.text()).isEqualTo("Gefasst: «0» Ende."); + assertThat(ergebnis.zitat(0)).contains("(2) Zweiter Absatz."); + } + + @Test + void meldetUeberzaehligesSchliessendesAnfuehrungszeichen() { + // Kommt in echten BGBl-Texten vor (z.B. BGBl. I 2020 S. 2397, Artikel 3). + var ergebnis = ZitatExtraktor.extrahiere("wird „alt“ ersetzt.“ Danach normal."); + + assertThat(ergebnis.text()).isEqualTo("wird «0» ersetzt.“ Danach normal."); + assertThat(ergebnis.warnungen()).hasSize(1); + assertThat(ergebnis.warnungen().get(0)).contains("ohne öffnendes"); + } + + @Test + void meldetAmTextendeOffeneZitate() { + var ergebnis = ZitatExtraktor.extrahiere("Es wird gefasst: „nie geschlossen"); + + assertThat(ergebnis.text()).isEqualTo("Es wird gefasst: «0»"); + assertThat(ergebnis.zitat(0)).isEqualTo("nie geschlossen"); + assertThat(ergebnis.warnungen()).hasSize(1); + assertThat(ergebnis.warnungen().get(0)).contains("offen"); + } + + @Test + void stelltZitateWiederHer() { + var original = "Das Wort „alt“ wird durch das Wort „neu“ ersetzt."; + var ergebnis = ZitatExtraktor.extrahiere(original); + + assertThat(ergebnis.stelleZitateWiederHer(ergebnis.text())).isEqualTo(original); + } +} diff --git a/src/test/java/eu/mulk/aendggner/anwendung/BefehlAnwenderTest.java b/src/test/java/eu/mulk/aendggner/anwendung/BefehlAnwenderTest.java new file mode 100644 index 0000000..26557d1 --- /dev/null +++ b/src/test/java/eu/mulk/aendggner/anwendung/BefehlAnwenderTest.java @@ -0,0 +1,373 @@ +package eu.mulk.aendggner.anwendung; + +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.Ebene; +import eu.mulk.aendggner.aenderung.Aenderungsbefehl.Ersetzung; +import eu.mulk.aendggner.aenderung.Aenderungsbefehl.Neufassung; +import eu.mulk.aendggner.aenderung.Aenderungsbefehl.Streichung; +import eu.mulk.aendggner.aenderung.Aenderungsbefehl.StrukturEinfuegung; +import eu.mulk.aendggner.aenderung.Aenderungsbefehl.Umnummerierung; +import eu.mulk.aendggner.aenderung.Aenderungsbefehl.UnbekannterBefehl; +import eu.mulk.aendggner.aenderung.Aenderungsbefehl.WoerterEinfuegung; +import eu.mulk.aendggner.aenderung.Aenderungsbefehl.WortAnker; +import eu.mulk.aendggner.aenderung.Provenienz; +import eu.mulk.aendggner.aenderung.Stelle; +import eu.mulk.aendggner.anwendung.BefehlAnwender.Status; +import eu.mulk.aendggner.gesetz.Absatz; +import eu.mulk.aendggner.gesetz.Gesetz; +import eu.mulk.aendggner.gesetz.Norm; +import java.util.List; +import org.junit.jupiter.api.Test; + +class BefehlAnwenderTest { + + private static final Provenienz PROV = new Provenienz("1", "1.", "(Test)"); + + private static Stelle stelle(Stelle.Komponente... komponenten) { + return new Stelle(List.of(komponenten)); + } + + @Test + void wendetErsetzungAn() { + var befehl = + new Ersetzung( + stelle(new Stelle.Paragraph("1"), new Stelle.AbsatzNr("1")), + "die Erprobung", + "die gründliche Erprobung", + false, + false, + PROV); + + var ergebnis = BefehlAnwender.anwenden(gesetz(), List.of(befehl)); + + assertThat(ergebnis.protokoll().get(0).status()).isEqualTo(Status.ANGEWANDT); + assertThat(absatzText(ergebnis.neu(), "§ 1", 0)) + .isEqualTo("Zweck dieses Gesetzes ist die gründliche Erprobung."); + } + + @Test + void meldetFehlendenAltTextAlsManuellPruefen() { + var befehl = + new Ersetzung( + stelle(new Stelle.Paragraph("1"), new Stelle.AbsatzNr("1")), + "gibt es nicht", + "egal", + false, + false, + PROV); + + var ergebnis = BefehlAnwender.anwenden(gesetz(), List.of(befehl)); + + var eintrag = ergebnis.protokoll().get(0); + assertThat(eintrag.status()).isEqualTo(Status.MANUELL_PRUEFEN); + assertThat(eintrag.begruendung()).contains("kommt im Zieltext nicht vor"); + // Das Gesetz bleibt unverändert. + assertThat(absatzText(ergebnis.neu(), "§ 1", 0)) + .isEqualTo("Zweck dieses Gesetzes ist die Erprobung."); + } + + @Test + void meldetMehrdeutigeErsetzungOhneJeweils() { + var befehl = + new Ersetzung( + stelle(new Stelle.Paragraph("2")), "Prüfung", "Untersuchung", false, false, PROV); + + var ergebnis = BefehlAnwender.anwenden(gesetz(), List.of(befehl)); + + assertThat(ergebnis.protokoll().get(0).status()).isEqualTo(Status.MANUELL_PRUEFEN); + assertThat(ergebnis.protokoll().get(0).begruendung()).contains("mehrdeutig"); + } + + @Test + void wendetJeweilsErsetzungAufAlleVorkommenAn() { + var befehl = + new Ersetzung( + stelle(new Stelle.Paragraph("2")), "Prüfung", "Untersuchung", true, false, PROV); + + var ergebnis = BefehlAnwender.anwenden(gesetz(), List.of(befehl)); + + assertThat(ergebnis.protokoll().get(0).status()).isEqualTo(Status.ANGEWANDT); + assertThat(absatzText(ergebnis.neu(), "§ 2", 0)) + .contains("die Untersuchung der Tauglichkeit") + .contains("Die Untersuchung erfolgt"); + } + + @Test + void wendetErsetzungInSatzAn() { + var befehl = + new Ersetzung( + stelle(new Stelle.Paragraph("2"), new Stelle.SatzNr("2")), + "sorgfältig", + "gewissenhaft", + false, + false, + PROV); + + var ergebnis = BefehlAnwender.anwenden(gesetz(), List.of(befehl)); + + assertThat(ergebnis.protokoll().get(0).status()).isEqualTo(Status.ANGEWANDT); + assertThat(absatzText(ergebnis.neu(), "§ 2", 0)).contains("erfolgt gewissenhaft"); + } + + @Test + void wendetErsetzungInNummerAn() { + var befehl = + new Ersetzung( + stelle(new Stelle.Paragraph("1"), new Stelle.AbsatzNr("2"), new Stelle.NummerNr("2")), + "Befehlen", + "Änderungsbefehlen", + false, + false, + PROV); + + var ergebnis = BefehlAnwender.anwenden(gesetz(), List.of(befehl)); + + assertThat(ergebnis.protokoll().get(0).status()).isEqualTo(Status.ANGEWANDT); + assertThat(absatzText(ergebnis.neu(), "§ 1", 1)) + .contains("2. die Anwendung von Änderungsbefehlen und"); + } + + @Test + void wendetPunktZuKommaErsetzungAmEndeAn() { + var befehl = + new Ersetzung( + stelle(new Stelle.Paragraph("1"), new Stelle.AbsatzNr("2"), new Stelle.NummerNr("3")), + ".", + ",", + false, + true, + PROV); + + var ergebnis = BefehlAnwender.anwenden(gesetz(), List.of(befehl)); + + assertThat(ergebnis.protokoll().get(0).status()).isEqualTo(Status.ANGEWANDT); + assertThat(absatzText(ergebnis.neu(), "§ 1", 1)).contains("3. die Ausgabe von Synopsen,"); + } + + @Test + void wendetNeufassungEinesAbsatzesAn() { + var befehl = + new Neufassung( + stelle(new Stelle.Paragraph("1"), new Stelle.AbsatzNr("1")), + "(1) Zweck dieses Gesetzes ist die umfassende Erprobung.", + PROV); + + var ergebnis = BefehlAnwender.anwenden(gesetz(), List.of(befehl)); + + assertThat(ergebnis.protokoll().get(0).status()).isEqualTo(Status.ANGEWANDT); + assertThat(absatzText(ergebnis.neu(), "§ 1", 0)) + .isEqualTo("Zweck dieses Gesetzes ist die umfassende Erprobung."); + } + + @Test + void wendetNeufassungEinesParagraphenAn() { + var befehl = + new Neufassung( + stelle(new Stelle.Paragraph("2")), + "§ 2 Begriffsbestimmungen (1) Erprobung ist die Untersuchung. (2) Bericht ist das" + + " Ergebnis.", + PROV); + + var ergebnis = BefehlAnwender.anwenden(gesetz(), List.of(befehl)); + + assertThat(ergebnis.protokoll().get(0).status()).isEqualTo(Status.ANGEWANDT); + var norm = ergebnis.neu().norm("§ 2").orElseThrow(); + assertThat(norm.titel()).isEqualTo("Begriffsbestimmungen"); + assertThat(norm.absaetze()).hasSize(2); + assertThat(norm.absaetze().get(0).nummer()).isEqualTo("1"); + assertThat(norm.absaetze().get(1).text()).isEqualTo("Bericht ist das Ergebnis."); + } + + @Test + void fuegtParagraphEin() { + var befehl = + new StrukturEinfuegung( + stelle(new Stelle.Paragraph("2")), + false, + Ebene.PARAGRAPH, + "2a", + "§ 2a Verfahren (1) Das Verfahren ist einfach.", + PROV); + + var ergebnis = BefehlAnwender.anwenden(gesetz(), List.of(befehl)); + + assertThat(ergebnis.protokoll().get(0).status()).isEqualTo(Status.ANGEWANDT); + var enbezListe = ergebnis.neu().normen().stream().map(Norm::enbez).toList(); + assertThat(enbezListe).containsExactly("§ 1", "§ 2", "§ 2a", "§ 3"); + var neueNorm = ergebnis.neu().norm("§ 2a").orElseThrow(); + assertThat(neueNorm.titel()).isEqualTo("Verfahren"); + assertThat(neueNorm.absaetze().get(0).text()).isEqualTo("Das Verfahren ist einfach."); + } + + @Test + void meldetBereitsVorhandenenParagraphen() { + var befehl = + new StrukturEinfuegung( + stelle(new Stelle.Paragraph("1")), false, Ebene.PARAGRAPH, "2", "§ 2 Doppelt", PROV); + + var ergebnis = BefehlAnwender.anwenden(gesetz(), List.of(befehl)); + + assertThat(ergebnis.protokoll().get(0).status()).isEqualTo(Status.MANUELL_PRUEFEN); + assertThat(ergebnis.protokoll().get(0).begruendung()).contains("existiert bereits"); + } + + @Test + void fuegtWoerterNachAnkerEin() { + var befehl = + new WoerterEinfuegung( + stelle(new Stelle.Paragraph("1"), new Stelle.AbsatzNr("1")), + new WortAnker.NachWoertern("Gesetzes"), + "und dieser Verordnung", + PROV); + + var ergebnis = BefehlAnwender.anwenden(gesetz(), List.of(befehl)); + + assertThat(ergebnis.protokoll().get(0).status()).isEqualTo(Status.ANGEWANDT); + assertThat(absatzText(ergebnis.neu(), "§ 1", 0)) + .isEqualTo("Zweck dieses Gesetzes und dieser Verordnung ist die Erprobung."); + } + + @Test + void fuegtAbsatzAn() { + var befehl = + new Anfuegung( + stelle(new Stelle.Paragraph("3")), + Ebene.ABSATZ, + "2", + "(2) Es tritt nie außer Kraft.", + PROV); + + var ergebnis = BefehlAnwender.anwenden(gesetz(), List.of(befehl)); + + assertThat(ergebnis.protokoll().get(0).status()).isEqualTo(Status.ANGEWANDT); + var norm = ergebnis.neu().norm("§ 3").orElseThrow(); + assertThat(norm.absaetze()).hasSize(2); + assertThat(norm.absaetze().get(1).nummer()).isEqualTo("2"); + } + + @Test + void hebtParagraphenAuf() { + var befehl = new Aufhebung(stelle(new Stelle.Paragraph("2")), PROV); + + var ergebnis = BefehlAnwender.anwenden(gesetz(), List.of(befehl)); + + assertThat(ergebnis.protokoll().get(0).status()).isEqualTo(Status.ANGEWANDT); + var norm = ergebnis.neu().norm("§ 2").orElseThrow(); + assertThat(norm.weggefallen()).isTrue(); + assertThat(norm.gesamtText()).isEqualTo("(weggefallen)"); + } + + @Test + void hebtAbsatzAuf() { + var befehl = new Aufhebung(stelle(new Stelle.Paragraph("1"), new Stelle.AbsatzNr("2")), PROV); + + var ergebnis = BefehlAnwender.anwenden(gesetz(), List.of(befehl)); + + assertThat(ergebnis.protokoll().get(0).status()).isEqualTo(Status.ANGEWANDT); + assertThat(absatzText(ergebnis.neu(), "§ 1", 1)).isEqualTo("(weggefallen)"); + } + + @Test + void streichtWoerter() { + var befehl = + new Streichung( + stelle(new Stelle.Paragraph("2"), new Stelle.SatzNr("2")), "sorgfältig", PROV); + + var ergebnis = BefehlAnwender.anwenden(gesetz(), List.of(befehl)); + + assertThat(ergebnis.protokoll().get(0).status()).isEqualTo(Status.ANGEWANDT); + assertThat(absatzText(ergebnis.neu(), "§ 2", 0)).contains("Die Prüfung erfolgt."); + } + + @Test + void nummeriertAbsatzUm() { + var befehl = + new Umnummerierung( + stelle(new Stelle.Paragraph("1"), new Stelle.AbsatzNr("2")), + stelle(new Stelle.Paragraph("1"), new Stelle.AbsatzNr("3")), + PROV); + + var ergebnis = BefehlAnwender.anwenden(gesetz(), List.of(befehl)); + + assertThat(ergebnis.protokoll().get(0).status()).isEqualTo(Status.ANGEWANDT); + var norm = ergebnis.neu().norm("§ 1").orElseThrow(); + assertThat(norm.absaetze().get(1).nummer()).isEqualTo("3"); + } + + @Test + void meldetUnbekanntenBefehlAlsManuellPruefen() { + var befehl = new UnbekannterBefehl(Stelle.LEER, "Die Nummern 1 bis 3 werden aufgehoben.", PROV); + + var ergebnis = BefehlAnwender.anwenden(gesetz(), List.of(befehl)); + + assertThat(ergebnis.protokoll().get(0).status()).isEqualTo(Status.MANUELL_PRUEFEN); + assertThat(ergebnis.anzahlManuell()).isEqualTo(1); + assertThat(ergebnis.anzahlAngewandt()).isEqualTo(0); + } + + @Test + void fuegtSatzNachSatzEin() { + var befehl = + new StrukturEinfuegung( + stelle(new Stelle.Paragraph("2"), new Stelle.SatzNr("1")), + false, + Ebene.SATZ, + null, + "Die Tauglichkeit ist zu dokumentieren.", + PROV); + + var ergebnis = BefehlAnwender.anwenden(gesetz(), List.of(befehl)); + + assertThat(ergebnis.protokoll().get(0).status()).isEqualTo(Status.ANGEWANDT); + assertThat(absatzText(ergebnis.neu(), "§ 2", 0)) + .contains( + "Erprobung ist die Prüfung der Tauglichkeit. Die Tauglichkeit ist zu dokumentieren." + + " Die Prüfung erfolgt sorgfältig."); + } + + // --- Helfer -------------------------------------------------------------------------------- + + private static Gesetz gesetz() { + return new Gesetz( + "TestG", + "Gesetz zur Erprobung", + "Testgesetz", + List.of( + new Norm( + "§ 1", + "Zweck", + null, + List.of( + new Absatz("1", "Zweck dieses Gesetzes ist die Erprobung."), + new Absatz( + "2", + "Die Erprobung umfasst\n" + + " 1. das Einlesen von Gesetzen,\n" + + " 2. die Anwendung von Befehlen und\n" + + " 3. die Ausgabe von Synopsen.")), + false), + new Norm( + "§ 2", + "Begriffe", + null, + List.of( + new Absatz( + null, + "Erprobung ist die Prüfung der Tauglichkeit. Die Prüfung erfolgt" + + " sorgfältig. Sie endet mit einem Bericht.")), + false), + new Norm( + "§ 3", + "Schlussvorschriften", + null, + List.of(new Absatz("1", "Dieses Gesetz tritt am 1. Januar 2021 in Kraft.")), + false))); + } + + private static String absatzText(Gesetz gesetz, String enbez, int index) { + return gesetz.norm(enbez).orElseThrow().absaetze().get(index).text(); + } +} diff --git a/src/test/java/eu/mulk/aendggner/anwendung/SatzTeilerTest.java b/src/test/java/eu/mulk/aendggner/anwendung/SatzTeilerTest.java new file mode 100644 index 0000000..7df8f37 --- /dev/null +++ b/src/test/java/eu/mulk/aendggner/anwendung/SatzTeilerTest.java @@ -0,0 +1,53 @@ +package eu.mulk.aendggner.anwendung; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Test; + +class SatzTeilerTest { + + @Test + void teiltEinfacheSaetze() { + assertThat(SatzTeiler.teileTexte("Erster Satz. Zweiter Satz. Dritter Satz.")) + .containsExactly("Erster Satz.", "Zweiter Satz.", "Dritter Satz."); + } + + @Test + void teiltNichtBeiAbkuerzungen() { + assertThat( + SatzTeiler.teileTexte( + "Die Meldung erfolgt nach Abs. 2 und § 5 Nr. 3 des Gesetzes. Sie ist" + + " schriftlich abzugeben.")) + .containsExactly( + "Die Meldung erfolgt nach Abs. 2 und § 5 Nr. 3 des Gesetzes.", + "Sie ist schriftlich abzugeben."); + } + + @Test + void teiltNichtBeiDatumsangaben() { + assertThat( + SatzTeiler.teileTexte( + "Die Regelung gilt bis zum 31. März 2021. Danach tritt sie außer Kraft.")) + .containsExactly( + "Die Regelung gilt bis zum 31. März 2021.", "Danach tritt sie außer Kraft."); + } + + @Test + void teiltVorZitatUndKlammer() { + assertThat(SatzTeiler.teileTexte("Es gilt Satz 1. „Zitat folgt.“ (Klammer folgt.)")).hasSize(3); + } + + @Test + void behandeltAufzaehlungenAlsTeilDesSatzes() { + var text = + "Die Erprobung umfasst\n" + + " 1. das Einlesen,\n" + + " 2. die Anwendung und\n" + + " 3. die Ausgabe. Weitere Einzelheiten regelt die Verordnung."; + var saetze = SatzTeiler.teileTexte(text); + + assertThat(saetze).hasSize(2); + assertThat(saetze.get(0)).contains("2. die Anwendung und"); + assertThat(saetze.get(1)).isEqualTo("Weitere Einzelheiten regelt die Verordnung."); + } +} diff --git a/src/test/java/eu/mulk/aendggner/gesetz/gii/GiiXmlLoaderTest.java b/src/test/java/eu/mulk/aendggner/gesetz/gii/GiiXmlLoaderTest.java new file mode 100644 index 0000000..b3e3f32 --- /dev/null +++ b/src/test/java/eu/mulk/aendggner/gesetz/gii/GiiXmlLoaderTest.java @@ -0,0 +1,83 @@ +package eu.mulk.aendggner.gesetz.gii; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.nio.file.Path; +import org.junit.jupiter.api.Test; + +class GiiXmlLoaderTest { + + private static Path fixture() { + return Path.of("src/test/resources/eu/mulk/aendggner/mini-gii.xml"); + } + + @Test + void liestMetadatenUndNormen() throws Exception { + var gesetz = new GiiXmlLoader().load(fixture()); + + assertThat(gesetz.jurabk()).isEqualTo("TestG"); + assertThat(gesetz.langue()).isEqualTo("Gesetz zur Erprobung des ÄndGgners"); + assertThat(gesetz.kurzue()).isEqualTo("Testgesetz"); + // Die Rahmen-Norm ohne enbez wird übersprungen. + assertThat(gesetz.normen()).hasSize(3); + assertThat(gesetz.normen().get(0).enbez()).isEqualTo("Inhaltsübersicht"); + assertThat(gesetz.norm("§ 1")).isPresent(); + assertThat(gesetz.norm("§ 3")).isEmpty(); + } + + @Test + void liestAbsaetzeMitNummern() throws Exception { + var gesetz = new GiiXmlLoader().load(fixture()); + var paragraph1 = gesetz.norm("§ 1").orElseThrow(); + + assertThat(paragraph1.titel()).isEqualTo("Zweck des Gesetzes"); + assertThat(paragraph1.gliederung()).isNotNull(); + assertThat(paragraph1.gliederung().bezeichnung()).isEqualTo("1. Abschnitt"); + assertThat(paragraph1.absaetze()).hasSize(2); + assertThat(paragraph1.absaetze().get(0).nummer()).isEqualTo("1"); + assertThat(paragraph1.absaetze().get(0).text()) + .isEqualTo("Zweck dieses Gesetzes ist die Erprobung. Die Erprobung erfolgt sorgfältig."); + } + + @Test + void flattetAufzaehlungenAlsEingerueckteZeilen() throws Exception { + var gesetz = new GiiXmlLoader().load(fixture()); + var absatz2 = gesetz.norm("§ 1").orElseThrow().absaetze().get(1); + + assertThat(absatz2.nummer()).isEqualTo("2"); + var zeilen = absatz2.text().lines().toList(); + assertThat(zeilen.get(0)).isEqualTo("Die Erprobung umfasst"); + assertThat(zeilen).anyMatch(z -> z.strip().startsWith("1. das Einlesen")); + assertThat(zeilen).anyMatch(z -> z.strip().startsWith("a) Ersetzungen")); + // Fließtext nach der Aufzählung bleibt erhalten. + assertThat(absatz2.text()).contains("Weitere Einzelheiten regelt die Rechtsverordnung."); + // Geschachtelte Aufzählung ist tiefer eingerückt als die äußere. + var aeussere = + zeilen.stream().filter(z -> z.strip().startsWith("1.")).findFirst().orElseThrow(); + var innere = zeilen.stream().filter(z -> z.strip().startsWith("a)")).findFirst().orElseThrow(); + assertThat(einrueckung(innere)).isGreaterThan(einrueckung(aeussere)); + } + + @Test + void flattetTabellenZuZeilen() throws Exception { + var gesetz = new GiiXmlLoader().load(fixture()); + var inhaltsuebersicht = gesetz.norm("Inhaltsübersicht").orElseThrow(); + + assertThat(inhaltsuebersicht.absaetze()).hasSize(1); + assertThat(inhaltsuebersicht.absaetze().get(0).text()) + .contains("§ 1 | Zweck des Gesetzes") + .contains("§ 2 | Begriffsbestimmungen"); + } + + @Test + void erkenntWeggefalleneNormen() throws Exception { + var gesetz = new GiiXmlLoader().load(fixture()); + + assertThat(gesetz.norm("§ 2").orElseThrow().weggefallen()).isTrue(); + assertThat(gesetz.norm("§ 1").orElseThrow().weggefallen()).isFalse(); + } + + private static int einrueckung(String zeile) { + return zeile.length() - zeile.stripLeading().length(); + } +} diff --git a/src/test/java/eu/mulk/aendggner/synopse/HtmlRendererTest.java b/src/test/java/eu/mulk/aendggner/synopse/HtmlRendererTest.java new file mode 100644 index 0000000..b82778c --- /dev/null +++ b/src/test/java/eu/mulk/aendggner/synopse/HtmlRendererTest.java @@ -0,0 +1,98 @@ +package eu.mulk.aendggner.synopse; + +import static org.assertj.core.api.Assertions.assertThat; + +import eu.mulk.aendggner.aenderung.Aenderungsbefehl.Ersetzung; +import eu.mulk.aendggner.aenderung.Aenderungsbefehl.UnbekannterBefehl; +import eu.mulk.aendggner.aenderung.Provenienz; +import eu.mulk.aendggner.aenderung.Stelle; +import eu.mulk.aendggner.anwendung.BefehlAnwender; +import eu.mulk.aendggner.gesetz.Absatz; +import eu.mulk.aendggner.gesetz.Gesetz; +import eu.mulk.aendggner.gesetz.Norm; +import java.util.List; +import org.junit.jupiter.api.Test; + +class HtmlRendererTest { + + private static final Provenienz PROV = new Provenienz("1", "1.", "In § 1 wird … ersetzt."); + + private static Gesetz gesetz(String absatzText) { + return new Gesetz( + "TestG", + "Testgesetz", + "TestG", + List.of(new Norm("§ 1", "Zweck", null, List.of(new Absatz("1", absatzText)), false))); + } + + private static Synopse synopse() { + var alt = gesetz("Der alte Wortlaut gilt."); + var befehl = + new Ersetzung( + new Stelle(List.of(new Stelle.Paragraph("1"))), "alte", "neue", false, false, PROV); + var anwendung = BefehlAnwender.anwenden(alt, List.of(befehl)); + return SynopseBuilder.baue(alt, anwendung, List.of("Eine Testwarnung."), false); + } + + @Test + void rendertBeideSpaltenMitDiffTags() { + var html = HtmlRenderer.rendere(synopse(), "test.xml + test.pdf"); + + assertThat(html).contains("Alte Fassung").contains("Neue Fassung"); + assertThat(html).contains("<del>alte</del>").contains("<ins>neue</ins>"); + assertThat(html).contains("§ 1 — Zweck"); + } + + @Test + void escapetHtmlInGesetzestexten() { + var alt = gesetz("Böser <script>alert('x')</script> Text & mehr."); + var anwendung = BefehlAnwender.anwenden(alt, List.of()); + var synopse = SynopseBuilder.baue(alt, anwendung, List.of(), true); + + var html = HtmlRenderer.rendere(synopse, "quelle"); + + assertThat(html).doesNotContain("<script>"); + assertThat(html).contains("<script>"); + assertThat(html).contains("& mehr."); + } + + @Test + void listetManuellZuPruefendeBefehleAuf() { + var alt = gesetz("Text."); + var prov = new Provenienz("1", "2.", "Die Nummern 1 bis 3 werden aufgehoben."); + var befehl = new UnbekannterBefehl(Stelle.LEER, "Die Nummern 1 bis 3 werden aufgehoben.", prov); + var anwendung = BefehlAnwender.anwenden(alt, List.of(befehl)); + var synopse = SynopseBuilder.baue(alt, anwendung, List.of(), false); + + var html = HtmlRenderer.rendere(synopse, "quelle"); + + assertThat(html).contains("Manuell prüfen"); + assertThat(html).contains("Die Nummern 1 bis 3 werden aufgehoben."); + } + + @Test + void zeigtWarnungenAn() { + var html = HtmlRenderer.rendere(synopse(), "quelle"); + + assertThat(html).contains("Eine Testwarnung."); + } + + @Test + void markiertNeueNormenAlsEingefuegt() { + var alt = gesetz("Text."); + var neuGesetz = + alt.mitNormen( + List.of( + alt.normen().get(0), + new Norm("§ 1a", "Neu", null, List.of(new Absatz(null, "Neuer Inhalt.")), false))); + var anwendung = new BefehlAnwender.AnwendungsErgebnis(neuGesetz, List.of()); + var synopse = SynopseBuilder.baue(alt, anwendung, List.of(), false); + + var html = HtmlRenderer.rendere(synopse, "quelle"); + + assertThat(html).contains("§ 1a"); + assertThat(html).contains("neu</span>"); + assertThat(html).contains("(nicht vorhanden)"); + assertThat(html).contains("<ins>Neuer Inhalt.</ins>"); + } +} diff --git a/src/test/resources/eu/mulk/aendggner/mini-gii.xml b/src/test/resources/eu/mulk/aendggner/mini-gii.xml new file mode 100644 index 0000000..866f81b --- /dev/null +++ b/src/test/resources/eu/mulk/aendggner/mini-gii.xml @@ -0,0 +1,81 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE dokumente SYSTEM "http://www.gesetze-im-internet.de/dtd/1.01/gii-norm.dtd"> +<dokumente builddate="20201120213007" doknr="BJNR000000000"> + <norm builddate="20201120213007" doknr="BJNR000000000"> + <metadaten> + <jurabk>TestG</jurabk> + <amtabk>TestG</amtabk> + <kurzue>Testgesetz</kurzue> + <langue>Gesetz zur Erprobung des ÄndGgners</langue> + </metadaten> + <textdaten> + <fussnoten> + <Content> + <P>Fußnote, die keine Einzelnorm ist.</P> + </Content> + </fussnoten> + </textdaten> + </norm> + <norm builddate="20201120213007" doknr="BJNR000000000BJNE000100000"> + <metadaten> + <jurabk>TestG</jurabk> + <enbez>Inhaltsübersicht</enbez> + </metadaten> + <textdaten> + <text format="XML"> + <Content> + <P> + <table frame="none"> + <tgroup cols="2"> + <tbody valign="top"> + <row> + <entry colname="col1">§ 1</entry> + <entry colname="col2">Zweck des Gesetzes</entry> + </row> + <row> + <entry colname="col1">§ 2</entry> + <entry colname="col2">Begriffsbestimmungen</entry> + </row> + </tbody> + </tgroup> + </table> + </P> + </Content> + </text> + </textdaten> + </norm> + <norm builddate="20201120213007" doknr="BJNR000000000BJNE000200000"> + <metadaten> + <jurabk>TestG</jurabk> + <gliederungseinheit> + <gliederungskennzahl>010</gliederungskennzahl> + <gliederungsbez>1. Abschnitt</gliederungsbez> + <gliederungstitel>Allgemeine Vorschriften</gliederungstitel> + </gliederungseinheit> + <enbez>§ 1</enbez> + <titel format="XML">Zweck des Gesetzes</titel> + </metadaten> + <textdaten> + <text format="XML"> + <Content> + <P>(1) Zweck dieses Gesetzes ist die Erprobung. Die Erprobung erfolgt sorgfältig.</P> + <P>(2) Die Erprobung umfasst <DL Font="normal" Type="arabic"><DT>1.</DT><DD Font="normal"><LA Size="normal">das Einlesen von Gesetzen und</LA></DD><DT>2.</DT><DD Font="normal"><LA Size="normal">die Anwendung von Änderungsbefehlen, insbesondere<DL Font="normal" Type="alpha"><DT>a)</DT><DD Font="normal"><LA Size="normal">Ersetzungen und</LA></DD><DT>b)</DT><DD Font="normal"><LA Size="normal">Einfügungen.</LA></DD></DL></LA></DD></DL>Weitere Einzelheiten regelt die Rechtsverordnung.</P> + </Content> + </text> + </textdaten> + </norm> + <norm builddate="20201120213007" doknr="BJNR000000000BJNE000300000"> + <metadaten> + <jurabk>TestG</jurabk> + <enbez>§ 2</enbez> + <titel format="XML">(weggefallen)</titel> + </metadaten> + <textdaten> + <text format="XML"> + <Content> + <P>(weggefallen)</P> + </Content> + </text> + </textdaten> + </norm> +</dokumente> |
