mirror of
https://gitee.com/mateos/mateclaw.git
synced 2026-09-13 19:23:42 +08:00
The base directory is canonicalized via toRealPath before walking, so the
walked files carry the symlink-resolved prefix. The PathMatcher was built
from the literal pattern, so a symlinked base never matched and files were
silently dropped. Rebuild the glob against the resolved scan root, escaping
glob metacharacters in the base so a real directory name containing */?/{}/[]
is treated literally.
80 lines
3.2 KiB
Java
80 lines
3.2 KiB
Java
package vip.mate.wiki.service;
|
|
|
|
import org.junit.jupiter.api.Test;
|
|
import org.junit.jupiter.api.io.TempDir;
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
import org.springframework.boot.test.context.SpringBootTest;
|
|
|
|
import java.io.IOException;
|
|
import java.nio.file.Files;
|
|
import java.nio.file.Path;
|
|
|
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
|
|
|
/**
|
|
* Verifies glob matching works when the pattern's base directory is reached
|
|
* through a symbolic link. {@code WikiSourcePathValidator} canonicalizes the
|
|
* base with {@code toRealPath()}, so the walked files carry the symlink-resolved
|
|
* prefix; the matcher must be built against that resolved scan root rather than
|
|
* the literal pattern prefix, otherwise nothing matches and files are silently
|
|
* dropped.
|
|
*/
|
|
@SpringBootTest(
|
|
webEnvironment = SpringBootTest.WebEnvironment.NONE,
|
|
properties = {
|
|
"spring.flyway.enabled=true",
|
|
"spring.flyway.locations=classpath:db/migration/h2",
|
|
"mateclaw.feature-flag.refresh-ms=999999",
|
|
"mate.wiki.auto-process-on-upload=false"
|
|
}
|
|
)
|
|
class WikiGlobSymlinkBaseE2ETest {
|
|
|
|
@Autowired
|
|
private WikiDirectoryScanService scanService;
|
|
|
|
private static final java.util.concurrent.atomic.AtomicLong SEQ =
|
|
new java.util.concurrent.atomic.AtomicLong(System.nanoTime());
|
|
|
|
@Test
|
|
void globWithSymlinkBase_singleLevel_matches(@TempDir Path base) throws IOException {
|
|
Path realDir = Files.createDirectories(base.resolve("real"));
|
|
Files.writeString(realDir.resolve("a.txt"), "alpha");
|
|
Files.write(realDir.resolve("b.pdf"), "PDF-bytes".getBytes());
|
|
|
|
Path linkDir = base.resolve("link");
|
|
try {
|
|
Files.createSymbolicLink(linkDir, realDir);
|
|
} catch (UnsupportedOperationException | IOException e) {
|
|
return; // filesystem without symlink support — skip
|
|
}
|
|
|
|
WikiDirectoryScanService.ScanResult result =
|
|
scanService.scanDirectory(SEQ.incrementAndGet(), linkDir + "/*.txt");
|
|
|
|
// Only a.txt matches; the .pdf is excluded by the explicit *.txt pattern.
|
|
// Before the fix the symlink-resolved file prefix never matched the literal
|
|
// pattern prefix, so added would be 0.
|
|
assertEquals(1, result.added(), "glob through a symlinked base must match the .txt file");
|
|
}
|
|
|
|
@Test
|
|
void globWithSymlinkBase_recursive_matchesSubdir(@TempDir Path base) throws IOException {
|
|
Path realDir = Files.createDirectories(base.resolve("real"));
|
|
Files.createDirectories(realDir.resolve("sub"));
|
|
Files.writeString(realDir.resolve("sub/c.txt"), "charlie");
|
|
|
|
Path linkDir = base.resolve("link");
|
|
try {
|
|
Files.createSymbolicLink(linkDir, realDir);
|
|
} catch (UnsupportedOperationException | IOException e) {
|
|
return; // filesystem without symlink support — skip
|
|
}
|
|
|
|
WikiDirectoryScanService.ScanResult result =
|
|
scanService.scanDirectory(SEQ.incrementAndGet(), linkDir + "/**/*.txt");
|
|
|
|
assertEquals(1, result.added(), "recursive glob through a symlinked base must match the nested .txt file");
|
|
}
|
|
}
|