Skip to content

Commit

Permalink
Merge pull request #5482 from thaJeztah/restart_stop_unit_tests
Browse files Browse the repository at this point in the history
cli/command/container: add unit tests for container restart and container stop
  • Loading branch information
thaJeztah authored Sep 26, 2024
2 parents bae4b67 + ac502b5 commit daabb15
Show file tree
Hide file tree
Showing 4 changed files with 189 additions and 0 deletions.
16 changes: 16 additions & 0 deletions cli/command/container/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ type fakeClient struct {
containerExportFunc func(string) (io.ReadCloser, error)
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)
Expand Down Expand Up @@ -175,6 +177,20 @@ func (f *fakeClient) ContainersPrune(ctx context.Context, pruneFilters filters.A
return container.PruneReport{}, nil
}

func (f *fakeClient) ContainerRestart(ctx context.Context, containerID string, options container.StopOptions) error {
if f.containerRestartFunc != nil {
return f.containerRestartFunc(ctx, containerID, options)
}
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)
Expand Down
2 changes: 2 additions & 0 deletions cli/command/container/restart.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ func runRestart(ctx context.Context, dockerCli command.Cli, opts *restartOptions
if opts.timeoutChanged {
timeout = &opts.timeout
}

// TODO(thaJeztah): consider using parallelOperation for restart, similar to "stop" and "remove"
for _, name := range opts.containers {
err := dockerCli.Client().ContainerRestart(ctx, name, container.StopOptions{
Signal: opts.signal,
Expand Down
85 changes: 85 additions & 0 deletions cli/command/container/restart_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
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 TestRestart(t *testing.T) {
for _, tc := range []struct {
name string
args []string
restarted []string
expectedOpts container.StopOptions
expectedErr string
}{
{
name: "without options",
args: []string{"container-1", "container-2"},
restarted: []string{"container-1", "container-2"},
},
{
name: "with unknown container",
args: []string{"container-1", "nosuchcontainer", "container-2"},
expectedErr: "no such container",
restarted: []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)},
restarted: []string{"container-1"},
},
{
name: "with --time",
args: []string{"--time", "2", "container-1"},
expectedOpts: container.StopOptions{Timeout: func(to int) *int { return &to }(2)},
restarted: []string{"container-1"},
},
} {
tc := tc
t.Run(tc.name, func(t *testing.T) {
var restarted []string
mutex := new(sync.Mutex)

cli := test.NewFakeCli(&fakeClient{
containerRestartFunc: 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))
}

// TODO(thaJeztah): consider using parallelOperation for restart, similar to "stop" and "remove"
mutex.Lock()
restarted = append(restarted, containerID)
mutex.Unlock()
return nil
},
Version: "1.36",
})
cmd := NewRestartCommand(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(restarted)
assert.Check(t, is.DeepEqual(restarted, tc.restarted))
})
}
}
86 changes: 86 additions & 0 deletions cli/command/container/stop_test.go
Original file line number Diff line number Diff line change
@@ -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))
})
}
}

0 comments on commit daabb15

Please sign in to comment.