Browse code

added unit tests for package cli/command/secret

Signed-off-by: Arash Deshmeh <adeshmeh@ca.ibm.com>

Arash Deshmeh authored on 2017/04/01 16:07:22
Showing 20 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,44 @@
0
+package secret
1
+
2
+import (
3
+	"github.com/docker/docker/api/types"
4
+	"github.com/docker/docker/api/types/swarm"
5
+	"github.com/docker/docker/client"
6
+	"golang.org/x/net/context"
7
+)
8
+
9
+type fakeClient struct {
10
+	client.Client
11
+	secretCreateFunc  func(swarm.SecretSpec) (types.SecretCreateResponse, error)
12
+	secretInspectFunc func(string) (swarm.Secret, []byte, error)
13
+	secretListFunc    func(types.SecretListOptions) ([]swarm.Secret, error)
14
+	secretRemoveFunc  func(string) error
15
+}
16
+
17
+func (c *fakeClient) SecretCreate(ctx context.Context, spec swarm.SecretSpec) (types.SecretCreateResponse, error) {
18
+	if c.secretCreateFunc != nil {
19
+		return c.secretCreateFunc(spec)
20
+	}
21
+	return types.SecretCreateResponse{}, nil
22
+}
23
+
24
+func (c *fakeClient) SecretInspectWithRaw(ctx context.Context, id string) (swarm.Secret, []byte, error) {
25
+	if c.secretInspectFunc != nil {
26
+		return c.secretInspectFunc(id)
27
+	}
28
+	return swarm.Secret{}, nil, nil
29
+}
30
+
31
+func (c *fakeClient) SecretList(ctx context.Context, options types.SecretListOptions) ([]swarm.Secret, error) {
32
+	if c.secretListFunc != nil {
33
+		return c.secretListFunc(options)
34
+	}
35
+	return []swarm.Secret{}, nil
36
+}
37
+
38
+func (c *fakeClient) SecretRemove(ctx context.Context, name string) error {
39
+	if c.secretRemoveFunc != nil {
40
+		return c.secretRemoveFunc(name)
41
+	}
42
+	return nil
43
+}
... ...
@@ -22,7 +22,7 @@ type createOptions struct {
22 22
 	labels opts.ListOpts
23 23
 }
24 24
 
25
-func newSecretCreateCommand(dockerCli *command.DockerCli) *cobra.Command {
25
+func newSecretCreateCommand(dockerCli command.Cli) *cobra.Command {
26 26
 	createOpts := createOptions{
27 27
 		labels: opts.NewListOpts(opts.ValidateEnv),
28 28
 	}
... ...
@@ -43,7 +43,7 @@ func newSecretCreateCommand(dockerCli *command.DockerCli) *cobra.Command {
43 43
 	return cmd
44 44
 }
45 45
 
46
-func runSecretCreate(dockerCli *command.DockerCli, options createOptions) error {
46
+func runSecretCreate(dockerCli command.Cli, options createOptions) error {
47 47
 	client := dockerCli.Client()
48 48
 	ctx := context.Background()
49 49
 
50 50
new file mode 100644
... ...
@@ -0,0 +1,126 @@
0
+package secret
1
+
2
+import (
3
+	"bytes"
4
+	"io/ioutil"
5
+	"path/filepath"
6
+	"strings"
7
+	"testing"
8
+
9
+	"github.com/docker/docker/api/types"
10
+	"github.com/docker/docker/api/types/swarm"
11
+	"github.com/docker/docker/cli/internal/test"
12
+	"github.com/docker/docker/pkg/testutil/assert"
13
+	"github.com/docker/docker/pkg/testutil/golden"
14
+	"github.com/pkg/errors"
15
+)
16
+
17
+const secretDataFile = "secret-create-with-name.golden"
18
+
19
+func TestSecretCreateErrors(t *testing.T) {
20
+	testCases := []struct {
21
+		args             []string
22
+		secretCreateFunc func(swarm.SecretSpec) (types.SecretCreateResponse, error)
23
+		expectedError    string
24
+	}{
25
+		{
26
+			args:          []string{"too_few"},
27
+			expectedError: "requires exactly 2 argument(s)",
28
+		},
29
+		{args: []string{"too", "many", "arguments"},
30
+			expectedError: "requires exactly 2 argument(s)",
31
+		},
32
+		{
33
+			args: []string{"name", filepath.Join("testdata", secretDataFile)},
34
+			secretCreateFunc: func(secretSpec swarm.SecretSpec) (types.SecretCreateResponse, error) {
35
+				return types.SecretCreateResponse{}, errors.Errorf("error creating secret")
36
+			},
37
+			expectedError: "error creating secret",
38
+		},
39
+	}
40
+	for _, tc := range testCases {
41
+		buf := new(bytes.Buffer)
42
+		cmd := newSecretCreateCommand(
43
+			test.NewFakeCli(&fakeClient{
44
+				secretCreateFunc: tc.secretCreateFunc,
45
+			}, buf),
46
+		)
47
+		cmd.SetArgs(tc.args)
48
+		cmd.SetOutput(ioutil.Discard)
49
+		assert.Error(t, cmd.Execute(), tc.expectedError)
50
+	}
51
+}
52
+
53
+func TestSecretCreateWithName(t *testing.T) {
54
+	name := "foo"
55
+	buf := new(bytes.Buffer)
56
+	var actual []byte
57
+	cli := test.NewFakeCli(&fakeClient{
58
+		secretCreateFunc: func(spec swarm.SecretSpec) (types.SecretCreateResponse, error) {
59
+			if spec.Name != name {
60
+				return types.SecretCreateResponse{}, errors.Errorf("expected name %q, got %q", name, spec.Name)
61
+			}
62
+
63
+			actual = spec.Data
64
+
65
+			return types.SecretCreateResponse{
66
+				ID: "ID-" + spec.Name,
67
+			}, nil
68
+		},
69
+	}, buf)
70
+
71
+	cmd := newSecretCreateCommand(cli)
72
+	cmd.SetArgs([]string{name, filepath.Join("testdata", secretDataFile)})
73
+	assert.NilError(t, cmd.Execute())
74
+	expected := golden.Get(t, actual, secretDataFile)
75
+	assert.Equal(t, string(actual), string(expected))
76
+	assert.Equal(t, strings.TrimSpace(buf.String()), "ID-"+name)
77
+}
78
+
79
+func TestSecretCreateWithLabels(t *testing.T) {
80
+	expectedLabels := map[string]string{
81
+		"lbl1": "Label-foo",
82
+		"lbl2": "Label-bar",
83
+	}
84
+	name := "foo"
85
+
86
+	buf := new(bytes.Buffer)
87
+	cli := test.NewFakeCli(&fakeClient{
88
+		secretCreateFunc: func(spec swarm.SecretSpec) (types.SecretCreateResponse, error) {
89
+			if spec.Name != name {
90
+				return types.SecretCreateResponse{}, errors.Errorf("expected name %q, got %q", name, spec.Name)
91
+			}
92
+
93
+			if !compareMap(spec.Labels, expectedLabels) {
94
+				return types.SecretCreateResponse{}, errors.Errorf("expected labels %v, got %v", expectedLabels, spec.Labels)
95
+			}
96
+
97
+			return types.SecretCreateResponse{
98
+				ID: "ID-" + spec.Name,
99
+			}, nil
100
+		},
101
+	}, buf)
102
+
103
+	cmd := newSecretCreateCommand(cli)
104
+	cmd.SetArgs([]string{name, filepath.Join("testdata", secretDataFile)})
105
+	cmd.Flags().Set("label", "lbl1=Label-foo")
106
+	cmd.Flags().Set("label", "lbl2=Label-bar")
107
+	assert.NilError(t, cmd.Execute())
108
+	assert.Equal(t, strings.TrimSpace(buf.String()), "ID-"+name)
109
+}
110
+
111
+func compareMap(actual map[string]string, expected map[string]string) bool {
112
+	if len(actual) != len(expected) {
113
+		return false
114
+	}
115
+	for key, value := range actual {
116
+		if expectedValue, ok := expected[key]; ok {
117
+			if expectedValue != value {
118
+				return false
119
+			}
120
+		} else {
121
+			return false
122
+		}
123
+	}
124
+	return true
125
+}
... ...
@@ -13,7 +13,7 @@ type inspectOptions struct {
13 13
 	format string
14 14
 }
15 15
 
16
-func newSecretInspectCommand(dockerCli *command.DockerCli) *cobra.Command {
16
+func newSecretInspectCommand(dockerCli command.Cli) *cobra.Command {
17 17
 	opts := inspectOptions{}
18 18
 	cmd := &cobra.Command{
19 19
 		Use:   "inspect [OPTIONS] SECRET [SECRET...]",
... ...
@@ -29,7 +29,7 @@ func newSecretInspectCommand(dockerCli *command.DockerCli) *cobra.Command {
29 29
 	return cmd
30 30
 }
31 31
 
32
-func runSecretInspect(dockerCli *command.DockerCli, opts inspectOptions) error {
32
+func runSecretInspect(dockerCli command.Cli, opts inspectOptions) error {
33 33
 	client := dockerCli.Client()
34 34
 	ctx := context.Background()
35 35
 
36 36
new file mode 100644
... ...
@@ -0,0 +1,149 @@
0
+package secret
1
+
2
+import (
3
+	"bytes"
4
+	"fmt"
5
+	"io/ioutil"
6
+	"testing"
7
+
8
+	"github.com/docker/docker/api/types/swarm"
9
+	"github.com/docker/docker/cli/internal/test"
10
+	"github.com/pkg/errors"
11
+	// Import builders to get the builder function as package function
12
+	. "github.com/docker/docker/cli/internal/test/builders"
13
+	"github.com/docker/docker/pkg/testutil/assert"
14
+	"github.com/docker/docker/pkg/testutil/golden"
15
+)
16
+
17
+func TestSecretInspectErrors(t *testing.T) {
18
+	testCases := []struct {
19
+		args              []string
20
+		flags             map[string]string
21
+		secretInspectFunc func(secretID string) (swarm.Secret, []byte, error)
22
+		expectedError     string
23
+	}{
24
+		{
25
+			expectedError: "requires at least 1 argument",
26
+		},
27
+		{
28
+			args: []string{"foo"},
29
+			secretInspectFunc: func(secretID string) (swarm.Secret, []byte, error) {
30
+				return swarm.Secret{}, nil, errors.Errorf("error while inspecting the secret")
31
+			},
32
+			expectedError: "error while inspecting the secret",
33
+		},
34
+		{
35
+			args: []string{"foo"},
36
+			flags: map[string]string{
37
+				"format": "{{invalid format}}",
38
+			},
39
+			expectedError: "Template parsing error",
40
+		},
41
+		{
42
+			args: []string{"foo", "bar"},
43
+			secretInspectFunc: func(secretID string) (swarm.Secret, []byte, error) {
44
+				if secretID == "foo" {
45
+					return *Secret(SecretName("foo")), nil, nil
46
+				}
47
+				return swarm.Secret{}, nil, errors.Errorf("error while inspecting the secret")
48
+			},
49
+			expectedError: "error while inspecting the secret",
50
+		},
51
+	}
52
+	for _, tc := range testCases {
53
+		buf := new(bytes.Buffer)
54
+		cmd := newSecretInspectCommand(
55
+			test.NewFakeCli(&fakeClient{
56
+				secretInspectFunc: tc.secretInspectFunc,
57
+			}, buf),
58
+		)
59
+		cmd.SetArgs(tc.args)
60
+		for key, value := range tc.flags {
61
+			cmd.Flags().Set(key, value)
62
+		}
63
+		cmd.SetOutput(ioutil.Discard)
64
+		assert.Error(t, cmd.Execute(), tc.expectedError)
65
+	}
66
+}
67
+
68
+func TestSecretInspectWithoutFormat(t *testing.T) {
69
+	testCases := []struct {
70
+		name              string
71
+		args              []string
72
+		secretInspectFunc func(secretID string) (swarm.Secret, []byte, error)
73
+	}{
74
+		{
75
+			name: "single-secret",
76
+			args: []string{"foo"},
77
+			secretInspectFunc: func(name string) (swarm.Secret, []byte, error) {
78
+				if name != "foo" {
79
+					return swarm.Secret{}, nil, errors.Errorf("Invalid name, expected %s, got %s", "foo", name)
80
+				}
81
+				return *Secret(SecretID("ID-foo"), SecretName("foo")), nil, nil
82
+			},
83
+		},
84
+		{
85
+			name: "multiple-secrets-with-labels",
86
+			args: []string{"foo", "bar"},
87
+			secretInspectFunc: func(name string) (swarm.Secret, []byte, error) {
88
+				return *Secret(SecretID("ID-"+name), SecretName(name), SecretLabels(map[string]string{
89
+					"label1": "label-foo",
90
+				})), nil, nil
91
+			},
92
+		},
93
+	}
94
+	for _, tc := range testCases {
95
+		buf := new(bytes.Buffer)
96
+		cmd := newSecretInspectCommand(
97
+			test.NewFakeCli(&fakeClient{
98
+				secretInspectFunc: tc.secretInspectFunc,
99
+			}, buf),
100
+		)
101
+		cmd.SetArgs(tc.args)
102
+		assert.NilError(t, cmd.Execute())
103
+		actual := buf.String()
104
+		expected := golden.Get(t, []byte(actual), fmt.Sprintf("secret-inspect-without-format.%s.golden", tc.name))
105
+		assert.EqualNormalizedString(t, assert.RemoveSpace, actual, string(expected))
106
+	}
107
+}
108
+
109
+func TestSecretInspectWithFormat(t *testing.T) {
110
+	secretInspectFunc := func(name string) (swarm.Secret, []byte, error) {
111
+		return *Secret(SecretName("foo"), SecretLabels(map[string]string{
112
+			"label1": "label-foo",
113
+		})), nil, nil
114
+	}
115
+	testCases := []struct {
116
+		name              string
117
+		format            string
118
+		args              []string
119
+		secretInspectFunc func(name string) (swarm.Secret, []byte, error)
120
+	}{
121
+		{
122
+			name:              "simple-template",
123
+			format:            "{{.Spec.Name}}",
124
+			args:              []string{"foo"},
125
+			secretInspectFunc: secretInspectFunc,
126
+		},
127
+		{
128
+			name:              "json-template",
129
+			format:            "{{json .Spec.Labels}}",
130
+			args:              []string{"foo"},
131
+			secretInspectFunc: secretInspectFunc,
132
+		},
133
+	}
134
+	for _, tc := range testCases {
135
+		buf := new(bytes.Buffer)
136
+		cmd := newSecretInspectCommand(
137
+			test.NewFakeCli(&fakeClient{
138
+				secretInspectFunc: tc.secretInspectFunc,
139
+			}, buf),
140
+		)
141
+		cmd.SetArgs(tc.args)
142
+		cmd.Flags().Set("format", tc.format)
143
+		assert.NilError(t, cmd.Execute())
144
+		actual := buf.String()
145
+		expected := golden.Get(t, []byte(actual), fmt.Sprintf("secret-inspect-with-format.%s.golden", tc.name))
146
+		assert.EqualNormalizedString(t, assert.RemoveSpace, actual, string(expected))
147
+	}
148
+}
... ...
@@ -16,7 +16,7 @@ type listOptions struct {
16 16
 	filter opts.FilterOpt
17 17
 }
18 18
 
19
-func newSecretListCommand(dockerCli *command.DockerCli) *cobra.Command {
19
+func newSecretListCommand(dockerCli command.Cli) *cobra.Command {
20 20
 	opts := listOptions{filter: opts.NewFilterOpt()}
21 21
 
22 22
 	cmd := &cobra.Command{
... ...
@@ -37,7 +37,7 @@ func newSecretListCommand(dockerCli *command.DockerCli) *cobra.Command {
37 37
 	return cmd
38 38
 }
39 39
 
40
-func runSecretList(dockerCli *command.DockerCli, opts listOptions) error {
40
+func runSecretList(dockerCli command.Cli, opts listOptions) error {
41 41
 	client := dockerCli.Client()
42 42
 	ctx := context.Background()
43 43
 
44 44
new file mode 100644
... ...
@@ -0,0 +1,172 @@
0
+package secret
1
+
2
+import (
3
+	"bytes"
4
+	"io/ioutil"
5
+	"testing"
6
+	"time"
7
+
8
+	"github.com/docker/docker/api/types"
9
+	"github.com/docker/docker/api/types/swarm"
10
+	"github.com/docker/docker/cli/config/configfile"
11
+	"github.com/docker/docker/cli/internal/test"
12
+	"github.com/pkg/errors"
13
+	// Import builders to get the builder function as package function
14
+	. "github.com/docker/docker/cli/internal/test/builders"
15
+	"github.com/docker/docker/pkg/testutil/assert"
16
+	"github.com/docker/docker/pkg/testutil/golden"
17
+)
18
+
19
+func TestSecretListErrors(t *testing.T) {
20
+	testCases := []struct {
21
+		args           []string
22
+		secretListFunc func(types.SecretListOptions) ([]swarm.Secret, error)
23
+		expectedError  string
24
+	}{
25
+		{
26
+			args:          []string{"foo"},
27
+			expectedError: "accepts no argument",
28
+		},
29
+		{
30
+			secretListFunc: func(options types.SecretListOptions) ([]swarm.Secret, error) {
31
+				return []swarm.Secret{}, errors.Errorf("error listing secrets")
32
+			},
33
+			expectedError: "error listing secrets",
34
+		},
35
+	}
36
+	for _, tc := range testCases {
37
+		buf := new(bytes.Buffer)
38
+		cmd := newSecretListCommand(
39
+			test.NewFakeCli(&fakeClient{
40
+				secretListFunc: tc.secretListFunc,
41
+			}, buf),
42
+		)
43
+		cmd.SetArgs(tc.args)
44
+		cmd.SetOutput(ioutil.Discard)
45
+		assert.Error(t, cmd.Execute(), tc.expectedError)
46
+	}
47
+}
48
+
49
+func TestSecretList(t *testing.T) {
50
+	buf := new(bytes.Buffer)
51
+	cli := test.NewFakeCli(&fakeClient{
52
+		secretListFunc: func(options types.SecretListOptions) ([]swarm.Secret, error) {
53
+			return []swarm.Secret{
54
+				*Secret(SecretID("ID-foo"),
55
+					SecretName("foo"),
56
+					SecretVersion(swarm.Version{Index: 10}),
57
+					SecretCreatedAt(time.Now().Add(-2*time.Hour)),
58
+					SecretUpdatedAt(time.Now().Add(-1*time.Hour)),
59
+				),
60
+				*Secret(SecretID("ID-bar"),
61
+					SecretName("bar"),
62
+					SecretVersion(swarm.Version{Index: 11}),
63
+					SecretCreatedAt(time.Now().Add(-2*time.Hour)),
64
+					SecretUpdatedAt(time.Now().Add(-1*time.Hour)),
65
+				),
66
+			}, nil
67
+		},
68
+	}, buf)
69
+	cli.SetConfigfile(&configfile.ConfigFile{})
70
+	cmd := newSecretListCommand(cli)
71
+	cmd.SetOutput(buf)
72
+	assert.NilError(t, cmd.Execute())
73
+	actual := buf.String()
74
+	expected := golden.Get(t, []byte(actual), "secret-list.golden")
75
+	assert.EqualNormalizedString(t, assert.RemoveSpace, actual, string(expected))
76
+}
77
+
78
+func TestSecretListWithQuietOption(t *testing.T) {
79
+	buf := new(bytes.Buffer)
80
+	cli := test.NewFakeCli(&fakeClient{
81
+		secretListFunc: func(options types.SecretListOptions) ([]swarm.Secret, error) {
82
+			return []swarm.Secret{
83
+				*Secret(SecretID("ID-foo"), SecretName("foo")),
84
+				*Secret(SecretID("ID-bar"), SecretName("bar"), SecretLabels(map[string]string{
85
+					"label": "label-bar",
86
+				})),
87
+			}, nil
88
+		},
89
+	}, buf)
90
+	cli.SetConfigfile(&configfile.ConfigFile{})
91
+	cmd := newSecretListCommand(cli)
92
+	cmd.Flags().Set("quiet", "true")
93
+	assert.NilError(t, cmd.Execute())
94
+	actual := buf.String()
95
+	expected := golden.Get(t, []byte(actual), "secret-list-with-quiet-option.golden")
96
+	assert.EqualNormalizedString(t, assert.RemoveSpace, actual, string(expected))
97
+}
98
+
99
+func TestSecretListWithConfigFormat(t *testing.T) {
100
+	buf := new(bytes.Buffer)
101
+	cli := test.NewFakeCli(&fakeClient{
102
+		secretListFunc: func(options types.SecretListOptions) ([]swarm.Secret, error) {
103
+			return []swarm.Secret{
104
+				*Secret(SecretID("ID-foo"), SecretName("foo")),
105
+				*Secret(SecretID("ID-bar"), SecretName("bar"), SecretLabels(map[string]string{
106
+					"label": "label-bar",
107
+				})),
108
+			}, nil
109
+		},
110
+	}, buf)
111
+	cli.SetConfigfile(&configfile.ConfigFile{
112
+		SecretFormat: "{{ .Name }} {{ .Labels }}",
113
+	})
114
+	cmd := newSecretListCommand(cli)
115
+	assert.NilError(t, cmd.Execute())
116
+	actual := buf.String()
117
+	expected := golden.Get(t, []byte(actual), "secret-list-with-config-format.golden")
118
+	assert.EqualNormalizedString(t, assert.RemoveSpace, actual, string(expected))
119
+}
120
+
121
+func TestSecretListWithFormat(t *testing.T) {
122
+	buf := new(bytes.Buffer)
123
+	cli := test.NewFakeCli(&fakeClient{
124
+		secretListFunc: func(options types.SecretListOptions) ([]swarm.Secret, error) {
125
+			return []swarm.Secret{
126
+				*Secret(SecretID("ID-foo"), SecretName("foo")),
127
+				*Secret(SecretID("ID-bar"), SecretName("bar"), SecretLabels(map[string]string{
128
+					"label": "label-bar",
129
+				})),
130
+			}, nil
131
+		},
132
+	}, buf)
133
+	cmd := newSecretListCommand(cli)
134
+	cmd.Flags().Set("format", "{{ .Name }} {{ .Labels }}")
135
+	assert.NilError(t, cmd.Execute())
136
+	actual := buf.String()
137
+	expected := golden.Get(t, []byte(actual), "secret-list-with-format.golden")
138
+	assert.EqualNormalizedString(t, assert.RemoveSpace, actual, string(expected))
139
+}
140
+
141
+func TestSecretListWithFilter(t *testing.T) {
142
+	buf := new(bytes.Buffer)
143
+	cli := test.NewFakeCli(&fakeClient{
144
+		secretListFunc: func(options types.SecretListOptions) ([]swarm.Secret, error) {
145
+			assert.Equal(t, options.Filters.Get("name")[0], "foo")
146
+			assert.Equal(t, options.Filters.Get("label")[0], "lbl1=Label-bar")
147
+			return []swarm.Secret{
148
+				*Secret(SecretID("ID-foo"),
149
+					SecretName("foo"),
150
+					SecretVersion(swarm.Version{Index: 10}),
151
+					SecretCreatedAt(time.Now().Add(-2*time.Hour)),
152
+					SecretUpdatedAt(time.Now().Add(-1*time.Hour)),
153
+				),
154
+				*Secret(SecretID("ID-bar"),
155
+					SecretName("bar"),
156
+					SecretVersion(swarm.Version{Index: 11}),
157
+					SecretCreatedAt(time.Now().Add(-2*time.Hour)),
158
+					SecretUpdatedAt(time.Now().Add(-1*time.Hour)),
159
+				),
160
+			}, nil
161
+		},
162
+	}, buf)
163
+	cli.SetConfigfile(&configfile.ConfigFile{})
164
+	cmd := newSecretListCommand(cli)
165
+	cmd.Flags().Set("filter", "name=foo")
166
+	cmd.Flags().Set("filter", "label=lbl1=Label-bar")
167
+	assert.NilError(t, cmd.Execute())
168
+	actual := buf.String()
169
+	expected := golden.Get(t, []byte(actual), "secret-list-with-filter.golden")
170
+	assert.EqualNormalizedString(t, assert.RemoveSpace, actual, string(expected))
171
+}
... ...
@@ -15,7 +15,7 @@ type removeOptions struct {
15 15
 	names []string
16 16
 }
17 17
 
18
-func newSecretRemoveCommand(dockerCli *command.DockerCli) *cobra.Command {
18
+func newSecretRemoveCommand(dockerCli command.Cli) *cobra.Command {
19 19
 	return &cobra.Command{
20 20
 		Use:     "rm SECRET [SECRET...]",
21 21
 		Aliases: []string{"remove"},
... ...
@@ -30,7 +30,7 @@ func newSecretRemoveCommand(dockerCli *command.DockerCli) *cobra.Command {
30 30
 	}
31 31
 }
32 32
 
33
-func runSecretRemove(dockerCli *command.DockerCli, opts removeOptions) error {
33
+func runSecretRemove(dockerCli command.Cli, opts removeOptions) error {
34 34
 	client := dockerCli.Client()
35 35
 	ctx := context.Background()
36 36
 
37 37
new file mode 100644
... ...
@@ -0,0 +1,81 @@
0
+package secret
1
+
2
+import (
3
+	"bytes"
4
+	"io/ioutil"
5
+	"strings"
6
+	"testing"
7
+
8
+	"github.com/docker/docker/cli/internal/test"
9
+	"github.com/docker/docker/pkg/testutil/assert"
10
+	"github.com/pkg/errors"
11
+)
12
+
13
+func TestSecretRemoveErrors(t *testing.T) {
14
+	testCases := []struct {
15
+		args             []string
16
+		secretRemoveFunc func(string) error
17
+		expectedError    string
18
+	}{
19
+		{
20
+			args:          []string{},
21
+			expectedError: "requires at least 1 argument(s).",
22
+		},
23
+		{
24
+			args: []string{"foo"},
25
+			secretRemoveFunc: func(name string) error {
26
+				return errors.Errorf("error removing secret")
27
+			},
28
+			expectedError: "error removing secret",
29
+		},
30
+	}
31
+	for _, tc := range testCases {
32
+		buf := new(bytes.Buffer)
33
+		cmd := newSecretRemoveCommand(
34
+			test.NewFakeCli(&fakeClient{
35
+				secretRemoveFunc: tc.secretRemoveFunc,
36
+			}, buf),
37
+		)
38
+		cmd.SetArgs(tc.args)
39
+		cmd.SetOutput(ioutil.Discard)
40
+		assert.Error(t, cmd.Execute(), tc.expectedError)
41
+	}
42
+}
43
+
44
+func TestSecretRemoveWithName(t *testing.T) {
45
+	names := []string{"foo", "bar"}
46
+	buf := new(bytes.Buffer)
47
+	var removedSecrets []string
48
+	cli := test.NewFakeCli(&fakeClient{
49
+		secretRemoveFunc: func(name string) error {
50
+			removedSecrets = append(removedSecrets, name)
51
+			return nil
52
+		},
53
+	}, buf)
54
+	cmd := newSecretRemoveCommand(cli)
55
+	cmd.SetArgs(names)
56
+	assert.NilError(t, cmd.Execute())
57
+	assert.EqualStringSlice(t, strings.Split(strings.TrimSpace(buf.String()), "\n"), names)
58
+	assert.EqualStringSlice(t, removedSecrets, names)
59
+}
60
+
61
+func TestSecretRemoveContinueAfterError(t *testing.T) {
62
+	names := []string{"foo", "bar"}
63
+	buf := new(bytes.Buffer)
64
+	var removedSecrets []string
65
+
66
+	cli := test.NewFakeCli(&fakeClient{
67
+		secretRemoveFunc: func(name string) error {
68
+			removedSecrets = append(removedSecrets, name)
69
+			if name == "foo" {
70
+				return errors.Errorf("error removing secret: %s", name)
71
+			}
72
+			return nil
73
+		},
74
+	}, buf)
75
+
76
+	cmd := newSecretRemoveCommand(cli)
77
+	cmd.SetArgs(names)
78
+	assert.Error(t, cmd.Execute(), "error removing secret: foo")
79
+	assert.EqualStringSlice(t, removedSecrets, names)
80
+}
0 81
new file mode 100644
... ...
@@ -0,0 +1 @@
0
+secret_foo_bar
0 1
new file mode 100644
... ...
@@ -0,0 +1 @@
0
+{"label1":"label-foo"}
0 1
new file mode 100644
... ...
@@ -0,0 +1 @@
0
+foo
0 1
new file mode 100644
... ...
@@ -0,0 +1,26 @@
0
+[
1
+    {
2
+        "ID": "ID-foo",
3
+	"Version": {},
4
+        "CreatedAt": "0001-01-01T00:00:00Z",
5
+        "UpdatedAt": "0001-01-01T00:00:00Z",
6
+        "Spec": {
7
+            "Name": "foo",
8
+            "Labels": {
9
+                "label1": "label-foo"
10
+            }
11
+        }
12
+    },
13
+    {
14
+        "ID": "ID-bar",
15
+	"Version": {},
16
+        "CreatedAt": "0001-01-01T00:00:00Z",
17
+        "UpdatedAt": "0001-01-01T00:00:00Z",
18
+        "Spec": {
19
+            "Name": "bar",
20
+            "Labels": {
21
+                "label1": "label-foo"
22
+            }
23
+        }
24
+    }
25
+]
0 26
new file mode 100644
... ...
@@ -0,0 +1,12 @@
0
+[
1
+    {
2
+        "ID": "ID-foo",
3
+	"Version": {},
4
+	"CreatedAt": "0001-01-01T00:00:00Z",
5
+	"UpdatedAt": "0001-01-01T00:00:00Z",
6
+        "Spec": {
7
+            "Name": "foo",
8
+            "Labels": null
9
+        }
10
+    }
11
+]
0 12
new file mode 100644
... ...
@@ -0,0 +1,2 @@
0
+foo
1
+bar label=label-bar
0 2
new file mode 100644
... ...
@@ -0,0 +1,3 @@
0
+ID                  NAME                CREATED             UPDATED
1
+ID-foo              foo                 2 hours ago         About an hour ago
2
+ID-bar              bar                 2 hours ago         About an hour ago
0 3
new file mode 100644
... ...
@@ -0,0 +1,2 @@
0
+foo
1
+bar label=label-bar
0 2
new file mode 100644
... ...
@@ -0,0 +1,2 @@
0
+ID-foo
1
+ID-bar
0 2
new file mode 100644
... ...
@@ -0,0 +1,3 @@
0
+ID                  NAME                CREATED             UPDATED
1
+ID-foo              foo                 2 hours ago         About an hour ago
2
+ID-bar              bar                 2 hours ago         About an hour ago
0 3
new file mode 100644
... ...
@@ -0,0 +1,61 @@
0
+package builders
1
+
2
+import (
3
+	"time"
4
+
5
+	"github.com/docker/docker/api/types/swarm"
6
+)
7
+
8
+// Secret creates a secret with default values.
9
+// Any number of secret builder functions can be passed to augment it.
10
+func Secret(builders ...func(secret *swarm.Secret)) *swarm.Secret {
11
+	secret := &swarm.Secret{}
12
+
13
+	for _, builder := range builders {
14
+		builder(secret)
15
+	}
16
+
17
+	return secret
18
+}
19
+
20
+// SecretLabels sets the secret's labels
21
+func SecretLabels(labels map[string]string) func(secret *swarm.Secret) {
22
+	return func(secret *swarm.Secret) {
23
+		secret.Spec.Labels = labels
24
+	}
25
+}
26
+
27
+// SecretName sets the secret's name
28
+func SecretName(name string) func(secret *swarm.Secret) {
29
+	return func(secret *swarm.Secret) {
30
+		secret.Spec.Name = name
31
+	}
32
+}
33
+
34
+// SecretID sets the secret's ID
35
+func SecretID(ID string) func(secret *swarm.Secret) {
36
+	return func(secret *swarm.Secret) {
37
+		secret.ID = ID
38
+	}
39
+}
40
+
41
+// SecretVersion sets the version for the secret
42
+func SecretVersion(v swarm.Version) func(*swarm.Secret) {
43
+	return func(secret *swarm.Secret) {
44
+		secret.Version = v
45
+	}
46
+}
47
+
48
+// SecretCreatedAt sets the creation time for the secret
49
+func SecretCreatedAt(t time.Time) func(*swarm.Secret) {
50
+	return func(secret *swarm.Secret) {
51
+		secret.CreatedAt = t
52
+	}
53
+}
54
+
55
+// SecretUpdatedAt sets the update time for the secret
56
+func SecretUpdatedAt(t time.Time) func(*swarm.Secret) {
57
+	return func(secret *swarm.Secret) {
58
+		secret.UpdatedAt = t
59
+	}
60
+}