diff --git a/cli/command/container/client_test.go b/cli/command/container/client_test.go index 8b3b0cd97ea9..adc39f9bedb4 100644 --- a/cli/command/container/client_test.go +++ b/cli/command/container/client_test.go @@ -36,6 +36,7 @@ type fakeClient struct { containerExecResizeFunc func(id string, options container.ResizeOptions) error containerRemoveFunc func(ctx context.Context, containerID string, options container.RemoveOptions) error containerRestartFunc func(ctx context.Context, containerID string, options container.StopOptions) error + containerStopFunc func(ctx context.Context, containerID string, options container.StopOptions) error containerKillFunc func(ctx context.Context, containerID, signal string) error containerPruneFunc func(ctx context.Context, pruneFilters filters.Args) (container.PruneReport, error) containerAttachFunc func(ctx context.Context, containerID string, options container.AttachOptions) (types.HijackedResponse, error) @@ -183,6 +184,13 @@ func (f *fakeClient) ContainerRestart(ctx context.Context, containerID string, o return nil } +func (f *fakeClient) ContainerStop(ctx context.Context, containerID string, options container.StopOptions) error { + if f.containerStopFunc != nil { + return f.containerStopFunc(ctx, containerID, options) + } + return nil +} + func (f *fakeClient) ContainerAttach(ctx context.Context, containerID string, options container.AttachOptions) (types.HijackedResponse, error) { if f.containerAttachFunc != nil { return f.containerAttachFunc(ctx, containerID, options) diff --git a/cli/command/container/stop_test.go b/cli/command/container/stop_test.go new file mode 100644 index 000000000000..44893d68abd9 --- /dev/null +++ b/cli/command/container/stop_test.go @@ -0,0 +1,86 @@ +package container + +import ( + "context" + "io" + "sort" + "sync" + "testing" + + "github.com/docker/cli/internal/test" + "github.com/docker/docker/api/types/container" + "github.com/docker/docker/errdefs" + "github.com/pkg/errors" + "gotest.tools/v3/assert" + is "gotest.tools/v3/assert/cmp" +) + +func TestStop(t *testing.T) { + for _, tc := range []struct { + name string + args []string + stopped []string + expectedOpts container.StopOptions + expectedErr string + }{ + { + name: "without options", + args: []string{"container-1", "container-2"}, + stopped: []string{"container-1", "container-2"}, + }, + { + name: "with unknown container", + args: []string{"container-1", "nosuchcontainer", "container-2"}, + expectedErr: "no such container", + stopped: []string{"container-1", "container-2"}, + }, + { + name: "with -t", + args: []string{"-t", "2", "container-1"}, + expectedOpts: container.StopOptions{Timeout: func(to int) *int { return &to }(2)}, + stopped: []string{"container-1"}, + }, + { + name: "with --time", + args: []string{"--time", "2", "container-1"}, + expectedOpts: container.StopOptions{Timeout: func(to int) *int { return &to }(2)}, + stopped: []string{"container-1"}, + }, + } { + tc := tc + t.Run(tc.name, func(t *testing.T) { + var stopped []string + mutex := new(sync.Mutex) + + cli := test.NewFakeCli(&fakeClient{ + containerStopFunc: func(ctx context.Context, containerID string, options container.StopOptions) error { + assert.Check(t, is.DeepEqual(options, tc.expectedOpts)) + if containerID == "nosuchcontainer" { + return errdefs.NotFound(errors.New("Error: no such container: " + containerID)) + } + + // containerStopFunc is called in parallel for each container + // so append must be synchronized. + mutex.Lock() + stopped = append(stopped, containerID) + mutex.Unlock() + return nil + }, + Version: "1.36", + }) + cmd := NewStopCommand(cli) + cmd.SetOut(io.Discard) + cmd.SetErr(io.Discard) + cmd.SetArgs(tc.args) + + err := cmd.Execute() + if tc.expectedErr != "" { + assert.Check(t, is.ErrorContains(err, tc.expectedErr)) + } else { + assert.Check(t, is.Nil(err)) + } + sort.Strings(stopped) + assert.Check(t, is.DeepEqual(stopped, tc.stopped)) + }) + } +}