GUI-Bugfixes: Defaults beim Start, kopierbare Meldungen mit Zeitstempel, Befundauflistung, Modell-ComboBox links, effektiver API-Key für Modellabruf
- Blank-Startzustand zeigt jetzt die Standardvorlage (wie nach "Neu"), neue Factory createEmptyStartState für Tests - Meldungsbereich ist per Kontextmenü bzw. Strg+C kopierbar - Jede Meldung trägt ein führendes [HH:mm:ss]-Präfix - Validieren- und Tests-Aktionen akkumulieren Meldungen, automatische Validierung ersetzt still ihre Einträge - Validieren-Meldung listet alle konkreten Befunde einzeln auf - Modell-ComboBox und manuelles Modellfeld sind linksbündig - ApiKeyResolutionPort liefert jetzt den effektiven API-Schlüsselwert (Default + Env-Adapter-Override), so dass der Modellliste-Test in den technischen Tests nicht mehr "API-Schlüssel fehlt" meldet, obwohl er gesetzt ist
This commit is contained in:
+31
@@ -1,5 +1,7 @@
|
||||
package de.gecheckt.pdf.umbenenner.application.validation.editor;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import de.gecheckt.pdf.umbenenner.application.config.provider.AiProviderFamily;
|
||||
import de.gecheckt.pdf.umbenenner.application.port.out.modelcatalog.EffectiveApiKeyDescriptor;
|
||||
|
||||
@@ -22,6 +24,7 @@ import de.gecheckt.pdf.umbenenner.application.port.out.modelcatalog.EffectiveApi
|
||||
* <p>
|
||||
* Implementierungen dieses Ports liegen im Adapter-Out-Modul.
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface ApiKeyResolutionPort {
|
||||
|
||||
/**
|
||||
@@ -37,4 +40,32 @@ public interface ApiKeyResolutionPort {
|
||||
* @return der Descriptor für die effektive Schlüsselherkunft; nie {@code null}
|
||||
*/
|
||||
EffectiveApiKeyDescriptor resolve(AiProviderFamily family, String propertyValue);
|
||||
|
||||
/**
|
||||
* Liefert den effektiven API-Schlüssel-Rohwert anhand derselben Vorrangregel wie {@link #resolve}.
|
||||
* <p>
|
||||
* Dient technischen Tests wie dem Modellkatalogabruf, die den tatsächlichen Schlüssel im
|
||||
* HTTP-Header benötigen. Die Herkunft selbst wird nicht mit zurückgegeben – dafür ist
|
||||
* {@link #resolve} zuständig.
|
||||
* <p>
|
||||
* Diese Default-Implementierung deckt den Fall ab, in dem ein Adapter ausschließlich die
|
||||
* Property-Datei kennt: liefert {@link #resolve} {@code ABSENT}, wird ein leerer Optional
|
||||
* zurückgegeben; andernfalls wird der nicht-leere Property-Wert geliefert. Adapter, die
|
||||
* Umgebungsvariablen lesen, müssen diese Methode überschreiben, damit der ENV-Wert auch
|
||||
* tatsächlich an HTTP-Aufrufer durchgereicht wird.
|
||||
*
|
||||
* @param family die Provider-Familie; darf nicht {@code null} sein
|
||||
* @param propertyValue aktueller Property-Wert aus dem Editor (kann leer sein); darf nicht {@code null} sein
|
||||
* @return der effektive Schlüssel-Rohwert, falls eine der Quellen einen Wert liefert; sonst leer
|
||||
*/
|
||||
default Optional<String> resolveEffectiveApiKeyValue(AiProviderFamily family, String propertyValue) {
|
||||
EffectiveApiKeyDescriptor descriptor = resolve(family, propertyValue);
|
||||
if (descriptor.isAbsent()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
if (propertyValue != null && !propertyValue.isBlank()) {
|
||||
return Optional.of(propertyValue);
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
+9
-1
@@ -28,10 +28,12 @@ import de.gecheckt.pdf.umbenenner.application.port.out.modelcatalog.EffectiveApi
|
||||
* @param claudeModel Rohtextwert des Claude-Modellnamens
|
||||
* @param claudeTimeoutSeconds Rohtextwert des Claude-Timeouts
|
||||
* @param claudeApiKeyDescriptor API-Key-Herkunft für den Claude-Provider; nie {@code null}
|
||||
* @param claudeApiKeyPropertyValue Rohwert des Claude-API-Schlüssels aus der Properties-Datei
|
||||
* @param openaiBaseUrl Rohtextwert der OpenAI-kompatiblen Basis-URL
|
||||
* @param openaiModel Rohtextwert des OpenAI-kompatiblen Modellnamens
|
||||
* @param openaiTimeoutSeconds Rohtextwert des OpenAI-kompatiblen Timeouts
|
||||
* @param openaiApiKeyDescriptor API-Key-Herkunft für den OpenAI-kompatiblen Provider; nie {@code null}
|
||||
* @param openaiApiKeyPropertyValue Rohwert des OpenAI-API-Schlüssels aus der Properties-Datei
|
||||
*/
|
||||
public record EditorValidationInput(
|
||||
String activeProviderIdentifier,
|
||||
@@ -46,10 +48,12 @@ public record EditorValidationInput(
|
||||
String claudeModel,
|
||||
String claudeTimeoutSeconds,
|
||||
EffectiveApiKeyDescriptor claudeApiKeyDescriptor,
|
||||
String claudeApiKeyPropertyValue,
|
||||
String openaiBaseUrl,
|
||||
String openaiModel,
|
||||
String openaiTimeoutSeconds,
|
||||
EffectiveApiKeyDescriptor openaiApiKeyDescriptor) {
|
||||
EffectiveApiKeyDescriptor openaiApiKeyDescriptor,
|
||||
String openaiApiKeyPropertyValue) {
|
||||
|
||||
/**
|
||||
* Erstellt eine neue Eingabe für den Validator.
|
||||
@@ -66,10 +70,12 @@ public record EditorValidationInput(
|
||||
* @param claudeModel Claude-Modellname; {@code null} wird zu leerem String
|
||||
* @param claudeTimeoutSeconds Claude-Timeout; {@code null} wird zu leerem String
|
||||
* @param claudeApiKeyDescriptor Claude-API-Key-Herkunft; darf nicht {@code null} sein
|
||||
* @param claudeApiKeyPropertyValue Claude-API-Key-Rohwert aus der Properties-Datei; {@code null} wird zu leerem String
|
||||
* @param openaiBaseUrl OpenAI-Basis-URL; {@code null} wird zu leerem String
|
||||
* @param openaiModel OpenAI-Modellname; {@code null} wird zu leerem String
|
||||
* @param openaiTimeoutSeconds OpenAI-Timeout; {@code null} wird zu leerem String
|
||||
* @param openaiApiKeyDescriptor OpenAI-API-Key-Herkunft; darf nicht {@code null} sein
|
||||
* @param openaiApiKeyPropertyValue OpenAI-API-Key-Rohwert aus der Properties-Datei; {@code null} wird zu leerem String
|
||||
* @throws NullPointerException wenn {@code claudeApiKeyDescriptor} oder {@code openaiApiKeyDescriptor} {@code null} sind
|
||||
*/
|
||||
public EditorValidationInput {
|
||||
@@ -86,11 +92,13 @@ public record EditorValidationInput(
|
||||
claudeTimeoutSeconds = normalizeText(claudeTimeoutSeconds);
|
||||
claudeApiKeyDescriptor = Objects.requireNonNull(claudeApiKeyDescriptor,
|
||||
"claudeApiKeyDescriptor must not be null");
|
||||
claudeApiKeyPropertyValue = normalizeText(claudeApiKeyPropertyValue);
|
||||
openaiBaseUrl = normalizeText(openaiBaseUrl);
|
||||
openaiModel = normalizeText(openaiModel);
|
||||
openaiTimeoutSeconds = normalizeText(openaiTimeoutSeconds);
|
||||
openaiApiKeyDescriptor = Objects.requireNonNull(openaiApiKeyDescriptor,
|
||||
"openaiApiKeyDescriptor must not be null");
|
||||
openaiApiKeyPropertyValue = normalizeText(openaiApiKeyPropertyValue);
|
||||
}
|
||||
|
||||
private static String normalizeText(String value) {
|
||||
|
||||
+25
-14
@@ -79,6 +79,8 @@ public class ProviderTechnicalTestService {
|
||||
private static final int DEFAULT_TIMEOUT_SECONDS = 30;
|
||||
|
||||
private final AiModelCatalogPort modelCatalogPort;
|
||||
private final ApiKeyResolutionPort apiKeyResolutionPort;
|
||||
|
||||
/**
|
||||
* Erstellt einen neuen Service mit den erforderlichen Ports.
|
||||
*
|
||||
@@ -89,7 +91,7 @@ public class ProviderTechnicalTestService {
|
||||
public ProviderTechnicalTestService(AiModelCatalogPort modelCatalogPort,
|
||||
ApiKeyResolutionPort apiKeyResolutionPort) {
|
||||
this.modelCatalogPort = Objects.requireNonNull(modelCatalogPort, "modelCatalogPort must not be null");
|
||||
Objects.requireNonNull(apiKeyResolutionPort,
|
||||
this.apiKeyResolutionPort = Objects.requireNonNull(apiKeyResolutionPort,
|
||||
"apiKeyResolutionPort must not be null");
|
||||
}
|
||||
|
||||
@@ -377,13 +379,10 @@ public class ProviderTechnicalTestService {
|
||||
/**
|
||||
* Baut den {@link ModelCatalogRequest} aus dem aktuellen Editorzustand auf.
|
||||
* <p>
|
||||
* Da {@link EditorValidationInput} keinen direkten API-Key-String enthält, sondern
|
||||
* nur einen bereits aufgelösten {@link EffectiveApiKeyDescriptor}, wird der Descriptor
|
||||
* aus dem Editorzustand direkt verwendet. Der Adapter-Out-Seitige Dispatcher erwartet
|
||||
* den Key entweder als ENV-Variable (die er selbst liest) oder als optionalen Wert
|
||||
* im Request. Da die Auflösung beim Service bereits über {@link ApiKeyResolutionPort}
|
||||
* erfolgt ist, wird für den Catalog-Request ein leerer Optional-Wert geliefert –
|
||||
* der Adapter verwendet dann intern seine eigene ENV-Variable-Auflösung.
|
||||
* Der effektive API-Key-Rohwert wird über den {@link ApiKeyResolutionPort} ermittelt
|
||||
* (Vorrangregel: providerspezifische ENV → Legacy-ENV → Property-Wert) und in den
|
||||
* Request übernommen. Dadurch ist der Schlüssel bereits beim Adapter verfügbar und
|
||||
* spiegelt exakt die Quelle wider, die zuvor im Deskriptor ausgewiesen wurde.
|
||||
*
|
||||
* @param input aktueller Editorzustand
|
||||
* @param family aktive Provider-Familie
|
||||
@@ -393,12 +392,9 @@ public class ProviderTechnicalTestService {
|
||||
private ModelCatalogRequest buildCatalogRequest(EditorValidationInput input,
|
||||
AiProviderFamily family,
|
||||
EffectiveApiKeyDescriptor apiKeyDesc) {
|
||||
// EditorValidationInput enthält keinen direkten API-Key-String-Wert, nur den Descriptor.
|
||||
// Für den ModelCatalogRequest übergeben wir einen leeren Optional für den apiKey,
|
||||
// sodass der Adapter seine eigene ENV-Variable-Auflösung durchführt.
|
||||
// Der Adapter liefert dann IncompleteConfiguration, wenn auch er keinen Key findet –
|
||||
// was aber nicht passiert, da wir oben bereits geprüft haben, dass apiKeyDesc nicht ABSENT ist.
|
||||
Optional<String> apiKeyForRequest = Optional.empty();
|
||||
String propertyValue = resolveApiKeyPropertyValue(input, family);
|
||||
Optional<String> apiKeyForRequest = apiKeyResolutionPort
|
||||
.resolveEffectiveApiKeyValue(family, propertyValue);
|
||||
|
||||
String rawBaseUrl = resolveBaseUrlValue(input, family);
|
||||
Optional<String> baseUrl = rawBaseUrl.isBlank() ? Optional.empty() : Optional.of(rawBaseUrl);
|
||||
@@ -412,6 +408,21 @@ public class ProviderTechnicalTestService {
|
||||
timeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* Liest den Roh-Property-Wert des API-Schlüssels für die angegebene Provider-Familie
|
||||
* aus dem Editorzustand.
|
||||
*
|
||||
* @param input aktueller Editorzustand
|
||||
* @param family aktive Provider-Familie
|
||||
* @return Property-Wert; nie {@code null}, leer wenn nicht gesetzt
|
||||
*/
|
||||
private String resolveApiKeyPropertyValue(EditorValidationInput input, AiProviderFamily family) {
|
||||
return switch (family) {
|
||||
case CLAUDE -> input.claudeApiKeyPropertyValue();
|
||||
case OPENAI_COMPATIBLE -> input.openaiApiKeyPropertyValue();
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Liest den bereits aufgelösten {@link EffectiveApiKeyDescriptor} für die aktive Provider-Familie
|
||||
* direkt aus dem {@link EditorValidationInput}.
|
||||
|
||||
+55
-53
@@ -41,10 +41,12 @@ class EditorConfigurationValidatorTest {
|
||||
"claude-3-5-sonnet", // claudeModel
|
||||
"30", // claudeTimeoutSeconds
|
||||
EffectiveApiKeyDescriptor.fromPropertyFile(), // claudeApiKeyDescriptor
|
||||
"sk-claude", // claudeApiKeyPropertyValue
|
||||
"https://api.openai.com", // openaiBaseUrl
|
||||
"gpt-4", // openaiModel
|
||||
"30", // openaiTimeoutSeconds
|
||||
EffectiveApiKeyDescriptor.fromPropertyFile() // openaiApiKeyDescriptor
|
||||
EffectiveApiKeyDescriptor.fromPropertyFile(), // openaiApiKeyDescriptor
|
||||
"sk-openai" // openaiApiKeyPropertyValue
|
||||
);
|
||||
}
|
||||
|
||||
@@ -57,8 +59,8 @@ class EditorConfigurationValidatorTest {
|
||||
EditorValidationInput input = new EditorValidationInput(
|
||||
"", "C:/source", "C:/target", "C:/db.sqlite", "C:/prompt.txt",
|
||||
"3", "10", "500",
|
||||
"", "", "30", EffectiveApiKeyDescriptor.absent(),
|
||||
"", "", "30", EffectiveApiKeyDescriptor.absent());
|
||||
"", "", "30", EffectiveApiKeyDescriptor.absent(), "",
|
||||
"", "", "30", EffectiveApiKeyDescriptor.absent(), "");
|
||||
|
||||
EditorValidationReport report = validator.validate(input);
|
||||
|
||||
@@ -74,8 +76,8 @@ class EditorConfigurationValidatorTest {
|
||||
EditorValidationInput input = new EditorValidationInput(
|
||||
"unknown-provider", "C:/source", "C:/target", "C:/db.sqlite", "C:/prompt.txt",
|
||||
"3", "10", "500",
|
||||
"", "", "30", EffectiveApiKeyDescriptor.absent(),
|
||||
"", "", "30", EffectiveApiKeyDescriptor.absent());
|
||||
"", "", "30", EffectiveApiKeyDescriptor.absent(), "",
|
||||
"", "", "30", EffectiveApiKeyDescriptor.absent(), "");
|
||||
|
||||
EditorValidationReport report = validator.validate(input);
|
||||
|
||||
@@ -94,8 +96,8 @@ class EditorConfigurationValidatorTest {
|
||||
"claude", "", "C:/target", "C:/db.sqlite", "C:/prompt.txt",
|
||||
"3", "10", "500",
|
||||
"https://api.anthropic.com", "claude-3-5-sonnet", "30",
|
||||
EffectiveApiKeyDescriptor.fromPropertyFile(),
|
||||
"", "", "30", EffectiveApiKeyDescriptor.absent());
|
||||
EffectiveApiKeyDescriptor.fromPropertyFile(), "sk-claude",
|
||||
"", "", "30", EffectiveApiKeyDescriptor.absent(), "");
|
||||
|
||||
EditorValidationReport report = validator.validate(input);
|
||||
|
||||
@@ -109,8 +111,8 @@ class EditorConfigurationValidatorTest {
|
||||
"claude", "C:/source", "", "C:/db.sqlite", "C:/prompt.txt",
|
||||
"3", "10", "500",
|
||||
"https://api.anthropic.com", "claude-3-5-sonnet", "30",
|
||||
EffectiveApiKeyDescriptor.fromPropertyFile(),
|
||||
"", "", "30", EffectiveApiKeyDescriptor.absent());
|
||||
EffectiveApiKeyDescriptor.fromPropertyFile(), "sk-claude",
|
||||
"", "", "30", EffectiveApiKeyDescriptor.absent(), "");
|
||||
|
||||
EditorValidationReport report = validator.validate(input);
|
||||
|
||||
@@ -124,8 +126,8 @@ class EditorConfigurationValidatorTest {
|
||||
"claude", "C:/source", "C:/target", "", "C:/prompt.txt",
|
||||
"3", "10", "500",
|
||||
"https://api.anthropic.com", "claude-3-5-sonnet", "30",
|
||||
EffectiveApiKeyDescriptor.fromPropertyFile(),
|
||||
"", "", "30", EffectiveApiKeyDescriptor.absent());
|
||||
EffectiveApiKeyDescriptor.fromPropertyFile(), "sk-claude",
|
||||
"", "", "30", EffectiveApiKeyDescriptor.absent(), "");
|
||||
|
||||
EditorValidationReport report = validator.validate(input);
|
||||
|
||||
@@ -139,8 +141,8 @@ class EditorConfigurationValidatorTest {
|
||||
"claude", "C:/source", "C:/target", "C:/db.sqlite", "",
|
||||
"3", "10", "500",
|
||||
"https://api.anthropic.com", "claude-3-5-sonnet", "30",
|
||||
EffectiveApiKeyDescriptor.fromPropertyFile(),
|
||||
"", "", "30", EffectiveApiKeyDescriptor.absent());
|
||||
EffectiveApiKeyDescriptor.fromPropertyFile(), "sk-claude",
|
||||
"", "", "30", EffectiveApiKeyDescriptor.absent(), "");
|
||||
|
||||
EditorValidationReport report = validator.validate(input);
|
||||
|
||||
@@ -158,8 +160,8 @@ class EditorConfigurationValidatorTest {
|
||||
"claude", "C:/source", "C:/target", "C:/db.sqlite", "C:/prompt.txt",
|
||||
"0", "10", "500",
|
||||
"https://api.anthropic.com", "claude-3-5-sonnet", "30",
|
||||
EffectiveApiKeyDescriptor.fromPropertyFile(),
|
||||
"", "", "30", EffectiveApiKeyDescriptor.absent());
|
||||
EffectiveApiKeyDescriptor.fromPropertyFile(), "sk-claude",
|
||||
"", "", "30", EffectiveApiKeyDescriptor.absent(), "");
|
||||
|
||||
EditorValidationReport report = validator.validate(input);
|
||||
|
||||
@@ -173,8 +175,8 @@ class EditorConfigurationValidatorTest {
|
||||
"claude", "C:/source", "C:/target", "C:/db.sqlite", "C:/prompt.txt",
|
||||
"-1", "10", "500",
|
||||
"https://api.anthropic.com", "claude-3-5-sonnet", "30",
|
||||
EffectiveApiKeyDescriptor.fromPropertyFile(),
|
||||
"", "", "30", EffectiveApiKeyDescriptor.absent());
|
||||
EffectiveApiKeyDescriptor.fromPropertyFile(), "sk-claude",
|
||||
"", "", "30", EffectiveApiKeyDescriptor.absent(), "");
|
||||
|
||||
EditorValidationReport report = validator.validate(input);
|
||||
|
||||
@@ -188,8 +190,8 @@ class EditorConfigurationValidatorTest {
|
||||
"claude", "C:/source", "C:/target", "C:/db.sqlite", "C:/prompt.txt",
|
||||
"1", "10", "500",
|
||||
"https://api.anthropic.com", "claude-3-5-sonnet", "30",
|
||||
EffectiveApiKeyDescriptor.fromPropertyFile(),
|
||||
"", "", "30", EffectiveApiKeyDescriptor.absent());
|
||||
EffectiveApiKeyDescriptor.fromPropertyFile(), "sk-claude",
|
||||
"", "", "30", EffectiveApiKeyDescriptor.absent(), "");
|
||||
|
||||
EditorValidationReport report = validator.validate(input);
|
||||
|
||||
@@ -204,8 +206,8 @@ class EditorConfigurationValidatorTest {
|
||||
"claude", "C:/source", "C:/target", "C:/db.sqlite", "C:/prompt.txt",
|
||||
"abc", "10", "500",
|
||||
"https://api.anthropic.com", "claude-3-5-sonnet", "30",
|
||||
EffectiveApiKeyDescriptor.fromPropertyFile(),
|
||||
"", "", "30", EffectiveApiKeyDescriptor.absent());
|
||||
EffectiveApiKeyDescriptor.fromPropertyFile(), "sk-claude",
|
||||
"", "", "30", EffectiveApiKeyDescriptor.absent(), "");
|
||||
|
||||
EditorValidationReport report = validator.validate(input);
|
||||
|
||||
@@ -223,8 +225,8 @@ class EditorConfigurationValidatorTest {
|
||||
"claude", "C:/source", "C:/target", "C:/db.sqlite", "C:/prompt.txt",
|
||||
"3", "0", "500",
|
||||
"https://api.anthropic.com", "claude-3-5-sonnet", "30",
|
||||
EffectiveApiKeyDescriptor.fromPropertyFile(),
|
||||
"", "", "30", EffectiveApiKeyDescriptor.absent());
|
||||
EffectiveApiKeyDescriptor.fromPropertyFile(), "sk-claude",
|
||||
"", "", "30", EffectiveApiKeyDescriptor.absent(), "");
|
||||
|
||||
EditorValidationReport report = validator.validate(input);
|
||||
|
||||
@@ -238,8 +240,8 @@ class EditorConfigurationValidatorTest {
|
||||
"claude", "C:/source", "C:/target", "C:/db.sqlite", "C:/prompt.txt",
|
||||
"3", "101", "500",
|
||||
"https://api.anthropic.com", "claude-3-5-sonnet", "30",
|
||||
EffectiveApiKeyDescriptor.fromPropertyFile(),
|
||||
"", "", "30", EffectiveApiKeyDescriptor.absent());
|
||||
EffectiveApiKeyDescriptor.fromPropertyFile(), "sk-claude",
|
||||
"", "", "30", EffectiveApiKeyDescriptor.absent(), "");
|
||||
|
||||
EditorValidationReport report = validator.validate(input);
|
||||
|
||||
@@ -253,8 +255,8 @@ class EditorConfigurationValidatorTest {
|
||||
"claude", "C:/source", "C:/target", "C:/db.sqlite", "C:/prompt.txt",
|
||||
"3", "100", "500",
|
||||
"https://api.anthropic.com", "claude-3-5-sonnet", "30",
|
||||
EffectiveApiKeyDescriptor.fromPropertyFile(),
|
||||
"", "", "30", EffectiveApiKeyDescriptor.absent());
|
||||
EffectiveApiKeyDescriptor.fromPropertyFile(), "sk-claude",
|
||||
"", "", "30", EffectiveApiKeyDescriptor.absent(), "");
|
||||
|
||||
EditorValidationReport report = validator.validate(input);
|
||||
|
||||
@@ -275,8 +277,8 @@ class EditorConfigurationValidatorTest {
|
||||
"claude", "C:/source", "C:/target", "C:/db.sqlite", "C:/prompt.txt",
|
||||
"3", "10", "1000",
|
||||
"https://api.anthropic.com", "claude-3-5-sonnet", "30",
|
||||
EffectiveApiKeyDescriptor.fromPropertyFile(),
|
||||
"", "", "30", EffectiveApiKeyDescriptor.absent());
|
||||
EffectiveApiKeyDescriptor.fromPropertyFile(), "sk-claude",
|
||||
"", "", "30", EffectiveApiKeyDescriptor.absent(), "");
|
||||
|
||||
EditorValidationReport report = validator.validate(input);
|
||||
|
||||
@@ -290,8 +292,8 @@ class EditorConfigurationValidatorTest {
|
||||
"claude", "C:/source", "C:/target", "C:/db.sqlite", "C:/prompt.txt",
|
||||
"3", "10", "1001",
|
||||
"https://api.anthropic.com", "claude-3-5-sonnet", "30",
|
||||
EffectiveApiKeyDescriptor.fromPropertyFile(),
|
||||
"", "", "30", EffectiveApiKeyDescriptor.absent());
|
||||
EffectiveApiKeyDescriptor.fromPropertyFile(), "sk-claude",
|
||||
"", "", "30", EffectiveApiKeyDescriptor.absent(), "");
|
||||
|
||||
EditorValidationReport report = validator.validate(input);
|
||||
|
||||
@@ -305,8 +307,8 @@ class EditorConfigurationValidatorTest {
|
||||
"claude", "C:/source", "C:/target", "C:/db.sqlite", "C:/prompt.txt",
|
||||
"3", "10", "3000",
|
||||
"https://api.anthropic.com", "claude-3-5-sonnet", "30",
|
||||
EffectiveApiKeyDescriptor.fromPropertyFile(),
|
||||
"", "", "30", EffectiveApiKeyDescriptor.absent());
|
||||
EffectiveApiKeyDescriptor.fromPropertyFile(), "sk-claude",
|
||||
"", "", "30", EffectiveApiKeyDescriptor.absent(), "");
|
||||
|
||||
EditorValidationReport report = validator.validate(input);
|
||||
|
||||
@@ -320,8 +322,8 @@ class EditorConfigurationValidatorTest {
|
||||
"claude", "C:/source", "C:/target", "C:/db.sqlite", "C:/prompt.txt",
|
||||
"3", "10", "3001",
|
||||
"https://api.anthropic.com", "claude-3-5-sonnet", "30",
|
||||
EffectiveApiKeyDescriptor.fromPropertyFile(),
|
||||
"", "", "30", EffectiveApiKeyDescriptor.absent());
|
||||
EffectiveApiKeyDescriptor.fromPropertyFile(), "sk-claude",
|
||||
"", "", "30", EffectiveApiKeyDescriptor.absent(), "");
|
||||
|
||||
EditorValidationReport report = validator.validate(input);
|
||||
|
||||
@@ -340,8 +342,8 @@ class EditorConfigurationValidatorTest {
|
||||
"claude", "C:/source", "C:/target", "C:/db.sqlite", "C:/prompt.txt",
|
||||
"3", "10", "0",
|
||||
"https://api.anthropic.com", "claude-3-5-sonnet", "30",
|
||||
EffectiveApiKeyDescriptor.fromPropertyFile(),
|
||||
"", "", "30", EffectiveApiKeyDescriptor.absent());
|
||||
EffectiveApiKeyDescriptor.fromPropertyFile(), "sk-claude",
|
||||
"", "", "30", EffectiveApiKeyDescriptor.absent(), "");
|
||||
|
||||
EditorValidationReport report = validator.validate(input);
|
||||
|
||||
@@ -359,8 +361,8 @@ class EditorConfigurationValidatorTest {
|
||||
"claude", "C:/source", "C:/target", "C:/db.sqlite", "C:/prompt.txt",
|
||||
"3", "10", "500",
|
||||
"https://api.anthropic.com", "", "30",
|
||||
EffectiveApiKeyDescriptor.fromPropertyFile(),
|
||||
"", "", "30", EffectiveApiKeyDescriptor.absent());
|
||||
EffectiveApiKeyDescriptor.fromPropertyFile(), "sk-claude",
|
||||
"", "", "30", EffectiveApiKeyDescriptor.absent(), "");
|
||||
|
||||
EditorValidationReport report = validator.validate(input);
|
||||
|
||||
@@ -374,8 +376,8 @@ class EditorConfigurationValidatorTest {
|
||||
"claude", "C:/source", "C:/target", "C:/db.sqlite", "C:/prompt.txt",
|
||||
"3", "10", "500",
|
||||
"", "claude-3-5-sonnet", "30",
|
||||
EffectiveApiKeyDescriptor.fromPropertyFile(),
|
||||
"", "", "30", EffectiveApiKeyDescriptor.absent());
|
||||
EffectiveApiKeyDescriptor.fromPropertyFile(), "sk-claude",
|
||||
"", "", "30", EffectiveApiKeyDescriptor.absent(), "");
|
||||
|
||||
EditorValidationReport report = validator.validate(input);
|
||||
|
||||
@@ -389,8 +391,8 @@ class EditorConfigurationValidatorTest {
|
||||
"claude", "C:/source", "C:/target", "C:/db.sqlite", "C:/prompt.txt",
|
||||
"3", "10", "500",
|
||||
"https://api.anthropic.com", "claude-3-5-sonnet", "-5",
|
||||
EffectiveApiKeyDescriptor.fromPropertyFile(),
|
||||
"", "", "30", EffectiveApiKeyDescriptor.absent());
|
||||
EffectiveApiKeyDescriptor.fromPropertyFile(), "sk-claude",
|
||||
"", "", "30", EffectiveApiKeyDescriptor.absent(), "");
|
||||
|
||||
EditorValidationReport report = validator.validate(input);
|
||||
|
||||
@@ -408,8 +410,8 @@ class EditorConfigurationValidatorTest {
|
||||
"claude", "C:/source", "C:/target", "C:/db.sqlite", "C:/prompt.txt",
|
||||
"3", "10", "500",
|
||||
"https://api.anthropic.com", "claude-3-5-sonnet", "30",
|
||||
EffectiveApiKeyDescriptor.absent(),
|
||||
"", "", "30", EffectiveApiKeyDescriptor.absent());
|
||||
EffectiveApiKeyDescriptor.absent(), "",
|
||||
"", "", "30", EffectiveApiKeyDescriptor.absent(), "");
|
||||
|
||||
EditorValidationReport report = validator.validate(input);
|
||||
|
||||
@@ -434,8 +436,8 @@ class EditorConfigurationValidatorTest {
|
||||
"claude", "C:/source", "C:/target", "C:/db.sqlite", "C:/prompt.txt",
|
||||
"3", "10", "500",
|
||||
"https://api.anthropic.com", "claude-3-5-sonnet", "30",
|
||||
EffectiveApiKeyDescriptor.fromProviderEnvVar("ANTHROPIC_API_KEY"),
|
||||
"", "", "30", EffectiveApiKeyDescriptor.absent());
|
||||
EffectiveApiKeyDescriptor.fromProviderEnvVar("ANTHROPIC_API_KEY"), "",
|
||||
"", "", "30", EffectiveApiKeyDescriptor.absent(), "");
|
||||
|
||||
EditorValidationReport report = validator.validate(input);
|
||||
|
||||
@@ -449,9 +451,9 @@ class EditorConfigurationValidatorTest {
|
||||
EditorValidationInput input = new EditorValidationInput(
|
||||
"openai-compatible", "C:/source", "C:/target", "C:/db.sqlite", "C:/prompt.txt",
|
||||
"3", "10", "500",
|
||||
"", "", "30", EffectiveApiKeyDescriptor.absent(),
|
||||
"", "", "30", EffectiveApiKeyDescriptor.absent(), "",
|
||||
"https://api.openai.com", "gpt-4", "30",
|
||||
EffectiveApiKeyDescriptor.fromLegacyEnvVar("PDF_UMBENENNER_API_KEY"));
|
||||
EffectiveApiKeyDescriptor.fromLegacyEnvVar("PDF_UMBENENNER_API_KEY"), "");
|
||||
|
||||
EditorValidationReport report = validator.validate(input);
|
||||
|
||||
@@ -478,9 +480,9 @@ class EditorConfigurationValidatorTest {
|
||||
EditorValidationInput input = new EditorValidationInput(
|
||||
"openai-compatible", "C:/source", "C:/target", "C:/db.sqlite", "C:/prompt.txt",
|
||||
"3", "10", "500",
|
||||
"", "", "30", EffectiveApiKeyDescriptor.absent(),
|
||||
"", "", "30", EffectiveApiKeyDescriptor.absent(), "",
|
||||
"https://api.openai.com", "gpt-4", "30",
|
||||
EffectiveApiKeyDescriptor.fromPropertyFile());
|
||||
EffectiveApiKeyDescriptor.fromPropertyFile(), "sk-openai");
|
||||
|
||||
EditorValidationReport report = validator.validate(input);
|
||||
|
||||
@@ -498,8 +500,8 @@ class EditorConfigurationValidatorTest {
|
||||
"claude", "C:/source", "C:/target", "C:/db.sqlite", "C:/prompt.txt",
|
||||
"3", "10", "500",
|
||||
"https://api.anthropic.com", "claude-3-5-sonnet", "30",
|
||||
EffectiveApiKeyDescriptor.fromPropertyFile(),
|
||||
"", "", "", EffectiveApiKeyDescriptor.absent());
|
||||
EffectiveApiKeyDescriptor.fromPropertyFile(), "sk-claude",
|
||||
"", "", "", EffectiveApiKeyDescriptor.absent(), "");
|
||||
|
||||
EditorValidationReport report = validator.validate(input);
|
||||
|
||||
|
||||
+29
-8
@@ -29,9 +29,9 @@ class ProviderTechnicalTestServiceTest {
|
||||
"/src", "/tgt", "/db.sqlite", "/prompt.txt",
|
||||
"3", "10", "2000",
|
||||
"https://api.anthropic.com", model, "30",
|
||||
apiKeyDescriptor,
|
||||
apiKeyDescriptor, "sk-test",
|
||||
"https://api.openai.com", "gpt-4", "30",
|
||||
EffectiveApiKeyDescriptor.absent());
|
||||
EffectiveApiKeyDescriptor.absent(), "");
|
||||
}
|
||||
|
||||
private static EditorValidationInput openaiInput(EffectiveApiKeyDescriptor apiKeyDescriptor,
|
||||
@@ -41,9 +41,9 @@ class ProviderTechnicalTestServiceTest {
|
||||
"/src", "/tgt", "/db.sqlite", "/prompt.txt",
|
||||
"3", "10", "2000",
|
||||
"https://api.anthropic.com", "claude-3-sonnet", "30",
|
||||
EffectiveApiKeyDescriptor.absent(),
|
||||
EffectiveApiKeyDescriptor.absent(), "",
|
||||
"https://api.openai.com", model, "30",
|
||||
apiKeyDescriptor);
|
||||
apiKeyDescriptor, "sk-test");
|
||||
}
|
||||
|
||||
private static EffectiveApiKeyDescriptor keyFromEnv() {
|
||||
@@ -60,11 +60,32 @@ class ProviderTechnicalTestServiceTest {
|
||||
|
||||
/**
|
||||
* Stub-Port der immer den über den Konstruktor übergebenen Descriptor zurückgibt,
|
||||
* unabhängig von family und propertyValue.
|
||||
* unabhängig von family und propertyValue. Liefert den Roh-API-Schlüssel aus
|
||||
* {@code propertyValue}, falls gesetzt; sonst einen festen Stub-Wert, sobald der
|
||||
* Descriptor eine Quelle meldet.
|
||||
*/
|
||||
private static de.gecheckt.pdf.umbenenner.application.validation.editor.ApiKeyResolutionPort
|
||||
apiKeyPort(EffectiveApiKeyDescriptor descriptor) {
|
||||
return (family, propertyValue) -> descriptor;
|
||||
return new de.gecheckt.pdf.umbenenner.application.validation.editor.ApiKeyResolutionPort() {
|
||||
@Override
|
||||
public EffectiveApiKeyDescriptor resolve(
|
||||
de.gecheckt.pdf.umbenenner.application.config.provider.AiProviderFamily family,
|
||||
String propertyValue) {
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public java.util.Optional<String> resolveEffectiveApiKeyValue(
|
||||
de.gecheckt.pdf.umbenenner.application.config.provider.AiProviderFamily family,
|
||||
String propertyValue) {
|
||||
if (propertyValue != null && !propertyValue.isBlank()) {
|
||||
return java.util.Optional.of(propertyValue);
|
||||
}
|
||||
return descriptor.isAbsent()
|
||||
? java.util.Optional.empty()
|
||||
: java.util.Optional.of("stub-key");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -388,9 +409,9 @@ class ProviderTechnicalTestServiceTest {
|
||||
"/src", "/tgt", "/db.sqlite", "/prompt.txt",
|
||||
"3", "10", "2000",
|
||||
"", "model", "30",
|
||||
EffectiveApiKeyDescriptor.absent(),
|
||||
EffectiveApiKeyDescriptor.absent(), "",
|
||||
"", "model", "30",
|
||||
EffectiveApiKeyDescriptor.absent());
|
||||
EffectiveApiKeyDescriptor.absent(), "");
|
||||
|
||||
List<CheckpointResult> results = service.runProviderChecks(input);
|
||||
|
||||
|
||||
+8
-8
@@ -31,9 +31,9 @@ class TechnicalTestOrchestratorTest {
|
||||
"/src", "/tgt", "/db.sqlite", "/prompt.txt",
|
||||
"3", "10", "500",
|
||||
"https://api.anthropic.com", "claude-3-sonnet", "30",
|
||||
EffectiveApiKeyDescriptor.fromPropertyFile(),
|
||||
EffectiveApiKeyDescriptor.fromPropertyFile(), "sk-test",
|
||||
"https://api.openai.com", "gpt-4", "30",
|
||||
EffectiveApiKeyDescriptor.absent());
|
||||
EffectiveApiKeyDescriptor.absent(), "");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -45,9 +45,9 @@ class TechnicalTestOrchestratorTest {
|
||||
"", "", "", "",
|
||||
"", "", "",
|
||||
"", "", "",
|
||||
EffectiveApiKeyDescriptor.absent(),
|
||||
EffectiveApiKeyDescriptor.absent(), "",
|
||||
"", "", "",
|
||||
EffectiveApiKeyDescriptor.absent());
|
||||
EffectiveApiKeyDescriptor.absent(), "");
|
||||
}
|
||||
|
||||
/** No-op {@link PathCheckPort}: alle Prüfungen liefern {@code false}. */
|
||||
@@ -372,9 +372,9 @@ class TechnicalTestOrchestratorTest {
|
||||
"", // kein Prompt-Pfad
|
||||
"3", "10", "500",
|
||||
"https://api.anthropic.com", "claude-3-sonnet", "30",
|
||||
EffectiveApiKeyDescriptor.fromPropertyFile(),
|
||||
EffectiveApiKeyDescriptor.fromPropertyFile(), "sk-test",
|
||||
"https://api.openai.com", "gpt-4", "30",
|
||||
EffectiveApiKeyDescriptor.absent());
|
||||
EffectiveApiKeyDescriptor.absent(), "");
|
||||
|
||||
// PathCheckPort: Dateien fehlen, aber Elternordner sind schreibbar
|
||||
PathCheckPort pathPort = new PathCheckPort() {
|
||||
@@ -417,9 +417,9 @@ class TechnicalTestOrchestratorTest {
|
||||
"", // kein Prompt-Pfad
|
||||
"3", "10", "500",
|
||||
"https://api.anthropic.com", "claude-3-sonnet", "30",
|
||||
EffectiveApiKeyDescriptor.fromPropertyFile(),
|
||||
EffectiveApiKeyDescriptor.fromPropertyFile(), "sk-test",
|
||||
"https://api.openai.com", "gpt-4", "30",
|
||||
EffectiveApiKeyDescriptor.absent());
|
||||
EffectiveApiKeyDescriptor.absent(), "");
|
||||
|
||||
PathCheckPort pathPort = new PathCheckPort() {
|
||||
@Override public boolean isDirectoryReadable(String p) { return true; }
|
||||
|
||||
+2
-2
@@ -16,8 +16,8 @@ class TechnicalTestRequestTest {
|
||||
private static EditorValidationInput minimalInput() {
|
||||
return new EditorValidationInput(
|
||||
"claude", "", "", "", "", "3", "10", "2000",
|
||||
"", "model-x", "60", EffectiveApiKeyDescriptor.absent(),
|
||||
"", "", "60", EffectiveApiKeyDescriptor.absent());
|
||||
"", "model-x", "60", EffectiveApiKeyDescriptor.absent(), "",
|
||||
"", "", "60", EffectiveApiKeyDescriptor.absent(), "");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user