|
21 | 21 | import com.salesforce.multicloudj.blob.driver.ObjectLockConfiguration; |
22 | 22 | import com.salesforce.multicloudj.blob.driver.RetentionMode; |
23 | 23 | import com.salesforce.multicloudj.common.exceptions.InvalidArgumentException; |
| 24 | +import com.salesforce.multicloudj.common.exceptions.SubstrateSdkException; |
24 | 25 | import com.salesforce.multicloudj.common.retries.RetryConfig; |
25 | 26 | import org.apache.commons.lang3.tuple.Pair; |
26 | 27 | import org.junit.jupiter.api.BeforeEach; |
@@ -1047,6 +1048,100 @@ public void testToFilePaths_FollowSymbolicLinks() throws IOException { |
1047 | 1048 | } |
1048 | 1049 | } |
1049 | 1050 |
|
| 1051 | + @Test |
| 1052 | + public void testToFilePaths_CircularSymbolicLinks() throws IOException { |
| 1053 | + Path tempDir = Files.createTempDirectory("test-circular-symlink"); |
| 1054 | + try { |
| 1055 | + Files.write(tempDir.resolve("file.txt"), "content".getBytes()); |
| 1056 | + Path dirA = Files.createDirectory(tempDir.resolve("dirA")); |
| 1057 | + Path dirB = Files.createDirectory(tempDir.resolve("dirB")); |
| 1058 | + Files.createSymbolicLink(dirA.resolve("link-to-b"), dirB); |
| 1059 | + Files.createSymbolicLink(dirB.resolve("link-to-a"), dirA); |
| 1060 | + |
| 1061 | + DirectoryUploadRequest request = DirectoryUploadRequest.builder() |
| 1062 | + .localSourceDirectory(tempDir.toString()) |
| 1063 | + .prefix("uploads/") |
| 1064 | + .includeSubFolders(true) |
| 1065 | + .followSymbolicLinks(true) |
| 1066 | + .build(); |
| 1067 | + |
| 1068 | + assertThrows(RuntimeException.class, () -> transformer.toFilePaths(request)); |
| 1069 | + } finally { |
| 1070 | + // Manual cleanup since Files.walk with FOLLOW_LINKS would fail on circular links |
| 1071 | + Files.deleteIfExists(tempDir.resolve("dirA/link-to-b")); |
| 1072 | + Files.deleteIfExists(tempDir.resolve("dirB/link-to-a")); |
| 1073 | + Files.walk(tempDir) |
| 1074 | + .sorted(Comparator.reverseOrder()) |
| 1075 | + .map(Path::toFile) |
| 1076 | + .forEach(File::delete); |
| 1077 | + } |
| 1078 | + } |
| 1079 | + |
| 1080 | + @Test |
| 1081 | + public void testToFilePaths_BrokenSymbolicLinks() throws IOException { |
| 1082 | + Path tempDir = Files.createTempDirectory("test-broken-symlink"); |
| 1083 | + try { |
| 1084 | + Path file1 = tempDir.resolve("file1.txt"); |
| 1085 | + Files.write(file1, "content".getBytes()); |
| 1086 | + Files.createSymbolicLink(tempDir.resolve("broken-link"), Paths.get("/non/existent/path")); |
| 1087 | + |
| 1088 | + DirectoryUploadRequest request = DirectoryUploadRequest.builder() |
| 1089 | + .localSourceDirectory(tempDir.toString()) |
| 1090 | + .prefix("uploads/") |
| 1091 | + .includeSubFolders(true) |
| 1092 | + .followSymbolicLinks(true) |
| 1093 | + .build(); |
| 1094 | + |
| 1095 | + List<Path> paths = transformer.toFilePaths(request); |
| 1096 | + assertEquals(1, paths.size()); |
| 1097 | + assertTrue(paths.contains(file1)); |
| 1098 | + } finally { |
| 1099 | + Files.walk(tempDir) |
| 1100 | + .sorted(Comparator.reverseOrder()) |
| 1101 | + .map(Path::toFile) |
| 1102 | + .forEach(File::delete); |
| 1103 | + } |
| 1104 | + } |
| 1105 | + |
| 1106 | + @Test |
| 1107 | + public void testToFilePaths_NestedSymbolicLinks() throws IOException { |
| 1108 | + Path tempDir = Files.createTempDirectory("test-nested-symlink"); |
| 1109 | + Path externalDir1 = Files.createTempDirectory("test-nested-target1"); |
| 1110 | + Path externalDir2 = Files.createTempDirectory("test-nested-target2"); |
| 1111 | + try { |
| 1112 | + Files.write(tempDir.resolve("root.txt"), "root".getBytes()); |
| 1113 | + Files.write(externalDir2.resolve("deep.txt"), "deep".getBytes()); |
| 1114 | + // externalDir1 contains a symlink to externalDir2 |
| 1115 | + Files.createSymbolicLink(externalDir1.resolve("link-to-dir2"), externalDir2); |
| 1116 | + // tempDir contains a symlink to externalDir1 |
| 1117 | + Files.createSymbolicLink(tempDir.resolve("link-to-dir1"), externalDir1); |
| 1118 | + |
| 1119 | + DirectoryUploadRequest request = DirectoryUploadRequest.builder() |
| 1120 | + .localSourceDirectory(tempDir.toString()) |
| 1121 | + .prefix("uploads/") |
| 1122 | + .includeSubFolders(true) |
| 1123 | + .followSymbolicLinks(true) |
| 1124 | + .build(); |
| 1125 | + |
| 1126 | + List<Path> paths = transformer.toFilePaths(request); |
| 1127 | + assertEquals(2, paths.size()); |
| 1128 | + assertTrue(paths.contains(tempDir.resolve("root.txt"))); |
| 1129 | + } finally { |
| 1130 | + Files.walk(externalDir2) |
| 1131 | + .sorted(Comparator.reverseOrder()) |
| 1132 | + .map(Path::toFile) |
| 1133 | + .forEach(File::delete); |
| 1134 | + Files.walk(externalDir1) |
| 1135 | + .sorted(Comparator.reverseOrder()) |
| 1136 | + .map(Path::toFile) |
| 1137 | + .forEach(File::delete); |
| 1138 | + Files.walk(tempDir) |
| 1139 | + .sorted(Comparator.reverseOrder()) |
| 1140 | + .map(Path::toFile) |
| 1141 | + .forEach(File::delete); |
| 1142 | + } |
| 1143 | + } |
| 1144 | + |
1050 | 1145 | @Test |
1051 | 1146 | public void testToBlobKey() { |
1052 | 1147 | Path sourceDir = Paths.get("/source"); |
|
0 commit comments