Fix Bug #12 (2/2): Zweite Validierungsstelle in TargetFilenameBuildingService angepasst

- TargetFilenameBuildingService.isAllowedTitleCharacters() erlaubt jetzt Bindestrich (-)
- Fehlermeldung nach Windows-Cleaning aktualisiert
- JavaDoc aktualisiert
- Test TargetFilenameBuildingServiceTest.buildBaseFilename_titleWithHyphen angepasst
- Test DocumentProcessingCoordinatorTest angepasst (Bindestrich → Ausrufezeichen)
- Vollständiger Reactor-Build grün: mvn clean verify
This commit is contained in:
2026-04-23 07:24:49 +02:00
parent 34c8245ae9
commit 2e6d0b1d6d
3 changed files with 12 additions and 12 deletions
@@ -93,7 +93,7 @@ public final class TargetFilenameBuildingService {
* <li>Validated title must be non-null and non-blank.</li> * <li>Validated title must be non-null and non-blank.</li>
* <li>Validated title must not exceed the configured maximum length * <li>Validated title must not exceed the configured maximum length
* (before Windows cleaning).</li> * (before Windows cleaning).</li>
* <li>After Windows-character cleaning, title must contain only letters, digits, and spaces.</li> * <li>After Windows-character cleaning, title must contain only letters, digits, spaces, and hyphens.</li>
* </ul> * </ul>
* If any rule is violated, the state is treated as an * If any rule is violated, the state is treated as an
* {@link InconsistentProposalState}. * {@link InconsistentProposalState}.
@@ -148,11 +148,11 @@ public final class TargetFilenameBuildingService {
+ title + "'"); + title + "'");
} }
// After cleaning, verify that only letters, digits, and spaces remain // After cleaning, verify that only letters, digits, spaces, and hyphens remain
if (!isAllowedTitleCharacters(cleanedTitle)) { if (!isAllowedTitleCharacters(cleanedTitle)) {
return new InconsistentProposalState( return new InconsistentProposalState(
"After Windows-compatibility cleaning, title contains disallowed characters " "After Windows-compatibility cleaning, title contains disallowed characters "
+ "(only letters, digits, and spaces are permitted): '" + "(only letters, digits, spaces, and hyphens are permitted): '"
+ cleanedTitle + "'"); + cleanedTitle + "'");
} }
@@ -166,13 +166,13 @@ public final class TargetFilenameBuildingService {
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
/** /**
* Returns {@code true} if every character in the title is a letter, a digit, or a space. * Returns {@code true} if every character in the title is a letter, a digit, space, or hyphen.
* Unicode letters (including German Umlauts and ß) are permitted. * Unicode letters (including German Umlauts and ß) are permitted.
*/ */
private static boolean isAllowedTitleCharacters(String title) { private static boolean isAllowedTitleCharacters(String title) {
for (int i = 0; i < title.length(); i++) { for (int i = 0; i < title.length(); i++) {
char c = title.charAt(i); char c = title.charAt(i);
if (!Character.isLetter(c) && !Character.isDigit(c) && c != ' ') { if (!Character.isLetter(c) && !Character.isDigit(c) && c != ' ' && c != '-') {
return false; return false;
} }
} }
@@ -978,14 +978,14 @@ class DocumentProcessingCoordinatorTest {
DocumentRecord existingRecord = buildRecord(ProcessingStatus.PROPOSAL_READY, FailureCounters.zero()); DocumentRecord existingRecord = buildRecord(ProcessingStatus.PROPOSAL_READY, FailureCounters.zero());
recordRepo.setLookupResult(new DocumentKnownProcessable(existingRecord)); recordRepo.setLookupResult(new DocumentKnownProcessable(existingRecord));
// Hyphen is a disallowed character in the fachliche Titelregel // Exclamation mark is a disallowed character in the fachliche Titelregel
ProcessingAttempt badProposal = new ProcessingAttempt( ProcessingAttempt badProposal = new ProcessingAttempt(
fingerprint, context.runId(), 1, Instant.now(), Instant.now(), fingerprint, context.runId(), 1, Instant.now(), Instant.now(),
ProcessingStatus.PROPOSAL_READY, null, null, false, ProcessingStatus.PROPOSAL_READY, null, null, false,
null, null,
"model", "prompt", 1, 100, "{}", "reason", "model", "prompt", 1, 100, "{}", "reason",
LocalDate.of(2026, 1, 15), DateSource.AI_PROVIDED, LocalDate.of(2026, 1, 15), DateSource.AI_PROVIDED,
"Rechnung-2026", null); "Rechnung!", null);
attemptRepo.savedAttempts.add(badProposal); attemptRepo.savedAttempts.add(badProposal);
boolean result = processor.processDeferredOutcome(candidate, fingerprint, context, attemptStart, c -> null); boolean result = processor.processDeferredOutcome(candidate, fingerprint, context, attemptStart, c -> null);
@@ -212,15 +212,15 @@ class TargetFilenameBuildingServiceTest {
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
@Test @Test
void buildBaseFilename_titleWithHyphen_returnsInconsistentProposalState() { void buildBaseFilename_titleWithHyphen_succeeds() {
// Hyphens are not letters, digits, or spaces — disallowed by fachliche Titelregel // Hyphens are allowed per fachliche Titelregel
ProcessingAttempt attempt = proposalAttempt(LocalDate.of(2026, 1, 1), "Rechnung-2026"); ProcessingAttempt attempt = proposalAttempt(LocalDate.of(2026, 1, 1), "Rechnung-2026");
BaseFilenameResult result = TargetFilenameBuildingService.buildBaseFilename(attempt, TEST_MAX_TITLE_LENGTH); BaseFilenameResult result = TargetFilenameBuildingService.buildBaseFilename(attempt, TEST_MAX_TITLE_LENGTH);
assertThat(result).isInstanceOf(InconsistentProposalState.class); assertThat(result).isInstanceOf(BaseFilenameReady.class);
assertThat(((InconsistentProposalState) result).reason()) assertThat(((BaseFilenameReady) result).baseFilename())
.contains("disallowed characters"); .isEqualTo("2026-01-01 - Rechnung-2026.pdf");
} }
@Test @Test