func TestCopyMultipleS3ObjectsToGivenLocalDirectory(t *testing.T) {
	t.Parallel()
	bucket := s3BucketFromTestName(t)
	s3client, s5cmd, cleanup := setup(t)
	defer cleanup()
	createBucket(t, s3client, bucket)
	filesToContent := map[string]string{
		"testfile1.txt":            "this is a test file 1",
		"readme.md":                "this is a readme file",
		"b/filename-with-hypen.gz": "file has hypen in its name",
		"a/another_test_file.txt":  "yet another txt file. yatf.",
	}
	for filename, content := range filesToContent {
		putFile(t, s3client, bucket, filename, content)
	}
	const dst = "given-directory"
	cmd := s5cmd("cp", "s3://"+bucket+"/*", dst)
	result := icmd.RunCmd(cmd)
	result.Assert(t, icmd.Success)
	assertLines(t, result.Stdout(), map[int]compareFunc{
		0: equals(`cp s3: 
		1: equals(`cp s3: 
		2: equals(`cp s3: 
		3: equals(`cp s3: 
	}, sortInput(true))
	var expectedFiles = []fs.PathOp{
		fs.WithDir(
			"a",
			fs.WithFile("another_test_file.txt", "yet another txt file. yatf."),
		),
		fs.WithDir(
			"b",
			fs.WithFile("filename-with-hypen.gz", "file has hypen in its name"),
		),
		fs.WithFile("readme.md", "this is a readme file"),
		fs.WithFile("testfile1.txt", "this is a test file 1"),
	}
	expected := fs.Expected(t, fs.WithDir(dst, expectedFiles...))
	assert.Assert(t, fs.Equal(cmd.Dir, expected))
	for filename, content := range filesToContent {
		assert.Assert(t, ensureS3Object(s3client, bucket, filename, content))
	}
}
cp s3://bucket/* dir/ (dir/ doesn't exist)
package e2e
import (
	"fmt"
	"os"
	"path/filepath"
	"runtime"
	"testing"
	"time"
	"gotest.tools/v3/assert"
	"gotest.tools/v3/fs"
	"gotest.tools/v3/icmd"
)
func TestCopySingleS3ObjectToLocal(t *testing.T) {
	t.Parallel()
	const (
		bucket      = "bucket"
		fileContent = "this is a file content"
	)
	testcases := []struct {
		name           string
		src            string
		dst            string
		expected       fs.PathOp
		expectedOutput string
	}{
		{
			name:           "cp s3://bucket/object .",
			src:            "file1.txt",
			dst:            ".",
			expected:       fs.WithFile("file1.txt", fileContent, fs.WithMode(0644)),
			expectedOutput: "cp s3://bucket/file1.txt file1.txt",
		},
		{
			name:           "cp s3://bucket/object file",
			src:            "file1.txt",
			dst:            "file1.txt",
			expected:       fs.WithFile("file1.txt", fileContent, fs.WithMode(0644)),
			expectedOutput: "cp s3://bucket/file1.txt file1.txt",
		},
		{
			name:           "cp s3://bucket/object dir/",
			src:            "file1.txt",
			dst:            "dir/",
			expected:       fs.WithDir("dir", fs.WithFile("file1.txt", fileContent, fs.WithMode(0644))),
			expectedOutput: "cp s3://bucket/file1.txt dir/file1.txt",
		},
		{
			name:           "cp s3://bucket/object dir/file",
			src:            "file1.txt",
			dst:            "dir/file1.txt",
			expected:       fs.WithDir("dir", fs.WithFile("file1.txt", fileContent, fs.WithMode(0644))),
			expectedOutput: "cp s3://bucket/file1.txt dir/file1.txt",
		},
	}
	for _, tc := range testcases {
		tc := tc
		t.Run(tc.name, func(t *testing.T) {
			t.Parallel()
			s3client, s5cmd, cleanup := setup(t)
			defer cleanup()
			createBucket(t, s3client, bucket)
			putFile(t, s3client, bucket, tc.src, fileContent)
			src := fmt.Sprintf("s3://%v/%v", bucket, tc.src)
			cmd := s5cmd("cp", src, tc.dst)
			result := icmd.RunCmd(cmd)
			result.Assert(t, icmd.Success)
			assertLines(t, result.Stdout(), map[int]compareFunc{
				0: equals(tc.expectedOutput),
			})
			expected := fs.Expected(t, tc.expected)
			assert.Assert(t, fs.Equal(cmd.Dir, expected))
			assert.Assert(t, ensureS3Object(s3client, bucket, tc.src, fileContent))
		})
	}
}
func TestCopySingleS3ObjectToLocalJSON(t *testing.T) {
	t.Parallel()
	bucket := s3BucketFromTestName(t)
	s3client, s5cmd, cleanup := setup(t)
	defer cleanup()
	createBucket(t, s3client, bucket)
	const (
		filename = "testfile1.txt"
		content  = "this is a file content"
	)
	putFile(t, s3client, bucket, filename, content)
	cmd := s5cmd("--json", "cp", "s3://"+bucket+"/"+filename, ".")
	result := icmd.RunCmd(cmd)
	result.Assert(t, icmd.Success)
	jsonText := `
		{
			"operation": "cp",
			"success": true,
			"source": "s3://%v/testfile1.txt",
			"destination": "testfile1.txt",
			"object": {
				"type": "file",
				"size": 22
			}
		}
	`
	assertLines(t, result.Stdout(), map[int]compareFunc{
		0: json(jsonText, bucket),
	}, jsonCheck(true))
	expected := fs.Expected(t, fs.WithFile(filename, content, fs.WithMode(0644)))
	assert.Assert(t, fs.Equal(cmd.Dir, expected))
	assert.Assert(t, ensureS3Object(s3client, bucket, filename, content))
}
func TestCopySingleS3ObjectToLocalWithDestinationWildcard(t *testing.T) {
	t.Parallel()
	bucket := s3BucketFromTestName(t)
	s3client, s5cmd, cleanup := setup(t)
	defer cleanup()
	createBucket(t, s3client, bucket)
	const (
		filename = "testfile1.txt"
		content  = "this is a file content"
	)
	putFile(t, s3client, bucket, filename, content)
	cmd := s5cmd("cp", "s3://"+bucket+"/"+filename, "*")
	result := icmd.RunCmd(cmd)
	result.Assert(t, icmd.Expected{ExitCode: 1})
	assertLines(t, result.Stderr(), map[int]compareFunc{
		0: equals(`ERROR "cp s3://%v/%v *": target "*" can not contain glob characters`, bucket, filename),
	})
	expected := fs.Expected(t)
	assert.Assert(t, fs.Equal(cmd.Dir, expected))
	assert.Assert(t, ensureS3Object(s3client, bucket, filename, content))
}
func TestCopyS3PrefixToLocalMustReturnError(t *testing.T) {
	t.Parallel()
	bucket := s3BucketFromTestName(t)
	s3client, s5cmd, cleanup := setup(t)
	defer cleanup()
	createBucket(t, s3client, bucket)
	const (
		prefix     = "prefix/"
		objectpath = prefix + "file1.txt"
		content    = "this is a file content"
	)
	putFile(t, s3client, bucket, objectpath, content)
	cmd := s5cmd("cp", "s3://"+bucket+"/"+prefix, ".")
	result := icmd.RunCmd(cmd)
	result.Assert(t, icmd.Expected{ExitCode: 1})
	assertLines(t, result.Stderr(), map[int]compareFunc{
		0: equals(`ERROR "cp s3://%v/%v .": source argument must contain wildcard character`, bucket, prefix),
	})
	expected := fs.Expected(t)
	assert.Assert(t, fs.Equal(cmd.Dir, expected))
	assert.Assert(t, ensureS3Object(s3client, bucket, objectpath, content))
}
func TestCopyMultipleFlatS3ObjectsToLocal(t *testing.T) {
	t.Parallel()
	bucket := s3BucketFromTestName(t)
	s3client, s5cmd, cleanup := setup(t)
	defer cleanup()
	createBucket(t, s3client, bucket)
	filesToContent := map[string]string{
		"testfile1.txt":            "this is a test file 1",
		"a/readme.md":              "this is a readme file",
		"a/filename-with-hypen.gz": "file has hypen in its name",
		"b/another_test_file.txt":  "yet another txt file. yatf.",
	}
	for filename, content := range filesToContent {
		putFile(t, s3client, bucket, filename, content)
	}
	cmd := s5cmd("cp", "--flatten", "s3://"+bucket+"/*", ".")
	result := icmd.RunCmd(cmd)
	result.Assert(t, icmd.Success)
	assertLines(t, result.Stdout(), map[int]compareFunc{
		0: equals(`cp s3: 
		1: equals(`cp s3: 
		2: equals(`cp s3: 
		3: equals(`cp s3: 
	}, sortInput(true))
	var expectedFiles = []fs.PathOp{
		fs.WithFile("testfile1.txt", "this is a test file 1"),
		fs.WithFile("readme.md", "this is a readme file"),
		fs.WithFile("filename-with-hypen.gz", "file has hypen in its name"),
		fs.WithFile("another_test_file.txt", "yet another txt file. yatf."),
	}
	expected := fs.Expected(t, expectedFiles...)
	assert.Assert(t, fs.Equal(cmd.Dir, expected))
	for filename, content := range filesToContent {
		assert.Assert(t, ensureS3Object(s3client, bucket, filename, content))
	}
}
func TestCopyMultipleFlatS3ObjectsToLocalWithPartialMatching(t *testing.T) {
	t.Parallel()
	bucket := s3BucketFromTestName(t)
	s3client, s5cmd, cleanup := setup(t)
	defer cleanup()
	createBucket(t, s3client, bucket)
	filesToContent := map[string]string{
		"testfile1.txt":             "this is a test file 1",
		"readme.md":                 "this is a readme file",
		"filename-with-hypen.gz":    "file has hypen in its name",
		"dir/another_test_file.txt": "yet another txt file",
	}
	for filename, content := range filesToContent {
		putFile(t, s3client, bucket, filename, content)
	}
	cmd := s5cmd("cp", "--flatten", "s3://"+bucket+"/*.txt", ".")
	result := icmd.RunCmd(cmd)
	result.Assert(t, icmd.Success)
	assertLines(t, result.Stdout(), map[int]compareFunc{
		0: equals(`cp s3: 
		1: equals(`cp s3: 
	}, sortInput(true))
	expectedFiles := []fs.PathOp{
		fs.WithFile("testfile1.txt", "this is a test file 1", fs.WithMode(0644)),
		fs.WithFile("another_test_file.txt", "yet another txt file", fs.WithMode(0644)),
	}
	expected := fs.Expected(t, expectedFiles...)
	assert.Assert(t, fs.Equal(cmd.Dir, expected))
	for filename, content := range filesToContent {
		assert.Assert(t, ensureS3Object(s3client, bucket, filename, content))
	}
}
func TestCopyMultipleFlatNestedS3ObjectsToLocalWithPartialMatching(t *testing.T) {
	t.Parallel()
	bucket := s3BucketFromTestName(t)
	s3client, s5cmd, cleanup := setup(t)
	defer cleanup()
	createBucket(t, s3client, bucket)
	filesToContent := map[string]string{
		"testfile1.txt":     "test file 1",
		"a/readme.md":       "this is a readme file",
		"a/b/testfile2.txt": "test file 2",
	}
	for filename, content := range filesToContent {
		putFile(t, s3client, bucket, filename, content)
	}
	cmd := s5cmd("cp", "--flatten", "s3://"+bucket+"/*/*.txt", ".")
	result := icmd.RunCmd(cmd)
	result.Assert(t, icmd.Success)
	assertLines(t, result.Stdout(), map[int]compareFunc{
		0: equals(`cp s3: 
	}, sortInput(true))
	expected := fs.Expected(t, fs.WithFile("testfile2.txt", "test file 2", fs.WithMode(0644)))
	assert.Assert(t, fs.Equal(cmd.Dir, expected))
	for filename, content := range filesToContent {
		assert.Assert(t, ensureS3Object(s3client, bucket, filename, content))
	}
}
func TestCopyMultipleFlatS3ObjectsToLocalJSON(t *testing.T) {
	t.Parallel()
	bucket := s3BucketFromTestName(t)
	s3client, s5cmd, cleanup := setup(t)
	defer cleanup()
	createBucket(t, s3client, bucket)
	filesToContent := map[string]string{
		"testfile1.txt":            "this is a test file 1",
		"readme.md":                "this is a readme file",
		"b/filename-with-hypen.gz": "file has hypen in its name",
		"a/another_test_file.txt":  "yet another txt file. yatf.",
	}
	for filename, content := range filesToContent {
		putFile(t, s3client, bucket, filename, content)
	}
	cmd := s5cmd("--json", "cp", "--flatten", "s3://"+bucket+"/*", ".")
	result := icmd.RunCmd(cmd)
	result.Assert(t, icmd.Success)
	assertLines(t, result.Stdout(), map[int]compareFunc{
		0: json(`
			{
				"operation": "cp",
				"success": true,
				"source": "s3://%v/a/another_test_file.txt",
				"destination": "another_test_file.txt",
				"object":{
					"type": "file",
					"size": 27
				}
			}
		`, bucket),
		1: json(`
			{
				"operation": "cp",
				"success": true,
				"source": "s3://%v/b/filename-with-hypen.gz",
				"destination": "filename-with-hypen.gz",
				"object": {
					"type": "file",
					"size": 26
				}
			}
		`, bucket),
		2: json(`
			{
				"operation": "cp",
				"success": true,
				"source": "s3://%v/readme.md",
				"destination": "readme.md",
				"object": {
					"type": "file",
					"size": 21
				}
			}
		`, bucket),
		3: json(`
			{
				"operation": "cp",
				"success": true,
				"source": "s3://%v/testfile1.txt",
				"destination": "testfile1.txt",
				"object": {
					"type": "file",
					"size": 21
				}
			}
		`, bucket),
	}, sortInput(true), jsonCheck(true))
	var expectedFiles = []fs.PathOp{
		fs.WithFile("testfile1.txt", "this is a test file 1"),
		fs.WithFile("readme.md", "this is a readme file"),
		fs.WithFile("filename-with-hypen.gz", "file has hypen in its name"),
		fs.WithFile("another_test_file.txt", "yet another txt file. yatf."),
	}
	expected := fs.Expected(t, expectedFiles...)
	assert.Assert(t, fs.Equal(cmd.Dir, expected))
	for filename, content := range filesToContent {
		assert.Assert(t, ensureS3Object(s3client, bucket, filename, content))
	}
}
func TestCopyMultipleNestedS3ObjectsToLocal(t *testing.T) {
	t.Parallel()
	bucket := s3BucketFromTestName(t)
	s3client, s5cmd, cleanup := setup(t)
	defer cleanup()
	createBucket(t, s3client, bucket)
	filesToContent := map[string]string{
		"testfile1.txt":               "this is a test file 1",
		"a/readme.md":                 "this is a readme file",
		"a/b/filename-with-hypen.gz":  "file has hypen in its name",
		"b/another_test_file.txt":     "yet another txt file. yatf.",
		"c/d/e/another_test_file.txt": "yet another txt file. yatf.",
	}
	for filename, content := range filesToContent {
		putFile(t, s3client, bucket, filename, content)
	}
	cmd := s5cmd("cp", "s3://"+bucket+"/*", ".")
	result := icmd.RunCmd(cmd)
	result.Assert(t, icmd.Success)
	assertLines(t, result.Stdout(), map[int]compareFunc{
		0: equals(`cp s3: 
		1: equals(`cp s3: 
		2: equals(`cp s3: 
		3: equals(`cp s3: 
		4: equals(`cp s3: 
	}, sortInput(true))
	var expectedFiles = []fs.PathOp{
		fs.WithFile("testfile1.txt", "this is a test file 1"),
		fs.WithDir(
			"a",
			fs.WithFile("readme.md", "this is a readme file"),
			fs.WithDir(
				"b",
				fs.WithFile("filename-with-hypen.gz", "file has hypen in its name"),
			),
		),
		fs.WithDir(
			"b",
			fs.WithFile("another_test_file.txt", "yet another txt file. yatf."),
		),
		fs.WithDir(
			"c",
			fs.WithDir(
				"d",
				fs.WithDir(
					"e",
					fs.WithFile("another_test_file.txt", "yet another txt file. yatf."),
				),
			),
		),
	}
	expected := fs.Expected(t, expectedFiles...)
	assert.Assert(t, fs.Equal(cmd.Dir, expected))
	for filename, content := range filesToContent {
		assert.Assert(t, ensureS3Object(s3client, bucket, filename, content))
	}
}
func TestCopyMultipleNestedS3ObjectsToLocalWithPartial(t *testing.T) {
	t.Parallel()
	bucket := s3BucketFromTestName(t)
	s3client, s5cmd, cleanup := setup(t)
	defer cleanup()
	createBucket(t, s3client, bucket)
	filesToContent := map[string]string{
		"testfile1.txt":               "this is a test file 1",
		"a/readme.md":                 "this is a readme file",
		"a/b/filename-with-hypen.gz":  "file has hypen in its name",
		"b/another_test_file.txt":     "yet another txt file. yatf.",
		"c/d/e/another_test_file.txt": "yet another txt file. yatf.",
	}
	for filename, content := range filesToContent {
		putFile(t, s3client, bucket, filename, content)
	}
	cmd := s5cmd("cp", "s3://"+bucket+"/*/*.txt", ".")
	result := icmd.RunCmd(cmd)
	result.Assert(t, icmd.Success)
	assertLines(t, result.Stdout(), map[int]compareFunc{
		0: equals(`cp s3: 
		1: equals(`cp s3: 
	}, sortInput(true))
	expected := fs.Expected(
		t,
		fs.WithDir(
			"b",
			fs.WithFile("another_test_file.txt", "yet another txt file. yatf."),
		),
		fs.WithDir(
			"c",
			fs.WithDir(
				"d",
				fs.WithDir(
					"e",
					fs.WithFile("another_test_file.txt", "yet another txt file. yatf."),
				),
			),
		),
	)
	assert.Assert(t, fs.Equal(cmd.Dir, expected))
	for filename, content := range filesToContent {
		assert.Assert(t, ensureS3Object(s3client, bucket, filename, content))
	}
}