From b0581cfb469eaab0f732b7c47742db2cf515ba2a Mon Sep 17 00:00:00 2001 From: Harald Albers Date: Sat, 19 Oct 2024 16:00:41 +0000 Subject: [PATCH] Add tests for completions that call the API Signed-off-by: Harald Albers --- cli/command/system/client_test.go | 68 +++++++++-- cli/command/system/completion_test.go | 165 ++++++++++++++++++++++++++ 2 files changed, 225 insertions(+), 8 deletions(-) create mode 100644 cli/command/system/completion_test.go diff --git a/cli/command/system/client_test.go b/cli/command/system/client_test.go index b6eeb3bd9294..10334f808939 100644 --- a/cli/command/system/client_test.go +++ b/cli/command/system/client_test.go @@ -7,7 +7,11 @@ import ( "github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/events" "github.com/docker/docker/api/types/filters" + "github.com/docker/docker/api/types/image" "github.com/docker/docker/api/types/network" + "github.com/docker/docker/api/types/swarm" + "github.com/docker/docker/api/types/system" + "github.com/docker/docker/api/types/volume" "github.com/docker/docker/client" ) @@ -15,22 +19,27 @@ type fakeClient struct { client.Client version string - serverVersion func(ctx context.Context) (types.Version, error) - eventsFn func(context.Context, events.ListOptions) (<-chan events.Message, <-chan error) + containerListFunc func(context.Context, container.ListOptions) ([]container.Summary, error) containerPruneFunc func(ctx context.Context, pruneFilters filters.Args) (container.PruneReport, error) + eventsFn func(context.Context, events.ListOptions) (<-chan events.Message, <-chan error) + imageListFunc func(ctx context.Context, options image.ListOptions) ([]image.Summary, error) + infoFunc func(ctx context.Context) (system.Info, error) + networkListFunc func(ctx context.Context, options network.ListOptions) ([]network.Summary, error) networkPruneFunc func(ctx context.Context, pruneFilter filters.Args) (network.PruneReport, error) -} - -func (cli *fakeClient) ServerVersion(ctx context.Context) (types.Version, error) { - return cli.serverVersion(ctx) + nodeListFunc func(ctx context.Context, options types.NodeListOptions) ([]swarm.Node, error) + serverVersion func(ctx context.Context) (types.Version, error) + volumeListFunc func(ctx context.Context, options volume.ListOptions) (volume.ListResponse, error) } func (cli *fakeClient) ClientVersion() string { return cli.version } -func (cli *fakeClient) Events(ctx context.Context, opts events.ListOptions) (<-chan events.Message, <-chan error) { - return cli.eventsFn(ctx, opts) +func (cli *fakeClient) ContainerList(ctx context.Context, options container.ListOptions) ([]container.Summary, error) { + if cli.containerListFunc != nil { + return cli.containerListFunc(ctx, options) + } + return []container.Summary{}, nil } func (cli *fakeClient) ContainersPrune(ctx context.Context, pruneFilters filters.Args) (container.PruneReport, error) { @@ -40,9 +49,52 @@ func (cli *fakeClient) ContainersPrune(ctx context.Context, pruneFilters filters return container.PruneReport{}, nil } +func (cli *fakeClient) Events(ctx context.Context, opts events.ListOptions) (<-chan events.Message, <-chan error) { + return cli.eventsFn(ctx, opts) +} + +func (cli *fakeClient) ImageList(ctx context.Context, options image.ListOptions) ([]image.Summary, error) { + if cli.imageListFunc != nil { + return cli.imageListFunc(ctx, options) + } + return []image.Summary{}, nil +} + +func (cli *fakeClient) Info(ctx context.Context) (system.Info, error) { + if cli.infoFunc != nil { + return cli.infoFunc(ctx) + } + return system.Info{}, nil +} + +func (cli *fakeClient) NetworkList(ctx context.Context, options network.ListOptions) ([]network.Summary, error) { + if cli.networkListFunc != nil { + return cli.networkListFunc(ctx, options) + } + return []network.Summary{}, nil +} + func (cli *fakeClient) NetworksPrune(ctx context.Context, pruneFilter filters.Args) (network.PruneReport, error) { if cli.networkPruneFunc != nil { return cli.networkPruneFunc(ctx, pruneFilter) } return network.PruneReport{}, nil } + +func (cli *fakeClient) NodeList(ctx context.Context, options types.NodeListOptions) ([]swarm.Node, error) { + if cli.nodeListFunc != nil { + return cli.nodeListFunc(ctx, options) + } + return []swarm.Node{}, nil +} + +func (cli *fakeClient) ServerVersion(ctx context.Context) (types.Version, error) { + return cli.serverVersion(ctx) +} + +func (cli *fakeClient) VolumeList(ctx context.Context, options volume.ListOptions) (volume.ListResponse, error) { + if cli.volumeListFunc != nil { + return cli.volumeListFunc(ctx, options) + } + return volume.ListResponse{}, nil +} diff --git a/cli/command/system/completion_test.go b/cli/command/system/completion_test.go new file mode 100644 index 000000000000..0e4aa8d5ce8e --- /dev/null +++ b/cli/command/system/completion_test.go @@ -0,0 +1,165 @@ +package system + +import ( + "context" + "errors" + "fmt" + "testing" + + "github.com/docker/cli/internal/test" + "github.com/docker/cli/internal/test/builders" + "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/container" + "github.com/docker/docker/api/types/image" + "github.com/docker/docker/api/types/network" + "github.com/docker/docker/api/types/swarm" + "github.com/docker/docker/api/types/system" + "github.com/docker/docker/api/types/volume" + "github.com/spf13/cobra" + "gotest.tools/v3/assert" +) + +func TestCompleteEventFilter(t *testing.T) { + tests := []struct { + client *fakeClient + toComplete string + expected []string + }{ + { + client: &fakeClient{ + containerListFunc: func(_ context.Context, _ container.ListOptions) ([]container.Summary, error) { + return []container.Summary{ + *builders.Container("c1"), + *builders.Container("c2"), + }, nil + }, + }, + toComplete: "container=", + expected: []string{"container=c1", "container=c2"}, + }, + { + client: &fakeClient{ + containerListFunc: func(_ context.Context, _ container.ListOptions) ([]container.Summary, error) { + return nil, errors.New("API error") + }, + }, + toComplete: "container=", + expected: []string{}, + }, + { + client: &fakeClient{ + infoFunc: func(ctx context.Context) (system.Info, error) { + return system.Info{ + ID: "daemon-id", + Name: "daemon-name", + }, nil + }, + }, + toComplete: "daemon=", + expected: []string{"daemon=daemon-name", "daemon=daemon-id"}, + }, + { + client: &fakeClient{ + infoFunc: func(ctx context.Context) (system.Info, error) { + return system.Info{}, errors.New("API error") + }, + }, + toComplete: "daemon=", + expected: []string{}, + }, + { + client: &fakeClient{ + imageListFunc: func(_ context.Context, _ image.ListOptions) ([]image.Summary, error) { + return []image.Summary{ + {RepoTags: []string{"img:1"}}, + {RepoTags: []string{"img:2"}}, + }, nil + }, + }, + toComplete: "image=", + expected: []string{"image=img:1", "image=img:2"}, + }, + { + client: &fakeClient{ + imageListFunc: func(_ context.Context, _ image.ListOptions) ([]image.Summary, error) { + return []image.Summary{}, errors.New("API error") + }, + }, + toComplete: "image=", + expected: []string{}, + }, + { + client: &fakeClient{ + networkListFunc: func(_ context.Context, _ network.ListOptions) ([]network.Summary, error) { + return []network.Summary{ + *builders.NetworkResource(builders.NetworkResourceName("nw1")), + *builders.NetworkResource(builders.NetworkResourceName("nw2")), + }, nil + }, + }, + toComplete: "network=", + expected: []string{"network=nw1", "network=nw2"}, + }, + { + client: &fakeClient{ + networkListFunc: func(_ context.Context, _ network.ListOptions) ([]network.Summary, error) { + return nil, errors.New("API error") + }, + }, + toComplete: "network=", + expected: []string{}, + }, + { + client: &fakeClient{ + nodeListFunc: func(_ context.Context, _ types.NodeListOptions) ([]swarm.Node, error) { + return []swarm.Node{ + *builders.Node(builders.Hostname("n1")), + }, nil + }, + }, + toComplete: "node=", + expected: []string{"node=n1"}, + }, + { + client: &fakeClient{ + nodeListFunc: func(_ context.Context, _ types.NodeListOptions) ([]swarm.Node, error) { + return []swarm.Node{}, errors.New("API error") + }, + }, + toComplete: "node=", + expected: []string{}, + }, + { + client: &fakeClient{ + volumeListFunc: func(ctx context.Context, options volume.ListOptions) (volume.ListResponse, error) { + return volume.ListResponse{ + Volumes: []*volume.Volume{ + builders.Volume(builders.VolumeName("v1")), + builders.Volume(builders.VolumeName("v2")), + }, + }, nil + }, + }, + toComplete: "volume=", + expected: []string{"volume=v1", "volume=v2"}, + }, + { + client: &fakeClient{ + volumeListFunc: func(ctx context.Context, options volume.ListOptions) (volume.ListResponse, error) { + return volume.ListResponse{}, errors.New("API error") + }, + }, + toComplete: "volume=", + expected: []string{}, + }, + } + + for _, tc := range tests { + cli := test.NewFakeCli(tc.client) + + completions, directive := completeFilters(cli)(NewEventsCommand(cli), nil, tc.toComplete) + + assert.DeepEqual(t, completions, tc.expected) + assert.Equal(t, directive, cobra.ShellCompDirectiveNoFileComp, fmt.Sprintf("wrong directive in completion for '%s'", tc.toComplete)) + } +}