cmd/uplinkng: tests for cp
this adds some stuff to ultest so that the set of files created by a test can be inspected after so that we can write some tests for the cp command to observe that it does what it is supposed to do. Change-Id: I98b8fb214058140dfbb117baa7acea6a2cc340e1
This commit is contained in:
parent
98be54b9a3
commit
d73287f043
@ -21,6 +21,7 @@ type cmdCp struct {
|
|||||||
|
|
||||||
recursive bool
|
recursive bool
|
||||||
dryrun bool
|
dryrun bool
|
||||||
|
progress bool
|
||||||
|
|
||||||
source ulloc.Location
|
source ulloc.Location
|
||||||
dest ulloc.Location
|
dest ulloc.Location
|
||||||
@ -36,6 +37,9 @@ func (c *cmdCp) Setup(a clingy.Arguments, f clingy.Flags) {
|
|||||||
c.dryrun = f.New("dryrun", "Print what operations would happen but don't execute them", false,
|
c.dryrun = f.New("dryrun", "Print what operations would happen but don't execute them", false,
|
||||||
clingy.Transform(strconv.ParseBool),
|
clingy.Transform(strconv.ParseBool),
|
||||||
).(bool)
|
).(bool)
|
||||||
|
c.progress = f.New("progress", "Show a progress bar when possible", true,
|
||||||
|
clingy.Transform(strconv.ParseBool),
|
||||||
|
).(bool)
|
||||||
|
|
||||||
c.source = a.New("source", "Source to copy", clingy.Transform(ulloc.Parse)).(ulloc.Location)
|
c.source = a.New("source", "Source to copy", clingy.Transform(ulloc.Parse)).(ulloc.Location)
|
||||||
c.dest = a.New("dest", "Desination to copy", clingy.Transform(ulloc.Parse)).(ulloc.Location)
|
c.dest = a.New("dest", "Desination to copy", clingy.Transform(ulloc.Parse)).(ulloc.Location)
|
||||||
@ -51,7 +55,7 @@ func (c *cmdCp) Execute(ctx clingy.Context) error {
|
|||||||
if c.recursive {
|
if c.recursive {
|
||||||
return c.copyRecursive(ctx, fs)
|
return c.copyRecursive(ctx, fs)
|
||||||
}
|
}
|
||||||
return c.copyFile(ctx, fs, c.source, c.dest, true)
|
return c.copyFile(ctx, fs, c.source, c.dest, c.progress)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *cmdCp) copyRecursive(ctx clingy.Context, fs ulfs.Filesystem) error {
|
func (c *cmdCp) copyRecursive(ctx clingy.Context, fs ulfs.Filesystem) error {
|
||||||
@ -99,7 +103,7 @@ func (c *cmdCp) copyFile(ctx clingy.Context, fs ulfs.Filesystem, source, dest ul
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !source.Std() && !dest.Std() {
|
if !source.Std() && !dest.Std() {
|
||||||
fmt.Println(copyVerb(source, dest), source, "to", dest)
|
fmt.Fprintln(ctx.Stdout(), copyVerb(source, dest), source, "to", dest)
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.dryrun {
|
if c.dryrun {
|
||||||
|
117
cmd/uplinkng/cmd_cp_test.go
Normal file
117
cmd/uplinkng/cmd_cp_test.go
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
// Copyright (C) 2021 Storj Labs, Inc.
|
||||||
|
// See LICENSE for copying information.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"storj.io/storj/cmd/uplinkng/ultest"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCpDownload(t *testing.T) {
|
||||||
|
state := ultest.Setup(commands,
|
||||||
|
ultest.WithFile("sj://user/file1.txt", "remote"),
|
||||||
|
)
|
||||||
|
|
||||||
|
state.Succeed(t, "cp", "sj://user/file1.txt", "/home/user/file1.txt").RequireFiles(t,
|
||||||
|
ultest.File{Loc: "/home/user/file1.txt", Contents: "remote"},
|
||||||
|
ultest.File{Loc: "sj://user/file1.txt", Contents: "remote"},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCpDownloadOverwrite(t *testing.T) {
|
||||||
|
state := ultest.Setup(commands,
|
||||||
|
ultest.WithFile("/home/user/file1.txt", "local"),
|
||||||
|
ultest.WithFile("sj://user/file1.txt", "remote"),
|
||||||
|
)
|
||||||
|
|
||||||
|
state.Succeed(t, "cp", "sj://user/file1.txt", "/home/user/file1.txt").RequireFiles(t,
|
||||||
|
ultest.File{Loc: "/home/user/file1.txt", Contents: "remote"},
|
||||||
|
ultest.File{Loc: "sj://user/file1.txt", Contents: "remote"},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCpUpload(t *testing.T) {
|
||||||
|
state := ultest.Setup(commands,
|
||||||
|
ultest.WithFile("/home/user/file1.txt", "local"),
|
||||||
|
ultest.WithBucket("user"),
|
||||||
|
)
|
||||||
|
|
||||||
|
state.Succeed(t, "cp", "/home/user/file1.txt", "sj://user/file1.txt").RequireFiles(t,
|
||||||
|
ultest.File{Loc: "/home/user/file1.txt", Contents: "local"},
|
||||||
|
ultest.File{Loc: "sj://user/file1.txt", Contents: "local"},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCpUploadOverwrite(t *testing.T) {
|
||||||
|
state := ultest.Setup(commands,
|
||||||
|
ultest.WithFile("/home/user/file1.txt", "local"),
|
||||||
|
ultest.WithFile("sj://user/file1.txt", "remote"),
|
||||||
|
)
|
||||||
|
|
||||||
|
state.Succeed(t, "cp", "/home/user/file1.txt", "sj://user/file1.txt").RequireFiles(t,
|
||||||
|
ultest.File{Loc: "/home/user/file1.txt", Contents: "local"},
|
||||||
|
ultest.File{Loc: "sj://user/file1.txt", Contents: "local"},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCpRecursiveDownload(t *testing.T) {
|
||||||
|
state := ultest.Setup(commands,
|
||||||
|
ultest.WithFile("sj://user/file1.txt", "data1"),
|
||||||
|
ultest.WithFile("sj://user/folder1/file2.txt", "data2"),
|
||||||
|
ultest.WithFile("sj://user/folder1/file3.txt", "data3"),
|
||||||
|
ultest.WithFile("sj://user/folder2/folder3/file4.txt", "data4"),
|
||||||
|
ultest.WithFile("sj://user/folder2/folder3/file5.txt", "data5"),
|
||||||
|
)
|
||||||
|
|
||||||
|
state.Succeed(t, "cp", "sj://user", "/home/user/dest", "--recursive").RequireFiles(t,
|
||||||
|
ultest.File{Loc: "sj://user/file1.txt", Contents: "data1"},
|
||||||
|
ultest.File{Loc: "sj://user/folder1/file2.txt", Contents: "data2"},
|
||||||
|
ultest.File{Loc: "sj://user/folder1/file3.txt", Contents: "data3"},
|
||||||
|
ultest.File{Loc: "sj://user/folder2/folder3/file4.txt", Contents: "data4"},
|
||||||
|
ultest.File{Loc: "sj://user/folder2/folder3/file5.txt", Contents: "data5"},
|
||||||
|
|
||||||
|
ultest.File{Loc: "/home/user/dest/file1.txt", Contents: "data1"},
|
||||||
|
ultest.File{Loc: "/home/user/dest/folder1/file2.txt", Contents: "data2"},
|
||||||
|
ultest.File{Loc: "/home/user/dest/folder1/file3.txt", Contents: "data3"},
|
||||||
|
ultest.File{Loc: "/home/user/dest/folder2/folder3/file4.txt", Contents: "data4"},
|
||||||
|
ultest.File{Loc: "/home/user/dest/folder2/folder3/file5.txt", Contents: "data5"},
|
||||||
|
)
|
||||||
|
|
||||||
|
state.Succeed(t, "cp", "sj://user/fo", "/home/user/dest", "--recursive").RequireFiles(t,
|
||||||
|
ultest.File{Loc: "sj://user/file1.txt", Contents: "data1"},
|
||||||
|
ultest.File{Loc: "sj://user/folder1/file2.txt", Contents: "data2"},
|
||||||
|
ultest.File{Loc: "sj://user/folder1/file3.txt", Contents: "data3"},
|
||||||
|
ultest.File{Loc: "sj://user/folder2/folder3/file4.txt", Contents: "data4"},
|
||||||
|
ultest.File{Loc: "sj://user/folder2/folder3/file5.txt", Contents: "data5"},
|
||||||
|
|
||||||
|
ultest.File{Loc: "/home/user/dest/folder1/file2.txt", Contents: "data2"},
|
||||||
|
ultest.File{Loc: "/home/user/dest/folder1/file3.txt", Contents: "data3"},
|
||||||
|
ultest.File{Loc: "/home/user/dest/folder2/folder3/file4.txt", Contents: "data4"},
|
||||||
|
ultest.File{Loc: "/home/user/dest/folder2/folder3/file5.txt", Contents: "data5"},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCpRecursiveDifficult(t *testing.T) {
|
||||||
|
state := ultest.Setup(commands,
|
||||||
|
ultest.WithFile("sj://user/dot-dot/../foo"),
|
||||||
|
ultest.WithFile("sj://user/dot-dot/../../foo"),
|
||||||
|
|
||||||
|
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"),
|
||||||
|
)
|
||||||
|
|
||||||
|
_ = state
|
||||||
|
}
|
@ -174,7 +174,11 @@ func (p Location) RelativeTo(target Location) (string, error) {
|
|||||||
} else if !strings.HasPrefix(target.key, p.key) {
|
} else if !strings.HasPrefix(target.key, p.key) {
|
||||||
return "", errs.New("cannot make relative location because keys are not prefixes")
|
return "", errs.New("cannot make relative location because keys are not prefixes")
|
||||||
}
|
}
|
||||||
return target.key[len(p.key):], nil
|
idx := strings.LastIndexByte(p.key, '/')
|
||||||
|
if idx == -1 {
|
||||||
|
idx = 0
|
||||||
|
}
|
||||||
|
return target.key[idx:], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// AppendKey adds the key to the end of the existing key, separating with the
|
// AppendKey adds the key to the end of the existing key, separating with the
|
||||||
|
@ -22,47 +22,53 @@ import (
|
|||||||
|
|
||||||
type testFilesystem struct {
|
type testFilesystem struct {
|
||||||
stdin string
|
stdin string
|
||||||
ops []Operation
|
|
||||||
created int64
|
created int64
|
||||||
files map[ulloc.Location]byteFileData
|
files map[ulloc.Location]memFileData
|
||||||
pending map[ulloc.Location][]*byteWriteHandle
|
pending map[ulloc.Location][]*memWriteHandle
|
||||||
buckets map[string]struct{}
|
buckets map[string]struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func newTestFilesystem() *testFilesystem {
|
func newTestFilesystem() *testFilesystem {
|
||||||
return &testFilesystem{
|
return &testFilesystem{
|
||||||
files: make(map[ulloc.Location]byteFileData),
|
files: make(map[ulloc.Location]memFileData),
|
||||||
pending: make(map[ulloc.Location][]*byteWriteHandle),
|
pending: make(map[ulloc.Location][]*memWriteHandle),
|
||||||
buckets: make(map[string]struct{}),
|
buckets: make(map[string]struct{}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type byteFileData struct {
|
type memFileData struct {
|
||||||
data []byte
|
contents string
|
||||||
created int64
|
created int64
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tfs *testFilesystem) ensureBucket(name string) {
|
func (tfs *testFilesystem) ensureBucket(name string) {
|
||||||
tfs.buckets[name] = struct{}{}
|
tfs.buckets[name] = struct{}{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (tfs *testFilesystem) Files() (files []File) {
|
||||||
|
for loc, mf := range tfs.files {
|
||||||
|
files = append(files, File{
|
||||||
|
Loc: loc.String(),
|
||||||
|
Contents: mf.contents,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
sort.Slice(files, func(i, j int) bool { return files[i].less(files[j]) })
|
||||||
|
return files
|
||||||
|
}
|
||||||
|
|
||||||
func (tfs *testFilesystem) Close() error {
|
func (tfs *testFilesystem) Close() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tfs *testFilesystem) Open(ctx clingy.Context, loc ulloc.Location) (_ ulfs.ReadHandle, err error) {
|
func (tfs *testFilesystem) Open(ctx clingy.Context, loc ulloc.Location) (_ ulfs.ReadHandle, err error) {
|
||||||
defer func() { tfs.ops = append(tfs.ops, newOp("open", loc, err)) }()
|
mf, ok := tfs.files[loc]
|
||||||
|
|
||||||
bf, ok := tfs.files[loc]
|
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, errs.New("file does not exist")
|
return nil, errs.New("file does not exist")
|
||||||
}
|
}
|
||||||
return &byteReadHandle{Buffer: bytes.NewBuffer(bf.data)}, nil
|
return &byteReadHandle{Buffer: bytes.NewBufferString(mf.contents)}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tfs *testFilesystem) Create(ctx clingy.Context, loc ulloc.Location) (_ ulfs.WriteHandle, err error) {
|
func (tfs *testFilesystem) Create(ctx clingy.Context, loc ulloc.Location) (_ ulfs.WriteHandle, err error) {
|
||||||
defer func() { tfs.ops = append(tfs.ops, newOp("create", loc, err)) }()
|
|
||||||
|
|
||||||
if bucket, _, ok := loc.RemoteParts(); ok {
|
if bucket, _, ok := loc.RemoteParts(); ok {
|
||||||
if _, ok := tfs.buckets[bucket]; !ok {
|
if _, ok := tfs.buckets[bucket]; !ok {
|
||||||
return nil, errs.New("bucket %q does not exist", bucket)
|
return nil, errs.New("bucket %q does not exist", bucket)
|
||||||
@ -70,7 +76,7 @@ func (tfs *testFilesystem) Create(ctx clingy.Context, loc ulloc.Location) (_ ulf
|
|||||||
}
|
}
|
||||||
|
|
||||||
tfs.created++
|
tfs.created++
|
||||||
wh := &byteWriteHandle{
|
wh := &memWriteHandle{
|
||||||
buf: bytes.NewBuffer(nil),
|
buf: bytes.NewBuffer(nil),
|
||||||
loc: loc,
|
loc: loc,
|
||||||
tfs: tfs,
|
tfs: tfs,
|
||||||
@ -84,11 +90,11 @@ func (tfs *testFilesystem) Create(ctx clingy.Context, loc ulloc.Location) (_ ulf
|
|||||||
|
|
||||||
func (tfs *testFilesystem) ListObjects(ctx context.Context, prefix ulloc.Location, recursive bool) (ulfs.ObjectIterator, error) {
|
func (tfs *testFilesystem) ListObjects(ctx context.Context, prefix ulloc.Location, recursive bool) (ulfs.ObjectIterator, error) {
|
||||||
var infos []ulfs.ObjectInfo
|
var infos []ulfs.ObjectInfo
|
||||||
for loc, bf := range tfs.files {
|
for loc, mf := range tfs.files {
|
||||||
if loc.HasPrefix(prefix) {
|
if loc.HasPrefix(prefix) {
|
||||||
infos = append(infos, ulfs.ObjectInfo{
|
infos = append(infos, ulfs.ObjectInfo{
|
||||||
Loc: loc,
|
Loc: loc,
|
||||||
Created: time.Unix(bf.created, 0),
|
Created: time.Unix(mf.created, 0),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -145,7 +151,7 @@ func (b *byteReadHandle) Info() ulfs.ObjectInfo { return ulfs.ObjectInfo{} }
|
|||||||
// ulfs.WriteHandle
|
// ulfs.WriteHandle
|
||||||
//
|
//
|
||||||
|
|
||||||
type byteWriteHandle struct {
|
type memWriteHandle struct {
|
||||||
buf *bytes.Buffer
|
buf *bytes.Buffer
|
||||||
loc ulloc.Location
|
loc ulloc.Location
|
||||||
tfs *testFilesystem
|
tfs *testFilesystem
|
||||||
@ -153,33 +159,31 @@ type byteWriteHandle struct {
|
|||||||
done bool
|
done bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *byteWriteHandle) Write(p []byte) (int, error) {
|
func (b *memWriteHandle) Write(p []byte) (int, error) {
|
||||||
return b.buf.Write(p)
|
return b.buf.Write(p)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *byteWriteHandle) Commit() error {
|
func (b *memWriteHandle) Commit() error {
|
||||||
if err := b.close(); err != nil {
|
if err := b.close(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
b.tfs.ops = append(b.tfs.ops, newOp("commit", b.loc, nil))
|
b.tfs.files[b.loc] = memFileData{
|
||||||
b.tfs.files[b.loc] = byteFileData{
|
contents: b.buf.String(),
|
||||||
data: b.buf.Bytes(),
|
created: b.cre,
|
||||||
created: b.cre,
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *byteWriteHandle) Abort() error {
|
func (b *memWriteHandle) Abort() error {
|
||||||
if err := b.close(); err != nil {
|
if err := b.close(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
b.tfs.ops = append(b.tfs.ops, newOp("append", b.loc, nil))
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *byteWriteHandle) close() error {
|
func (b *memWriteHandle) close() error {
|
||||||
if b.done {
|
if b.done {
|
||||||
return errs.New("already done")
|
return errs.New("already done")
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
package ultest
|
package ultest
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
@ -14,11 +15,11 @@ import (
|
|||||||
|
|
||||||
// Result captures all the output of running a command for inspection.
|
// Result captures all the output of running a command for inspection.
|
||||||
type Result struct {
|
type Result struct {
|
||||||
Stdout string
|
Stdout string
|
||||||
Stderr string
|
Stderr string
|
||||||
Ok bool
|
Ok bool
|
||||||
Err error
|
Err error
|
||||||
Operations []Operation
|
Files []File
|
||||||
}
|
}
|
||||||
|
|
||||||
// RequireSuccess fails if the Result did not observe a successful execution.
|
// RequireSuccess fails if the Result did not observe a successful execution.
|
||||||
@ -32,20 +33,32 @@ func (r Result) RequireSuccess(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// RequireFailure fails if the Result did not observe a failed execution.
|
// RequireFailure fails if the Result did not observe a failed execution.
|
||||||
func (r Result) RequireFailure(t *testing.T) {
|
func (r Result) RequireFailure(t *testing.T) Result {
|
||||||
require.False(t, r.Ok && r.Err == nil, "command ran with no error")
|
require.False(t, r.Ok && r.Err == nil, "command ran with no error")
|
||||||
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
// RequireStdout requires that the execution wrote to stdout the provided string.
|
// RequireStdout requires that the execution wrote to stdout the provided string.
|
||||||
// Blank lines are ignored and all lines are space trimmed for the comparison.
|
// Blank lines are ignored and all lines are space trimmed for the comparison.
|
||||||
func (r Result) RequireStdout(t *testing.T, stdout string) {
|
func (r Result) RequireStdout(t *testing.T, stdout string) Result {
|
||||||
require.Equal(t, trimNewlineSpaces(stdout), trimNewlineSpaces(r.Stdout))
|
require.Equal(t, trimNewlineSpaces(stdout), trimNewlineSpaces(r.Stdout))
|
||||||
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
// RequireStderr requires that the execution wrote to stderr the provided string.
|
// RequireStderr requires that the execution wrote to stderr the provided string.
|
||||||
// Blank lines are ignored and all lines are space trimmed for the comparison.
|
// Blank lines are ignored and all lines are space trimmed for the comparison.
|
||||||
func (r Result) RequireStderr(t *testing.T, stderr string) {
|
func (r Result) RequireStderr(t *testing.T, stderr string) Result {
|
||||||
require.Equal(t, trimNewlineSpaces(stderr), trimNewlineSpaces(r.Stderr))
|
require.Equal(t, trimNewlineSpaces(stderr), trimNewlineSpaces(r.Stderr))
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
// RequireFiles requires that the set of files provided are all of the files that
|
||||||
|
// existed at the end of the execution.
|
||||||
|
func (r Result) RequireFiles(t *testing.T, files ...File) Result {
|
||||||
|
files = append([]File(nil), files...)
|
||||||
|
sort.Slice(files, func(i, j int) bool { return files[i].less(files[j]) })
|
||||||
|
require.Equal(t, files, r.Files)
|
||||||
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseErrors(s string) []string {
|
func parseErrors(s string) []string {
|
||||||
@ -74,18 +87,14 @@ func trimNewlineSpaces(s string) string {
|
|||||||
return strings.Join(lines[:j], "\n")
|
return strings.Join(lines[:j], "\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Operation represents some kind of filesystem operation that happened
|
// File represents a file existing either locally or remotely.
|
||||||
// on some location, and if the operation failed.
|
type File struct {
|
||||||
type Operation struct {
|
Loc string
|
||||||
Kind string
|
Contents string
|
||||||
Loc string
|
|
||||||
Error bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func newOp(kind string, loc ulloc.Location, err error) Operation {
|
func (f File) less(g File) bool {
|
||||||
return Operation{
|
fl, _ := ulloc.Parse(f.Loc)
|
||||||
Kind: kind,
|
gl, _ := ulloc.Parse(g.Loc)
|
||||||
Loc: loc.String(),
|
return fl.Less(gl)
|
||||||
Error: err != nil,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,6 @@ import (
|
|||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"github.com/zeebo/clingy"
|
"github.com/zeebo/clingy"
|
||||||
"github.com/zeebo/errs"
|
|
||||||
|
|
||||||
"storj.io/storj/cmd/uplinkng/ulfs"
|
"storj.io/storj/cmd/uplinkng/ulfs"
|
||||||
"storj.io/storj/cmd/uplinkng/ulloc"
|
"storj.io/storj/cmd/uplinkng/ulloc"
|
||||||
@ -57,9 +56,10 @@ func (st State) Run(t *testing.T, args ...string) Result {
|
|||||||
var stdout bytes.Buffer
|
var stdout bytes.Buffer
|
||||||
var stderr bytes.Buffer
|
var stderr bytes.Buffer
|
||||||
var stdin bytes.Buffer
|
var stdin bytes.Buffer
|
||||||
var ops []Operation
|
|
||||||
var ran bool
|
var ran bool
|
||||||
|
|
||||||
|
tfs := newTestFilesystem()
|
||||||
|
|
||||||
ok, err := clingy.Environment{
|
ok, err := clingy.Environment{
|
||||||
Name: "uplink-test",
|
Name: "uplink-test",
|
||||||
Args: args,
|
Args: args,
|
||||||
@ -69,13 +69,9 @@ func (st State) Run(t *testing.T, args ...string) Result {
|
|||||||
Stderr: &stderr,
|
Stderr: &stderr,
|
||||||
|
|
||||||
Wrap: func(ctx clingy.Context, cmd clingy.Cmd) error {
|
Wrap: func(ctx clingy.Context, cmd clingy.Cmd) error {
|
||||||
tfs := newTestFilesystem()
|
|
||||||
for _, opt := range st.opts {
|
for _, opt := range st.opts {
|
||||||
if err := opt.fn(ctx, tfs); err != nil {
|
opt.fn(t, ctx, tfs)
|
||||||
return errs.Wrap(err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
tfs.ops = nil
|
|
||||||
|
|
||||||
if len(tfs.stdin) > 0 {
|
if len(tfs.stdin) > 0 {
|
||||||
_, _ = stdin.WriteString(tfs.stdin)
|
_, _ = stdin.WriteString(tfs.stdin)
|
||||||
@ -88,67 +84,89 @@ func (st State) Run(t *testing.T, args ...string) Result {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ran = true
|
ran = true
|
||||||
err := cmd.Execute(ctx)
|
return cmd.Execute(ctx)
|
||||||
ops = tfs.ops
|
|
||||||
return err
|
|
||||||
},
|
},
|
||||||
}.Run(context.Background(), st.cmds)
|
}.Run(context.Background(), st.cmds)
|
||||||
|
|
||||||
if ok && err == nil {
|
if ok && err == nil {
|
||||||
require.True(t, ran, "no command was executed: %q", args)
|
require.True(t, ran, "no command was executed: %q", args)
|
||||||
}
|
}
|
||||||
|
|
||||||
return Result{
|
return Result{
|
||||||
Stdout: stdout.String(),
|
Stdout: stdout.String(),
|
||||||
Stderr: stderr.String(),
|
Stderr: stderr.String(),
|
||||||
Ok: ok,
|
Ok: ok,
|
||||||
Err: err,
|
Err: err,
|
||||||
Operations: ops,
|
Files: tfs.Files(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExecuteOption allows one to control the environment that a command executes in.
|
// ExecuteOption allows one to control the environment that a command executes in.
|
||||||
type ExecuteOption struct {
|
type ExecuteOption struct {
|
||||||
fn func(ctx clingy.Context, tfs *testFilesystem) error
|
fn func(t *testing.T, ctx clingy.Context, tfs *testFilesystem)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithFilesystem lets one do arbitrary setup on the filesystem in a callback.
|
||||||
|
func WithFilesystem(cb func(t *testing.T, ctx clingy.Context, fs ulfs.Filesystem)) ExecuteOption {
|
||||||
|
return ExecuteOption{func(t *testing.T, ctx clingy.Context, tfs *testFilesystem) {
|
||||||
|
cb(t, ctx, tfs)
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithBucket ensures the bucket exists.
|
||||||
|
func WithBucket(name string) ExecuteOption {
|
||||||
|
return ExecuteOption{func(_ *testing.T, _ clingy.Context, tfs *testFilesystem) {
|
||||||
|
tfs.ensureBucket(name)
|
||||||
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithStdin sets the command to execute with the provided string as standard input.
|
// WithStdin sets the command to execute with the provided string as standard input.
|
||||||
func WithStdin(stdin string) ExecuteOption {
|
func WithStdin(stdin string) ExecuteOption {
|
||||||
return ExecuteOption{func(_ clingy.Context, tfs *testFilesystem) error {
|
return ExecuteOption{func(_ *testing.T, _ clingy.Context, tfs *testFilesystem) {
|
||||||
tfs.stdin = stdin
|
tfs.stdin = stdin
|
||||||
return nil
|
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithFile sets the command to execute with a file created at the given location.
|
// WithFile sets the command to execute with a file created at the given location.
|
||||||
func WithFile(location string) ExecuteOption {
|
func WithFile(location string, contents ...string) ExecuteOption {
|
||||||
return ExecuteOption{func(ctx clingy.Context, tfs *testFilesystem) error {
|
contents = append([]string(nil), contents...)
|
||||||
|
return ExecuteOption{func(t *testing.T, ctx clingy.Context, tfs *testFilesystem) {
|
||||||
loc, err := ulloc.Parse(location)
|
loc, err := ulloc.Parse(location)
|
||||||
if err != nil {
|
require.NoError(t, err)
|
||||||
return err
|
|
||||||
}
|
|
||||||
if bucket, _, ok := loc.RemoteParts(); ok {
|
if bucket, _, ok := loc.RemoteParts(); ok {
|
||||||
tfs.ensureBucket(bucket)
|
tfs.ensureBucket(bucket)
|
||||||
}
|
}
|
||||||
|
|
||||||
wh, err := tfs.Create(ctx, loc)
|
wh, err := tfs.Create(ctx, loc)
|
||||||
if err != nil {
|
require.NoError(t, err)
|
||||||
return err
|
defer func() { _ = wh.Abort() }()
|
||||||
|
|
||||||
|
for _, content := range contents {
|
||||||
|
_, err := wh.Write([]byte(content))
|
||||||
|
require.NoError(t, err)
|
||||||
}
|
}
|
||||||
return wh.Commit()
|
if len(contents) == 0 {
|
||||||
|
_, err := wh.Write([]byte(location))
|
||||||
|
require.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
require.NoError(t, wh.Commit())
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithPendingFile sets the command to execute with a pending upload happening to
|
// WithPendingFile sets the command to execute with a pending upload happening to
|
||||||
// the provided location.
|
// the provided location.
|
||||||
func WithPendingFile(location string) ExecuteOption {
|
func WithPendingFile(location string) ExecuteOption {
|
||||||
return ExecuteOption{func(ctx clingy.Context, tfs *testFilesystem) error {
|
return ExecuteOption{func(t *testing.T, ctx clingy.Context, tfs *testFilesystem) {
|
||||||
loc, err := ulloc.Parse(location)
|
loc, err := ulloc.Parse(location)
|
||||||
if err != nil {
|
require.NoError(t, err)
|
||||||
return err
|
|
||||||
}
|
|
||||||
if bucket, _, ok := loc.RemoteParts(); ok {
|
if bucket, _, ok := loc.RemoteParts(); ok {
|
||||||
tfs.ensureBucket(bucket)
|
tfs.ensureBucket(bucket)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = tfs.Create(ctx, loc)
|
_, err = tfs.Create(ctx, loc)
|
||||||
return err
|
require.NoError(t, err)
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user