From 8a785f1baaa6cdd5414892610a6e043f27be11bb Mon Sep 17 00:00:00 2001 From: Marcus van Elst Date: Fri, 10 Apr 2026 07:50:51 +0200 Subject: [PATCH] Kleinere Korrekturen --- docs/specs/V1.1 - Ist-Stand.md | 332 ++++++++++++++++++ .../StartConfigurationValidator.java | 22 +- .../PropertiesConfigurationPortAdapter.java | 4 - .../out/DocumentPersistenceException.java | 4 +- .../service/AiResponseValidator.java | 1 - .../DocumentProcessingCoordinatorTest.java | 3 - .../BatchRunProcessingUseCaseTest.java | 2 - pdf-umbenenner-bootstrap/pom.xml | 1 - .../BootstrapRunnerEdgeCasesTest.java | 4 +- .../PdfUmbenennerApplicationTest.java | 2 +- 10 files changed, 339 insertions(+), 36 deletions(-) create mode 100644 docs/specs/V1.1 - Ist-Stand.md diff --git a/docs/specs/V1.1 - Ist-Stand.md b/docs/specs/V1.1 - Ist-Stand.md new file mode 100644 index 0000000..619dbc8 --- /dev/null +++ b/docs/specs/V1.1 - Ist-Stand.md @@ -0,0 +1,332 @@ +# V1.1 – Ist-Stand des PDF-Umbenenners + +## Zweck dieses Dokuments + +Dieses Dokument beschreibt den **tatsächlichen Ist-Stand der Software bis einschließlich V1.1**. +Es ergänzt die bestehenden Spezifikations- und Projektdokumente um den konkret erreichten Erweiterungsstand nach der vollständig umgesetzten und freigegebenen V1. + +Ziel ist, dass eine KI oder ein Entwickler zusammen mit den übrigen Repository-Dokumenten den aktuellen Funktionsumfang und die Architektur **präzise und ohne Rückgriff auf Chat-Verläufe** verstehen kann. + +Dieses Dokument ist **kein Soll-Konzept** und **kein neues Arbeitspaket**, sondern eine Beschreibung des erreichten Stands. + +--- + +## Einordnung des Stands + +### Ausgangsbasis + +Vor V1.1 lag eine vollständig umgesetzte, getestete, dokumentierte und freigegebene **V1** des Projekts vor. +Diese V1 entsprach dem Umsetzungsstand der Meilensteine **M1 bis M8**. + +Damit war insbesondere bereits vorhanden: + +- vollständiges Maven-Multi-Module-Projekt +- strikte hexagonale Architektur +- Batch-Verarbeitung über Standalone-JAR +- PDF-Erkennung und PDF-Textauslese +- SQLite-Persistenz +- Retry-, Skip- und Statuslogik +- KI-gestützte Ermittlung eines Benennungsvorschlags +- Dateinamensbildung und Zielkopie +- Logging, Qualitätsabsicherung, Dokumentation und Freigabestand + +### Ziel von V1.1 + +V1.1 wurde bewusst **klein und minimal-invasiv** definiert. + +Es handelt sich **nicht** um einen allgemeinen V2-Ausbau und **nicht** um einen Produktumbau mit GUI, Installer oder EXE. + +Der Fokus von V1.1 ist ausschließlich: + +- Erweiterung der bestehenden KI-Anbindung um **native Claude-Unterstützung** +- gleichzeitiger Erhalt des bisherigen **OpenAI-kompatiblen Wegs** +- Mehrprovider-Konfiguration mit **genau einem aktiven Provider** +- Wahrung der bestehenden Architekturprinzipien +- Wahrung der fachlichen und technischen Regeln aus V1 +- Abwärtskompatibilität bestehender Konfigurationen + +--- + +## Inhaltlicher Umfang von V1.1 + +V1.1 erweitert die bestehende Anwendung um eine **Mehrprovider-fähige KI-Konfiguration**, bei der genau **ein Provider aktiv** ist. + +### In V1.1 enthalten + +- Unterstützung von **zwei real nutzbaren KI-Providern** + - OpenAI-kompatibler Provider + - nativer Claude-Provider +- Properties-basierte Konfiguration für mehrere Provider +- Auswahl genau **eines aktiven Providers** +- Beibehaltung des bisherigen fachlichen KI-Vertrags +- Integration der Providerwahl in die bestehende technische Konfiguration +- Migration alter V1-Konfigurationen auf die neue Struktur +- Sicherung alter Konfigurationen über `.bak` +- vollständige Tests und Nachschärfung der Build-/Qualitätshygiene + +### In V1.1 ausdrücklich **nicht** enthalten + +- GUI +- Installer +- EXE-Paketierung +- mehrere Profile pro Provider +- automatischer Fallback zwischen Providern +- neue fachliche Regeln für Dateinamen +- neue Retry-Semantik +- neue Statussemantik +- neue Persistenz-Wahrheiten +- Änderungen am Grundprinzip des Batch-Betriebs + +--- + +## Fachlich-technische Kernaussage von V1.1 + +Die Anwendung verarbeitet weiterhin OCR-verarbeitete PDF-Dateien lokal und erzeugt daraus im Erfolgsfall korrekt benannte Zielkopien. + +Der Unterschied zu V1 ist: + +> Die Ermittlung des KI-basierten Benennungsvorschlags kann nun wahlweise +> über einen **OpenAI-kompatiblen Provider** oder über die **native Claude-API** +> erfolgen. + +Dabei bleibt für den restlichen fachlichen Ablauf entscheidend: + +- der Input bleibt ein aufbereiteter Dokumenttext +- die KI liefert weiterhin denselben fachlichen Vorschlagsinhalt +- der restliche Verarbeitungsfluss bleibt unverändert + +--- + +## Unveränderte Regeln aus V1 + +V1.1 ändert **nicht** die fachlichen Kernregeln des Systems. + +Insbesondere unverändert bleiben: + +- Zielformat: `YYYY-MM-DD - Titel.pdf` +- Dublettenregel `(1)`, `(2)`, … +- 20-Zeichen-Regel für den Basistitel +- deutsche, verständliche Titel +- Quelldatei bleibt unverändert +- Fingerprint-basierte Identifikation +- SQLite als lokaler Persistenzspeicher +- Retry- und Skip-Regeln +- Statusmodell +- Run-Lock +- Start über Standalone-JAR / Task Scheduler +- keine Dauerlauf-Anwendung +- keine GUI / kein Webserver / kein App-Server + +--- + +## Architekturstand in V1.1 + +V1.1 wahrt die bestehende Architektur. + +### Unverändert gültig + +- strikte hexagonale Architektur +- Ports and Adapters +- Abhängigkeiten zeigen nach innen +- keine Infrastruktur im Domain-Kern +- keine direkte Adapter-zu-Adapter-Kopplung +- Logging bleibt technische Infrastruktur +- Konfiguration bleibt technische Infrastruktur +- Bootstrap verdrahtet die konkrete Laufzeitumgebung + +### Bedeutung für die KI-Integration + +Die Mehrprovider-Fähigkeit wird **architekturtreu** eingeführt: + +- der fachliche/application-seitige KI-Vertrag bleibt erhalten +- die Provider-spezifischen Unterschiede liegen in den technischen Adaptern +- die konkrete Providerauswahl erfolgt über Konfiguration und Bootstrap +- es entsteht keine provider-spezifische Fachlogik im Kern + +--- + +## KI-Provider in V1.1 + +### Unterstützte Provider + +V1.1 unterstützt genau diese zwei Providerarten: + +1. **OpenAI-kompatibel** +2. **Claude nativ** + +### Aktiver Provider + +Es gibt immer **genau einen aktiven Provider**. + +V1.1 führt **keinen** automatischen Fallback zwischen Providern ein. +Wenn der aktive Provider fehlschlägt, gilt der bestehende Fehler- und Retry-Rahmen. + +### Keine Profilverwaltung + +Pro Provider gibt es in V1.1 genau **eine** Konfiguration. +Benannte Profile oder mehrere alternative Konfigurationssätze pro Provider sind nicht Bestandteil von V1.1. + +--- + +## KI-Vertrag bleibt unverändert + +V1.1 ändert **nicht** den fachlichen Ergebnisvertrag der KI. + +Die Erweiterung betrifft den technischen Zugriffsweg auf die KI, **nicht** den fachlichen Inhalt der KI-Antwort. + +Damit bleibt der Grundsatz bestehen: + +- die KI liefert denselben fachlichen Vorschlagsinhalt wie zuvor +- die Anwendung behält die Hoheit über Validierung, Datumsauflösung, Titelregeln und weitere technische Verarbeitung +- der restliche Systemablauf bleibt gegenüber V1 stabil + +--- + +## Konfigurationsmodell in V1.1 + +### Allgemeiner Grundsatz + +Die `.properties`-Datei bleibt die **Wahrheit** der Konfiguration. + +### Erweiterung in V1.1 + +Die Konfiguration kann nun mehrere Provider-Konfigurationen enthalten, von denen genau **eine aktiv** ist. + +### Abwärtskompatibilität + +Bestehende V1-Konfigurationen bleiben nutzbar. + +Dazu wurde in V1.1 vorgesehen: + +- Erkennung alter Konfigurationsstruktur +- Migration auf die neue Struktur beim ersten Start +- vorherige Anlage einer Sicherung mit `.bak` + +### Legacy-Kompatibilität beim API-Key + +Für den OpenAI-kompatiblen Provider wurde die Abwärtskompatibilität auch für bestehende API-Key-Setups beibehalten. + +Die Auflösung erfolgt in dieser Reihenfolge: + +1. spezifische Umgebungsvariable für den OpenAI-kompatiblen Provider +2. bisherige Legacy-Umgebungsvariable +3. Property-basierter API-Key + +Dadurch brechen bestehende Setups nicht still. + +### Strikte Provider-Validierung + +Die Konfiguration des aktiven Providers wird vor dem eigentlichen Lauf sauber validiert. + +Dazu gehört insbesondere die Base-URL-Prüfung: + +- syntaktisch gültige URI +- absolute URI +- nur `http` oder `https` + +Ungültige Provider-Konfiguration ist eine ungültige Startkonfiguration und verhindert den Laufbeginn. + +--- + +## Persistenz und Nachvollziehbarkeit + +Das bestehende Zwei-Ebenen-Modell bleibt erhalten: + +- Dokument-Stammsatz +- Versuchshistorie + +V1.1 führt **keine neue Persistenz-Wahrheit** ein. + +Die durch V1 etablierten Regeln zu Status, Retry, Zielerfolg, Proposal-Quelle und Historisierung bleiben in Kraft. + +Die Erweiterung um mehrere Provider dient der technisch sauberen Mehrprovider-Nutzung und der konsistenten Nachvollziehbarkeit des verwendeten Providers im Rahmen der bestehenden Architektur. + +--- + +## Qualitäts- und Stabilisierungsschritte nach der V1.1-Implementierung + +Nach der funktionalen Einführung von V1.1 wurde der Stand zusätzlich qualitativ nachgeschärft. + +Diese Nachschärfungen gehören zum erreichten Ist-Stand. + +### 1. Rückwärtskompatibilität und Provider-Validierung + +Es wurden Korrekturen durchgeführt für: + +- Legacy-Fallback beim OpenAI-kompatiblen API-Key +- strikte Validierung der Provider-Base-URL +- Sicherstellung, dass alte Setups nicht still brechen + +### 2. Dokumentation + +Es wurde eine README für das Repository ergänzt, damit der Projektstand und die Grundbenutzung nachvollziehbar dokumentiert sind. + +### 3. Compiler-, Test- und Build-Hygiene + +Es wurden mehrere kleinere Qualitätskorrekturen durchgeführt, unter anderem: + +- Bereinigung von Raw-Type-/Unchecked-Warnungen in Tests +- Beseitigung veralteter API-Nutzung in Bootstrap-Testhilfen +- Bereinigung des Logging-Klassenpfads im Testkontext +- bewusste Konfiguration des Annotation Processings + +### 4. Mutationstest- und Build-Verbesserungen + +Die Qualität des Test- und PIT-Stands wurde gezielt verbessert, insbesondere in: + +- `adapter-out` +- `bootstrap` + +Zusätzlich wurde ein PIT-Timeout bereinigt, das durch einen langsamen Integrationstest mit Netzwerkaufruf verursacht wurde. + +### 5. Bewertung verbliebener Build-Hinweise + +Verbleibende Log4j2-/Shade-Hinweise wurden geprüft und als funktional unkritisch eingeordnet, soweit sie keine reale Auswirkung auf den Produktivbetrieb haben. + +--- + +## Ergebnisstatus von V1.1 + +V1.1 ist als **fertiger, implementierter und getesteter Ist-Stand** zu verstehen. + +Das bedeutet: + +- die Erweiterung ist umgesetzt +- die Erweiterung ist in die bestehende Architektur eingebettet +- die relevanten Qualitäts- und Stabilitätskorrekturen wurden nachgezogen +- der Projektstand nach V1.1 ist gegenüber V1 fachlich stabil erweitert und technisch nachgeschärft + +--- + +## Verhältnis zu den übrigen Repository-Dokumenten + +Dieses Dokument ersetzt **nicht** die bestehenden Spezifikationsdokumente. + +Es ergänzt sie um die Aussage: + +> **Was ist nach Abschluss von V1.1 zusätzlich tatsächlich vorhanden?** + +Für eine vollständige Einordnung bleiben weiterhin maßgeblich: + +- `CLAUDE.md` +- `docs/specs/technik-und-architektur.md` +- `docs/specs/fachliche-anforderungen.md` +- `docs/specs/meilensteine.md` + +Dieses Dokument beschreibt den **erreichten zusätzlichen Ist-Zustand bis einschließlich V1.1**. + +--- + +## Kompakte Zusammenfassung für KI-Systeme + +Wenn eine KI diesen Stand kurz einordnen soll, gilt: + +- V1 des `pdf-umbenenner` war vollständig umgesetzt und freigegeben +- V1.1 ist eine **kleine, minimal-invasive Erweiterung** +- V1.1 fügt **native Claude-Unterstützung** hinzu +- der bisherige **OpenAI-kompatible Weg bleibt erhalten** +- die Konfiguration unterstützt mehrere Provider, aber **genau einen aktiven Provider** +- bestehende Konfigurationen bleiben per Migration und Legacy-Fallback kompatibel +- fachliche Regeln, Architekturprinzipien und Kernverhalten aus V1 bleiben unverändert +- nach der Implementierung wurden zusätzliche Qualitäts-, Build- und Testhygiene-Maßnahmen durchgeführt +- der Ist-Zustand des Projekts umfasst damit **V1 + V1.1** diff --git a/pdf-umbenenner-adapter-out/src/main/java/de/gecheckt/pdf/umbenenner/adapter/out/bootstrap/validation/StartConfigurationValidator.java b/pdf-umbenenner-adapter-out/src/main/java/de/gecheckt/pdf/umbenenner/adapter/out/bootstrap/validation/StartConfigurationValidator.java index bf0c57e..87e39a5 100644 --- a/pdf-umbenenner-adapter-out/src/main/java/de/gecheckt/pdf/umbenenner/adapter/out/bootstrap/validation/StartConfigurationValidator.java +++ b/pdf-umbenenner-adapter-out/src/main/java/de/gecheckt/pdf/umbenenner/adapter/out/bootstrap/validation/StartConfigurationValidator.java @@ -248,23 +248,6 @@ public class StartConfigurationValidator { // === Helper methods for common validation patterns === - /** - * Validates that a required directory path is not null, exists, and is a directory. - *

- * Used for paths like source and target folders that must already exist before processing can begin. - */ - private void validateRequiredExistingDirectory(Path path, String fieldName, List errors) { - if (path == null) { - errors.add("- " + fieldName + ": must not be null"); - return; - } - if (!Files.exists(path)) { - errors.add("- " + fieldName + ": path does not exist: " + path); - } else if (!Files.isDirectory(path)) { - errors.add("- " + fieldName + ": path is not a directory: " + path); - } - } - /** * Validates that a required file path is not null and its parent directory exists and is a directory. *

@@ -324,14 +307,13 @@ public class StartConfigurationValidator { * or exists and is a directory. */ private void validateOptionalExistingDirectory(Path directoryPath, String fieldName, List errors) { - if (directoryPath != null && !directoryPath.toString().isBlank()) { - if (Files.exists(directoryPath)) { + if (directoryPath != null && !directoryPath.toString().isBlank() && Files.exists(directoryPath)) { if (!Files.isDirectory(directoryPath)) { errors.add("- " + fieldName + ": exists but is not a directory: " + directoryPath); } } // If it doesn't exist yet, that's acceptable - we don't auto-create - } + } /** diff --git a/pdf-umbenenner-adapter-out/src/main/java/de/gecheckt/pdf/umbenenner/adapter/out/configuration/PropertiesConfigurationPortAdapter.java b/pdf-umbenenner-adapter-out/src/main/java/de/gecheckt/pdf/umbenenner/adapter/out/configuration/PropertiesConfigurationPortAdapter.java index fed8086..9af3389 100644 --- a/pdf-umbenenner-adapter-out/src/main/java/de/gecheckt/pdf/umbenenner/adapter/out/configuration/PropertiesConfigurationPortAdapter.java +++ b/pdf-umbenenner-adapter-out/src/main/java/de/gecheckt/pdf/umbenenner/adapter/out/configuration/PropertiesConfigurationPortAdapter.java @@ -9,9 +9,6 @@ import java.nio.file.Paths; import java.util.Properties; import java.util.function.Function; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - import de.gecheckt.pdf.umbenenner.application.config.provider.MultiProviderConfiguration; import de.gecheckt.pdf.umbenenner.application.config.startup.StartConfiguration; import de.gecheckt.pdf.umbenenner.application.port.out.ConfigurationPort; @@ -28,7 +25,6 @@ import de.gecheckt.pdf.umbenenner.application.port.out.ConfigurationPort; */ public class PropertiesConfigurationPortAdapter implements ConfigurationPort { - private static final Logger LOG = LogManager.getLogger(PropertiesConfigurationPortAdapter.class); private static final String DEFAULT_CONFIG_FILE_PATH = "config/application.properties"; private final Function environmentLookup; diff --git a/pdf-umbenenner-application/src/main/java/de/gecheckt/pdf/umbenenner/application/port/out/DocumentPersistenceException.java b/pdf-umbenenner-application/src/main/java/de/gecheckt/pdf/umbenenner/application/port/out/DocumentPersistenceException.java index d0be297..1666da9 100644 --- a/pdf-umbenenner-application/src/main/java/de/gecheckt/pdf/umbenenner/application/port/out/DocumentPersistenceException.java +++ b/pdf-umbenenner-application/src/main/java/de/gecheckt/pdf/umbenenner/application/port/out/DocumentPersistenceException.java @@ -26,7 +26,9 @@ package de.gecheckt.pdf.umbenenner.application.port.out; */ public class DocumentPersistenceException extends RuntimeException { - /** + private static final long serialVersionUID = -8362115097253107643L; + + /** * Constructs a new {@code DocumentPersistenceException} with the given message. * * @param message human-readable description of the persistence failure diff --git a/pdf-umbenenner-application/src/main/java/de/gecheckt/pdf/umbenenner/application/service/AiResponseValidator.java b/pdf-umbenenner-application/src/main/java/de/gecheckt/pdf/umbenenner/application/service/AiResponseValidator.java index 7c7f3a3..34b8821 100644 --- a/pdf-umbenenner-application/src/main/java/de/gecheckt/pdf/umbenenner/application/service/AiResponseValidator.java +++ b/pdf-umbenenner-application/src/main/java/de/gecheckt/pdf/umbenenner/application/service/AiResponseValidator.java @@ -6,7 +6,6 @@ import java.util.Objects; import java.util.Set; import de.gecheckt.pdf.umbenenner.application.port.out.ClockPort; -import de.gecheckt.pdf.umbenenner.application.service.AiResponseValidator.AiValidationResult; import de.gecheckt.pdf.umbenenner.domain.model.AiErrorClassification; import de.gecheckt.pdf.umbenenner.domain.model.DateSource; import de.gecheckt.pdf.umbenenner.domain.model.NamingProposal; diff --git a/pdf-umbenenner-application/src/test/java/de/gecheckt/pdf/umbenenner/application/service/DocumentProcessingCoordinatorTest.java b/pdf-umbenenner-application/src/test/java/de/gecheckt/pdf/umbenenner/application/service/DocumentProcessingCoordinatorTest.java index 82eae28..69c533e 100644 --- a/pdf-umbenenner-application/src/test/java/de/gecheckt/pdf/umbenenner/application/service/DocumentProcessingCoordinatorTest.java +++ b/pdf-umbenenner-application/src/test/java/de/gecheckt/pdf/umbenenner/application/service/DocumentProcessingCoordinatorTest.java @@ -1339,8 +1339,6 @@ class DocumentProcessingCoordinatorTest { private final CapturingDocumentRecordRepository recordRepo; private final CapturingProcessingAttemptRepository attemptRepo; boolean failOnExecute = false; - Consumer lastOperations = null; - CapturingUnitOfWorkPort(CapturingDocumentRecordRepository recordRepo, CapturingProcessingAttemptRepository attemptRepo) { this.recordRepo = recordRepo; @@ -1349,7 +1347,6 @@ class DocumentProcessingCoordinatorTest { @Override public void executeInTransaction(Consumer operations) { - this.lastOperations = operations; if (failOnExecute) { throw new DocumentPersistenceException("Simulated transaction failure"); } diff --git a/pdf-umbenenner-application/src/test/java/de/gecheckt/pdf/umbenenner/application/usecase/BatchRunProcessingUseCaseTest.java b/pdf-umbenenner-application/src/test/java/de/gecheckt/pdf/umbenenner/application/usecase/BatchRunProcessingUseCaseTest.java index 7559c5b..33c777b 100644 --- a/pdf-umbenenner-application/src/test/java/de/gecheckt/pdf/umbenenner/application/usecase/BatchRunProcessingUseCaseTest.java +++ b/pdf-umbenenner-application/src/test/java/de/gecheckt/pdf/umbenenner/application/usecase/BatchRunProcessingUseCaseTest.java @@ -1488,7 +1488,6 @@ class BatchRunProcessingUseCaseTest { private static class CapturingProcessingLogger implements ProcessingLogger { int infoCallCount = 0; int debugCallCount = 0; - int debugSensitiveAiContentCallCount = 0; int warnCallCount = 0; int errorCallCount = 0; @@ -1504,7 +1503,6 @@ class BatchRunProcessingUseCaseTest { @Override public void debugSensitiveAiContent(String message, Object... args) { - debugSensitiveAiContentCallCount++; } @Override diff --git a/pdf-umbenenner-bootstrap/pom.xml b/pdf-umbenenner-bootstrap/pom.xml index 81f1bc3..b1813b3 100644 --- a/pdf-umbenenner-bootstrap/pom.xml +++ b/pdf-umbenenner-bootstrap/pom.xml @@ -94,7 +94,6 @@ org.apache.maven.plugins maven-failsafe-plugin - ${maven-failsafe-plugin.version} diff --git a/pdf-umbenenner-bootstrap/src/test/java/de/gecheckt/pdf/umbenenner/bootstrap/BootstrapRunnerEdgeCasesTest.java b/pdf-umbenenner-bootstrap/src/test/java/de/gecheckt/pdf/umbenenner/bootstrap/BootstrapRunnerEdgeCasesTest.java index f130a77..b5c2318 100644 --- a/pdf-umbenenner-bootstrap/src/test/java/de/gecheckt/pdf/umbenenner/bootstrap/BootstrapRunnerEdgeCasesTest.java +++ b/pdf-umbenenner-bootstrap/src/test/java/de/gecheckt/pdf/umbenenner/bootstrap/BootstrapRunnerEdgeCasesTest.java @@ -159,9 +159,7 @@ class BootstrapRunnerEdgeCasesTest { @Test void createRunContext_generatesUniqueRunId() throws Exception { - // While we can't directly call the private createRunContext method, - // we can verify its effects through BatchRunContext behavior - StartConfiguration mockConfig = new StartConfiguration( + new StartConfiguration( Files.createDirectories(tempDir.resolve("source")), Files.createDirectories(tempDir.resolve("target")), Files.createFile(tempDir.resolve("db.sqlite")), diff --git a/pdf-umbenenner-bootstrap/src/test/java/de/gecheckt/pdf/umbenenner/bootstrap/PdfUmbenennerApplicationTest.java b/pdf-umbenenner-bootstrap/src/test/java/de/gecheckt/pdf/umbenenner/bootstrap/PdfUmbenennerApplicationTest.java index 1767527..4fbca77 100644 --- a/pdf-umbenenner-bootstrap/src/test/java/de/gecheckt/pdf/umbenenner/bootstrap/PdfUmbenennerApplicationTest.java +++ b/pdf-umbenenner-bootstrap/src/test/java/de/gecheckt/pdf/umbenenner/bootstrap/PdfUmbenennerApplicationTest.java @@ -136,7 +136,7 @@ class PdfUmbenennerApplicationTest { void applicationEntry_isStaticMain() throws Exception { // Verify main is static and returns void Method main = PdfUmbenennerApplication.class.getDeclaredMethod("main", String[].class); - int modifiers = main.getModifiers(); + main.getModifiers(); // Main should be public (inherited from Object or declared) assertEquals("void", main.getReturnType().getSimpleName(),