github.com/justincormack/cli@v0.0.0-20201215022714-831ebeae9675/cli/command/container/cp_test.go (about)

     1  package container
     2  
     3  import (
     4  	"io"
     5  	"io/ioutil"
     6  	"os"
     7  	"runtime"
     8  	"strings"
     9  	"testing"
    10  
    11  	"github.com/docker/cli/internal/test"
    12  	"github.com/docker/docker/api/types"
    13  	"github.com/docker/docker/pkg/archive"
    14  	"gotest.tools/v3/assert"
    15  	is "gotest.tools/v3/assert/cmp"
    16  	"gotest.tools/v3/fs"
    17  	"gotest.tools/v3/skip"
    18  )
    19  
    20  func TestRunCopyWithInvalidArguments(t *testing.T) {
    21  	var testcases = []struct {
    22  		doc         string
    23  		options     copyOptions
    24  		expectedErr string
    25  	}{
    26  		{
    27  			doc: "copy between container",
    28  			options: copyOptions{
    29  				source:      "first:/path",
    30  				destination: "second:/path",
    31  			},
    32  			expectedErr: "copying between containers is not supported",
    33  		},
    34  		{
    35  			doc: "copy without a container",
    36  			options: copyOptions{
    37  				source:      "./source",
    38  				destination: "./dest",
    39  			},
    40  			expectedErr: "must specify at least one container source",
    41  		},
    42  	}
    43  	for _, testcase := range testcases {
    44  		t.Run(testcase.doc, func(t *testing.T) {
    45  			err := runCopy(test.NewFakeCli(nil), testcase.options)
    46  			assert.Error(t, err, testcase.expectedErr)
    47  		})
    48  	}
    49  }
    50  
    51  func TestRunCopyFromContainerToStdout(t *testing.T) {
    52  	tarContent := "the tar content"
    53  
    54  	fakeClient := &fakeClient{
    55  		containerCopyFromFunc: func(container, srcPath string) (io.ReadCloser, types.ContainerPathStat, error) {
    56  			assert.Check(t, is.Equal("container", container))
    57  			return ioutil.NopCloser(strings.NewReader(tarContent)), types.ContainerPathStat{}, nil
    58  		},
    59  	}
    60  	options := copyOptions{source: "container:/path", destination: "-"}
    61  	cli := test.NewFakeCli(fakeClient)
    62  	err := runCopy(cli, options)
    63  	assert.NilError(t, err)
    64  	assert.Check(t, is.Equal(tarContent, cli.OutBuffer().String()))
    65  	assert.Check(t, is.Equal("", cli.ErrBuffer().String()))
    66  }
    67  
    68  func TestRunCopyFromContainerToFilesystem(t *testing.T) {
    69  	destDir := fs.NewDir(t, "cp-test",
    70  		fs.WithFile("file1", "content\n"))
    71  	defer destDir.Remove()
    72  
    73  	fakeClient := &fakeClient{
    74  		containerCopyFromFunc: func(container, srcPath string) (io.ReadCloser, types.ContainerPathStat, error) {
    75  			assert.Check(t, is.Equal("container", container))
    76  			readCloser, err := archive.TarWithOptions(destDir.Path(), &archive.TarOptions{})
    77  			return readCloser, types.ContainerPathStat{}, err
    78  		},
    79  	}
    80  	options := copyOptions{source: "container:/path", destination: destDir.Path()}
    81  	cli := test.NewFakeCli(fakeClient)
    82  	err := runCopy(cli, options)
    83  	assert.NilError(t, err)
    84  	assert.Check(t, is.Equal("", cli.OutBuffer().String()))
    85  	assert.Check(t, is.Equal("", cli.ErrBuffer().String()))
    86  
    87  	content, err := ioutil.ReadFile(destDir.Join("file1"))
    88  	assert.NilError(t, err)
    89  	assert.Check(t, is.Equal("content\n", string(content)))
    90  }
    91  
    92  func TestRunCopyFromContainerToFilesystemMissingDestinationDirectory(t *testing.T) {
    93  	destDir := fs.NewDir(t, "cp-test",
    94  		fs.WithFile("file1", "content\n"))
    95  	defer destDir.Remove()
    96  
    97  	fakeClient := &fakeClient{
    98  		containerCopyFromFunc: func(container, srcPath string) (io.ReadCloser, types.ContainerPathStat, error) {
    99  			assert.Check(t, is.Equal("container", container))
   100  			readCloser, err := archive.TarWithOptions(destDir.Path(), &archive.TarOptions{})
   101  			return readCloser, types.ContainerPathStat{}, err
   102  		},
   103  	}
   104  
   105  	options := copyOptions{
   106  		source:      "container:/path",
   107  		destination: destDir.Join("missing", "foo"),
   108  	}
   109  	cli := test.NewFakeCli(fakeClient)
   110  	err := runCopy(cli, options)
   111  	assert.ErrorContains(t, err, destDir.Join("missing"))
   112  }
   113  
   114  func TestRunCopyToContainerFromFileWithTrailingSlash(t *testing.T) {
   115  	srcFile := fs.NewFile(t, t.Name())
   116  	defer srcFile.Remove()
   117  
   118  	options := copyOptions{
   119  		source:      srcFile.Path() + string(os.PathSeparator),
   120  		destination: "container:/path",
   121  	}
   122  	cli := test.NewFakeCli(&fakeClient{})
   123  	err := runCopy(cli, options)
   124  
   125  	expectedError := "not a directory"
   126  	if runtime.GOOS == "windows" {
   127  		expectedError = "The filename, directory name, or volume label syntax is incorrect"
   128  	}
   129  	assert.ErrorContains(t, err, expectedError)
   130  }
   131  
   132  func TestRunCopyToContainerSourceDoesNotExist(t *testing.T) {
   133  	options := copyOptions{
   134  		source:      "/does/not/exist",
   135  		destination: "container:/path",
   136  	}
   137  	cli := test.NewFakeCli(&fakeClient{})
   138  	err := runCopy(cli, options)
   139  	expected := "no such file or directory"
   140  	if runtime.GOOS == "windows" {
   141  		expected = "cannot find the file specified"
   142  	}
   143  	assert.ErrorContains(t, err, expected)
   144  }
   145  
   146  func TestSplitCpArg(t *testing.T) {
   147  	var testcases = []struct {
   148  		doc               string
   149  		path              string
   150  		os                string
   151  		expectedContainer string
   152  		expectedPath      string
   153  	}{
   154  		{
   155  			doc:          "absolute path with colon",
   156  			os:           "linux",
   157  			path:         "/abs/path:withcolon",
   158  			expectedPath: "/abs/path:withcolon",
   159  		},
   160  		{
   161  			doc:          "relative path with colon",
   162  			path:         "./relative:path",
   163  			expectedPath: "./relative:path",
   164  		},
   165  		{
   166  			doc:          "absolute path with drive",
   167  			os:           "windows",
   168  			path:         `d:\abs\path`,
   169  			expectedPath: `d:\abs\path`,
   170  		},
   171  		{
   172  			doc:          "no separator",
   173  			path:         "relative/path",
   174  			expectedPath: "relative/path",
   175  		},
   176  		{
   177  			doc:               "with separator",
   178  			path:              "container:/opt/foo",
   179  			expectedPath:      "/opt/foo",
   180  			expectedContainer: "container",
   181  		},
   182  	}
   183  	for _, testcase := range testcases {
   184  		t.Run(testcase.doc, func(t *testing.T) {
   185  			skip.If(t, testcase.os != "" && testcase.os != runtime.GOOS)
   186  
   187  			container, path := splitCpArg(testcase.path)
   188  			assert.Check(t, is.Equal(testcase.expectedContainer, container))
   189  			assert.Check(t, is.Equal(testcase.expectedPath, path))
   190  		})
   191  	}
   192  }
   193  
   194  func TestRunCopyFromContainerToFilesystemIrregularDestination(t *testing.T) {
   195  	options := copyOptions{source: "container:/dev/null", destination: "/dev/random"}
   196  	cli := test.NewFakeCli(nil)
   197  	err := runCopy(cli, options)
   198  	assert.Assert(t, err != nil)
   199  	expected := `"/dev/random" must be a directory or a regular file`
   200  	assert.ErrorContains(t, err, expected)
   201  }