Refactoring #19: Benutzerfreundliche deutsche KI-Fehlermeldungen im Begründungsbereich
Neue Klasse AiFailureMessageTranslator übersetzt technische englische Fehlertexte (z.B. HTTP-Statuscodes, Verbindungsfehler) in lesbare deutsche Benutzerhinweise. GuiBatchRunTab nutzt den Translator vor der Anzeige; das Datenmodell (aiFailureMessage) bleibt unverändert. Unit-Tests decken alle definierten Mapping-Fälle ab. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+54
@@ -0,0 +1,54 @@
|
||||
package de.gecheckt.pdf.umbenenner.adapter.in.gui.batchrun;
|
||||
|
||||
/**
|
||||
* Übersetzt technische KI-Fehlermeldungen in benutzerfreundliche deutsche Texte.
|
||||
* <p>
|
||||
* Die Klasse wertet die rohe, englischsprachige Fehlermeldung aus dem
|
||||
* Verarbeitungsversuch aus und liefert eine für den Endbenutzer lesbare
|
||||
* Beschreibung des Fehlergrunds. Das ursprüngliche Datenmodell bleibt
|
||||
* unverändert; die Übersetzung findet ausschließlich in der Darstellungsschicht
|
||||
* statt.
|
||||
* <p>
|
||||
* Die Mustererkennung erfolgt ohne Berücksichtigung der Groß-/Kleinschreibung
|
||||
* und prüft die definierten Schlüsselbegriffe in festgelegter Reihenfolge,
|
||||
* damit spezifischere Muster vor allgemeineren greifen.
|
||||
*/
|
||||
final class AiFailureMessageTranslator {
|
||||
|
||||
private AiFailureMessageTranslator() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Liefert eine benutzerfreundliche deutsche Fehlermeldung für die angegebene
|
||||
* technische Fehlerbeschreibung.
|
||||
* <p>
|
||||
* Ist {@code technicalMessage} {@code null} oder leer, wird der allgemeine
|
||||
* Fallback-Text zurückgegeben.
|
||||
*
|
||||
* @param technicalMessage die rohe technische Fehlermeldung; darf {@code null} sein
|
||||
* @return eine nicht-leere deutsche Benutzerfehlermeldung
|
||||
*/
|
||||
static String translate(String technicalMessage) {
|
||||
if (technicalMessage == null || technicalMessage.isBlank()) {
|
||||
return "KI-Aufruf fehlgeschlagen. Details im Anwendungslog.";
|
||||
}
|
||||
String lower = technicalMessage.toLowerCase(java.util.Locale.ROOT);
|
||||
|
||||
if (lower.contains("http_401")) {
|
||||
return "KI-Dienst: Ungültiger API-Schlüssel. Bitte in den Einstellungen prüfen.";
|
||||
}
|
||||
if (lower.contains("http_403")) {
|
||||
return "KI-Dienst: Zugriff verweigert. Bitte API-Schlüssel und Berechtigungen prüfen.";
|
||||
}
|
||||
if (lower.contains("http_429")) {
|
||||
return "KI-Dienst: Anfragelimit erreicht. Bitte später erneut versuchen.";
|
||||
}
|
||||
if (lower.contains("http_5")) {
|
||||
return "KI-Dienst vorübergehend nicht erreichbar. Bitte später erneut versuchen.";
|
||||
}
|
||||
if (lower.contains("connection") || lower.contains("timeout") || lower.contains("refused")) {
|
||||
return "KI-Dienst nicht erreichbar. Bitte Netzwerkverbindung prüfen.";
|
||||
}
|
||||
return "KI-Aufruf fehlgeschlagen. Details im Anwendungslog.";
|
||||
}
|
||||
}
|
||||
+3
-1
@@ -901,7 +901,9 @@ public final class GuiBatchRunTab {
|
||||
reasoning -> builder.append(reasoning),
|
||||
() -> {
|
||||
row.aiFailureMessage().ifPresent(msg ->
|
||||
builder.append("\u26A0 Fehler: ").append(msg).append("\n\n"));
|
||||
builder.append("\u26A0 Fehler: ")
|
||||
.append(AiFailureMessageTranslator.translate(msg))
|
||||
.append("\n\n"));
|
||||
builder.append(NO_REASONING_TEXT);
|
||||
});
|
||||
return builder.toString();
|
||||
|
||||
+135
@@ -0,0 +1,135 @@
|
||||
package de.gecheckt.pdf.umbenenner.adapter.in.gui.batchrun;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
* Unit-Tests für {@link AiFailureMessageTranslator}.
|
||||
* <p>
|
||||
* Prüft alle definierten Mapping-Fälle sowie Randbedingungen wie
|
||||
* {@code null}-Eingaben und unbekannte Fehlertexte.
|
||||
*/
|
||||
class AiFailureMessageTranslatorTest {
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// HTTP-Statuscodes
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@Test
|
||||
void http401_liefertApiSchluesselMeldung() {
|
||||
String result = AiFailureMessageTranslator.translate(
|
||||
"Processing failed (retryable). AI technical error: AI invocation failed [HTTP_401]: AI service returned status 401");
|
||||
assertEquals("KI-Dienst: Ungültiger API-Schlüssel. Bitte in den Einstellungen prüfen.", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void http401_kleinschreibung_wirdErkannt() {
|
||||
String result = AiFailureMessageTranslator.translate("http_401 ungültig");
|
||||
assertEquals("KI-Dienst: Ungültiger API-Schlüssel. Bitte in den Einstellungen prüfen.", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void http403_liefertZugriffVerweigertMeldung() {
|
||||
String result = AiFailureMessageTranslator.translate("HTTP_403 Forbidden");
|
||||
assertEquals("KI-Dienst: Zugriff verweigert. Bitte API-Schlüssel und Berechtigungen prüfen.", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void http429_liefertRateLimitMeldung() {
|
||||
String result = AiFailureMessageTranslator.translate("AI invocation failed [HTTP_429]: rate limit");
|
||||
assertEquals("KI-Dienst: Anfragelimit erreicht. Bitte später erneut versuchen.", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void http500_liefertServerFehlerMeldung() {
|
||||
String result = AiFailureMessageTranslator.translate("AI invocation failed [HTTP_500]: internal error");
|
||||
assertEquals("KI-Dienst vorübergehend nicht erreichbar. Bitte später erneut versuchen.", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void http503_liefertServerFehlerMeldung() {
|
||||
String result = AiFailureMessageTranslator.translate("HTTP_503 Service Unavailable");
|
||||
assertEquals("KI-Dienst vorübergehend nicht erreichbar. Bitte später erneut versuchen.", result);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Verbindungsfehler
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@Test
|
||||
void connectionFehler_liefertNetzwerkMeldung() {
|
||||
String result = AiFailureMessageTranslator.translate("Connection refused to https://api.example.com");
|
||||
assertEquals("KI-Dienst nicht erreichbar. Bitte Netzwerkverbindung prüfen.", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void timeoutFehler_liefertNetzwerkMeldung() {
|
||||
String result = AiFailureMessageTranslator.translate("AI technical error: timeout after 30s");
|
||||
assertEquals("KI-Dienst nicht erreichbar. Bitte Netzwerkverbindung prüfen.", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void refusedFehler_liefertNetzwerkMeldung() {
|
||||
String result = AiFailureMessageTranslator.translate("connect refused");
|
||||
assertEquals("KI-Dienst nicht erreichbar. Bitte Netzwerkverbindung prüfen.", result);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Fallback
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@Test
|
||||
void unbekannterFehler_liefertFallback() {
|
||||
String result = AiFailureMessageTranslator.translate("some completely unknown error text");
|
||||
assertEquals("KI-Aufruf fehlgeschlagen. Details im Anwendungslog.", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void null_liefertFallback() {
|
||||
String result = AiFailureMessageTranslator.translate(null);
|
||||
assertEquals("KI-Aufruf fehlgeschlagen. Details im Anwendungslog.", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void leerstring_liefertFallback() {
|
||||
String result = AiFailureMessageTranslator.translate("");
|
||||
assertEquals("KI-Aufruf fehlgeschlagen. Details im Anwendungslog.", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void nurLeerzeichen_liefertFallback() {
|
||||
String result = AiFailureMessageTranslator.translate(" ");
|
||||
assertEquals("KI-Aufruf fehlgeschlagen. Details im Anwendungslog.", result);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Allgemeine Qualität der Rückgabe
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@Test
|
||||
void alleEingaben_liefernNichtLeereTexte() {
|
||||
String[] eingaben = {
|
||||
null, "", " ", "HTTP_401", "HTTP_403", "HTTP_429", "HTTP_500",
|
||||
"connection error", "timeout", "refused", "unbekannt"
|
||||
};
|
||||
for (String eingabe : eingaben) {
|
||||
String result = AiFailureMessageTranslator.translate(eingabe);
|
||||
assertNotNull(result, "Ergebnis darf nicht null sein für: " + eingabe);
|
||||
assertTrue(!result.isBlank(), "Ergebnis darf nicht leer sein für: " + eingabe);
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Reihenfolge: HTTP_401 hat Vorrang vor HTTP_5xx
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@Test
|
||||
void http401VorHttp5xx_http401GewinntNicht_daKeinHttp5Enthalten() {
|
||||
// HTTP_401 enthält kein "http_5", daher gilt 401-Regel
|
||||
String result = AiFailureMessageTranslator.translate("HTTP_401");
|
||||
assertEquals("KI-Dienst: Ungültiger API-Schlüssel. Bitte in den Einstellungen prüfen.", result);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user