Fix #18: Leeres Datumsfeld wird als Fehler behandelt statt als fehlendes Datum

Die KI liefert manchmal "date": "" statt das Feld wegzulassen. Laut Spezifikation
ist date optional – fehlt es oder ist es leer, soll das aktuelle Datum als Fallback
verwendet werden.

Änderung in AiResponseValidator:
- Leere Strings (nach trim) werden identisch wie fehlende date-Felder behandelt
- Fallback auf aktuelles Datum über ClockPort mit DateSource.FALLBACK_CURRENT
- Validierungsfehler "date could not be parsed" wird nicht mehr geworfen

Neuer Test:
- validate_aiProvidesEmptyDateString_usesFallbackCurrentDate überprüft, dass
  "date": "" zum Fallback-Datum führt

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-04-23 17:42:39 +02:00
parent e7f5590934
commit 955adc0c45
2 changed files with 27 additions and 9 deletions
@@ -119,7 +119,12 @@ public final class AiResponseValidator {
DateSource dateSource;
if (parsed.dateString().isPresent()) {
String dateStr = parsed.dateString().get();
String dateStr = parsed.dateString().get().trim();
// Leere Strings werden wie fehlende Daten behandelt Fallback auf aktuelles Datum
if (dateStr.isEmpty()) {
resolvedDate = clockPort.now().atZone(java.time.ZoneOffset.UTC).toLocalDate();
dateSource = DateSource.FALLBACK_CURRENT;
} else {
try {
resolvedDate = LocalDate.parse(dateStr);
dateSource = DateSource.AI_PROVIDED;
@@ -128,8 +133,9 @@ public final class AiResponseValidator {
"AI-provided date '" + dateStr + "' is not a valid YYYY-MM-DD date: " + e.getMessage(),
AiErrorClassification.FUNCTIONAL);
}
}
} else {
// No date provided by the AIfall back to current date from the clock
// Kein Datum von der KI bereitgestelltFallback auf aktuelles Datum
resolvedDate = clockPort.now().atZone(java.time.ZoneOffset.UTC).toLocalDate();
dateSource = DateSource.FALLBACK_CURRENT;
}
@@ -65,6 +65,18 @@ class AiResponseValidatorTest {
assertThat(proposal.dateSource()).isEqualTo(DateSource.FALLBACK_CURRENT);
}
@Test
void validate_aiProvidesEmptyDateString_usesFallbackCurrentDate() {
ParsedAiResponse parsed = ParsedAiResponse.of("Kontoauszug", "No date in document", "");
AiResponseValidator.AiValidationResult result = validator.validate(parsed);
assertThat(result).isInstanceOf(AiResponseValidator.AiValidationResult.Valid.class);
NamingProposal proposal = ((AiResponseValidator.AiValidationResult.Valid) result).proposal();
assertThat(proposal.resolvedDate()).isEqualTo(FIXED_DATE);
assertThat(proposal.dateSource()).isEqualTo(DateSource.FALLBACK_CURRENT);
}
@Test
void validate_titleWithUmlauts_isAccepted() {
ParsedAiResponse parsed = ParsedAiResponse.of("Mietvertrag Müller", "Rental contract", null);