From 4afbd6146b766083ac5ff3bf40eeb1e002b97cda Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Sat, 25 Oct 2025 01:36:38 +0200 Subject: [PATCH] implement some ad-hoc mocks for responses Signed-off-by: Sebastiaan van Stijn --- cli/command/image/client_test.go | 13 +++++++++-- cli/command/image/load_test.go | 28 +++++++++++++++++------- cli/command/image/pull_test.go | 7 +++--- cli/command/image/push_test.go | 4 ++-- cli/command/plugin/client_test.go | 4 ++-- cli/command/trust/inspect_pretty_test.go | 12 ++++++++-- cli/command/trust/sign_test.go | 1 - 7 files changed, 48 insertions(+), 21 deletions(-) diff --git a/cli/command/image/client_test.go b/cli/command/image/client_test.go index 1ca583c9d..2146d1ae4 100644 --- a/cli/command/image/client_test.go +++ b/cli/command/image/client_test.go @@ -3,6 +3,7 @@ package image import ( "context" "io" + "net/http" "strings" "time" @@ -28,6 +29,14 @@ type fakeClient struct { imageBuildFunc func(context.Context, io.Reader, client.ImageBuildOptions) (client.ImageBuildResult, error) } +type fakeStreamResult struct { + io.ReadCloser + client.ImagePushResponse // same interface as [client.ImagePullResponse] +} + +func (e fakeStreamResult) Read(p []byte) (int, error) { return e.ReadCloser.Read(p) } +func (e fakeStreamResult) Close() error { return e.ReadCloser.Close() } + func (cli *fakeClient) ImageTag(_ context.Context, options client.ImageTagOptions) (client.ImageTagResult, error) { if cli.imageTagFunc != nil { return cli.imageTagFunc(options) @@ -54,7 +63,7 @@ func (cli *fakeClient) ImagePush(_ context.Context, ref string, options client.I return cli.imagePushFunc(ref, options) } // FIXME(thaJeztah): how to mock this? - return nil, nil + return fakeStreamResult{ReadCloser: http.NoBody}, nil } func (cli *fakeClient) Info(_ context.Context) (system.Info, error) { @@ -69,7 +78,7 @@ func (cli *fakeClient) ImagePull(_ context.Context, ref string, options client.I return cli.imagePullFunc(ref, options) } // FIXME(thaJeztah): how to mock this? - return nil, nil + return fakeStreamResult{ReadCloser: http.NoBody}, nil } func (cli *fakeClient) ImagesPrune(_ context.Context, opts client.ImagePruneOptions) (client.ImagePruneResult, error) { diff --git a/cli/command/image/load_test.go b/cli/command/image/load_test.go index 0140012cf..91f4273cd 100644 --- a/cli/command/image/load_test.go +++ b/cli/command/image/load_test.go @@ -4,7 +4,10 @@ import ( "errors" "fmt" "io" + "reflect" + "strings" "testing" + "unsafe" "github.com/docker/cli/internal/test" "github.com/moby/moby/client" @@ -71,8 +74,18 @@ func TestNewLoadCommandInvalidInput(t *testing.T) { assert.ErrorContains(t, err, expectedError) } +func mockImageLoadResult(content string, json bool) client.ImageLoadResult { + out := client.ImageLoadResult{JSON: json} + + // Set unexported field "body" + v := reflect.ValueOf(&out).Elem() + f := v.FieldByName("body") + r := io.NopCloser(strings.NewReader(content)) + reflect.NewAt(f.Type(), unsafe.Pointer(f.UnsafeAddr())).Elem().Set(reflect.ValueOf(r)) + return out +} + func TestNewLoadCommandSuccess(t *testing.T) { - t.Skip("FIXME(thaJeztah): how to mock this?") testCases := []struct { name string args []string @@ -84,7 +97,7 @@ func TestNewLoadCommandSuccess(t *testing.T) { imageLoadFunc: func(input io.Reader, options ...client.ImageLoadOption) (client.ImageLoadResult, error) { // FIXME(thaJeztah): how to mock this? // return client.ImageLoadResult{Body: io.NopCloser(strings.NewReader("Success"))}, nil - return client.ImageLoadResult{}, nil + return mockImageLoadResult(`Success`, false), nil }, }, { @@ -92,12 +105,11 @@ func TestNewLoadCommandSuccess(t *testing.T) { args: []string{}, imageLoadFunc: func(input io.Reader, options ...client.ImageLoadOption) (client.ImageLoadResult, error) { // FIXME(thaJeztah): how to mock this? - // return client.ImageLoadResult{Body: io.NopCloser(strings.NewReader("Success"))}, nil // return client.ImageLoadResult{ // Body: io.NopCloser(strings.NewReader(`{"ID": "1"}`)), // JSON: true, // }, nil - return client.ImageLoadResult{JSON: true}, nil + return mockImageLoadResult(`{"ID":"1"}`, true), nil }, }, { @@ -106,7 +118,7 @@ func TestNewLoadCommandSuccess(t *testing.T) { imageLoadFunc: func(input io.Reader, options ...client.ImageLoadOption) (client.ImageLoadResult, error) { // FIXME(thaJeztah): how to mock this? // return client.ImageLoadResult{Body: io.NopCloser(strings.NewReader("Success"))}, nil - return client.ImageLoadResult{}, nil + return mockImageLoadResult(`Success`, false), nil }, }, { @@ -118,7 +130,7 @@ func TestNewLoadCommandSuccess(t *testing.T) { // assert.Check(t, is.Contains(options, client.ImageHistoryWithPlatform(ocispec.Platform{OS: "linux", Architecture: "amd64"}))) // FIXME(thaJeztah): how to mock this? // return client.ImageLoadResult{Body: io.NopCloser(strings.NewReader("Success"))}, nil - return client.ImageLoadResult{}, nil + return mockImageLoadResult(`Success`, false), nil }, }, { @@ -128,7 +140,7 @@ func TestNewLoadCommandSuccess(t *testing.T) { assert.Check(t, len(options) > 0) // can be 1 or two depending on whether a terminal is attached :/ // FIXME(thaJeztah): how to mock this? // return client.ImageLoadResult{Body: io.NopCloser(strings.NewReader("Success"))}, nil - return client.ImageLoadResult{}, nil + return mockImageLoadResult(`Success`, false), nil }, }, { @@ -138,7 +150,7 @@ func TestNewLoadCommandSuccess(t *testing.T) { assert.Check(t, len(options) > 0) // can be 1 or two depending on whether a terminal is attached :/ // FIXME(thaJeztah): how to mock this? // return client.ImageLoadResult{Body: io.NopCloser(strings.NewReader("Success"))}, nil - return client.ImageLoadResult{}, nil + return mockImageLoadResult(`Success`, false), nil }, }, } diff --git a/cli/command/image/pull_test.go b/cli/command/image/pull_test.go index 9949d9ab5..73e2fc7d4 100644 --- a/cli/command/image/pull_test.go +++ b/cli/command/image/pull_test.go @@ -1,9 +1,9 @@ package image import ( - "errors" "fmt" "io" + "net/http" "testing" "github.com/docker/cli/internal/test" @@ -49,7 +49,6 @@ func TestNewPullCommandErrors(t *testing.T) { } func TestNewPullCommandSuccess(t *testing.T) { - t.Skip("FIXME(thaJeztah): how to mock this?") testCases := []struct { name string args []string @@ -77,7 +76,7 @@ func TestNewPullCommandSuccess(t *testing.T) { imagePullFunc: func(ref string, options client.ImagePullOptions) (client.ImagePullResponse, error) { assert.Check(t, is.Equal(tc.expectedTag, ref), tc.name) // FIXME(thaJeztah): how to mock this? - return nil, nil + return fakeStreamResult{ReadCloser: http.NoBody}, nil }, }) cmd := newPullCommand(cli) @@ -123,7 +122,7 @@ func TestNewPullCommandWithContentTrustErrors(t *testing.T) { cli := test.NewFakeCli(&fakeClient{ imagePullFunc: func(ref string, options client.ImagePullOptions) (client.ImagePullResponse, error) { // FIXME(thaJeztah): how to mock this? - return nil, errors.New("shouldn't try to pull image") + return fakeStreamResult{ReadCloser: http.NoBody}, nil }, }) cli.SetNotaryClient(tc.notaryFunc) diff --git a/cli/command/image/push_test.go b/cli/command/image/push_test.go index bebc5c07d..dd38cdc84 100644 --- a/cli/command/image/push_test.go +++ b/cli/command/image/push_test.go @@ -3,6 +3,7 @@ package image import ( "errors" "io" + "net/http" "testing" "github.com/docker/cli/internal/test" @@ -49,7 +50,6 @@ func TestNewPushCommandErrors(t *testing.T) { } func TestNewPushCommandSuccess(t *testing.T) { - t.Skip("FIXME(thaJeztah): how to mock this?") testCases := []struct { name string args []string @@ -72,7 +72,7 @@ func TestNewPushCommandSuccess(t *testing.T) { cli := test.NewFakeCli(&fakeClient{ imagePushFunc: func(ref string, options client.ImagePushOptions) (client.ImagePushResponse, error) { // FIXME(thaJeztah): how to mock this? - return nil, nil + return fakeStreamResult{ReadCloser: http.NoBody}, nil }, }) cmd := newPushCommand(cli) diff --git a/cli/command/plugin/client_test.go b/cli/command/plugin/client_test.go index 455af0b43..453740e03 100644 --- a/cli/command/plugin/client_test.go +++ b/cli/command/plugin/client_test.go @@ -3,7 +3,7 @@ package plugin import ( "context" "io" - "strings" + "net/http" "github.com/moby/moby/api/types/system" "github.com/moby/moby/client" @@ -79,5 +79,5 @@ func (c *fakeClient) PluginUpgrade(_ context.Context, name string, options clien return c.pluginUpgradeFunc(name, options) } // FIXME(thaJeztah): how to mock this? - return io.NopCloser(strings.NewReader("")), nil + return http.NoBody, nil } diff --git a/cli/command/trust/inspect_pretty_test.go b/cli/command/trust/inspect_pretty_test.go index be69436d6..8cb79cff9 100644 --- a/cli/command/trust/inspect_pretty_test.go +++ b/cli/command/trust/inspect_pretty_test.go @@ -4,8 +4,8 @@ import ( "bytes" "context" "encoding/hex" - "errors" "io" + "net/http" "testing" "github.com/docker/cli/cli/trust" @@ -27,6 +27,14 @@ type fakeClient struct { client.Client } +type fakeStreamResult struct { + io.ReadCloser + client.ImagePushResponse // same interface as [client.ImagePullResponse] +} + +func (e fakeStreamResult) Read(p []byte) (int, error) { return e.ReadCloser.Read(p) } +func (e fakeStreamResult) Close() error { return e.ReadCloser.Close() } + func (*fakeClient) Info(context.Context) (system.Info, error) { return system.Info{}, nil } @@ -37,7 +45,7 @@ func (*fakeClient) ImageInspect(context.Context, string, ...client.ImageInspectO func (*fakeClient) ImagePush(context.Context, string, client.ImagePushOptions) (client.ImagePushResponse, error) { // FIXME(thaJeztah): how to mock this? - return nil, errors.New("don't handle response") + return fakeStreamResult{ReadCloser: http.NoBody}, nil } func TestTrustInspectPrettyCommandErrors(t *testing.T) { diff --git a/cli/command/trust/sign_test.go b/cli/command/trust/sign_test.go index edc359955..635dc30f2 100644 --- a/cli/command/trust/sign_test.go +++ b/cli/command/trust/sign_test.go @@ -272,7 +272,6 @@ func TestSignCommandChangeListIsCleanedOnError(t *testing.T) { } func TestSignCommandLocalFlag(t *testing.T) { - t.Skip("FIXME(thaJeztah): how to mock this?") cli := test.NewFakeCli(&fakeClient{}) cli.SetNotaryClient(notaryfake.GetEmptyTargetsNotaryRepository) cmd := newSignCommand(cli)