Skip to content

Commit 31eeed7

Browse files
authored
Merge pull request docker#5533 from thaJeztah/completions_coverage
cli/command/completion: add more unit-tests
2 parents 089448b + e1c472a commit 31eeed7

File tree

2 files changed

+336
-1
lines changed

2 files changed

+336
-1
lines changed

cli/command/completion/functions.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ func ContainerNames(dockerCLI APIClientProvider, all bool, filters ...func(conta
5959
for _, ctr := range list {
6060
skip := false
6161
for _, fn := range filters {
62-
if !fn(ctr) {
62+
if fn != nil && !fn(ctr) {
6363
skip = true
6464
break
6565
}
+335
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,350 @@
11
package completion
22

33
import (
4+
"context"
5+
"errors"
6+
"sort"
47
"testing"
58

9+
"github.com/docker/docker/api/types/container"
10+
"github.com/docker/docker/api/types/filters"
11+
"github.com/docker/docker/api/types/image"
12+
"github.com/docker/docker/api/types/network"
13+
"github.com/docker/docker/api/types/volume"
14+
"github.com/docker/docker/client"
15+
"github.com/google/go-cmp/cmp/cmpopts"
616
"github.com/spf13/cobra"
717
"gotest.tools/v3/assert"
818
is "gotest.tools/v3/assert/cmp"
19+
"gotest.tools/v3/env"
920
)
1021

22+
type fakeCLI struct {
23+
*fakeClient
24+
}
25+
26+
// Client implements [APIClientProvider].
27+
func (c fakeCLI) Client() client.APIClient {
28+
return c.fakeClient
29+
}
30+
31+
type fakeClient struct {
32+
client.Client
33+
containerListFunc func(options container.ListOptions) ([]container.Summary, error)
34+
imageListFunc func(options image.ListOptions) ([]image.Summary, error)
35+
networkListFunc func(ctx context.Context, options network.ListOptions) ([]network.Summary, error)
36+
volumeListFunc func(filter filters.Args) (volume.ListResponse, error)
37+
}
38+
39+
func (c *fakeClient) ContainerList(_ context.Context, options container.ListOptions) ([]container.Summary, error) {
40+
if c.containerListFunc != nil {
41+
return c.containerListFunc(options)
42+
}
43+
return []container.Summary{}, nil
44+
}
45+
46+
func (c *fakeClient) ImageList(_ context.Context, options image.ListOptions) ([]image.Summary, error) {
47+
if c.imageListFunc != nil {
48+
return c.imageListFunc(options)
49+
}
50+
return []image.Summary{}, nil
51+
}
52+
53+
func (c *fakeClient) NetworkList(ctx context.Context, options network.ListOptions) ([]network.Summary, error) {
54+
if c.networkListFunc != nil {
55+
return c.networkListFunc(ctx, options)
56+
}
57+
return []network.Inspect{}, nil
58+
}
59+
60+
func (c *fakeClient) VolumeList(_ context.Context, options volume.ListOptions) (volume.ListResponse, error) {
61+
if c.volumeListFunc != nil {
62+
return c.volumeListFunc(options.Filters)
63+
}
64+
return volume.ListResponse{}, nil
65+
}
66+
67+
func TestCompleteContainerNames(t *testing.T) {
68+
tests := []struct {
69+
doc string
70+
showAll, showIDs bool
71+
filters []func(container.Summary) bool
72+
containers []container.Summary
73+
expOut []string
74+
expOpts container.ListOptions
75+
expDirective cobra.ShellCompDirective
76+
}{
77+
{
78+
doc: "no results",
79+
expDirective: cobra.ShellCompDirectiveNoFileComp,
80+
},
81+
{
82+
doc: "all containers",
83+
showAll: true,
84+
containers: []container.Summary{
85+
{ID: "id-c", State: "running", Names: []string{"/container-c", "/container-c/link-b"}},
86+
{ID: "id-b", State: "created", Names: []string{"/container-b"}},
87+
{ID: "id-a", State: "exited", Names: []string{"/container-a"}},
88+
},
89+
expOut: []string{"container-c", "container-c/link-b", "container-b", "container-a"},
90+
expOpts: container.ListOptions{All: true},
91+
expDirective: cobra.ShellCompDirectiveNoFileComp,
92+
},
93+
{
94+
doc: "all containers with ids",
95+
showAll: true,
96+
showIDs: true,
97+
containers: []container.Summary{
98+
{ID: "id-c", State: "running", Names: []string{"/container-c", "/container-c/link-b"}},
99+
{ID: "id-b", State: "created", Names: []string{"/container-b"}},
100+
{ID: "id-a", State: "exited", Names: []string{"/container-a"}},
101+
},
102+
expOut: []string{"id-c", "container-c", "container-c/link-b", "id-b", "container-b", "id-a", "container-a"},
103+
expOpts: container.ListOptions{All: true},
104+
expDirective: cobra.ShellCompDirectiveNoFileComp,
105+
},
106+
{
107+
doc: "only running containers",
108+
showAll: false,
109+
containers: []container.Summary{
110+
{ID: "id-c", State: "running", Names: []string{"/container-c", "/container-c/link-b"}},
111+
},
112+
expOut: []string{"container-c", "container-c/link-b"},
113+
expDirective: cobra.ShellCompDirectiveNoFileComp,
114+
},
115+
{
116+
doc: "with filter",
117+
showAll: true,
118+
filters: []func(container.Summary) bool{
119+
func(container container.Summary) bool { return container.State == "created" },
120+
},
121+
containers: []container.Summary{
122+
{ID: "id-c", State: "running", Names: []string{"/container-c", "/container-c/link-b"}},
123+
{ID: "id-b", State: "created", Names: []string{"/container-b"}},
124+
{ID: "id-a", State: "exited", Names: []string{"/container-a"}},
125+
},
126+
expOut: []string{"container-b"},
127+
expOpts: container.ListOptions{All: true},
128+
expDirective: cobra.ShellCompDirectiveNoFileComp,
129+
},
130+
{
131+
doc: "multiple filters",
132+
showAll: true,
133+
filters: []func(container.Summary) bool{
134+
func(container container.Summary) bool { return container.ID == "id-a" },
135+
func(container container.Summary) bool { return container.State == "created" },
136+
},
137+
containers: []container.Summary{
138+
{ID: "id-c", State: "running", Names: []string{"/container-c", "/container-c/link-b"}},
139+
{ID: "id-b", State: "created", Names: []string{"/container-b"}},
140+
{ID: "id-a", State: "created", Names: []string{"/container-a"}},
141+
},
142+
expOut: []string{"container-a"},
143+
expOpts: container.ListOptions{All: true},
144+
expDirective: cobra.ShellCompDirectiveNoFileComp,
145+
},
146+
{
147+
doc: "with error",
148+
expDirective: cobra.ShellCompDirectiveError,
149+
},
150+
}
151+
152+
for _, tc := range tests {
153+
tc := tc
154+
t.Run(tc.doc, func(t *testing.T) {
155+
if tc.showIDs {
156+
t.Setenv("DOCKER_COMPLETION_SHOW_CONTAINER_IDS", "yes")
157+
}
158+
comp := ContainerNames(fakeCLI{&fakeClient{
159+
containerListFunc: func(opts container.ListOptions) ([]container.Summary, error) {
160+
assert.Check(t, is.DeepEqual(opts, tc.expOpts, cmpopts.IgnoreUnexported(container.ListOptions{}, filters.Args{})))
161+
if tc.expDirective == cobra.ShellCompDirectiveError {
162+
return nil, errors.New("some error occurred")
163+
}
164+
return tc.containers, nil
165+
},
166+
}}, tc.showAll, tc.filters...)
167+
168+
containers, directives := comp(&cobra.Command{}, nil, "")
169+
assert.Check(t, is.Equal(directives&tc.expDirective, tc.expDirective))
170+
assert.Check(t, is.DeepEqual(containers, tc.expOut))
171+
})
172+
}
173+
}
174+
175+
func TestCompleteEnvVarNames(t *testing.T) {
176+
env.PatchAll(t, map[string]string{
177+
"ENV_A": "hello-a",
178+
"ENV_B": "hello-b",
179+
})
180+
values, directives := EnvVarNames(nil, nil, "")
181+
assert.Check(t, is.Equal(directives&cobra.ShellCompDirectiveNoFileComp, cobra.ShellCompDirectiveNoFileComp), "Should not perform file completion")
182+
183+
sort.Strings(values)
184+
expected := []string{"ENV_A", "ENV_B"}
185+
assert.Check(t, is.DeepEqual(values, expected))
186+
}
187+
188+
func TestCompleteFileNames(t *testing.T) {
189+
values, directives := FileNames(nil, nil, "")
190+
assert.Check(t, is.Equal(directives, cobra.ShellCompDirectiveDefault))
191+
assert.Check(t, is.Len(values, 0))
192+
}
193+
194+
func TestCompleteFromList(t *testing.T) {
195+
expected := []string{"one", "two", "three"}
196+
197+
values, directives := FromList(expected...)(nil, nil, "")
198+
assert.Check(t, is.Equal(directives&cobra.ShellCompDirectiveNoFileComp, cobra.ShellCompDirectiveNoFileComp), "Should not perform file completion")
199+
assert.Check(t, is.DeepEqual(values, expected))
200+
}
201+
202+
func TestCompleteImageNames(t *testing.T) {
203+
tests := []struct {
204+
doc string
205+
images []image.Summary
206+
expOut []string
207+
expDirective cobra.ShellCompDirective
208+
}{
209+
{
210+
doc: "no results",
211+
expDirective: cobra.ShellCompDirectiveNoFileComp,
212+
},
213+
{
214+
doc: "with results",
215+
images: []image.Summary{
216+
{RepoTags: []string{"image-c:latest", "image-c:other"}},
217+
{RepoTags: []string{"image-b:latest", "image-b:other"}},
218+
{RepoTags: []string{"image-a:latest", "image-a:other"}},
219+
},
220+
expOut: []string{"image-c:latest", "image-c:other", "image-b:latest", "image-b:other", "image-a:latest", "image-a:other"},
221+
expDirective: cobra.ShellCompDirectiveNoFileComp,
222+
},
223+
{
224+
doc: "with error",
225+
expDirective: cobra.ShellCompDirectiveError,
226+
},
227+
}
228+
229+
for _, tc := range tests {
230+
tc := tc
231+
t.Run(tc.doc, func(t *testing.T) {
232+
comp := ImageNames(fakeCLI{&fakeClient{
233+
imageListFunc: func(options image.ListOptions) ([]image.Summary, error) {
234+
if tc.expDirective == cobra.ShellCompDirectiveError {
235+
return nil, errors.New("some error occurred")
236+
}
237+
return tc.images, nil
238+
},
239+
}})
240+
241+
volumes, directives := comp(&cobra.Command{}, nil, "")
242+
assert.Check(t, is.Equal(directives&tc.expDirective, tc.expDirective))
243+
assert.Check(t, is.DeepEqual(volumes, tc.expOut))
244+
})
245+
}
246+
}
247+
248+
func TestCompleteNetworkNames(t *testing.T) {
249+
tests := []struct {
250+
doc string
251+
networks []network.Summary
252+
expOut []string
253+
expDirective cobra.ShellCompDirective
254+
}{
255+
{
256+
doc: "no results",
257+
expDirective: cobra.ShellCompDirectiveNoFileComp,
258+
},
259+
{
260+
doc: "with results",
261+
networks: []network.Summary{
262+
{ID: "nw-c", Name: "network-c"},
263+
{ID: "nw-b", Name: "network-b"},
264+
{ID: "nw-a", Name: "network-a"},
265+
},
266+
expOut: []string{"network-c", "network-b", "network-a"},
267+
expDirective: cobra.ShellCompDirectiveNoFileComp,
268+
},
269+
{
270+
doc: "with error",
271+
expDirective: cobra.ShellCompDirectiveError,
272+
},
273+
}
274+
275+
for _, tc := range tests {
276+
tc := tc
277+
t.Run(tc.doc, func(t *testing.T) {
278+
comp := NetworkNames(fakeCLI{&fakeClient{
279+
networkListFunc: func(ctx context.Context, options network.ListOptions) ([]network.Summary, error) {
280+
if tc.expDirective == cobra.ShellCompDirectiveError {
281+
return nil, errors.New("some error occurred")
282+
}
283+
return tc.networks, nil
284+
},
285+
}})
286+
287+
volumes, directives := comp(&cobra.Command{}, nil, "")
288+
assert.Check(t, is.Equal(directives&tc.expDirective, tc.expDirective))
289+
assert.Check(t, is.DeepEqual(volumes, tc.expOut))
290+
})
291+
}
292+
}
293+
294+
func TestCompleteNoComplete(t *testing.T) {
295+
values, directives := NoComplete(nil, nil, "")
296+
assert.Check(t, is.Equal(directives, cobra.ShellCompDirectiveNoFileComp))
297+
assert.Check(t, is.Len(values, 0))
298+
}
299+
11300
func TestCompletePlatforms(t *testing.T) {
12301
values, directives := Platforms(nil, nil, "")
13302
assert.Check(t, is.Equal(directives&cobra.ShellCompDirectiveNoFileComp, cobra.ShellCompDirectiveNoFileComp), "Should not perform file completion")
14303
assert.Check(t, is.DeepEqual(values, commonPlatforms))
15304
}
305+
306+
func TestCompleteVolumeNames(t *testing.T) {
307+
tests := []struct {
308+
doc string
309+
volumes []*volume.Volume
310+
expOut []string
311+
expDirective cobra.ShellCompDirective
312+
}{
313+
{
314+
doc: "no results",
315+
expDirective: cobra.ShellCompDirectiveNoFileComp,
316+
},
317+
{
318+
doc: "with results",
319+
volumes: []*volume.Volume{
320+
{Name: "volume-c"},
321+
{Name: "volume-b"},
322+
{Name: "volume-a"},
323+
},
324+
expOut: []string{"volume-c", "volume-b", "volume-a"},
325+
expDirective: cobra.ShellCompDirectiveNoFileComp,
326+
},
327+
{
328+
doc: "with error",
329+
expDirective: cobra.ShellCompDirectiveError,
330+
},
331+
}
332+
333+
for _, tc := range tests {
334+
tc := tc
335+
t.Run(tc.doc, func(t *testing.T) {
336+
comp := VolumeNames(fakeCLI{&fakeClient{
337+
volumeListFunc: func(filter filters.Args) (volume.ListResponse, error) {
338+
if tc.expDirective == cobra.ShellCompDirectiveError {
339+
return volume.ListResponse{}, errors.New("some error occurred")
340+
}
341+
return volume.ListResponse{Volumes: tc.volumes}, nil
342+
},
343+
}})
344+
345+
volumes, directives := comp(&cobra.Command{}, nil, "")
346+
assert.Check(t, is.Equal(directives&tc.expDirective, tc.expDirective))
347+
assert.Check(t, is.DeepEqual(volumes, tc.expOut))
348+
})
349+
}
350+
}

0 commit comments

Comments
 (0)