Bugfix Pfaderkennung
This commit is contained in:
+36
-8
@@ -1,5 +1,6 @@
|
|||||||
package de.gecheckt.pdf.umbenenner.adapter.out.pathcheck;
|
package de.gecheckt.pdf.umbenenner.adapter.out.pathcheck;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.InvalidPathException;
|
import java.nio.file.InvalidPathException;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
@@ -14,8 +15,8 @@ import de.gecheckt.pdf.umbenenner.application.validation.technicaltest.PathCheck
|
|||||||
* Dateisystem-basierte Implementierung von {@link PathCheckPort}.
|
* Dateisystem-basierte Implementierung von {@link PathCheckPort}.
|
||||||
* <p>
|
* <p>
|
||||||
* Prüft die Zugänglichkeit von Pfaden für Quellordner, Zielordner, SQLite-Datei
|
* Prüft die Zugänglichkeit von Pfaden für Quellordner, Zielordner, SQLite-Datei
|
||||||
* und Prompt-Datei ausschließlich lesend. Es werden keinerlei Dateien, Ordner oder
|
* und Prompt-Datei. Schreibbarkeitstests erfolgen über kurzlebige Probe-Dateien,
|
||||||
* andere Ressourcen angelegt, verändert oder gelöscht.
|
* die unmittelbar nach dem Schreibversuch wieder gelöscht werden.
|
||||||
*
|
*
|
||||||
* <h2>Windows- und Netzlaufwerk-Unterstützung</h2>
|
* <h2>Windows- und Netzlaufwerk-Unterstützung</h2>
|
||||||
* <p>
|
* <p>
|
||||||
@@ -27,8 +28,9 @@ import de.gecheckt.pdf.umbenenner.application.validation.technicaltest.PathCheck
|
|||||||
* Laufwerksbuchstaben und UNC-Pfaden statt.
|
* Laufwerksbuchstaben und UNC-Pfaden statt.
|
||||||
* <p>
|
* <p>
|
||||||
* Die Implementierung nutzt {@link Paths#get(String)}, {@link Files#exists(Path, java.nio.file.LinkOption...)},
|
* Die Implementierung nutzt {@link Paths#get(String)}, {@link Files#exists(Path, java.nio.file.LinkOption...)},
|
||||||
* {@link Files#isReadable(Path)} und {@link Files#isWritable(Path)}, die unter Windows
|
* {@link Files#isReadable(Path)} sowie einen Probe-Datei-Ansatz ({@link Files#createTempFile})
|
||||||
* gemappte Laufwerke korrekt respektieren.
|
* für Schreibbarkeitstests. Dieser Ansatz ist auf gemappten Windows-Netzlaufwerken
|
||||||
|
* zuverlässiger als {@link Files#isWritable(Path)}, das dort bekannte False Negatives liefert.
|
||||||
*
|
*
|
||||||
* <h2>Thread-Safety</h2>
|
* <h2>Thread-Safety</h2>
|
||||||
* <p>
|
* <p>
|
||||||
@@ -108,7 +110,7 @@ public class FilesystemPathCheckAdapter implements PathCheckPort {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (Files.exists(resolved)) {
|
if (Files.exists(resolved)) {
|
||||||
boolean writable = Files.isDirectory(resolved) && Files.isWritable(resolved);
|
boolean writable = Files.isDirectory(resolved) && probeDirectoryWritable(resolved);
|
||||||
if (writable) {
|
if (writable) {
|
||||||
LOG.debug("Ordner vorhanden und schreibbar: {}", resolved);
|
LOG.debug("Ordner vorhanden und schreibbar: {}", resolved);
|
||||||
} else {
|
} else {
|
||||||
@@ -118,7 +120,7 @@ public class FilesystemPathCheckAdapter implements PathCheckPort {
|
|||||||
}
|
}
|
||||||
// Ordner existiert nicht — prüfen ob Elternpfad schreibbar ist
|
// Ordner existiert nicht — prüfen ob Elternpfad schreibbar ist
|
||||||
Path parent = resolved.getParent();
|
Path parent = resolved.getParent();
|
||||||
if (parent != null && Files.exists(parent) && Files.isDirectory(parent) && Files.isWritable(parent)) {
|
if (parent != null && Files.exists(parent) && Files.isDirectory(parent) && probeDirectoryWritable(parent)) {
|
||||||
LOG.debug("Ordner nicht vorhanden, aber anlegbar (Elternpfad schreibbar): {}", resolved);
|
LOG.debug("Ordner nicht vorhanden, aber anlegbar (Elternpfad schreibbar): {}", resolved);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -179,9 +181,11 @@ public class FilesystemPathCheckAdapter implements PathCheckPort {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (Files.exists(resolved)) {
|
if (Files.exists(resolved)) {
|
||||||
|
Path parentDir = resolved.getParent();
|
||||||
boolean usable = Files.isRegularFile(resolved)
|
boolean usable = Files.isRegularFile(resolved)
|
||||||
&& Files.isReadable(resolved)
|
&& Files.isReadable(resolved)
|
||||||
&& Files.isWritable(resolved);
|
&& parentDir != null
|
||||||
|
&& probeDirectoryWritable(parentDir);
|
||||||
if (usable) {
|
if (usable) {
|
||||||
LOG.debug("SQLite-Datei vorhanden und nutzbar: {}", resolved);
|
LOG.debug("SQLite-Datei vorhanden und nutzbar: {}", resolved);
|
||||||
} else {
|
} else {
|
||||||
@@ -191,7 +195,7 @@ public class FilesystemPathCheckAdapter implements PathCheckPort {
|
|||||||
}
|
}
|
||||||
// Datei existiert nicht — prüfen ob Elternordner schreibbar ist
|
// Datei existiert nicht — prüfen ob Elternordner schreibbar ist
|
||||||
Path parent = resolved.getParent();
|
Path parent = resolved.getParent();
|
||||||
if (parent != null && Files.exists(parent) && Files.isDirectory(parent) && Files.isWritable(parent)) {
|
if (parent != null && Files.exists(parent) && Files.isDirectory(parent) && probeDirectoryWritable(parent)) {
|
||||||
LOG.debug("SQLite-Datei nicht vorhanden, aber anlegbar (Elternordner schreibbar): {}", resolved);
|
LOG.debug("SQLite-Datei nicht vorhanden, aber anlegbar (Elternordner schreibbar): {}", resolved);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -199,6 +203,30 @@ public class FilesystemPathCheckAdapter implements PathCheckPort {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prüft die Schreibbarkeit eines Verzeichnisses durch einen echten Schreibversuch.
|
||||||
|
* <p>
|
||||||
|
* Eine temporäre Probe-Datei wird im übergebenen Verzeichnis angelegt und sofort
|
||||||
|
* wieder gelöscht. Dieses Vorgehen ist auf gemappten Windows-Netzlaufwerken
|
||||||
|
* zuverlässiger als {@link Files#isWritable(Path)}, das dort False Negatives liefern kann.
|
||||||
|
*
|
||||||
|
* @param directory das zu prüfende Verzeichnis; muss ein existierendes Verzeichnis sein
|
||||||
|
* @return {@code true} wenn eine Datei im Verzeichnis angelegt werden konnte
|
||||||
|
*/
|
||||||
|
private static boolean probeDirectoryWritable(Path directory) {
|
||||||
|
try {
|
||||||
|
Path probe = Files.createTempFile(directory, ".writetest-", ".tmp");
|
||||||
|
try {
|
||||||
|
Files.deleteIfExists(probe);
|
||||||
|
} catch (IOException cleanupFailure) {
|
||||||
|
LOG.debug("Probe-Datei konnte nicht gelöscht werden: {} — {}", probe, cleanupFailure.getMessage());
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} catch (IOException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Konvertiert den übergebenen Pfad-String in ein {@link Path}-Objekt.
|
* Konvertiert den übergebenen Pfad-String in ein {@link Path}-Objekt.
|
||||||
* <p>
|
* <p>
|
||||||
|
|||||||
+14
-7
@@ -195,18 +195,25 @@ class FilesystemPathCheckAdapterTest {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Stellt sicher, dass Pfade mit gemapptem Laufwerksbuchstaben syntaktisch akzeptiert
|
* Stellt sicher, dass Pfade mit gemapptem Laufwerksbuchstaben syntaktisch akzeptiert
|
||||||
* werden (kein sofortiger Syntaxfehler). Das Ergebnis ist {@code false}, weil das
|
* werden (kein sofortiger Syntaxfehler). Für Methoden ohne Elternpfad-Prüfung ist
|
||||||
* Laufwerk in dieser Testumgebung nicht existiert — aber es darf nicht wegen des
|
* das Ergebnis {@code false}, weil das Laufwerk in dieser Testumgebung nicht existiert.
|
||||||
* Laufwerksbuchstabens allein abgelehnt werden.
|
* <p>
|
||||||
|
* Bei {@link FilesystemPathCheckAdapter#isDirectoryWritableOrCreatable} wird der
|
||||||
|
* Elternpfad (das Laufwerk selbst) per Probe-Datei geprüft. Existiert das Laufwerk
|
||||||
|
* {@code H:\} auf der Testmaschine und ist schreibbar, ist {@code true} das korrekte
|
||||||
|
* Ergebnis — die Prüfung wird in diesem Fall ohne Ergebnis-Assertion ausgeführt.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
@EnabledOnOs(OS.WINDOWS)
|
@EnabledOnOs(OS.WINDOWS)
|
||||||
void windowsMappedDriveSyntax_isAcceptedByAdapter() {
|
void windowsMappedDriveSyntax_isAcceptedByAdapter() {
|
||||||
// Ein Pfad mit gemapptem Laufwerksbuchstaben darf nicht wegen der Syntax abgelehnt
|
|
||||||
// werden. Da das Laufwerk in der Testumgebung nicht existiert, ist das Ergebnis
|
|
||||||
// false — aber es darf nicht zu einer Exception führen.
|
|
||||||
assertFalse(adapter.isDirectoryReadable("S:\\nonexistent-in-test"));
|
assertFalse(adapter.isDirectoryReadable("S:\\nonexistent-in-test"));
|
||||||
assertFalse(adapter.isDirectoryWritableOrCreatable("H:\\nonexistent-in-test"));
|
// isDirectoryWritableOrCreatable prüft den Elternpfad per Probe-Datei.
|
||||||
|
// Existiert H:\ und ist schreibbar, gibt die Methode korrekt true zurück.
|
||||||
|
if (!Files.exists(Path.of("H:\\"))) {
|
||||||
|
assertFalse(adapter.isDirectoryWritableOrCreatable("H:\\nonexistent-in-test"));
|
||||||
|
} else {
|
||||||
|
adapter.isDirectoryWritableOrCreatable("H:\\nonexistent-in-test");
|
||||||
|
}
|
||||||
assertFalse(adapter.isFileReadable("X:\\nonexistent-in-test\\file.txt"));
|
assertFalse(adapter.isFileReadable("X:\\nonexistent-in-test\\file.txt"));
|
||||||
assertFalse(adapter.isSqlitePathUsable("Z:\\nonexistent-in-test\\db.db"));
|
assertFalse(adapter.isSqlitePathUsable("Z:\\nonexistent-in-test\\db.db"));
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user