Fix #16: TitleCharacterRule um Punkt, Komma und Ampersand erweitern
- Erweitere TitleCharacterRule.isAllowed() um die Zeichen: . (Punkt), , (Komma), & (Ampersand) - Passe JavaDoc-Kommentare auf Deutsch an - Aktualisiere TitleCharacterRuleTest: ändere Punkt-Test von disallowed zu allowed - Füge Tests für Komma und Ampersand hinzu - Füge Tests hinzu, die Windows-Sonderzeichen (\ / : * ? " < > |) weiterhin als ungültig bestätigen - Aktualisiere TargetFilenameBuildingServiceTest für den neuen Test-Fall - Dokumentation: fachliche-anforderungen.md und CLAUDE.md aktualisiert mvn clean verify erfolgreich bestanden Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
+18
-15
@@ -1,15 +1,15 @@
|
||||
package de.gecheckt.pdf.umbenenner.application.service;
|
||||
|
||||
/**
|
||||
* Canonical rule for allowed title characters.
|
||||
* Kanonische Regel für erlaubte Titelzeichen.
|
||||
* <p>
|
||||
* A title may contain only Unicode letters (including German Umlauts and ß),
|
||||
* decimal digits, ASCII spaces, and hyphens ({@code -}).
|
||||
* All other characters are considered disallowed.
|
||||
* Ein Titel darf nur Unicode-Buchstaben (einschließlich deutscher Umlaute und ß),
|
||||
* Ziffern, ASCII-Leerzeichen, Bindestriche ({@code -}), Punkte ({@code .}),
|
||||
* Kommas ({@code ,}) und Ampersands ({@code &}) enthalten.
|
||||
* Alle anderen Zeichen sind nicht erlaubt.
|
||||
* <p>
|
||||
* This class is the single authoritative implementation of the character-allowlist
|
||||
* rule. All services that need to validate or verify title characters must delegate
|
||||
* to {@link #isAllowed(String)} instead of maintaining their own copies.
|
||||
* Diese Klasse ist die einzige autoritative Implementierung der Zeichenerlaubnis-Regel.
|
||||
* Alle Services, die Titelzeichen validieren müssen, delegieren an {@link #isAllowed(String)}.
|
||||
*/
|
||||
public final class TitleCharacterRule {
|
||||
|
||||
@@ -18,20 +18,23 @@ public final class TitleCharacterRule {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if every character in {@code title} is a letter, digit,
|
||||
* ASCII space, or hyphen ({@code -}).
|
||||
* Gibt {@code true} zurück, wenn jedes Zeichen im {@code title} ein Buchstabe, eine Ziffer,
|
||||
* ein ASCII-Leerzeichen, ein Bindestrich ({@code -}), ein Punkt ({@code .}),
|
||||
* ein Komma ({@code ,}) oder ein Ampersand ({@code &}) ist.
|
||||
* <p>
|
||||
* Unicode letters — including German Umlauts (ä, ö, ü, Ä, Ö, Ü) and ß — are
|
||||
* permitted. An empty string is considered allowed.
|
||||
* Unicode-Buchstaben — einschließlich deutscher Umlaute (ä, ö, ü, Ä, Ö, Ü) und ß — sind
|
||||
* erlaubt. Eine leere Zeichenkette ist erlaubt.
|
||||
*
|
||||
* @param title the title to check; must not be null
|
||||
* @return {@code true} if all characters pass the allowlist; {@code false} otherwise
|
||||
* @throws NullPointerException if {@code title} is null
|
||||
* @param title der zu prüfende Titel; darf nicht null sein
|
||||
* @return {@code true} wenn alle Zeichen die Erlaubnisliste erfüllen; {@code false} andernfalls
|
||||
* @throws NullPointerException wenn {@code title} null ist
|
||||
*/
|
||||
public static boolean isAllowed(String title) {
|
||||
for (int i = 0; i < title.length(); i++) {
|
||||
char c = title.charAt(i);
|
||||
if (!Character.isLetter(c) && !Character.isDigit(c) && c != ' ' && c != '-') {
|
||||
// Erlaubt: Buchstaben, Ziffern, Leerzeichen, Bindestrich, Punkt, Komma, Ampersand
|
||||
if (!Character.isLetter(c) && !Character.isDigit(c) && c != ' ' && c != '-'
|
||||
&& c != '.' && c != ',' && c != '&') {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
+6
-5
@@ -250,14 +250,15 @@ class TargetFilenameBuildingServiceTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void buildBaseFilename_titleWithDot_returnsInconsistentProposalState() {
|
||||
// Dot (.) is NOT a Windows-incompatible character (as per our list < > : " / \ | ? *)
|
||||
// So it remains in the cleaned title and causes validation to fail
|
||||
ProcessingAttempt attempt = proposalAttempt(LocalDate.of(2026, 1, 1), "Rechnung.pdf");
|
||||
void buildBaseFilename_titleWithDot_returnsBaseFilenameReady() {
|
||||
// Punkt (.) ist nun ein erlaubtes Zeichen in Titeln
|
||||
ProcessingAttempt attempt = proposalAttempt(LocalDate.of(2026, 1, 1), "Rechnung.Kopie");
|
||||
|
||||
BaseFilenameResult result = TargetFilenameBuildingService.buildBaseFilename(attempt, TEST_MAX_TITLE_LENGTH);
|
||||
|
||||
assertThat(result).isInstanceOf(InconsistentProposalState.class);
|
||||
assertThat(result).isInstanceOf(BaseFilenameReady.class);
|
||||
assertThat((BaseFilenameReady) result).extracting("baseFilename")
|
||||
.isEqualTo("2026-01-01 - Rechnung.Kopie.pdf");
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
+41
-4
@@ -7,8 +7,9 @@ import org.junit.jupiter.api.Test;
|
||||
/**
|
||||
* Unit tests for {@link TitleCharacterRule}.
|
||||
* <p>
|
||||
* Verifies the canonical character-allowlist rule: letters (including German Umlauts
|
||||
* and ß), digits, ASCII spaces, and hyphens are permitted; everything else is not.
|
||||
* Prüft die kanonische Zeichenerlaubnis-Regel: Buchstaben (einschließlich deutscher Umlaute
|
||||
* und ß), Ziffern, ASCII-Leerzeichen, Bindestriche, Punkte, Kommas und Ampersands sind
|
||||
* erlaubt; alles andere ist nicht erlaubt.
|
||||
*/
|
||||
class TitleCharacterRuleTest {
|
||||
|
||||
@@ -46,6 +47,21 @@ class TitleCharacterRuleTest {
|
||||
assertThat(TitleCharacterRule.isAllowed("Kfz-Haftpflicht 2026")).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void isAllowed_comma_returnsTrue() {
|
||||
assertThat(TitleCharacterRule.isAllowed("Müller, Hans")).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void isAllowed_ampersand_returnsTrue() {
|
||||
assertThat(TitleCharacterRule.isAllowed("Müller & Söhne")).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void isAllowed_dotCommaAmpersand_returnsTrue() {
|
||||
assertThat(TitleCharacterRule.isAllowed("Firma Dr. Meyer, Müller & Co.")).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void isAllowed_emptyString_returnsTrue() {
|
||||
assertThat(TitleCharacterRule.isAllowed("")).isTrue();
|
||||
@@ -61,8 +77,8 @@ class TitleCharacterRuleTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void isAllowed_dot_returnsFalse() {
|
||||
assertThat(TitleCharacterRule.isAllowed("Rechnung.pdf")).isFalse();
|
||||
void isAllowed_dot_returnsTrue() {
|
||||
assertThat(TitleCharacterRule.isAllowed("Rechnung.Kopie")).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -104,4 +120,25 @@ class TitleCharacterRuleTest {
|
||||
void isAllowed_underscore_returnsFalse() {
|
||||
assertThat(TitleCharacterRule.isAllowed("Rechnung_2026")).isFalse();
|
||||
}
|
||||
|
||||
// Windows-Sonderzeichen müssen weiterhin abgelehnt werden
|
||||
@Test
|
||||
void isAllowed_doubleQuote_returnsFalse() {
|
||||
assertThat(TitleCharacterRule.isAllowed("Rechnung \"2026\"")).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void isAllowed_lessThan_returnsFalse() {
|
||||
assertThat(TitleCharacterRule.isAllowed("Rg < 2026")).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void isAllowed_greaterThan_returnsFalse() {
|
||||
assertThat(TitleCharacterRule.isAllowed("Rg > 2026")).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void isAllowed_pipe_returnsFalse() {
|
||||
assertThat(TitleCharacterRule.isAllowed("Rg | Strom")).isFalse();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user