1616#include < string>
1717#include < vector>
1818#include " common/EasyAssert.h"
19+ #include " storage/FileManager.h"
1920#include " storage/LocalChunkManagerSingleton.h"
2021#include " storage/RemoteChunkManagerSingleton.h"
2122#include " storage/Util.h"
@@ -241,4 +242,76 @@ TEST_F(StorageUtilTest, TestInitArrowFileSystem) {
241242 // auto fs = InitArrowFileSystem(remote_config);
242243 // ASSERT_NE(fs, nullptr);
243244 // }
245+ }
246+
247+ // Test cases for NormalizePath function
248+ // NormalizePath uses boost::filesystem::path::lexically_normal() and then
249+ // removes trailing "/." (only the dot, keeping the slash)
250+ TEST_F (StorageUtilTest, NormalizePath) {
251+ // === Basic paths ===
252+ EXPECT_EQ (NormalizePath (boost::filesystem::path (" a/b/c" )), " a/b/c" );
253+ EXPECT_EQ (NormalizePath (boost::filesystem::path (" " )), " " );
254+ EXPECT_EQ (NormalizePath (boost::filesystem::path (" file" )), " file" );
255+
256+ // === Dot handling ===
257+ EXPECT_EQ (NormalizePath (boost::filesystem::path (" ." )), " ." );
258+ EXPECT_EQ (NormalizePath (boost::filesystem::path (" ./a/b" )), " a/b" );
259+ EXPECT_EQ (NormalizePath (boost::filesystem::path (" a/./b" )), " a/b" );
260+ EXPECT_EQ (NormalizePath (boost::filesystem::path (" a/b/." )), " a/b/" );
261+ EXPECT_EQ (NormalizePath (boost::filesystem::path (" ./a/./b/." )), " a/b/" );
262+
263+ // === Double dot (..) handling ===
264+ EXPECT_EQ (NormalizePath (boost::filesystem::path (" a/b/../c" )), " a/c" );
265+ EXPECT_EQ (NormalizePath (boost::filesystem::path (" a/b/c/../../d" )), " a/d" );
266+ EXPECT_EQ (NormalizePath (boost::filesystem::path (" ../a/b" )), " ../a/b" );
267+
268+ // === Trailing slash ===
269+ EXPECT_EQ (NormalizePath (boost::filesystem::path (" a/b/" )), " a/b/" );
270+ EXPECT_EQ (NormalizePath (boost::filesystem::path (" files/" )), " files/" );
271+
272+ // === Absolute paths ===
273+ EXPECT_EQ (NormalizePath (boost::filesystem::path (" /a/b/c" )), " /a/b/c" );
274+ EXPECT_EQ (NormalizePath (boost::filesystem::path (" /a/./b" )), " /a/b" );
275+ EXPECT_EQ (NormalizePath (boost::filesystem::path (" /a/b/." )), " /a/b/" );
276+ EXPECT_EQ (NormalizePath (boost::filesystem::path (" /" )), " /" );
277+
278+ // === Multiple slashes ===
279+ EXPECT_EQ (NormalizePath (boost::filesystem::path (" a//b//c" )), " a/b/c" );
280+ EXPECT_EQ (NormalizePath (boost::filesystem::path (" a/./b//c" )), " a/b/c" );
281+
282+ // === Real-world scenarios (S3/MinIO) ===
283+ EXPECT_EQ (NormalizePath (boost::filesystem::path (" bucket/index_files/123" )),
284+ " bucket/index_files/123" );
285+ // Key fix for 403 error
286+ EXPECT_EQ (
287+ NormalizePath (boost::filesystem::path (" ./index_files/segment_123" )),
288+ " index_files/segment_123" );
289+
290+ // Path construction with root_path = "."
291+ boost::filesystem::path prefix = " ." ;
292+ boost::filesystem::path path = " index_files" ;
293+ boost::filesystem::path path1 = " segment_123" ;
294+ EXPECT_EQ (NormalizePath (prefix / path / path1), " index_files/segment_123" );
295+
296+ // Non-empty root path
297+ boost::filesystem::path prefix2 = " files" ;
298+ EXPECT_EQ (NormalizePath (prefix2 / path / path1),
299+ " files/index_files/segment_123" );
300+
301+ // Root path with trailing slash
302+ boost::filesystem::path prefix3 = " files/" ;
303+ EXPECT_EQ (NormalizePath (prefix3 / path / path1),
304+ " files/index_files/segment_123" );
305+
306+ // Empty root path
307+ boost::filesystem::path prefix4 = " " ;
308+ EXPECT_EQ (NormalizePath (prefix4 / path / path1), " index_files/segment_123" );
309+
310+ // === Edge cases ===
311+ EXPECT_EQ (NormalizePath (boost::filesystem::path (" a/b/.." )), " a" );
312+ EXPECT_EQ (NormalizePath (boost::filesystem::path (" ./a/../b/./c/../d" )),
313+ " b/d" );
314+ EXPECT_EQ (NormalizePath (boost::filesystem::path (" a/b c/d" )), " a/b c/d" );
315+ EXPECT_EQ (NormalizePath (boost::filesystem::path (" ./." )), " ." );
316+ EXPECT_EQ (NormalizePath (boost::filesystem::path (" ./.." )), " .." );
244317}
0 commit comments