github.com/divyam234/rclone@v1.64.1/fs/fspath/path_test.go (about)

     1  package fspath
     2  
     3  import (
     4  	"flag"
     5  	"fmt"
     6  	"os"
     7  	"path/filepath"
     8  	"runtime"
     9  	"testing"
    10  
    11  	"github.com/divyam234/rclone/fs/config/configmap"
    12  	"github.com/stretchr/testify/assert"
    13  	"github.com/stretchr/testify/require"
    14  )
    15  
    16  var (
    17  	makeCorpus = flag.Bool("make-corpus", false, "Set to make the fuzzing corpus")
    18  )
    19  
    20  func TestCheckConfigName(t *testing.T) {
    21  	for _, test := range []struct {
    22  		in      string
    23  		problem error
    24  		fixed   string
    25  	}{
    26  		{"remote", nil, "remote"},
    27  		{"REMOTE", nil, "REMOTE"},
    28  		{"", errInvalidCharacters, "_"},
    29  		{":remote:", errInvalidCharacters, "_remote_"},
    30  		{"remote:", errInvalidCharacters, "remote_"},
    31  		{"rem:ote", errInvalidCharacters, "rem_ote"},
    32  		{"rem/ote", errInvalidCharacters, "rem_ote"},
    33  		{"rem\\ote", errInvalidCharacters, "rem_ote"},
    34  		{"[remote", errInvalidCharacters, "_remote"},
    35  		{"*", errInvalidCharacters, "_"},
    36  		{"-remote", errInvalidCharacters, "_remote"},
    37  		{"r-emote-", nil, "r-emote-"},
    38  		{"---rem:::ote???", errInvalidCharacters, "_rem_ote_"},
    39  		{"_rem_ote_", nil, "_rem_ote_"},
    40  		{".", nil, "."},
    41  		{"..", nil, ".."},
    42  		{".r.e.m.o.t.e.", nil, ".r.e.m.o.t.e."},
    43  		{"rem ote", nil, "rem ote"},
    44  		{"user@example.com", nil, "user@example.com"},
    45  		{"user+junkmail@example.com", nil, "user+junkmail@example.com"},
    46  		{"blåbær", nil, "blåbær"},
    47  		{"chữ Quốc ngữ", nil, "chữ Quốc ngữ"},
    48  		{"remote ", errInvalidCharacters, "remote_"},
    49  		{" remote", errInvalidCharacters, "_remote"},
    50  		{" remote ", errInvalidCharacters, "_remote_"},
    51  	} {
    52  		problem := CheckConfigName(test.in)
    53  		assert.Equal(t, test.problem, problem, test.in)
    54  		fixed := MakeConfigName(test.in)
    55  		assert.Equal(t, test.fixed, fixed, test.in)
    56  	}
    57  }
    58  
    59  func TestCheckRemoteName(t *testing.T) {
    60  	for _, test := range []struct {
    61  		in   string
    62  		want error
    63  	}{
    64  		{":remote:", nil},
    65  		{":REMOTE:", nil},
    66  		{":s3:", nil},
    67  		{"remote:", nil},
    68  		{".:", nil},
    69  		{"..:", nil},
    70  		{".r.e.m.o.t.e.:", nil},
    71  		{"-r-emote-:", errInvalidCharacters},
    72  		{"rem ote:", nil},
    73  		{"user@example.com:", nil},
    74  		{"user+junkmail@example.com:", nil},
    75  		{"blåbær:", nil},
    76  		{"chữ Quốc ngữ:", nil},
    77  		{"remote :", errInvalidCharacters},
    78  		{" remote:", errInvalidCharacters},
    79  		{" remote :", errInvalidCharacters},
    80  		{"", errInvalidCharacters},
    81  		{"rem:ote", errInvalidCharacters},
    82  		{"rem:ote:", errInvalidCharacters},
    83  		{"remote", errInvalidCharacters},
    84  		{"rem/ote:", errInvalidCharacters},
    85  		{"rem\\ote:", errInvalidCharacters},
    86  		{"[remote:", errInvalidCharacters},
    87  		{"*:", errInvalidCharacters},
    88  	} {
    89  		got := checkRemoteName(test.in)
    90  		assert.Equal(t, test.want, got, test.in)
    91  	}
    92  }
    93  
    94  func TestParse(t *testing.T) {
    95  	for testNumber, test := range []struct {
    96  		in         string
    97  		wantParsed Parsed
    98  		wantErr    error
    99  		win        bool // only run these tests on Windows
   100  		noWin      bool // only run these tests on !Windows
   101  	}{
   102  		{
   103  			in:      "",
   104  			wantErr: errCantBeEmpty,
   105  		}, {
   106  			in:      ":",
   107  			wantErr: errConfigName,
   108  		}, {
   109  			in:      "::",
   110  			wantErr: errConfigNameEmpty,
   111  		}, {
   112  			in:      ":/:",
   113  			wantErr: errInvalidCharacters,
   114  		}, {
   115  			in: "/:",
   116  			wantParsed: Parsed{
   117  				ConfigString: "",
   118  				Path:         "/:",
   119  			},
   120  		}, {
   121  			in: "\\backslash:",
   122  			wantParsed: Parsed{
   123  				ConfigString: "",
   124  				Path:         "/backslash:",
   125  			},
   126  			win: true,
   127  		}, {
   128  			in: "\\backslash:",
   129  			wantParsed: Parsed{
   130  				ConfigString: "",
   131  				Path:         "\\backslash:",
   132  			},
   133  			noWin: true,
   134  		}, {
   135  			in: "/slash:",
   136  			wantParsed: Parsed{
   137  				ConfigString: "",
   138  				Path:         "/slash:",
   139  			},
   140  		}, {
   141  			in: "with\\backslash:",
   142  			wantParsed: Parsed{
   143  				ConfigString: "",
   144  				Path:         "with/backslash:",
   145  			},
   146  			win: true,
   147  		}, {
   148  			in: "with\\backslash:",
   149  			wantParsed: Parsed{
   150  				ConfigString: "",
   151  				Path:         "with\\backslash:",
   152  			},
   153  			noWin: true,
   154  		}, {
   155  			in: "with/slash:",
   156  			wantParsed: Parsed{
   157  				ConfigString: "",
   158  				Path:         "with/slash:",
   159  			},
   160  		}, {
   161  			in: "/path/to/file",
   162  			wantParsed: Parsed{
   163  				ConfigString: "",
   164  				Path:         "/path/to/file",
   165  			},
   166  		}, {
   167  			in: "/path:/to/file",
   168  			wantParsed: Parsed{
   169  				ConfigString: "",
   170  				Path:         "/path:/to/file",
   171  			},
   172  		}, {
   173  			in: "./path:/to/file",
   174  			wantParsed: Parsed{
   175  				ConfigString: "",
   176  				Path:         "./path:/to/file",
   177  			},
   178  		}, {
   179  			in: "./:colon.txt",
   180  			wantParsed: Parsed{
   181  				ConfigString: "",
   182  				Path:         "./:colon.txt",
   183  			},
   184  		}, {
   185  			in: "path/to/file",
   186  			wantParsed: Parsed{
   187  				ConfigString: "",
   188  				Path:         "path/to/file",
   189  			},
   190  		}, {
   191  			in: ".:",
   192  			wantParsed: Parsed{
   193  				ConfigString: ".",
   194  				Name:         ".",
   195  				Path:         "",
   196  			},
   197  		}, {
   198  			in: "..:",
   199  			wantParsed: Parsed{
   200  				ConfigString: "..",
   201  				Name:         "..",
   202  				Path:         "",
   203  			},
   204  		}, {
   205  			in: ".:colon.txt",
   206  			wantParsed: Parsed{
   207  				ConfigString: ".",
   208  				Name:         ".",
   209  				Path:         "colon.txt",
   210  			},
   211  		}, {
   212  			in: "remote:path/to/file",
   213  			wantParsed: Parsed{
   214  				ConfigString: "remote",
   215  				Name:         "remote",
   216  				Path:         "path/to/file",
   217  			},
   218  		}, {
   219  			in:      "rem*ote:path/to/file",
   220  			wantErr: errInvalidCharacters,
   221  		}, {
   222  			in: "remote:/path/to/file",
   223  			wantParsed: Parsed{
   224  				ConfigString: "remote",
   225  				Name:         "remote",
   226  				Path:         "/path/to/file",
   227  			},
   228  		}, {
   229  			in: "rem.ote:/path/to/file",
   230  			wantParsed: Parsed{
   231  				ConfigString: "rem.ote",
   232  				Name:         "rem.ote",
   233  				Path:         "/path/to/file",
   234  			},
   235  		}, {
   236  			in: "rem ote:/path/to/file",
   237  			wantParsed: Parsed{
   238  				ConfigString: "rem ote",
   239  				Name:         "rem ote",
   240  				Path:         "/path/to/file",
   241  			},
   242  		}, {
   243  			in:      "remote :/path/to/file",
   244  			wantErr: errInvalidCharacters,
   245  		}, {
   246  			in:      " remote:/path/to/file",
   247  			wantErr: errInvalidCharacters,
   248  		}, {
   249  			in:      " remote :/path/to/file",
   250  			wantErr: errInvalidCharacters,
   251  		}, {
   252  			in:      "rem#ote:/path/to/file",
   253  			wantErr: errInvalidCharacters,
   254  		}, {
   255  			in: ":backend:/path/to/file",
   256  			wantParsed: Parsed{
   257  				ConfigString: ":backend",
   258  				Name:         ":backend",
   259  				Path:         "/path/to/file",
   260  			},
   261  		}, {
   262  			in: ":back.end:/path/to/file",
   263  			wantParsed: Parsed{
   264  				ConfigString: ":back.end",
   265  				Name:         ":back.end",
   266  				Path:         "/path/to/file",
   267  			},
   268  		}, {
   269  			in:      ":bac*kend:/path/to/file",
   270  			wantErr: errInvalidCharacters,
   271  		}, {
   272  			in: `C:\path\to\file`,
   273  			wantParsed: Parsed{
   274  				Name: "",
   275  				Path: `C:/path/to/file`,
   276  			},
   277  			win: true,
   278  		}, {
   279  			in: `C:\path\to\file`,
   280  			wantParsed: Parsed{
   281  				Name:         "C",
   282  				ConfigString: "C",
   283  				Path:         `\path\to\file`,
   284  			},
   285  			noWin: true,
   286  		}, {
   287  			in: `\path\to\file`,
   288  			wantParsed: Parsed{
   289  				Name: "",
   290  				Path: `/path/to/file`,
   291  			},
   292  			win: true,
   293  		}, {
   294  			in: `\path\to\file`,
   295  			wantParsed: Parsed{
   296  				Name: "",
   297  				Path: `\path\to\file`,
   298  			},
   299  			noWin: true,
   300  		}, {
   301  			in: `.`,
   302  			wantParsed: Parsed{
   303  				Name: "",
   304  				Path: `.`,
   305  			},
   306  			noWin: true,
   307  		}, {
   308  			in: `..`,
   309  			wantParsed: Parsed{
   310  				Name: "",
   311  				Path: `..`,
   312  			},
   313  			noWin: true,
   314  		}, {
   315  			in: `remote:\path\to\file`,
   316  			wantParsed: Parsed{
   317  				Name:         "remote",
   318  				ConfigString: "remote",
   319  				Path:         `/path/to/file`,
   320  			},
   321  			win: true,
   322  		}, {
   323  			in: `remote:\path\to\file`,
   324  			wantParsed: Parsed{
   325  				Name:         "remote",
   326  				ConfigString: "remote",
   327  				Path:         `\path\to\file`,
   328  			},
   329  			noWin: true,
   330  		}, {
   331  			in: `D:/path/to/file`,
   332  			wantParsed: Parsed{
   333  				Name: "",
   334  				Path: `D:/path/to/file`,
   335  			},
   336  			win: true,
   337  		}, {
   338  			in: `D:/path/to/file`,
   339  			wantParsed: Parsed{
   340  				Name:         "D",
   341  				ConfigString: "D",
   342  				Path:         `/path/to/file`,
   343  			},
   344  			noWin: true,
   345  		}, {
   346  			in: `:backend,param1:/path/to/file`,
   347  			wantParsed: Parsed{
   348  				ConfigString: `:backend,param1`,
   349  				Name:         ":backend",
   350  				Path:         "/path/to/file",
   351  				Config: configmap.Simple{
   352  					"param1": "true",
   353  				},
   354  			},
   355  		}, {
   356  			in: `:backend,param1=value:/path/to/file`,
   357  			wantParsed: Parsed{
   358  				ConfigString: `:backend,param1=value`,
   359  				Name:         ":backend",
   360  				Path:         "/path/to/file",
   361  				Config: configmap.Simple{
   362  					"param1": "value",
   363  				},
   364  			},
   365  		}, {
   366  			in: `:backend,param1=value1,param2,param3=value3:/path/to/file`,
   367  			wantParsed: Parsed{
   368  				ConfigString: `:backend,param1=value1,param2,param3=value3`,
   369  				Name:         ":backend",
   370  				Path:         "/path/to/file",
   371  				Config: configmap.Simple{
   372  					"param1": "value1",
   373  					"param2": "true",
   374  					"param3": "value3",
   375  				},
   376  			},
   377  		}, {
   378  			in: `:backend,param1=value1,param2="value2",param3='value3':/path/to/file`,
   379  			wantParsed: Parsed{
   380  				ConfigString: `:backend,param1=value1,param2="value2",param3='value3'`,
   381  				Name:         ":backend",
   382  				Path:         "/path/to/file",
   383  				Config: configmap.Simple{
   384  					"param1": "value1",
   385  					"param2": "value2",
   386  					"param3": "value3",
   387  				},
   388  			},
   389  		}, {
   390  			in:      `:backend,param-1=value:/path/to/file`,
   391  			wantErr: errBadConfigParam,
   392  		}, {
   393  			in:      `:backend,param1="value"x:/path/to/file`,
   394  			wantErr: errAfterQuote,
   395  		}, {
   396  			in:      `:backend,`,
   397  			wantErr: errParam,
   398  		}, {
   399  			in:      `:backend,param=value`,
   400  			wantErr: errValue,
   401  		}, {
   402  			in:      `:backend,param="value'`,
   403  			wantErr: errQuotedValue,
   404  		}, {
   405  			in:      `:backend,param1="value"`,
   406  			wantErr: errAfterQuote,
   407  		}, {
   408  			in:      `:backend,=value:`,
   409  			wantErr: errEmptyConfigParam,
   410  		}, {
   411  			in:      `:backend,:`,
   412  			wantErr: errEmptyConfigParam,
   413  		}, {
   414  			in:      `:backend,,:`,
   415  			wantErr: errEmptyConfigParam,
   416  		}, {
   417  			in: `:backend,param=:path`,
   418  			wantParsed: Parsed{
   419  				ConfigString: `:backend,param=`,
   420  				Name:         ":backend",
   421  				Path:         "path",
   422  				Config: configmap.Simple{
   423  					"param": "",
   424  				},
   425  			},
   426  		}, {
   427  			in: `:backend,param="with""quote":path`,
   428  			wantParsed: Parsed{
   429  				ConfigString: `:backend,param="with""quote"`,
   430  				Name:         ":backend",
   431  				Path:         "path",
   432  				Config: configmap.Simple{
   433  					"param": `with"quote`,
   434  				},
   435  			},
   436  		}, {
   437  			in: `:backend,param='''''':`,
   438  			wantParsed: Parsed{
   439  				ConfigString: `:backend,param=''''''`,
   440  				Name:         ":backend",
   441  				Path:         "",
   442  				Config: configmap.Simple{
   443  					"param": `''`,
   444  				},
   445  			},
   446  		}, {
   447  			in:      `:backend,param=''bad'':`,
   448  			wantErr: errAfterQuote,
   449  		},
   450  	} {
   451  		gotParsed, gotErr := Parse(test.in)
   452  		if runtime.GOOS == "windows" && test.noWin {
   453  			continue
   454  		}
   455  		if runtime.GOOS != "windows" && test.win {
   456  			continue
   457  		}
   458  		assert.Equal(t, test.wantErr, gotErr, test.in)
   459  		if test.wantErr == nil {
   460  			assert.Equal(t, test.wantParsed, gotParsed, test.in)
   461  		}
   462  		if *makeCorpus {
   463  			// write the test corpus for fuzzing
   464  			require.NoError(t, os.MkdirAll("corpus", 0777))
   465  			require.NoError(t, os.WriteFile(fmt.Sprintf("corpus/%02d", testNumber), []byte(test.in), 0666))
   466  		}
   467  
   468  	}
   469  }
   470  
   471  func TestSplitFs(t *testing.T) {
   472  	for _, test := range []struct {
   473  		remote, wantRemoteName, wantRemotePath string
   474  		wantErr                                error
   475  	}{
   476  		{"", "", "", errCantBeEmpty},
   477  
   478  		{"remote:", "remote:", "", nil},
   479  		{"remote:potato", "remote:", "potato", nil},
   480  		{"remote:/", "remote:", "/", nil},
   481  		{"remote:/potato", "remote:", "/potato", nil},
   482  		{"remote:/potato/potato", "remote:", "/potato/potato", nil},
   483  		{"remote:potato/sausage", "remote:", "potato/sausage", nil},
   484  		{"rem.ote:potato/sausage", "rem.ote:", "potato/sausage", nil},
   485  
   486  		{"rem ote:", "rem ote:", "", nil},
   487  		{"remote :", "", "", errInvalidCharacters},
   488  		{" remote:", "", "", errInvalidCharacters},
   489  		{" remote :", "", "", errInvalidCharacters},
   490  
   491  		{".:", ".:", "", nil},
   492  		{"..:", "..:", "", nil},
   493  		{".:potato/sausage", ".:", "potato/sausage", nil},
   494  		{"..:potato/sausage", "..:", "potato/sausage", nil},
   495  
   496  		{":remote:", ":remote:", "", nil},
   497  		{":remote:potato", ":remote:", "potato", nil},
   498  		{":remote:/", ":remote:", "/", nil},
   499  		{":remote:/potato", ":remote:", "/potato", nil},
   500  		{":remote:/potato/potato", ":remote:", "/potato/potato", nil},
   501  		{":remote:potato/sausage", ":remote:", "potato/sausage", nil},
   502  		{":rem.ote:potato/sausage", ":rem.ote:", "potato/sausage", nil},
   503  		{":rem[ote:potato/sausage", "", "", errInvalidCharacters},
   504  
   505  		{":.:", ":.:", "", nil},
   506  		{":..:", ":..:", "", nil},
   507  		{":.:potato/sausage", ":.:", "potato/sausage", nil},
   508  		{":..:potato/sausage", ":..:", "potato/sausage", nil},
   509  
   510  		{"/", "", "/", nil},
   511  		{"/root", "", "/root", nil},
   512  		{"/a/b", "", "/a/b", nil},
   513  		{"root", "", "root", nil},
   514  		{"a/b", "", "a/b", nil},
   515  		{"root/", "", "root/", nil},
   516  		{"a/b/", "", "a/b/", nil},
   517  	} {
   518  		gotRemoteName, gotRemotePath, gotErr := SplitFs(test.remote)
   519  		assert.Equal(t, test.wantErr, gotErr)
   520  		assert.Equal(t, test.wantRemoteName, gotRemoteName, test.remote)
   521  		assert.Equal(t, test.wantRemotePath, gotRemotePath, test.remote)
   522  		if gotErr == nil {
   523  			assert.Equal(t, test.remote, gotRemoteName+gotRemotePath, fmt.Sprintf("%s: %q + %q != %q", test.remote, gotRemoteName, gotRemotePath, test.remote))
   524  		}
   525  	}
   526  }
   527  
   528  func TestSplit(t *testing.T) {
   529  	for _, test := range []struct {
   530  		remote, wantParent, wantLeaf string
   531  		wantErr                      error
   532  	}{
   533  		{"", "", "", errCantBeEmpty},
   534  
   535  		{"remote:", "remote:", "", nil},
   536  		{"remote:potato", "remote:", "potato", nil},
   537  		{"remote:/", "remote:/", "", nil},
   538  		{"remote:/potato", "remote:/", "potato", nil},
   539  		{"remote:/potato/potato", "remote:/potato/", "potato", nil},
   540  		{"remote:potato/sausage", "remote:potato/", "sausage", nil},
   541  		{"rem.ote:potato/sausage", "rem.ote:potato/", "sausage", nil},
   542  
   543  		{"rem ote:", "rem ote:", "", nil},
   544  		{"remote :", "", "", errInvalidCharacters},
   545  		{" remote:", "", "", errInvalidCharacters},
   546  		{" remote :", "", "", errInvalidCharacters},
   547  
   548  		{".:", ".:", "", nil},
   549  		{"..:", "..:", "", nil},
   550  		{".:potato/sausage", ".:potato/", "sausage", nil},
   551  		{"..:potato/sausage", "..:potato/", "sausage", nil},
   552  
   553  		{":remote:", ":remote:", "", nil},
   554  		{":remote:potato", ":remote:", "potato", nil},
   555  		{":remote:/", ":remote:/", "", nil},
   556  		{":remote:/potato", ":remote:/", "potato", nil},
   557  		{":remote:/potato/potato", ":remote:/potato/", "potato", nil},
   558  		{":remote:potato/sausage", ":remote:potato/", "sausage", nil},
   559  		{":rem.ote:potato/sausage", ":rem.ote:potato/", "sausage", nil},
   560  		{":rem[ote:potato/sausage", "", "", errInvalidCharacters},
   561  
   562  		{":.:", ":.:", "", nil},
   563  		{":..:", ":..:", "", nil},
   564  		{":.:potato/sausage", ":.:potato/", "sausage", nil},
   565  		{":..:potato/sausage", ":..:potato/", "sausage", nil},
   566  
   567  		{"/", "/", "", nil},
   568  		{"/root", "/", "root", nil},
   569  		{"/a/b", "/a/", "b", nil},
   570  		{"root", "", "root", nil},
   571  		{"a/b", "a/", "b", nil},
   572  		{"root/", "root/", "", nil},
   573  		{"a/b/", "a/b/", "", nil},
   574  	} {
   575  		gotParent, gotLeaf, gotErr := Split(test.remote)
   576  		assert.Equal(t, test.wantErr, gotErr)
   577  		assert.Equal(t, test.wantParent, gotParent, test.remote)
   578  		assert.Equal(t, test.wantLeaf, gotLeaf, test.remote)
   579  		if gotErr == nil {
   580  			assert.Equal(t, test.remote, gotParent+gotLeaf, fmt.Sprintf("%s: %q + %q != %q", test.remote, gotParent, gotLeaf, test.remote))
   581  		}
   582  	}
   583  }
   584  
   585  func TestMakeAbsolute(t *testing.T) {
   586  	for _, test := range []struct {
   587  		in   string
   588  		want string
   589  	}{
   590  		{"", ""},
   591  		{".", ""},
   592  		{"/.", "/"},
   593  		{"../potato", "potato"},
   594  		{"/../potato", "/potato"},
   595  		{"./../potato", "potato"},
   596  		{"//../potato", "/potato"},
   597  		{"././../potato", "potato"},
   598  		{"././potato/../../onion", "onion"},
   599  	} {
   600  		got := makeAbsolute(test.in)
   601  		assert.Equal(t, test.want, got, test)
   602  	}
   603  }
   604  
   605  func TestJoinRootPath(t *testing.T) {
   606  	for _, test := range []struct {
   607  		remote   string
   608  		filePath string
   609  		want     string
   610  	}{
   611  		{"", "", ""},
   612  		{"", "/", "/"},
   613  		{"/", "", "/"},
   614  		{"/", "/", "/"},
   615  		{"/", "//", "/"},
   616  		{"/root", "", "/root"},
   617  		{"/root", "/", "/root"},
   618  		{"/root", "//", "/root"},
   619  		{"/a/b", "", "/a/b"},
   620  		{"//", "/", "//"},
   621  		{"//server", "path", "//server/path"},
   622  		{"//server/sub", "path", "//server/sub/path"},
   623  		{"//server", "//path", "//server/path"},
   624  		{"//server/sub", "//path", "//server/sub/path"},
   625  		{"//", "/", "//"},
   626  		{"//server", "path", "//server/path"},
   627  		{"//server/sub", "path", "//server/sub/path"},
   628  		{"//server", "//path", "//server/path"},
   629  		{"//server/sub", "//path", "//server/sub/path"},
   630  		{filepath.FromSlash("//server/sub"), filepath.FromSlash("//path"), "//server/sub/path"},
   631  		{"s3:", "", "s3:"},
   632  		{"s3:", ".", "s3:"},
   633  		{"s3:.", ".", "s3:"},
   634  		{"s3:", "..", "s3:"},
   635  		{"s3:dir", "sub", "s3:dir/sub"},
   636  		{"s3:dir", "/sub", "s3:dir/sub"},
   637  		{"s3:dir", "./sub", "s3:dir/sub"},
   638  		{"s3:/dir", "/sub/", "s3:/dir/sub"},
   639  		{"s3:dir", "..", "s3:dir"},
   640  		{"s3:dir", "/..", "s3:dir"},
   641  		{"s3:dir", "/../", "s3:dir"},
   642  	} {
   643  		got := JoinRootPath(test.remote, test.filePath)
   644  		assert.Equal(t, test.want, got, test)
   645  	}
   646  }