08d860570b
this was just supposed to add parallel uploads/downloads and it does do that, but i then found a bunch of bugs with respect to path handling that i thought i had under control. oops. so this adds a ton of tests and tries to make the logic in ulloc to be more consistent. almost all of the actual file handling bits and knowledge happens in cmd_cp now where it should belong. additionally, the s3 command has the behavior that if your bucket has the file s3://bucket/file, then executing s3 ls s3://bucket/fi returns nothing. this change makes uplinkng match that behavior even if i don't personally like it. a big portion of the weirdness is the concept introduced that i've named "directoryish", which intends to capture the behavior that if a user copies a file to that location then the base name of the source should be appended on rather than a direct copy. this concept is entirely a based on the string value and not the actual filesystem state. hence, the cp command is responsible for checking if local paths are actually a directory, and adding a trailing slash if necessary to make them "directoryish". additionally, the empty key for a bucket and the empty string for local paths are considered "directoryish". Change-Id: I9120d18616fd813b29ff81beed4f5993caa99fb6
395 lines
13 KiB
Go
395 lines
13 KiB
Go
// Copyright (C) 2021 Storj Labs, Inc.
|
|
// See LICENSE for copying information.
|
|
|
|
package main
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"storj.io/storj/cmd/uplinkng/ultest"
|
|
)
|
|
|
|
func TestLsErrors(t *testing.T) {
|
|
state := ultest.Setup(commands)
|
|
|
|
// empty bucket name is a parse error
|
|
state.Fail(t, "ls", "sj:///user")
|
|
}
|
|
|
|
func TestLsRemote(t *testing.T) {
|
|
state := ultest.Setup(commands,
|
|
ultest.WithFile("sj://user/deep/aaa/bbb/1"),
|
|
ultest.WithFile("sj://user/deep/aaa/bbb/2"),
|
|
ultest.WithFile("sj://user/deep/aaa/bbb/3"),
|
|
ultest.WithFile("sj://user/foobar"),
|
|
ultest.WithFile("sj://user/foobar/"),
|
|
ultest.WithFile("sj://user/foobar/1"),
|
|
ultest.WithFile("sj://user/foobar/2"),
|
|
ultest.WithFile("sj://user/foobar/3"),
|
|
ultest.WithFile("sj://user/foobaz/1"),
|
|
|
|
ultest.WithPendingFile("sj://user/invisible"),
|
|
)
|
|
|
|
t.Run("Recursive", func(t *testing.T) {
|
|
state.Succeed(t, "ls", "sj://user", "--recursive", "--utc").RequireStdout(t, `
|
|
KIND CREATED SIZE KEY
|
|
OBJ 1970-01-01 00:00:01 0 deep/aaa/bbb/1
|
|
OBJ 1970-01-01 00:00:02 0 deep/aaa/bbb/2
|
|
OBJ 1970-01-01 00:00:03 0 deep/aaa/bbb/3
|
|
OBJ 1970-01-01 00:00:04 0 foobar
|
|
OBJ 1970-01-01 00:00:05 0 foobar/
|
|
OBJ 1970-01-01 00:00:06 0 foobar/1
|
|
OBJ 1970-01-01 00:00:07 0 foobar/2
|
|
OBJ 1970-01-01 00:00:08 0 foobar/3
|
|
OBJ 1970-01-01 00:00:09 0 foobaz/1
|
|
`)
|
|
})
|
|
|
|
t.Run("Basic", func(t *testing.T) {
|
|
state.Succeed(t, "ls", "sj://user/fo", "--utc").RequireStdout(t, ``)
|
|
})
|
|
|
|
t.Run("ExactPrefix", func(t *testing.T) {
|
|
state.Succeed(t, "ls", "sj://user/foobar", "--utc").RequireStdout(t, `
|
|
KIND CREATED SIZE KEY
|
|
OBJ 1970-01-01 00:00:04 0 foobar
|
|
PRE foobar/
|
|
`)
|
|
})
|
|
|
|
t.Run("ExactPrefixWithSlash", func(t *testing.T) {
|
|
state.Succeed(t, "ls", "sj://user/foobar/", "--utc").RequireStdout(t, `
|
|
KIND CREATED SIZE KEY
|
|
OBJ 1970-01-01 00:00:05 0
|
|
OBJ 1970-01-01 00:00:06 0 1
|
|
OBJ 1970-01-01 00:00:07 0 2
|
|
OBJ 1970-01-01 00:00:08 0 3
|
|
`)
|
|
})
|
|
|
|
t.Run("MultipleLayers", func(t *testing.T) {
|
|
state.Succeed(t, "ls", "sj://user/deep/").RequireStdout(t, `
|
|
KIND CREATED SIZE KEY
|
|
PRE aaa/
|
|
`)
|
|
|
|
state.Succeed(t, "ls", "sj://user/deep/aaa/").RequireStdout(t, `
|
|
KIND CREATED SIZE KEY
|
|
PRE bbb/
|
|
`)
|
|
|
|
state.Succeed(t, "ls", "sj://user/deep/aaa/bbb/", "--utc").RequireStdout(t, `
|
|
KIND CREATED SIZE KEY
|
|
OBJ 1970-01-01 00:00:01 0 1
|
|
OBJ 1970-01-01 00:00:02 0 2
|
|
OBJ 1970-01-01 00:00:03 0 3
|
|
`)
|
|
})
|
|
}
|
|
|
|
func TestLsPending(t *testing.T) {
|
|
state := ultest.Setup(commands,
|
|
ultest.WithPendingFile("sj://user/deep/aaa/bbb/1"),
|
|
ultest.WithPendingFile("sj://user/deep/aaa/bbb/2"),
|
|
ultest.WithPendingFile("sj://user/deep/aaa/bbb/3"),
|
|
ultest.WithPendingFile("sj://user/foobar"),
|
|
ultest.WithPendingFile("sj://user/foobar/"),
|
|
ultest.WithPendingFile("sj://user/foobar/1"),
|
|
ultest.WithPendingFile("sj://user/foobar/2"),
|
|
ultest.WithPendingFile("sj://user/foobar/3"),
|
|
ultest.WithPendingFile("sj://user/foobaz/1"),
|
|
|
|
ultest.WithFile("sj://user/invisible"),
|
|
)
|
|
|
|
t.Run("Recursive", func(t *testing.T) {
|
|
state.Succeed(t, "ls", "sj://user", "--recursive", "--pending", "--utc").RequireStdout(t, `
|
|
KIND CREATED SIZE KEY
|
|
OBJ 1970-01-01 00:00:01 0 deep/aaa/bbb/1
|
|
OBJ 1970-01-01 00:00:02 0 deep/aaa/bbb/2
|
|
OBJ 1970-01-01 00:00:03 0 deep/aaa/bbb/3
|
|
OBJ 1970-01-01 00:00:04 0 foobar
|
|
OBJ 1970-01-01 00:00:05 0 foobar/
|
|
OBJ 1970-01-01 00:00:06 0 foobar/1
|
|
OBJ 1970-01-01 00:00:07 0 foobar/2
|
|
OBJ 1970-01-01 00:00:08 0 foobar/3
|
|
OBJ 1970-01-01 00:00:09 0 foobaz/1
|
|
`)
|
|
})
|
|
|
|
t.Run("Basic", func(t *testing.T) {
|
|
state.Succeed(t, "ls", "sj://user/fo", "--pending", "--utc").RequireStdout(t, ``)
|
|
})
|
|
|
|
t.Run("ExactPrefix", func(t *testing.T) {
|
|
state.Succeed(t, "ls", "sj://user/foobar", "--pending", "--utc").RequireStdout(t, `
|
|
KIND CREATED SIZE KEY
|
|
OBJ 1970-01-01 00:00:04 0 foobar
|
|
PRE foobar/
|
|
`)
|
|
})
|
|
|
|
t.Run("ExactPrefixWithSlash", func(t *testing.T) {
|
|
state.Succeed(t, "ls", "sj://user/foobar/", "--pending", "--utc").RequireStdout(t, `
|
|
KIND CREATED SIZE KEY
|
|
OBJ 1970-01-01 00:00:05 0
|
|
OBJ 1970-01-01 00:00:06 0 1
|
|
OBJ 1970-01-01 00:00:07 0 2
|
|
OBJ 1970-01-01 00:00:08 0 3
|
|
`)
|
|
})
|
|
|
|
t.Run("MultipleLayers", func(t *testing.T) {
|
|
state.Succeed(t, "ls", "sj://user/deep/", "--pending").RequireStdout(t, `
|
|
KIND CREATED SIZE KEY
|
|
PRE aaa/
|
|
`)
|
|
|
|
state.Succeed(t, "ls", "sj://user/deep/aaa/", "--pending").RequireStdout(t, `
|
|
KIND CREATED SIZE KEY
|
|
PRE bbb/
|
|
`)
|
|
|
|
state.Succeed(t, "ls", "sj://user/deep/aaa/bbb/", "--pending", "--utc").RequireStdout(t, `
|
|
KIND CREATED SIZE KEY
|
|
OBJ 1970-01-01 00:00:01 0 1
|
|
OBJ 1970-01-01 00:00:02 0 2
|
|
OBJ 1970-01-01 00:00:03 0 3
|
|
`)
|
|
})
|
|
}
|
|
|
|
func TestLsDifficult(t *testing.T) {
|
|
state := ultest.Setup(commands,
|
|
ultest.WithFile("sj://user//"),
|
|
ultest.WithFile("sj://user///"),
|
|
ultest.WithFile("sj://user////"),
|
|
|
|
ultest.WithFile("sj://user//starts-slash"),
|
|
|
|
ultest.WithFile("sj://user/ends-slash"),
|
|
ultest.WithFile("sj://user/ends-slash/"),
|
|
ultest.WithFile("sj://user/ends-slash//"),
|
|
|
|
ultest.WithFile("sj://user/mid-slash"),
|
|
ultest.WithFile("sj://user/mid-slash//2"),
|
|
ultest.WithFile("sj://user/mid-slash/1"),
|
|
)
|
|
|
|
t.Run("Recursive", func(t *testing.T) {
|
|
state.Succeed(t, "ls", "sj://user", "--recursive", "--utc").RequireStdout(t, `
|
|
KIND CREATED SIZE KEY
|
|
OBJ 1970-01-01 00:00:01 0 /
|
|
OBJ 1970-01-01 00:00:02 0 //
|
|
OBJ 1970-01-01 00:00:03 0 ///
|
|
OBJ 1970-01-01 00:00:04 0 /starts-slash
|
|
OBJ 1970-01-01 00:00:05 0 ends-slash
|
|
OBJ 1970-01-01 00:00:06 0 ends-slash/
|
|
OBJ 1970-01-01 00:00:07 0 ends-slash//
|
|
OBJ 1970-01-01 00:00:08 0 mid-slash
|
|
OBJ 1970-01-01 00:00:09 0 mid-slash//2
|
|
OBJ 1970-01-01 00:00:10 0 mid-slash/1
|
|
`)
|
|
})
|
|
|
|
t.Run("Basic", func(t *testing.T) {
|
|
state.Succeed(t, "ls", "sj://user", "--utc").RequireStdout(t, `
|
|
KIND CREATED SIZE KEY
|
|
PRE /
|
|
OBJ 1970-01-01 00:00:05 0 ends-slash
|
|
PRE ends-slash/
|
|
OBJ 1970-01-01 00:00:08 0 mid-slash
|
|
PRE mid-slash/
|
|
`)
|
|
|
|
state.Succeed(t, "ls", "sj://user/", "--utc").RequireStdout(t, `
|
|
KIND CREATED SIZE KEY
|
|
PRE /
|
|
OBJ 1970-01-01 00:00:05 0 ends-slash
|
|
PRE ends-slash/
|
|
OBJ 1970-01-01 00:00:08 0 mid-slash
|
|
PRE mid-slash/
|
|
`)
|
|
})
|
|
|
|
t.Run("OnlySlash", func(t *testing.T) {
|
|
state.Succeed(t, "ls", "sj://user//", "--utc").RequireStdout(t, `
|
|
KIND CREATED SIZE KEY
|
|
OBJ 1970-01-01 00:00:01 0
|
|
PRE /
|
|
OBJ 1970-01-01 00:00:04 0 starts-slash
|
|
`)
|
|
|
|
state.Succeed(t, "ls", "sj://user///", "--utc").RequireStdout(t, `
|
|
KIND CREATED SIZE KEY
|
|
OBJ 1970-01-01 00:00:02 0
|
|
PRE /
|
|
`)
|
|
|
|
state.Succeed(t, "ls", "sj://user////", "--utc").RequireStdout(t, `
|
|
KIND CREATED SIZE KEY
|
|
OBJ 1970-01-01 00:00:03 0
|
|
`)
|
|
})
|
|
|
|
t.Run("EndsSlash", func(t *testing.T) {
|
|
state.Succeed(t, "ls", "sj://user/ends-slash", "--utc").RequireStdout(t, `
|
|
KIND CREATED SIZE KEY
|
|
OBJ 1970-01-01 00:00:05 0 ends-slash
|
|
PRE ends-slash/
|
|
`)
|
|
|
|
state.Succeed(t, "ls", "sj://user/ends-slash/", "--utc").RequireStdout(t, `
|
|
KIND CREATED SIZE KEY
|
|
OBJ 1970-01-01 00:00:06 0
|
|
PRE /
|
|
`)
|
|
|
|
state.Succeed(t, "ls", "sj://user/ends-slash//", "--utc").RequireStdout(t, `
|
|
KIND CREATED SIZE KEY
|
|
OBJ 1970-01-01 00:00:07 0
|
|
`)
|
|
})
|
|
|
|
t.Run("MidSlash", func(t *testing.T) {
|
|
state.Succeed(t, "ls", "sj://user/mid-slash", "--utc").RequireStdout(t, `
|
|
KIND CREATED SIZE KEY
|
|
OBJ 1970-01-01 00:00:08 0 mid-slash
|
|
PRE mid-slash/
|
|
`)
|
|
|
|
state.Succeed(t, "ls", "sj://user/mid-slash/", "--utc").RequireStdout(t, `
|
|
KIND CREATED SIZE KEY
|
|
PRE /
|
|
OBJ 1970-01-01 00:00:10 0 1
|
|
`)
|
|
|
|
state.Succeed(t, "ls", "sj://user/mid-slash//", "--utc").RequireStdout(t, `
|
|
KIND CREATED SIZE KEY
|
|
OBJ 1970-01-01 00:00:09 0 2
|
|
`)
|
|
})
|
|
}
|
|
|
|
func TestLsLocal(t *testing.T) {
|
|
state := ultest.Setup(commands,
|
|
ultest.WithFile("/user/deep/aaa/bbb/1"),
|
|
ultest.WithFile("/user/deep/aaa/bbb/2"),
|
|
ultest.WithFile("/user/deep/aaa/bbb/3"),
|
|
ultest.WithFile("/user/foobar/1"),
|
|
ultest.WithFile("/user/foobar/2"),
|
|
ultest.WithFile("/user/foobar/3"),
|
|
ultest.WithFile("/user/foobaz/1"),
|
|
)
|
|
|
|
t.Run("Recursive", func(t *testing.T) {
|
|
state.Succeed(t, "ls", "/user", "--recursive", "--utc").RequireStdout(t, `
|
|
KIND CREATED SIZE KEY
|
|
OBJ 1970-01-01 00:00:01 0 /user/deep/aaa/bbb/1
|
|
OBJ 1970-01-01 00:00:02 0 /user/deep/aaa/bbb/2
|
|
OBJ 1970-01-01 00:00:03 0 /user/deep/aaa/bbb/3
|
|
OBJ 1970-01-01 00:00:04 0 /user/foobar/1
|
|
OBJ 1970-01-01 00:00:05 0 /user/foobar/2
|
|
OBJ 1970-01-01 00:00:06 0 /user/foobar/3
|
|
OBJ 1970-01-01 00:00:07 0 /user/foobaz/1
|
|
`)
|
|
})
|
|
|
|
t.Run("Basic", func(t *testing.T) {
|
|
state.Succeed(t, "ls", "/user/fo", "--utc").RequireStdout(t, ``)
|
|
})
|
|
|
|
t.Run("ExactPrefix", func(t *testing.T) {
|
|
state.Succeed(t, "ls", "/user/foobar", "--utc").RequireStdout(t, `
|
|
KIND CREATED SIZE KEY
|
|
OBJ 1970-01-01 00:00:04 0 1
|
|
OBJ 1970-01-01 00:00:05 0 2
|
|
OBJ 1970-01-01 00:00:06 0 3
|
|
`)
|
|
})
|
|
|
|
t.Run("ExactPrefixWithSlash", func(t *testing.T) {
|
|
state.Succeed(t, "ls", "/user/foobar/", "--utc").RequireStdout(t, `
|
|
KIND CREATED SIZE KEY
|
|
OBJ 1970-01-01 00:00:04 0 1
|
|
OBJ 1970-01-01 00:00:05 0 2
|
|
OBJ 1970-01-01 00:00:06 0 3
|
|
`)
|
|
})
|
|
|
|
t.Run("MultipleLayers", func(t *testing.T) {
|
|
state.Succeed(t, "ls", "/user/deep/").RequireStdout(t, `
|
|
KIND CREATED SIZE KEY
|
|
PRE aaa/
|
|
`)
|
|
|
|
state.Succeed(t, "ls", "/user/deep/aaa/").RequireStdout(t, `
|
|
KIND CREATED SIZE KEY
|
|
PRE bbb/
|
|
`)
|
|
|
|
state.Succeed(t, "ls", "/user/deep/aaa/bbb/", "--utc").RequireStdout(t, `
|
|
KIND CREATED SIZE KEY
|
|
OBJ 1970-01-01 00:00:01 0 1
|
|
OBJ 1970-01-01 00:00:02 0 2
|
|
OBJ 1970-01-01 00:00:03 0 3
|
|
`)
|
|
})
|
|
}
|
|
|
|
func TestLsRelative(t *testing.T) {
|
|
state := ultest.Setup(commands,
|
|
ultest.WithFile("deep/aaa/bbb/1"),
|
|
ultest.WithFile("deep/aaa/bbb/2"),
|
|
ultest.WithFile("deep/aaa/bbb/3"),
|
|
ultest.WithFile("foobar/1"),
|
|
ultest.WithFile("foobar/2"),
|
|
ultest.WithFile("foobar/3"),
|
|
ultest.WithFile("foobaz/1"),
|
|
)
|
|
|
|
basic := `
|
|
KIND CREATED SIZE KEY
|
|
PRE deep/
|
|
PRE foobar/
|
|
PRE foobaz/
|
|
`
|
|
|
|
t.Run("Basic", func(t *testing.T) {
|
|
state.Succeed(t, "ls", "", "--utc").RequireStdout(t, basic)
|
|
})
|
|
|
|
t.Run("BasicDot", func(t *testing.T) {
|
|
state.Succeed(t, "ls", ".", "--utc").RequireStdout(t, basic)
|
|
})
|
|
|
|
t.Run("BasicDotSlash", func(t *testing.T) {
|
|
state.Succeed(t, "ls", "./", "--utc").RequireStdout(t, basic)
|
|
})
|
|
|
|
recursive := `
|
|
KIND CREATED SIZE KEY
|
|
OBJ 1970-01-01 00:00:01 0 deep/aaa/bbb/1
|
|
OBJ 1970-01-01 00:00:02 0 deep/aaa/bbb/2
|
|
OBJ 1970-01-01 00:00:03 0 deep/aaa/bbb/3
|
|
OBJ 1970-01-01 00:00:04 0 foobar/1
|
|
OBJ 1970-01-01 00:00:05 0 foobar/2
|
|
OBJ 1970-01-01 00:00:06 0 foobar/3
|
|
OBJ 1970-01-01 00:00:07 0 foobaz/1
|
|
`
|
|
|
|
t.Run("Recursive", func(t *testing.T) {
|
|
state.Succeed(t, "ls", "", "--recursive", "--utc").RequireStdout(t, recursive)
|
|
})
|
|
|
|
t.Run("RecursiveDot", func(t *testing.T) {
|
|
state.Succeed(t, "ls", ".", "--recursive", "--utc").RequireStdout(t, recursive)
|
|
})
|
|
|
|
t.Run("RecursiveDotSlash", func(t *testing.T) {
|
|
state.Succeed(t, "ls", "./", "--recursive", "--utc").RequireStdout(t, recursive)
|
|
})
|
|
|
|
}
|