diff --git a/components/cli/appveyor.yml b/components/cli/appveyor.yml new file mode 100644 index 0000000000..262021d8ab --- /dev/null +++ b/components/cli/appveyor.yml @@ -0,0 +1,23 @@ +version: "{build}" + +clone_folder: c:\gopath\src\github.com\docker\cli + +environment: + GOPATH: c:\gopath + GOVERSION: 1.10 + DEPVERSION: v0.4.1 + +install: + - rmdir c:\go /s /q + - appveyor DownloadFile https://storage.googleapis.com/golang/go%GOVERSION%.windows-amd64.msi + - msiexec /i go%GOVERSION%.windows-amd64.msi /q + - go version + - go env + +deploy: false + +build_script: + - ps: .\scripts\make.ps1 -Binary + +test_script: + - ps: .\scripts\make.ps1 -TestUnit \ No newline at end of file diff --git a/components/cli/cli/command/bundlefile/bundlefile_test.go b/components/cli/cli/command/bundlefile/bundlefile_test.go index bd059c4dca..8837aa1a27 100644 --- a/components/cli/cli/command/bundlefile/bundlefile_test.go +++ b/components/cli/cli/command/bundlefile/bundlefile_test.go @@ -5,7 +5,8 @@ import ( "strings" "testing" - "github.com/stretchr/testify/assert" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" ) func TestLoadFileV01Success(t *testing.T) { @@ -25,9 +26,9 @@ func TestLoadFileV01Success(t *testing.T) { }`) bundle, err := LoadFile(reader) - assert.NoError(t, err) - assert.Equal(t, "0.1", bundle.Version) - assert.Len(t, bundle.Services, 2) + assert.NilError(t, err) + assert.Check(t, is.Equal("0.1", bundle.Version)) + assert.Check(t, is.Len(bundle.Services, 2)) } func TestLoadFileSyntaxError(t *testing.T) { @@ -37,7 +38,7 @@ func TestLoadFileSyntaxError(t *testing.T) { }`) _, err := LoadFile(reader) - assert.EqualError(t, err, "JSON syntax error at byte 37: invalid character 'u' looking for beginning of value") + assert.Error(t, err, "JSON syntax error at byte 37: invalid character 'u' looking for beginning of value") } func TestLoadFileTypeError(t *testing.T) { @@ -52,7 +53,7 @@ func TestLoadFileTypeError(t *testing.T) { }`) _, err := LoadFile(reader) - assert.EqualError(t, err, "Unexpected type at byte 94. Expected []string but received string.") + assert.Error(t, err, "Unexpected type at byte 94. Expected []string but received string.") } func TestPrint(t *testing.T) { @@ -66,12 +67,12 @@ func TestPrint(t *testing.T) { }, }, } - assert.NoError(t, Print(&buffer, bundle)) + assert.Check(t, Print(&buffer, bundle)) output := buffer.String() - assert.Contains(t, output, "\"Image\": \"image\"") - assert.Contains(t, output, + assert.Check(t, is.Contains(output, "\"Image\": \"image\"")) + assert.Check(t, is.Contains(output, `"Command": [ "echo", "something" - ]`) + ]`)) } diff --git a/components/cli/cli/command/checkpoint/create_test.go b/components/cli/cli/command/checkpoint/create_test.go index 19bd3920c0..59fdf9d108 100644 --- a/components/cli/cli/command/checkpoint/create_test.go +++ b/components/cli/cli/command/checkpoint/create_test.go @@ -6,10 +6,10 @@ import ( "testing" "github.com/docker/cli/internal/test" - "github.com/docker/cli/internal/test/testutil" "github.com/docker/docker/api/types" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" "github.com/pkg/errors" - "github.com/stretchr/testify/assert" ) func TestCheckpointCreateErrors(t *testing.T) { @@ -42,7 +42,7 @@ func TestCheckpointCreateErrors(t *testing.T) { cmd := newCreateCommand(cli) cmd.SetArgs(tc.args) cmd.SetOutput(ioutil.Discard) - testutil.ErrorContains(t, cmd.Execute(), tc.expectedError) + assert.ErrorContains(t, cmd.Execute(), tc.expectedError) } } @@ -63,10 +63,10 @@ func TestCheckpointCreateWithOptions(t *testing.T) { cmd.SetArgs([]string{"container-foo", checkpoint}) cmd.Flags().Set("leave-running", "true") cmd.Flags().Set("checkpoint-dir", "/dir/foo") - assert.NoError(t, cmd.Execute()) - assert.Equal(t, "container-foo", containerID) - assert.Equal(t, checkpoint, checkpointID) - assert.Equal(t, "/dir/foo", checkpointDir) - assert.Equal(t, false, exit) - assert.Equal(t, checkpoint, strings.TrimSpace(cli.OutBuffer().String())) + assert.NilError(t, cmd.Execute()) + assert.Check(t, is.Equal("container-foo", containerID)) + assert.Check(t, is.Equal(checkpoint, checkpointID)) + assert.Check(t, is.Equal("/dir/foo", checkpointDir)) + assert.Check(t, is.Equal(false, exit)) + assert.Check(t, is.Equal(checkpoint, strings.TrimSpace(cli.OutBuffer().String()))) } diff --git a/components/cli/cli/command/checkpoint/list_test.go b/components/cli/cli/command/checkpoint/list_test.go index 373460d3be..2523bba566 100644 --- a/components/cli/cli/command/checkpoint/list_test.go +++ b/components/cli/cli/command/checkpoint/list_test.go @@ -5,11 +5,11 @@ import ( "testing" "github.com/docker/cli/internal/test" - "github.com/docker/cli/internal/test/testutil" "github.com/docker/docker/api/types" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" "github.com/gotestyourself/gotestyourself/golden" "github.com/pkg/errors" - "github.com/stretchr/testify/assert" ) func TestCheckpointListErrors(t *testing.T) { @@ -42,7 +42,7 @@ func TestCheckpointListErrors(t *testing.T) { cmd := newListCommand(cli) cmd.SetArgs(tc.args) cmd.SetOutput(ioutil.Discard) - testutil.ErrorContains(t, cmd.Execute(), tc.expectedError) + assert.ErrorContains(t, cmd.Execute(), tc.expectedError) } } @@ -60,8 +60,8 @@ func TestCheckpointListWithOptions(t *testing.T) { cmd := newListCommand(cli) cmd.SetArgs([]string{"container-foo"}) cmd.Flags().Set("checkpoint-dir", "/dir/foo") - assert.NoError(t, cmd.Execute()) - assert.Equal(t, "container-foo", containerID) - assert.Equal(t, "/dir/foo", checkpointDir) + assert.NilError(t, cmd.Execute()) + assert.Check(t, is.Equal("container-foo", containerID)) + assert.Check(t, is.Equal("/dir/foo", checkpointDir)) golden.Assert(t, cli.OutBuffer().String(), "checkpoint-list-with-options.golden") } diff --git a/components/cli/cli/command/checkpoint/remove_test.go b/components/cli/cli/command/checkpoint/remove_test.go index 6100d75e41..9816b2f16c 100644 --- a/components/cli/cli/command/checkpoint/remove_test.go +++ b/components/cli/cli/command/checkpoint/remove_test.go @@ -5,10 +5,10 @@ import ( "testing" "github.com/docker/cli/internal/test" - "github.com/docker/cli/internal/test/testutil" "github.com/docker/docker/api/types" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" "github.com/pkg/errors" - "github.com/stretchr/testify/assert" ) func TestCheckpointRemoveErrors(t *testing.T) { @@ -41,7 +41,7 @@ func TestCheckpointRemoveErrors(t *testing.T) { cmd := newRemoveCommand(cli) cmd.SetArgs(tc.args) cmd.SetOutput(ioutil.Discard) - testutil.ErrorContains(t, cmd.Execute(), tc.expectedError) + assert.ErrorContains(t, cmd.Execute(), tc.expectedError) } } @@ -58,8 +58,8 @@ func TestCheckpointRemoveWithOptions(t *testing.T) { cmd := newRemoveCommand(cli) cmd.SetArgs([]string{"container-foo", "checkpoint-bar"}) cmd.Flags().Set("checkpoint-dir", "/dir/foo") - assert.NoError(t, cmd.Execute()) - assert.Equal(t, "container-foo", containerID) - assert.Equal(t, "checkpoint-bar", checkpointID) - assert.Equal(t, "/dir/foo", checkpointDir) + assert.NilError(t, cmd.Execute()) + assert.Check(t, is.Equal("container-foo", containerID)) + assert.Check(t, is.Equal("checkpoint-bar", checkpointID)) + assert.Check(t, is.Equal("/dir/foo", checkpointDir)) } diff --git a/components/cli/cli/command/cli.go b/components/cli/cli/command/cli.go index 5a95ab06b2..55a6c436d4 100644 --- a/components/cli/cli/command/cli.go +++ b/components/cli/cli/command/cli.go @@ -22,7 +22,6 @@ import ( "github.com/docker/docker/api/types" registrytypes "github.com/docker/docker/api/types/registry" "github.com/docker/docker/client" - "github.com/docker/go-connections/sockets" "github.com/docker/go-connections/tlsconfig" "github.com/pkg/errors" "github.com/spf13/cobra" @@ -53,18 +52,20 @@ type Cli interface { DefaultVersion() string ManifestStore() manifeststore.Store RegistryClient(bool) registryclient.RegistryClient + ContentTrustEnabled() bool } // DockerCli is an instance the docker command line client. // Instances of the client can be returned from NewDockerCli. type DockerCli struct { - configFile *configfile.ConfigFile - in *InStream - out *OutStream - err io.Writer - client client.APIClient - serverInfo ServerInfo - clientInfo ClientInfo + configFile *configfile.ConfigFile + in *InStream + out *OutStream + err io.Writer + client client.APIClient + serverInfo ServerInfo + clientInfo ClientInfo + contentTrust bool } // DefaultVersion returns api.defaultVersion or DOCKER_API_VERSION if specified. @@ -122,6 +123,12 @@ func (cli *DockerCli) ClientInfo() ClientInfo { return cli.clientInfo } +// ContentTrustEnabled returns whether content trust has been enabled by an +// environment variable. +func (cli *DockerCli) ContentTrustEnabled() bool { + return cli.contentTrust +} + // ManifestStore returns a store for local manifests func (cli *DockerCli) ManifestStore() manifeststore.Store { // TODO: support override default location from config file @@ -238,8 +245,8 @@ func (c ClientInfo) HasKubernetes() bool { } // NewDockerCli returns a DockerCli instance with IO output and error streams set by in, out and err. -func NewDockerCli(in io.ReadCloser, out, err io.Writer) *DockerCli { - return &DockerCli{in: NewInStream(in), out: NewOutStream(out), err: err} +func NewDockerCli(in io.ReadCloser, out, err io.Writer, isTrusted bool) *DockerCli { + return &DockerCli{in: NewInStream(in), out: NewOutStream(out), err: err, contentTrust: isTrusted} } // NewAPIClientFromFlags creates a new APIClient from command line flags @@ -260,12 +267,12 @@ func NewAPIClientFromFlags(opts *cliflags.CommonOptions, configFile *configfile. verStr = tmpStr } - httpClient, err := newHTTPClient(host, opts.TLSOptions) - if err != nil { - return &client.Client{}, err - } - - return client.NewClient(host, verStr, httpClient, customHeaders) + return client.NewClientWithOpts( + withHTTPClient(opts.TLSOptions), + client.WithHTTPHeaders(customHeaders), + client.WithVersion(verStr), + client.WithHost(host), + ) } func getServerHost(hosts []string, tlsOptions *tlsconfig.Options) (string, error) { @@ -282,35 +289,32 @@ func getServerHost(hosts []string, tlsOptions *tlsconfig.Options) (string, error return dopts.ParseHost(tlsOptions != nil, host) } -func newHTTPClient(host string, tlsOptions *tlsconfig.Options) (*http.Client, error) { - if tlsOptions == nil { - // let the api client configure the default transport. - return nil, nil - } - opts := *tlsOptions - opts.ExclusiveRootPools = true - config, err := tlsconfig.Client(opts) - if err != nil { - return nil, err - } - tr := &http.Transport{ - TLSClientConfig: config, - DialContext: (&net.Dialer{ - KeepAlive: 30 * time.Second, - Timeout: 30 * time.Second, - }).DialContext, - } - hostURL, err := client.ParseHostURL(host) - if err != nil { - return nil, err - } +func withHTTPClient(tlsOpts *tlsconfig.Options) func(*client.Client) error { + return func(c *client.Client) error { + if tlsOpts == nil { + // Use the default HTTPClient + return nil + } - sockets.ConfigureTransport(tr, hostURL.Scheme, hostURL.Host) + opts := *tlsOpts + opts.ExclusiveRootPools = true + tlsConfig, err := tlsconfig.Client(opts) + if err != nil { + return err + } - return &http.Client{ - Transport: tr, - CheckRedirect: client.CheckRedirect, - }, nil + httpClient := &http.Client{ + Transport: &http.Transport{ + TLSClientConfig: tlsConfig, + DialContext: (&net.Dialer{ + KeepAlive: 30 * time.Second, + Timeout: 30 * time.Second, + }).DialContext, + }, + CheckRedirect: client.CheckRedirect, + } + return client.WithHTTPClient(httpClient)(c) + } } // UserAgent returns the user agent string used for making API requests diff --git a/components/cli/cli/command/cli_test.go b/components/cli/cli/command/cli_test.go index 9a440ef138..432e653fdc 100644 --- a/components/cli/cli/command/cli_test.go +++ b/components/cli/cli/command/cli_test.go @@ -3,24 +3,27 @@ package command import ( "crypto/x509" "os" + "runtime" "testing" cliconfig "github.com/docker/cli/cli/config" "github.com/docker/cli/cli/config/configfile" "github.com/docker/cli/cli/flags" - "github.com/docker/cli/internal/test/testutil" "github.com/docker/docker/api" "github.com/docker/docker/api/types" "github.com/docker/docker/client" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" "github.com/gotestyourself/gotestyourself/fs" "github.com/pkg/errors" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" "golang.org/x/net/context" ) func TestNewAPIClientFromFlags(t *testing.T) { host := "unix://path" + if runtime.GOOS == "windows" { + host = "npipe://./" + } opts := &flags.CommonOptions{Hosts: []string{host}} configFile := &configfile.ConfigFile{ HTTPHeaders: map[string]string{ @@ -28,15 +31,15 @@ func TestNewAPIClientFromFlags(t *testing.T) { }, } apiclient, err := NewAPIClientFromFlags(opts, configFile) - require.NoError(t, err) - assert.Equal(t, host, apiclient.DaemonHost()) + assert.NilError(t, err) + assert.Check(t, is.Equal(host, apiclient.DaemonHost())) expectedHeaders := map[string]string{ "My-Header": "Custom-Value", "User-Agent": UserAgent(), } - assert.Equal(t, expectedHeaders, apiclient.(*client.Client).CustomHTTPHeaders()) - assert.Equal(t, api.DefaultVersion, apiclient.ClientVersion()) + assert.Check(t, is.DeepEqual(expectedHeaders, apiclient.(*client.Client).CustomHTTPHeaders())) + assert.Check(t, is.Equal(api.DefaultVersion, apiclient.ClientVersion())) } func TestNewAPIClientFromFlagsWithAPIVersionFromEnv(t *testing.T) { @@ -46,20 +49,20 @@ func TestNewAPIClientFromFlagsWithAPIVersionFromEnv(t *testing.T) { opts := &flags.CommonOptions{} configFile := &configfile.ConfigFile{} apiclient, err := NewAPIClientFromFlags(opts, configFile) - require.NoError(t, err) - assert.Equal(t, customVersion, apiclient.ClientVersion()) + assert.NilError(t, err) + assert.Check(t, is.Equal(customVersion, apiclient.ClientVersion())) } // TODO: use gotestyourself/env.Patch func patchEnvVariable(t *testing.T, key, value string) func() { oldValue, ok := os.LookupEnv(key) - require.NoError(t, os.Setenv(key, value)) + assert.NilError(t, os.Setenv(key, value)) return func() { if !ok { - require.NoError(t, os.Unsetenv(key)) + assert.NilError(t, os.Unsetenv(key)) return } - require.NoError(t, os.Setenv(key, oldValue)) + assert.NilError(t, os.Setenv(key, oldValue)) } } @@ -125,8 +128,8 @@ func TestInitializeFromClient(t *testing.T) { cli := &DockerCli{client: apiclient} cli.initializeFromClient() - assert.Equal(t, testcase.expectedServer, cli.serverInfo) - assert.Equal(t, testcase.negotiated, apiclient.negotiated) + assert.Check(t, is.DeepEqual(testcase.expectedServer, cli.serverInfo)) + assert.Check(t, is.Equal(testcase.negotiated, apiclient.negotiated)) }) } } @@ -164,8 +167,8 @@ func TestExperimentalCLI(t *testing.T) { cli := &DockerCli{client: apiclient, err: os.Stderr} cliconfig.SetDir(dir.Path()) err := cli.Initialize(flags.NewClientOptions()) - assert.NoError(t, err) - assert.Equal(t, testcase.expectedExperimentalCLI, cli.ClientInfo().HasExperimental) + assert.NilError(t, err) + assert.Check(t, is.Equal(testcase.expectedExperimentalCLI, cli.ClientInfo().HasExperimental)) }) } } @@ -267,9 +270,9 @@ func TestOrchestratorSwitch(t *testing.T) { options.Common.Orchestrator = testcase.flagOrchestrator } err := cli.Initialize(options) - assert.NoError(t, err) - assert.Equal(t, testcase.expectedKubernetes, cli.ClientInfo().HasKubernetes()) - assert.Equal(t, testcase.expectedOrchestrator, string(cli.ClientInfo().Orchestrator)) + assert.NilError(t, err) + assert.Check(t, is.Equal(testcase.expectedKubernetes, cli.ClientInfo().HasKubernetes())) + assert.Check(t, is.Equal(testcase.expectedOrchestrator, string(cli.ClientInfo().Orchestrator))) }) } } @@ -331,11 +334,11 @@ func TestGetClientWithPassword(t *testing.T) { _, err := getClientWithPassword(passRetriever, newClient) if testcase.expectedErr != "" { - testutil.ErrorContains(t, err, testcase.expectedErr) + assert.ErrorContains(t, err, testcase.expectedErr) return } - assert.NoError(t, err) + assert.NilError(t, err) }) } } diff --git a/components/cli/cli/command/config/create_test.go b/components/cli/cli/command/config/create_test.go index 5838ae6bb7..46515ca524 100644 --- a/components/cli/cli/command/config/create_test.go +++ b/components/cli/cli/command/config/create_test.go @@ -8,12 +8,12 @@ import ( "testing" "github.com/docker/cli/internal/test" - "github.com/docker/cli/internal/test/testutil" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/swarm" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" "github.com/gotestyourself/gotestyourself/golden" "github.com/pkg/errors" - "github.com/stretchr/testify/assert" ) const configDataFile = "config-create-with-name.golden" @@ -47,7 +47,7 @@ func TestConfigCreateErrors(t *testing.T) { ) cmd.SetArgs(tc.args) cmd.SetOutput(ioutil.Discard) - testutil.ErrorContains(t, cmd.Execute(), tc.expectedError) + assert.ErrorContains(t, cmd.Execute(), tc.expectedError) } } @@ -70,9 +70,9 @@ func TestConfigCreateWithName(t *testing.T) { cmd := newConfigCreateCommand(cli) cmd.SetArgs([]string{name, filepath.Join("testdata", configDataFile)}) - assert.NoError(t, cmd.Execute()) + assert.NilError(t, cmd.Execute()) golden.Assert(t, string(actual), configDataFile) - assert.Equal(t, "ID-"+name, strings.TrimSpace(cli.OutBuffer().String())) + assert.Check(t, is.Equal("ID-"+name, strings.TrimSpace(cli.OutBuffer().String()))) } func TestConfigCreateWithLabels(t *testing.T) { @@ -83,7 +83,7 @@ func TestConfigCreateWithLabels(t *testing.T) { name := "foo" data, err := ioutil.ReadFile(filepath.Join("testdata", configDataFile)) - assert.NoError(t, err) + assert.NilError(t, err) expected := swarm.ConfigSpec{ Annotations: swarm.Annotations{ @@ -109,8 +109,8 @@ func TestConfigCreateWithLabels(t *testing.T) { cmd.SetArgs([]string{name, filepath.Join("testdata", configDataFile)}) cmd.Flags().Set("label", "lbl1=Label-foo") cmd.Flags().Set("label", "lbl2=Label-bar") - assert.NoError(t, cmd.Execute()) - assert.Equal(t, "ID-"+name, strings.TrimSpace(cli.OutBuffer().String())) + assert.NilError(t, cmd.Execute()) + assert.Check(t, is.Equal("ID-"+name, strings.TrimSpace(cli.OutBuffer().String()))) } func TestConfigCreateWithTemplatingDriver(t *testing.T) { @@ -138,6 +138,6 @@ func TestConfigCreateWithTemplatingDriver(t *testing.T) { cmd := newConfigCreateCommand(cli) cmd.SetArgs([]string{name, filepath.Join("testdata", configDataFile)}) cmd.Flags().Set("template-driver", expectedDriver.Name) - assert.NoError(t, cmd.Execute()) - assert.Equal(t, "ID-"+name, strings.TrimSpace(cli.OutBuffer().String())) + assert.NilError(t, cmd.Execute()) + assert.Check(t, is.Equal("ID-"+name, strings.TrimSpace(cli.OutBuffer().String()))) } diff --git a/components/cli/cli/command/config/inspect_test.go b/components/cli/cli/command/config/inspect_test.go index 5ff280fd2a..b714e95830 100644 --- a/components/cli/cli/command/config/inspect_test.go +++ b/components/cli/cli/command/config/inspect_test.go @@ -11,9 +11,8 @@ import ( "github.com/pkg/errors" // Import builders to get the builder function as package function . "github.com/docker/cli/internal/test/builders" - "github.com/docker/cli/internal/test/testutil" + "github.com/gotestyourself/gotestyourself/assert" "github.com/gotestyourself/gotestyourself/golden" - "github.com/stretchr/testify/assert" ) func TestConfigInspectErrors(t *testing.T) { @@ -62,7 +61,7 @@ func TestConfigInspectErrors(t *testing.T) { cmd.Flags().Set(key, value) } cmd.SetOutput(ioutil.Discard) - testutil.ErrorContains(t, cmd.Execute(), tc.expectedError) + assert.ErrorContains(t, cmd.Execute(), tc.expectedError) } } @@ -96,7 +95,7 @@ func TestConfigInspectWithoutFormat(t *testing.T) { cli := test.NewFakeCli(&fakeClient{configInspectFunc: tc.configInspectFunc}) cmd := newConfigInspectCommand(cli) cmd.SetArgs(tc.args) - assert.NoError(t, cmd.Execute()) + assert.NilError(t, cmd.Execute()) golden.Assert(t, cli.OutBuffer().String(), fmt.Sprintf("config-inspect-without-format.%s.golden", tc.name)) } } @@ -133,7 +132,7 @@ func TestConfigInspectWithFormat(t *testing.T) { cmd := newConfigInspectCommand(cli) cmd.SetArgs(tc.args) cmd.Flags().Set("format", tc.format) - assert.NoError(t, cmd.Execute()) + assert.NilError(t, cmd.Execute()) golden.Assert(t, cli.OutBuffer().String(), fmt.Sprintf("config-inspect-with-format.%s.golden", tc.name)) } } @@ -167,7 +166,7 @@ func TestConfigInspectPretty(t *testing.T) { cmd.SetArgs([]string{"configID"}) cmd.Flags().Set("pretty", "true") - assert.NoError(t, cmd.Execute()) + assert.NilError(t, cmd.Execute()) golden.Assert(t, cli.OutBuffer().String(), fmt.Sprintf("config-inspect-pretty.%s.golden", tc.name)) } } diff --git a/components/cli/cli/command/config/ls_test.go b/components/cli/cli/command/config/ls_test.go index f3946d2e5f..3ef7a91783 100644 --- a/components/cli/cli/command/config/ls_test.go +++ b/components/cli/cli/command/config/ls_test.go @@ -12,9 +12,9 @@ import ( "github.com/pkg/errors" // Import builders to get the builder function as package function . "github.com/docker/cli/internal/test/builders" - "github.com/docker/cli/internal/test/testutil" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" "github.com/gotestyourself/gotestyourself/golden" - "github.com/stretchr/testify/assert" ) func TestConfigListErrors(t *testing.T) { @@ -42,7 +42,7 @@ func TestConfigListErrors(t *testing.T) { ) cmd.SetArgs(tc.args) cmd.SetOutput(ioutil.Discard) - testutil.ErrorContains(t, cmd.Execute(), tc.expectedError) + assert.ErrorContains(t, cmd.Execute(), tc.expectedError) } } @@ -72,7 +72,7 @@ func TestConfigList(t *testing.T) { }, }) cmd := newConfigListCommand(cli) - assert.NoError(t, cmd.Execute()) + assert.NilError(t, cmd.Execute()) golden.Assert(t, cli.OutBuffer().String(), "config-list-sort.golden") } @@ -89,7 +89,7 @@ func TestConfigListWithQuietOption(t *testing.T) { }) cmd := newConfigListCommand(cli) cmd.Flags().Set("quiet", "true") - assert.NoError(t, cmd.Execute()) + assert.NilError(t, cmd.Execute()) golden.Assert(t, cli.OutBuffer().String(), "config-list-with-quiet-option.golden") } @@ -108,7 +108,7 @@ func TestConfigListWithConfigFormat(t *testing.T) { ConfigFormat: "{{ .Name }} {{ .Labels }}", }) cmd := newConfigListCommand(cli) - assert.NoError(t, cmd.Execute()) + assert.NilError(t, cmd.Execute()) golden.Assert(t, cli.OutBuffer().String(), "config-list-with-config-format.golden") } @@ -125,15 +125,15 @@ func TestConfigListWithFormat(t *testing.T) { }) cmd := newConfigListCommand(cli) cmd.Flags().Set("format", "{{ .Name }} {{ .Labels }}") - assert.NoError(t, cmd.Execute()) + assert.NilError(t, cmd.Execute()) golden.Assert(t, cli.OutBuffer().String(), "config-list-with-format.golden") } func TestConfigListWithFilter(t *testing.T) { cli := test.NewFakeCli(&fakeClient{ configListFunc: func(options types.ConfigListOptions) ([]swarm.Config, error) { - assert.Equal(t, "foo", options.Filters.Get("name")[0]) - assert.Equal(t, "lbl1=Label-bar", options.Filters.Get("label")[0]) + assert.Check(t, is.Equal("foo", options.Filters.Get("name")[0])) + assert.Check(t, is.Equal("lbl1=Label-bar", options.Filters.Get("label")[0])) return []swarm.Config{ *Config(ConfigID("ID-foo"), ConfigName("foo"), @@ -153,6 +153,6 @@ func TestConfigListWithFilter(t *testing.T) { cmd := newConfigListCommand(cli) cmd.Flags().Set("filter", "name=foo") cmd.Flags().Set("filter", "label=lbl1=Label-bar") - assert.NoError(t, cmd.Execute()) + assert.NilError(t, cmd.Execute()) golden.Assert(t, cli.OutBuffer().String(), "config-list-with-filter.golden") } diff --git a/components/cli/cli/command/config/remove_test.go b/components/cli/cli/command/config/remove_test.go index f7142a2566..6501210dcf 100644 --- a/components/cli/cli/command/config/remove_test.go +++ b/components/cli/cli/command/config/remove_test.go @@ -6,9 +6,9 @@ import ( "testing" "github.com/docker/cli/internal/test" - "github.com/docker/cli/internal/test/testutil" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" "github.com/pkg/errors" - "github.com/stretchr/testify/assert" ) func TestConfigRemoveErrors(t *testing.T) { @@ -37,7 +37,7 @@ func TestConfigRemoveErrors(t *testing.T) { ) cmd.SetArgs(tc.args) cmd.SetOutput(ioutil.Discard) - testutil.ErrorContains(t, cmd.Execute(), tc.expectedError) + assert.ErrorContains(t, cmd.Execute(), tc.expectedError) } } @@ -52,9 +52,9 @@ func TestConfigRemoveWithName(t *testing.T) { }) cmd := newConfigRemoveCommand(cli) cmd.SetArgs(names) - assert.NoError(t, cmd.Execute()) - assert.Equal(t, names, strings.Split(strings.TrimSpace(cli.OutBuffer().String()), "\n")) - assert.Equal(t, names, removedConfigs) + assert.NilError(t, cmd.Execute()) + assert.Check(t, is.DeepEqual(names, strings.Split(strings.TrimSpace(cli.OutBuffer().String()), "\n"))) + assert.Check(t, is.DeepEqual(names, removedConfigs)) } func TestConfigRemoveContinueAfterError(t *testing.T) { @@ -74,6 +74,6 @@ func TestConfigRemoveContinueAfterError(t *testing.T) { cmd := newConfigRemoveCommand(cli) cmd.SetArgs(names) cmd.SetOutput(ioutil.Discard) - assert.EqualError(t, cmd.Execute(), "error removing config: foo") - assert.Equal(t, names, removedConfigs) + assert.Error(t, cmd.Execute(), "error removing config: foo") + assert.Check(t, is.DeepEqual(names, removedConfigs)) } diff --git a/components/cli/cli/command/container/attach_test.go b/components/cli/cli/command/container/attach_test.go index 1c305aa7a1..9f7831ca06 100644 --- a/components/cli/cli/command/container/attach_test.go +++ b/components/cli/cli/command/container/attach_test.go @@ -7,11 +7,10 @@ import ( "github.com/docker/cli/cli" "github.com/docker/cli/internal/test" - "github.com/docker/cli/internal/test/testutil" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/container" + "github.com/gotestyourself/gotestyourself/assert" "github.com/pkg/errors" - "github.com/stretchr/testify/assert" ) func TestNewAttachCommandErrors(t *testing.T) { @@ -74,7 +73,7 @@ func TestNewAttachCommandErrors(t *testing.T) { cmd := NewAttachCommand(test.NewFakeCli(&fakeClient{inspectFunc: tc.containerInspectFunc})) cmd.SetOutput(ioutil.Discard) cmd.SetArgs(tc.args) - testutil.ErrorContains(t, cmd.Execute(), tc.expectedError) + assert.ErrorContains(t, cmd.Execute(), tc.expectedError) } } @@ -101,9 +100,7 @@ func TestGetExitStatus(t *testing.T) { }, { result: &container.ContainerWaitOKBody{ - Error: &container.ContainerWaitOKBodyError{ - expectedErr.Error(), - }, + Error: &container.ContainerWaitOKBodyError{expectedErr.Error()}, }, expectedError: expectedErr, }, @@ -123,6 +120,10 @@ func TestGetExitStatus(t *testing.T) { resultC <- *testcase.result } err := getExitStatus(errC, resultC) - assert.Equal(t, testcase.expectedError, err) + if testcase.expectedError == nil { + assert.NilError(t, err) + } else { + assert.Error(t, err, testcase.expectedError.Error()) + } } } diff --git a/components/cli/cli/command/container/cp_test.go b/components/cli/cli/command/container/cp_test.go index ce4430746e..7d706f354c 100644 --- a/components/cli/cli/command/container/cp_test.go +++ b/components/cli/cli/command/container/cp_test.go @@ -9,13 +9,12 @@ import ( "testing" "github.com/docker/cli/internal/test" - "github.com/docker/cli/internal/test/testutil" "github.com/docker/docker/api/types" "github.com/docker/docker/pkg/archive" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" "github.com/gotestyourself/gotestyourself/fs" "github.com/gotestyourself/gotestyourself/skip" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" ) func TestRunCopyWithInvalidArguments(t *testing.T) { @@ -44,7 +43,7 @@ func TestRunCopyWithInvalidArguments(t *testing.T) { for _, testcase := range testcases { t.Run(testcase.doc, func(t *testing.T) { err := runCopy(test.NewFakeCli(nil), testcase.options) - assert.EqualError(t, err, testcase.expectedErr) + assert.Error(t, err, testcase.expectedErr) }) } } @@ -54,16 +53,16 @@ func TestRunCopyFromContainerToStdout(t *testing.T) { fakeClient := &fakeClient{ containerCopyFromFunc: func(container, srcPath string) (io.ReadCloser, types.ContainerPathStat, error) { - assert.Equal(t, "container", container) + assert.Check(t, is.Equal("container", container)) return ioutil.NopCloser(strings.NewReader(tarContent)), types.ContainerPathStat{}, nil }, } options := copyOptions{source: "container:/path", destination: "-"} cli := test.NewFakeCli(fakeClient) err := runCopy(cli, options) - require.NoError(t, err) - assert.Equal(t, tarContent, cli.OutBuffer().String()) - assert.Equal(t, "", cli.ErrBuffer().String()) + assert.NilError(t, err) + assert.Check(t, is.Equal(tarContent, cli.OutBuffer().String())) + assert.Check(t, is.Equal("", cli.ErrBuffer().String())) } func TestRunCopyFromContainerToFilesystem(t *testing.T) { @@ -73,7 +72,7 @@ func TestRunCopyFromContainerToFilesystem(t *testing.T) { fakeClient := &fakeClient{ containerCopyFromFunc: func(container, srcPath string) (io.ReadCloser, types.ContainerPathStat, error) { - assert.Equal(t, "container", container) + assert.Check(t, is.Equal("container", container)) readCloser, err := archive.TarWithOptions(destDir.Path(), &archive.TarOptions{}) return readCloser, types.ContainerPathStat{}, err }, @@ -81,13 +80,13 @@ func TestRunCopyFromContainerToFilesystem(t *testing.T) { options := copyOptions{source: "container:/path", destination: destDir.Path()} cli := test.NewFakeCli(fakeClient) err := runCopy(cli, options) - require.NoError(t, err) - assert.Equal(t, "", cli.OutBuffer().String()) - assert.Equal(t, "", cli.ErrBuffer().String()) + assert.NilError(t, err) + assert.Check(t, is.Equal("", cli.OutBuffer().String())) + assert.Check(t, is.Equal("", cli.ErrBuffer().String())) content, err := ioutil.ReadFile(destDir.Join("file1")) - require.NoError(t, err) - assert.Equal(t, "content\n", string(content)) + assert.NilError(t, err) + assert.Check(t, is.Equal("content\n", string(content))) } func TestRunCopyFromContainerToFilesystemMissingDestinationDirectory(t *testing.T) { @@ -97,7 +96,7 @@ func TestRunCopyFromContainerToFilesystemMissingDestinationDirectory(t *testing. fakeClient := &fakeClient{ containerCopyFromFunc: func(container, srcPath string) (io.ReadCloser, types.ContainerPathStat, error) { - assert.Equal(t, "container", container) + assert.Check(t, is.Equal("container", container)) readCloser, err := archive.TarWithOptions(destDir.Path(), &archive.TarOptions{}) return readCloser, types.ContainerPathStat{}, err }, @@ -109,7 +108,7 @@ func TestRunCopyFromContainerToFilesystemMissingDestinationDirectory(t *testing. } cli := test.NewFakeCli(fakeClient) err := runCopy(cli, options) - testutil.ErrorContains(t, err, destDir.Join("missing")) + assert.ErrorContains(t, err, destDir.Join("missing")) } func TestRunCopyToContainerFromFileWithTrailingSlash(t *testing.T) { @@ -122,7 +121,12 @@ func TestRunCopyToContainerFromFileWithTrailingSlash(t *testing.T) { } cli := test.NewFakeCli(&fakeClient{}) err := runCopy(cli, options) - testutil.ErrorContains(t, err, "not a directory") + + expectedError := "not a directory" + if runtime.GOOS == "windows" { + expectedError = "The filename, directory name, or volume label syntax is incorrect" + } + assert.ErrorContains(t, err, expectedError) } func TestRunCopyToContainerSourceDoesNotExist(t *testing.T) { @@ -136,7 +140,7 @@ func TestRunCopyToContainerSourceDoesNotExist(t *testing.T) { if runtime.GOOS == "windows" { expected = "cannot find the file specified" } - testutil.ErrorContains(t, err, expected) + assert.ErrorContains(t, err, expected) } func TestSplitCpArg(t *testing.T) { @@ -181,8 +185,8 @@ func TestSplitCpArg(t *testing.T) { skip.IfCondition(t, testcase.os != "" && testcase.os != runtime.GOOS) container, path := splitCpArg(testcase.path) - assert.Equal(t, testcase.expectedContainer, container) - assert.Equal(t, testcase.expectedPath, path) + assert.Check(t, is.Equal(testcase.expectedContainer, container)) + assert.Check(t, is.Equal(testcase.expectedPath, path)) }) } } diff --git a/components/cli/cli/command/container/create.go b/components/cli/cli/command/container/create.go index 099184d940..7fe4c8f260 100644 --- a/components/cli/cli/command/container/create.go +++ b/components/cli/cli/command/container/create.go @@ -21,8 +21,9 @@ import ( ) type createOptions struct { - name string - platform string + name string + platform string + untrusted bool } // NewCreateCommand creates a new cobra.Command for `docker create` @@ -53,7 +54,7 @@ func NewCreateCommand(dockerCli command.Cli) *cobra.Command { flags.Bool("help", false, "Print usage") command.AddPlatformFlag(flags, &opts.platform) - command.AddTrustVerificationFlags(flags) + command.AddTrustVerificationFlags(flags, &opts.untrusted, dockerCli.ContentTrustEnabled()) copts = addFlags(flags) return cmd } @@ -64,7 +65,7 @@ func runCreate(dockerCli command.Cli, flags *pflag.FlagSet, opts *createOptions, reportError(dockerCli.Err(), "create", err.Error(), true) return cli.StatusError{StatusCode: 125} } - response, err := createContainer(context.Background(), dockerCli, containerConfig, opts.name, opts.platform) + response, err := createContainer(context.Background(), dockerCli, containerConfig, opts) if err != nil { return err } @@ -158,7 +159,7 @@ func newCIDFile(path string) (*cidFile, error) { return &cidFile{path: path, file: f}, nil } -func createContainer(ctx context.Context, dockerCli command.Cli, containerConfig *containerConfig, name string, platform string) (*container.ContainerCreateCreatedBody, error) { +func createContainer(ctx context.Context, dockerCli command.Cli, containerConfig *containerConfig, opts *createOptions) (*container.ContainerCreateCreatedBody, error) { config := containerConfig.Config hostConfig := containerConfig.HostConfig networkingConfig := containerConfig.NetworkingConfig @@ -182,7 +183,7 @@ func createContainer(ctx context.Context, dockerCli command.Cli, containerConfig if named, ok := ref.(reference.Named); ok { namedRef = reference.TagNameOnly(named) - if taggedRef, ok := namedRef.(reference.NamedTagged); ok && command.IsTrusted() { + if taggedRef, ok := namedRef.(reference.NamedTagged); ok && !opts.untrusted { var err error trustedRef, err = image.TrustedReference(ctx, dockerCli, taggedRef, nil) if err != nil { @@ -193,7 +194,7 @@ func createContainer(ctx context.Context, dockerCli command.Cli, containerConfig } //create the container - response, err := dockerCli.Client().ContainerCreate(ctx, config, hostConfig, networkingConfig, name) + response, err := dockerCli.Client().ContainerCreate(ctx, config, hostConfig, networkingConfig, opts.name) //if image not found try to pull it if err != nil { @@ -201,7 +202,7 @@ func createContainer(ctx context.Context, dockerCli command.Cli, containerConfig fmt.Fprintf(stderr, "Unable to find image '%s' locally\n", reference.FamiliarString(namedRef)) // we don't want to write to stdout anything apart from container.ID - if err := pullImage(ctx, dockerCli, config.Image, platform, stderr); err != nil { + if err := pullImage(ctx, dockerCli, config.Image, opts.platform, stderr); err != nil { return nil, err } if taggedRef, ok := namedRef.(reference.NamedTagged); ok && trustedRef != nil { @@ -211,7 +212,7 @@ func createContainer(ctx context.Context, dockerCli command.Cli, containerConfig } // Retry var retryErr error - response, retryErr = dockerCli.Client().ContainerCreate(ctx, config, hostConfig, networkingConfig, name) + response, retryErr = dockerCli.Client().ContainerCreate(ctx, config, hostConfig, networkingConfig, opts.name) if retryErr != nil { return nil, retryErr } diff --git a/components/cli/cli/command/container/create_test.go b/components/cli/cli/command/container/create_test.go index 6bbdb909c0..a5367dfc66 100644 --- a/components/cli/cli/command/container/create_test.go +++ b/components/cli/cli/command/container/create_test.go @@ -2,6 +2,7 @@ package container import ( "context" + "fmt" "io" "io/ioutil" "os" @@ -10,23 +11,24 @@ import ( "testing" "github.com/docker/cli/internal/test" - "github.com/docker/cli/internal/test/testutil" + "github.com/docker/cli/internal/test/notary" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/network" + "github.com/google/go-cmp/cmp" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" "github.com/gotestyourself/gotestyourself/fs" "github.com/pkg/errors" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" ) func TestCIDFileNoOPWithNoFilename(t *testing.T) { file, err := newCIDFile("") - require.NoError(t, err) - assert.Equal(t, &cidFile{}, file) + assert.NilError(t, err) + assert.DeepEqual(t, &cidFile{}, file, cmp.AllowUnexported(cidFile{})) - assert.NoError(t, file.Write("id")) - assert.NoError(t, file.Close()) + assert.NilError(t, file.Write("id")) + assert.NilError(t, file.Close()) } func TestNewCIDFileWhenFileAlreadyExists(t *testing.T) { @@ -34,7 +36,7 @@ func TestNewCIDFileWhenFileAlreadyExists(t *testing.T) { defer tempfile.Remove() _, err := newCIDFile(tempfile.Path()) - testutil.ErrorContains(t, err, "Container ID file found") + assert.ErrorContains(t, err, "Container ID file found") } func TestCIDFileCloseWithNoWrite(t *testing.T) { @@ -43,12 +45,12 @@ func TestCIDFileCloseWithNoWrite(t *testing.T) { path := tempdir.Join("cidfile") file, err := newCIDFile(path) - require.NoError(t, err) - assert.Equal(t, file.path, path) + assert.NilError(t, err) + assert.Check(t, is.Equal(file.path, path)) - assert.NoError(t, file.Close()) + assert.NilError(t, file.Close()) _, err = os.Stat(path) - assert.True(t, os.IsNotExist(err)) + assert.Check(t, os.IsNotExist(err)) } func TestCIDFileCloseWithWrite(t *testing.T) { @@ -57,18 +59,18 @@ func TestCIDFileCloseWithWrite(t *testing.T) { path := tempdir.Join("cidfile") file, err := newCIDFile(path) - require.NoError(t, err) + assert.NilError(t, err) content := "id" - assert.NoError(t, file.Write(content)) + assert.NilError(t, file.Write(content)) actual, err := ioutil.ReadFile(path) - require.NoError(t, err) - assert.Equal(t, content, string(actual)) + assert.NilError(t, err) + assert.Check(t, is.Equal(content, string(actual))) - assert.NoError(t, file.Close()) + assert.NilError(t, file.Close()) _, err = os.Stat(path) - require.NoError(t, err) + assert.NilError(t, err) } func TestCreateContainerPullsImageIfMissing(t *testing.T) { @@ -107,12 +109,61 @@ func TestCreateContainerPullsImageIfMissing(t *testing.T) { }, HostConfig: &container.HostConfig{}, } - body, err := createContainer(context.Background(), cli, config, "name", runtime.GOOS) - require.NoError(t, err) + body, err := createContainer(context.Background(), cli, config, &createOptions{ + name: "name", + platform: runtime.GOOS, + untrusted: true, + }) + assert.NilError(t, err) expected := container.ContainerCreateCreatedBody{ID: containerID} - assert.Equal(t, expected, *body) + assert.Check(t, is.DeepEqual(expected, *body)) stderr := cli.ErrBuffer().String() - assert.Contains(t, stderr, "Unable to find image 'does-not-exist-locally:latest' locally") + assert.Check(t, is.Contains(stderr, "Unable to find image 'does-not-exist-locally:latest' locally")) +} + +func TestNewCreateCommandWithContentTrustErrors(t *testing.T) { + testCases := []struct { + name string + args []string + expectedError string + notaryFunc test.NotaryClientFuncType + }{ + { + name: "offline-notary-server", + notaryFunc: notary.GetOfflineNotaryRepository, + expectedError: "client is offline", + args: []string{"image:tag"}, + }, + { + name: "uninitialized-notary-server", + notaryFunc: notary.GetUninitializedNotaryRepository, + expectedError: "remote trust data does not exist", + args: []string{"image:tag"}, + }, + { + name: "empty-notary-server", + notaryFunc: notary.GetEmptyTargetsNotaryRepository, + expectedError: "No valid trust data for tag", + args: []string{"image:tag"}, + }, + } + for _, tc := range testCases { + cli := test.NewFakeCli(&fakeClient{ + createContainerFunc: func(config *container.Config, + hostConfig *container.HostConfig, + networkingConfig *network.NetworkingConfig, + containerName string, + ) (container.ContainerCreateCreatedBody, error) { + return container.ContainerCreateCreatedBody{}, fmt.Errorf("shouldn't try to pull image") + }, + }, test.EnableContentTrust) + cli.SetNotaryClient(tc.notaryFunc) + cmd := NewCreateCommand(cli) + cmd.SetOutput(ioutil.Discard) + cmd.SetArgs(tc.args) + err := cmd.Execute() + assert.ErrorContains(t, err, tc.expectedError) + } } type fakeNotFound struct{} diff --git a/components/cli/cli/command/container/exec_test.go b/components/cli/cli/command/container/exec_test.go index 492e3a558d..d40c74fff3 100644 --- a/components/cli/cli/command/container/exec_test.go +++ b/components/cli/cli/command/container/exec_test.go @@ -7,11 +7,11 @@ import ( "github.com/docker/cli/cli" "github.com/docker/cli/cli/config/configfile" "github.com/docker/cli/internal/test" - "github.com/docker/cli/internal/test/testutil" "github.com/docker/cli/opts" "github.com/docker/docker/api/types" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" "github.com/pkg/errors" - "github.com/stretchr/testify/assert" "golang.org/x/net/context" ) @@ -106,7 +106,7 @@ func TestParseExec(t *testing.T) { for _, testcase := range testcases { execConfig := parseExec(testcase.options, &testcase.configFile) - assert.Equal(t, testcase.expected, *execConfig) + assert.Check(t, is.DeepEqual(testcase.expected, *execConfig)) } } @@ -150,14 +150,14 @@ func TestRunExec(t *testing.T) { err := runExec(cli, testcase.options) if testcase.expectedError != "" { - testutil.ErrorContains(t, err, testcase.expectedError) + assert.ErrorContains(t, err, testcase.expectedError) } else { - if !assert.NoError(t, err) { + if !assert.Check(t, err) { return } } - assert.Equal(t, testcase.expectedOut, cli.OutBuffer().String()) - assert.Equal(t, testcase.expectedErr, cli.ErrBuffer().String()) + assert.Check(t, is.Equal(testcase.expectedOut, cli.OutBuffer().String())) + assert.Check(t, is.Equal(testcase.expectedErr, cli.ErrBuffer().String())) }) } } @@ -192,12 +192,12 @@ func TestGetExecExitStatus(t *testing.T) { for _, testcase := range testcases { client := &fakeClient{ execInspectFunc: func(id string) (types.ContainerExecInspect, error) { - assert.Equal(t, execID, id) + assert.Check(t, is.Equal(execID, id)) return types.ContainerExecInspect{ExitCode: testcase.exitCode}, testcase.inspectError }, } err := getExecExitStatus(context.Background(), client, execID) - assert.Equal(t, testcase.expectedError, err) + assert.Check(t, is.Equal(testcase.expectedError, err)) } } @@ -222,6 +222,6 @@ func TestNewExecCommandErrors(t *testing.T) { cmd := NewExecCommand(cli) cmd.SetOutput(ioutil.Discard) cmd.SetArgs(tc.args) - testutil.ErrorContains(t, cmd.Execute(), tc.expectedError) + assert.ErrorContains(t, cmd.Execute(), tc.expectedError) } } diff --git a/components/cli/cli/command/container/list_test.go b/components/cli/cli/command/container/list_test.go index 848d2a60a9..9a064eb735 100644 --- a/components/cli/cli/command/container/list_test.go +++ b/components/cli/cli/command/container/list_test.go @@ -7,11 +7,10 @@ import ( "github.com/docker/cli/cli/config/configfile" "github.com/docker/cli/internal/test" - "github.com/docker/cli/internal/test/testutil" "github.com/docker/docker/api/types" - "github.com/stretchr/testify/assert" // Import builders to get the builder function as package function . "github.com/docker/cli/internal/test/builders" + "github.com/gotestyourself/gotestyourself/assert" "github.com/gotestyourself/gotestyourself/golden" ) @@ -52,7 +51,7 @@ func TestContainerListErrors(t *testing.T) { cmd.Flags().Set(key, value) } cmd.SetOutput(ioutil.Discard) - testutil.ErrorContains(t, cmd.Execute(), tc.expectedError) + assert.ErrorContains(t, cmd.Execute(), tc.expectedError) } } @@ -69,7 +68,7 @@ func TestContainerListWithoutFormat(t *testing.T) { }, }) cmd := newListCommand(cli) - assert.NoError(t, cmd.Execute()) + assert.NilError(t, cmd.Execute()) golden.Assert(t, cli.OutBuffer().String(), "container-list-without-format.golden") } @@ -84,7 +83,7 @@ func TestContainerListNoTrunc(t *testing.T) { }) cmd := newListCommand(cli) cmd.Flags().Set("no-trunc", "true") - assert.NoError(t, cmd.Execute()) + assert.NilError(t, cmd.Execute()) golden.Assert(t, cli.OutBuffer().String(), "container-list-without-format-no-trunc.golden") } @@ -100,7 +99,7 @@ func TestContainerListNamesMultipleTime(t *testing.T) { }) cmd := newListCommand(cli) cmd.Flags().Set("format", "{{.Names}} {{.Names}}") - assert.NoError(t, cmd.Execute()) + assert.NilError(t, cmd.Execute()) golden.Assert(t, cli.OutBuffer().String(), "container-list-format-name-name.golden") } @@ -116,20 +115,20 @@ func TestContainerListFormatTemplateWithArg(t *testing.T) { }) cmd := newListCommand(cli) cmd.Flags().Set("format", `{{.Names}} {{.Label "some.label"}}`) - assert.NoError(t, cmd.Execute()) + assert.NilError(t, cmd.Execute()) golden.Assert(t, cli.OutBuffer().String(), "container-list-format-with-arg.golden") } func TestContainerListFormatSizeSetsOption(t *testing.T) { cli := test.NewFakeCli(&fakeClient{ containerListFunc: func(options types.ContainerListOptions) ([]types.Container, error) { - assert.True(t, options.Size) + assert.Check(t, options.Size) return []types.Container{}, nil }, }) cmd := newListCommand(cli) cmd.Flags().Set("format", `{{.Size}}`) - assert.NoError(t, cmd.Execute()) + assert.NilError(t, cmd.Execute()) } func TestContainerListWithConfigFormat(t *testing.T) { @@ -145,7 +144,7 @@ func TestContainerListWithConfigFormat(t *testing.T) { PsFormat: "{{ .Names }} {{ .Image }} {{ .Labels }}", }) cmd := newListCommand(cli) - assert.NoError(t, cmd.Execute()) + assert.NilError(t, cmd.Execute()) golden.Assert(t, cli.OutBuffer().String(), "container-list-with-config-format.golden") } @@ -160,6 +159,6 @@ func TestContainerListWithFormat(t *testing.T) { }) cmd := newListCommand(cli) cmd.Flags().Set("format", "{{ .Names }} {{ .Image }} {{ .Labels }}") - assert.NoError(t, cmd.Execute()) + assert.NilError(t, cmd.Execute()) golden.Assert(t, cli.OutBuffer().String(), "container-list-with-format.golden") } diff --git a/components/cli/cli/command/container/logs_test.go b/components/cli/cli/command/container/logs_test.go index da64802454..4465df9a56 100644 --- a/components/cli/cli/command/container/logs_test.go +++ b/components/cli/cli/command/container/logs_test.go @@ -7,10 +7,10 @@ import ( "testing" "github.com/docker/cli/internal/test" - "github.com/docker/cli/internal/test/testutil" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/container" - "github.com/stretchr/testify/assert" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" ) var logFn = func(expectedOut string) func(string, types.ContainerLogsOptions) (io.ReadCloser, error) { @@ -49,14 +49,14 @@ func TestRunLogs(t *testing.T) { err := runLogs(cli, testcase.options) if testcase.expectedError != "" { - testutil.ErrorContains(t, err, testcase.expectedError) + assert.ErrorContains(t, err, testcase.expectedError) } else { - if !assert.NoError(t, err) { + if !assert.Check(t, err) { return } } - assert.Equal(t, testcase.expectedOut, cli.OutBuffer().String()) - assert.Equal(t, testcase.expectedErr, cli.ErrBuffer().String()) + assert.Check(t, is.Equal(testcase.expectedOut, cli.OutBuffer().String())) + assert.Check(t, is.Equal(testcase.expectedErr, cli.ErrBuffer().String())) }) } } diff --git a/components/cli/cli/command/container/opts_test.go b/components/cli/cli/command/container/opts_test.go index 9403898660..5d43409662 100644 --- a/components/cli/cli/command/container/opts_test.go +++ b/components/cli/cli/command/container/opts_test.go @@ -9,14 +9,13 @@ import ( "testing" "time" - "github.com/docker/cli/internal/test/testutil" "github.com/docker/docker/api/types/container" networktypes "github.com/docker/docker/api/types/network" "github.com/docker/go-connections/nat" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" "github.com/pkg/errors" "github.com/spf13/pflag" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" ) func TestValidateAttach(t *testing.T) { @@ -67,12 +66,12 @@ func setupRunFlags() (*pflag.FlagSet, *containerOptions) { func parseMustError(t *testing.T, args string) { _, _, _, err := parseRun(strings.Split(args+" ubuntu bash", " ")) - assert.Error(t, err, args) + assert.ErrorContains(t, err, "", args) } func mustParse(t *testing.T, args string) (*container.Config, *container.HostConfig) { config, hostConfig, _, err := parseRun(append(strings.Split(args, " "), "ubuntu", "bash")) - assert.NoError(t, err) + assert.NilError(t, err) return config, hostConfig } @@ -236,23 +235,23 @@ func TestRunFlagsParseWithMemory(t *testing.T) { flags, _ := setupRunFlags() args := []string{"--memory=invalid", "img", "cmd"} err := flags.Parse(args) - testutil.ErrorContains(t, err, `invalid argument "invalid" for "-m, --memory" flag`) + assert.ErrorContains(t, err, `invalid argument "invalid" for "-m, --memory" flag`) _, hostconfig := mustParse(t, "--memory=1G") - assert.Equal(t, int64(1073741824), hostconfig.Memory) + assert.Check(t, is.Equal(int64(1073741824), hostconfig.Memory)) } func TestParseWithMemorySwap(t *testing.T) { flags, _ := setupRunFlags() args := []string{"--memory-swap=invalid", "img", "cmd"} err := flags.Parse(args) - testutil.ErrorContains(t, err, `invalid argument "invalid" for "--memory-swap" flag`) + assert.ErrorContains(t, err, `invalid argument "invalid" for "--memory-swap" flag`) _, hostconfig := mustParse(t, "--memory-swap=1G") - assert.Equal(t, int64(1073741824), hostconfig.MemorySwap) + assert.Check(t, is.Equal(int64(1073741824), hostconfig.MemorySwap)) _, hostconfig = mustParse(t, "--memory-swap=-1") - assert.Equal(t, int64(-1), hostconfig.MemorySwap) + assert.Check(t, is.Equal(int64(-1), hostconfig.MemorySwap)) } func TestParseHostname(t *testing.T) { @@ -373,24 +372,24 @@ func TestParseModes(t *testing.T) { // pid ko flags, copts := setupRunFlags() args := []string{"--pid=container:", "img", "cmd"} - require.NoError(t, flags.Parse(args)) + assert.NilError(t, flags.Parse(args)) _, err := parse(flags, copts) - testutil.ErrorContains(t, err, "--pid: invalid PID mode") + assert.ErrorContains(t, err, "--pid: invalid PID mode") // pid ok _, hostconfig, _, err := parseRun([]string{"--pid=host", "img", "cmd"}) - require.NoError(t, err) + assert.NilError(t, err) if !hostconfig.PidMode.Valid() { t.Fatalf("Expected a valid PidMode, got %v", hostconfig.PidMode) } // uts ko _, _, _, err = parseRun([]string{"--uts=container:", "img", "cmd"}) - testutil.ErrorContains(t, err, "--uts: invalid UTS mode") + assert.ErrorContains(t, err, "--uts: invalid UTS mode") // uts ok _, hostconfig, _, err = parseRun([]string{"--uts=host", "img", "cmd"}) - require.NoError(t, err) + assert.NilError(t, err) if !hostconfig.UTSMode.Valid() { t.Fatalf("Expected a valid UTSMode, got %v", hostconfig.UTSMode) } @@ -402,11 +401,11 @@ func TestRunFlagsParseShmSize(t *testing.T) { args := []string{"--shm-size=a128m", "img", "cmd"} expectedErr := `invalid argument "a128m" for "--shm-size" flag: invalid size: 'a128m'` err := flags.Parse(args) - testutil.ErrorContains(t, err, expectedErr) + assert.ErrorContains(t, err, expectedErr) // shm-size ok _, hostconfig, _, err := parseRun([]string{"--shm-size=128m", "img", "cmd"}) - require.NoError(t, err) + assert.NilError(t, err) if hostconfig.ShmSize != 134217728 { t.Fatalf("Expected a valid ShmSize, got %d", hostconfig.ShmSize) } diff --git a/components/cli/cli/command/container/ps_test.go b/components/cli/cli/command/container/ps_test.go index d5a51d92eb..4ca11dcb41 100644 --- a/components/cli/cli/command/container/ps_test.go +++ b/components/cli/cli/command/container/ps_test.go @@ -4,13 +4,14 @@ import ( "testing" "github.com/docker/cli/opts" - "github.com/stretchr/testify/assert" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" ) func TestBuildContainerListOptions(t *testing.T) { filters := opts.NewFilterOpt() - assert.NoError(t, filters.Set("foo=bar")) - assert.NoError(t, filters.Set("baz=foo")) + assert.NilError(t, filters.Set("foo=bar")) + assert.NilError(t, filters.Set("baz=foo")) contexts := []struct { psOpts *psOptions @@ -101,12 +102,12 @@ func TestBuildContainerListOptions(t *testing.T) { for _, c := range contexts { options, err := buildContainerListOptions(c.psOpts) - assert.NoError(t, err) + assert.NilError(t, err) - assert.Equal(t, c.expectedAll, options.All) - assert.Equal(t, c.expectedSize, options.Size) - assert.Equal(t, c.expectedLimit, options.Limit) - assert.Equal(t, len(c.expectedFilters), options.Filters.Len()) + assert.Check(t, is.Equal(c.expectedAll, options.All)) + assert.Check(t, is.Equal(c.expectedSize, options.Size)) + assert.Check(t, is.Equal(c.expectedLimit, options.Limit)) + assert.Check(t, is.Equal(len(c.expectedFilters), options.Filters.Len())) for k, v := range c.expectedFilters { f := options.Filters diff --git a/components/cli/cli/command/container/run.go b/components/cli/cli/command/container/run.go index 40fecdc717..10b7ab8d9a 100644 --- a/components/cli/cli/command/container/run.go +++ b/components/cli/cli/command/container/run.go @@ -25,11 +25,10 @@ import ( ) type runOptions struct { + createOptions detach bool sigProxy bool - name string detachKeys string - platform string } // NewRunCommand create a new `docker run` command @@ -64,7 +63,7 @@ func NewRunCommand(dockerCli command.Cli) *cobra.Command { flags.Bool("help", false, "Print usage") command.AddPlatformFlag(flags, &opts.platform) - command.AddTrustVerificationFlags(flags) + command.AddTrustVerificationFlags(flags, &opts.untrusted, dockerCli.ContentTrustEnabled()) copts = addFlags(flags) return cmd } @@ -162,7 +161,7 @@ func runContainer(dockerCli command.Cli, opts *runOptions, copts *containerOptio ctx, cancelFun := context.WithCancel(context.Background()) - createResponse, err := createContainer(ctx, dockerCli, containerConfig, opts.name, opts.platform) + createResponse, err := createContainer(ctx, dockerCli, containerConfig, &opts.createOptions) if err != nil { reportError(stderr, cmdPath, err.Error(), true) return runStartContainerErr(err) diff --git a/components/cli/cli/command/container/run_test.go b/components/cli/cli/command/container/run_test.go index ca08fb3362..94dea46805 100644 --- a/components/cli/cli/command/container/run_test.go +++ b/components/cli/cli/command/container/run_test.go @@ -1,12 +1,15 @@ package container import ( + "fmt" "testing" "github.com/docker/cli/internal/test" + "github.com/docker/cli/internal/test/notary" "github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/network" - "github.com/stretchr/testify/assert" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" ) func TestRunLabel(t *testing.T) { @@ -21,5 +24,50 @@ func TestRunLabel(t *testing.T) { cmd := NewRunCommand(cli) cmd.Flags().Set("detach", "true") cmd.SetArgs([]string{"--label", "foo", "busybox"}) - assert.NoError(t, cmd.Execute()) + assert.NilError(t, cmd.Execute()) +} + +func TestRunCommandWithContentTrustErrors(t *testing.T) { + testCases := []struct { + name string + args []string + expectedError string + notaryFunc test.NotaryClientFuncType + }{ + { + name: "offline-notary-server", + notaryFunc: notary.GetOfflineNotaryRepository, + expectedError: "client is offline", + args: []string{"image:tag"}, + }, + { + name: "uninitialized-notary-server", + notaryFunc: notary.GetUninitializedNotaryRepository, + expectedError: "remote trust data does not exist", + args: []string{"image:tag"}, + }, + { + name: "empty-notary-server", + notaryFunc: notary.GetEmptyTargetsNotaryRepository, + expectedError: "No valid trust data for tag", + args: []string{"image:tag"}, + }, + } + for _, tc := range testCases { + cli := test.NewFakeCli(&fakeClient{ + createContainerFunc: func(config *container.Config, + hostConfig *container.HostConfig, + networkingConfig *network.NetworkingConfig, + containerName string, + ) (container.ContainerCreateCreatedBody, error) { + return container.ContainerCreateCreatedBody{}, fmt.Errorf("shouldn't try to pull image") + }, + }, test.EnableContentTrust) + cli.SetNotaryClient(tc.notaryFunc) + cmd := NewRunCommand(cli) + cmd.SetArgs(tc.args) + err := cmd.Execute() + assert.Assert(t, err != nil) + assert.Assert(t, is.Contains(cli.ErrBuffer().String(), tc.expectedError)) + } } diff --git a/components/cli/cli/command/container/stats_helpers_test.go b/components/cli/cli/command/container/stats_helpers_test.go index 0425a3ba13..099f425d49 100644 --- a/components/cli/cli/command/container/stats_helpers_test.go +++ b/components/cli/cli/command/container/stats_helpers_test.go @@ -1,10 +1,11 @@ package container import ( + "fmt" "testing" "github.com/docker/docker/api/types" - "github.com/stretchr/testify/assert" + "github.com/gotestyourself/gotestyourself/assert" ) func TestCalculateMemUsageUnixNoCache(t *testing.T) { @@ -15,7 +16,7 @@ func TestCalculateMemUsageUnixNoCache(t *testing.T) { result := calculateMemUsageUnixNoCache(stats) // Then - assert.InDelta(t, 100.0, result, 1e-6) + assert.Assert(t, inDelta(100.0, result, 1e-6)) } func TestCalculateMemPercentUnixNoCache(t *testing.T) { @@ -27,10 +28,20 @@ func TestCalculateMemPercentUnixNoCache(t *testing.T) { // When and Then t.Run("Limit is set", func(t *testing.T) { result := calculateMemPercentUnixNoCache(someLimit, used) - assert.InDelta(t, 70.0, result, 1e-6) + assert.Assert(t, inDelta(70.0, result, 1e-6)) }) t.Run("No limit, no cgroup data", func(t *testing.T) { result := calculateMemPercentUnixNoCache(noLimit, used) - assert.InDelta(t, 0.0, result, 1e-6) + assert.Assert(t, inDelta(0.0, result, 1e-6)) }) } + +func inDelta(x, y, delta float64) func() (bool, string) { + return func() (bool, string) { + diff := x - y + if diff < -delta || diff > delta { + return false, fmt.Sprintf("%f != %f within %f", x, y, delta) + } + return true, "" + } +} diff --git a/components/cli/cli/command/container/utils_test.go b/components/cli/cli/command/container/utils_test.go index 13c43ff074..c4aa3eddc7 100644 --- a/components/cli/cli/command/container/utils_test.go +++ b/components/cli/cli/command/container/utils_test.go @@ -7,8 +7,9 @@ import ( "github.com/docker/cli/internal/test" "github.com/docker/docker/api" "github.com/docker/docker/api/types/container" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" "github.com/pkg/errors" - "github.com/stretchr/testify/assert" "golang.org/x/net/context" ) @@ -64,6 +65,6 @@ func TestWaitExitOrRemoved(t *testing.T) { for _, testcase := range testcases { statusC := waitExitOrRemoved(context.Background(), client, testcase.cid, true) exitCode := <-statusC - assert.Equal(t, testcase.exitCode, exitCode) + assert.Check(t, is.Equal(testcase.exitCode, exitCode)) } } diff --git a/components/cli/cli/command/formatter/checkpoint_test.go b/components/cli/cli/command/formatter/checkpoint_test.go index e88c4d0132..26c517d580 100644 --- a/components/cli/cli/command/formatter/checkpoint_test.go +++ b/components/cli/cli/command/formatter/checkpoint_test.go @@ -5,7 +5,7 @@ import ( "testing" "github.com/docker/docker/api/types" - "github.com/stretchr/testify/assert" + "github.com/gotestyourself/gotestyourself/assert" ) func TestCheckpointContextFormatWrite(t *testing.T) { @@ -46,10 +46,7 @@ checkpoint-3: out := bytes.NewBufferString("") testcase.context.Output = out err := CheckpointWrite(testcase.context, checkpoints) - if err != nil { - assert.Error(t, err, testcase.expected) - } else { - assert.Equal(t, out.String(), testcase.expected) - } + assert.NilError(t, err) + assert.Equal(t, out.String(), testcase.expected) } } diff --git a/components/cli/cli/command/formatter/config_test.go b/components/cli/cli/command/formatter/config_test.go index 227f454ffa..3a5efb80d2 100644 --- a/components/cli/cli/command/formatter/config_test.go +++ b/components/cli/cli/command/formatter/config_test.go @@ -6,7 +6,8 @@ import ( "time" "github.com/docker/docker/api/types/swarm" - "github.com/stretchr/testify/assert" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" ) func TestConfigContextFormatWrite(t *testing.T) { @@ -55,9 +56,9 @@ id_rsa out := bytes.NewBufferString("") testcase.context.Output = out if err := ConfigWrite(testcase.context, configs); err != nil { - assert.Error(t, err, testcase.expected) + assert.ErrorContains(t, err, testcase.expected) } else { - assert.Equal(t, out.String(), testcase.expected) + assert.Check(t, is.Equal(out.String(), testcase.expected)) } } } diff --git a/components/cli/cli/command/formatter/container_test.go b/components/cli/cli/command/formatter/container_test.go index cccd6cb62d..8b1893f8d5 100644 --- a/components/cli/cli/command/formatter/container_test.go +++ b/components/cli/cli/command/formatter/container_test.go @@ -10,9 +10,9 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/pkg/stringid" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" "github.com/gotestyourself/gotestyourself/golden" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" ) func TestContainerPsContext(t *testing.T) { @@ -244,9 +244,9 @@ size: 0B testcase.context.Output = out err := ContainerWrite(testcase.context, containers) if err != nil { - assert.EqualError(t, err, testcase.expected) + assert.Error(t, err, testcase.expected) } else { - assert.Equal(t, testcase.expected, out.String()) + assert.Check(t, is.Equal(testcase.expected, out.String())) } } } @@ -305,7 +305,7 @@ func TestContainerContextWriteWithNoContainers(t *testing.T) { for _, context := range contexts { ContainerWrite(context.context, containers) - assert.Equal(t, context.expected, out.String()) + assert.Check(t, is.Equal(context.expected, out.String())) // Clean buffer out.Reset() } @@ -359,8 +359,8 @@ func TestContainerContextWriteJSON(t *testing.T) { msg := fmt.Sprintf("Output: line %d: %s", i, line) var m map[string]interface{} err := json.Unmarshal([]byte(line), &m) - require.NoError(t, err, msg) - assert.Equal(t, expectedJSONs[i], m, msg) + assert.NilError(t, err, msg) + assert.Check(t, is.DeepEqual(expectedJSONs[i], m), msg) } } @@ -378,8 +378,8 @@ func TestContainerContextWriteJSONField(t *testing.T) { msg := fmt.Sprintf("Output: line %d: %s", i, line) var s string err := json.Unmarshal([]byte(line), &s) - require.NoError(t, err, msg) - assert.Equal(t, containers[i].ID, s, msg) + assert.NilError(t, err, msg) + assert.Check(t, is.Equal(containers[i].ID, s), msg) } } @@ -653,6 +653,6 @@ func TestDisplayablePorts(t *testing.T) { for _, port := range cases { actual := DisplayablePorts(port.ports) - assert.Equal(t, port.expected, actual) + assert.Check(t, is.Equal(port.expected, actual)) } } diff --git a/components/cli/cli/command/formatter/custom_test.go b/components/cli/cli/command/formatter/custom_test.go index a9f6ccdac9..02df8d9f85 100644 --- a/components/cli/cli/command/formatter/custom_test.go +++ b/components/cli/cli/command/formatter/custom_test.go @@ -4,7 +4,8 @@ import ( "strings" "testing" - "github.com/stretchr/testify/assert" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" ) func compareMultipleValues(t *testing.T, value, expected string) { @@ -23,5 +24,5 @@ func compareMultipleValues(t *testing.T, value, expected string) { keyval := strings.Split(expected, "=") expMap[keyval[0]] = keyval[1] } - assert.Equal(t, expMap, entriesMap) + assert.Check(t, is.DeepEqual(expMap, entriesMap)) } diff --git a/components/cli/cli/command/formatter/diff_test.go b/components/cli/cli/command/formatter/diff_test.go index 1aa7b53056..c1f6650beb 100644 --- a/components/cli/cli/command/formatter/diff_test.go +++ b/components/cli/cli/command/formatter/diff_test.go @@ -6,7 +6,8 @@ import ( "github.com/docker/docker/api/types/container" "github.com/docker/docker/pkg/archive" - "github.com/stretchr/testify/assert" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" ) func TestDiffContextFormatWrite(t *testing.T) { @@ -51,9 +52,9 @@ D: /usr/app/old_app.js testcase.context.Output = out err := DiffWrite(testcase.context, diffs) if err != nil { - assert.EqualError(t, err, testcase.expected) + assert.Error(t, err, testcase.expected) } else { - assert.Equal(t, testcase.expected, out.String()) + assert.Check(t, is.Equal(testcase.expected, out.String())) } } } diff --git a/components/cli/cli/command/formatter/disk_usage_test.go b/components/cli/cli/command/formatter/disk_usage_test.go index 293fe6d82a..dc206dea4c 100644 --- a/components/cli/cli/command/formatter/disk_usage_test.go +++ b/components/cli/cli/command/formatter/disk_usage_test.go @@ -4,8 +4,9 @@ import ( "bytes" "testing" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" "github.com/gotestyourself/gotestyourself/golden" - "github.com/stretchr/testify/assert" ) func TestDiskUsageContextFormatWrite(t *testing.T) { @@ -101,9 +102,9 @@ Build Cache 0B out := bytes.NewBufferString("") testcase.context.Output = out if err := testcase.context.Write(); err != nil { - assert.Equal(t, testcase.expected, err.Error()) + assert.Check(t, is.Equal(testcase.expected, err.Error())) } else { - assert.Equal(t, testcase.expected, out.String()) + assert.Check(t, is.Equal(testcase.expected, out.String())) } } } diff --git a/components/cli/cli/command/formatter/displayutils_test.go b/components/cli/cli/command/formatter/displayutils_test.go index d0bdca9d26..0182d70759 100644 --- a/components/cli/cli/command/formatter/displayutils_test.go +++ b/components/cli/cli/command/formatter/displayutils_test.go @@ -3,7 +3,8 @@ package formatter import ( "testing" - "github.com/stretchr/testify/assert" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" ) func TestEllipsis(t *testing.T) { @@ -25,6 +26,6 @@ func TestEllipsis(t *testing.T) { } for _, testcase := range testcases { - assert.Equal(t, testcase.expected, Ellipsis(testcase.source, testcase.width)) + assert.Check(t, is.Equal(testcase.expected, Ellipsis(testcase.source, testcase.width))) } } diff --git a/components/cli/cli/command/formatter/history_test.go b/components/cli/cli/command/formatter/history_test.go index dcbdc027da..77423a8010 100644 --- a/components/cli/cli/command/formatter/history_test.go +++ b/components/cli/cli/command/formatter/history_test.go @@ -1,16 +1,16 @@ package formatter import ( + "bytes" "strconv" "strings" "testing" "time" - "bytes" - "github.com/docker/docker/api/types/image" "github.com/docker/docker/pkg/stringid" - "github.com/stretchr/testify/assert" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" ) type historyCase struct { @@ -50,6 +50,7 @@ func TestHistoryContext_ID(t *testing.T) { } func TestHistoryContext_CreatedSince(t *testing.T) { + dateStr := "2009-11-10T23:00:00Z" var ctx historyContext cases := []historyCase{ { @@ -64,7 +65,7 @@ func TestHistoryContext_CreatedSince(t *testing.T) { h: image.HistoryResponseItem{Created: time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC).Unix()}, trunc: false, human: false, - }, "2009-11-10T23:00:00Z", ctx.CreatedSince, + }, dateStr, ctx.CreatedSince, }, } @@ -218,7 +219,7 @@ imageID4 24 hours ago /bin/bash grep for _, context := range contexts { HistoryWrite(context.context, true, histories) - assert.Equal(t, context.expected, out.String()) + assert.Check(t, is.Equal(context.expected, out.String())) // Clean buffer out.Reset() } diff --git a/components/cli/cli/command/formatter/image_test.go b/components/cli/cli/command/formatter/image_test.go index 20b73a52c1..26488f335a 100644 --- a/components/cli/cli/command/formatter/image_test.go +++ b/components/cli/cli/command/formatter/image_test.go @@ -9,7 +9,8 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/pkg/stringid" - "github.com/stretchr/testify/assert" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" ) func TestImageContext(t *testing.T) { @@ -83,7 +84,7 @@ func TestImageContext(t *testing.T) { if strings.Contains(v, ",") { compareMultipleValues(t, v, c.expValue) } else { - assert.Equal(t, c.expValue, v) + assert.Check(t, is.Equal(c.expValue, v)) } } } @@ -293,9 +294,9 @@ image_id: imageID3 testcase.context.Output = out err := ImageWrite(testcase.context, images) if err != nil { - assert.EqualError(t, err, testcase.expected) + assert.Error(t, err, testcase.expected) } else { - assert.Equal(t, testcase.expected, out.String()) + assert.Check(t, is.Equal(testcase.expected, out.String())) } } } @@ -348,7 +349,7 @@ func TestImageContextWriteWithNoImage(t *testing.T) { for _, context := range contexts { ImageWrite(context.context, images) - assert.Equal(t, context.expected, out.String()) + assert.Check(t, is.Equal(context.expected, out.String())) // Clean buffer out.Reset() } diff --git a/components/cli/cli/command/formatter/network_test.go b/components/cli/cli/command/formatter/network_test.go index 201838cf74..79b5880543 100644 --- a/components/cli/cli/command/formatter/network_test.go +++ b/components/cli/cli/command/formatter/network_test.go @@ -10,8 +10,8 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/pkg/stringid" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" ) func TestNetworkContext(t *testing.T) { @@ -162,9 +162,9 @@ foobar_bar 2017-01-01 00:00:00 +0000 UTC testcase.context.Output = out err := NetworkWrite(testcase.context, networks) if err != nil { - assert.EqualError(t, err, testcase.expected) + assert.Error(t, err, testcase.expected) } else { - assert.Equal(t, testcase.expected, out.String()) + assert.Check(t, is.Equal(testcase.expected, out.String())) } } } @@ -188,8 +188,8 @@ func TestNetworkContextWriteJSON(t *testing.T) { msg := fmt.Sprintf("Output: line %d: %s", i, line) var m map[string]interface{} err := json.Unmarshal([]byte(line), &m) - require.NoError(t, err, msg) - assert.Equal(t, expectedJSONs[i], m, msg) + assert.NilError(t, err, msg) + assert.Check(t, is.DeepEqual(expectedJSONs[i], m), msg) } } @@ -207,7 +207,7 @@ func TestNetworkContextWriteJSONField(t *testing.T) { msg := fmt.Sprintf("Output: line %d: %s", i, line) var s string err := json.Unmarshal([]byte(line), &s) - require.NoError(t, err, msg) - assert.Equal(t, networks[i].ID, s, msg) + assert.NilError(t, err, msg) + assert.Check(t, is.Equal(networks[i].ID, s), msg) } } diff --git a/components/cli/cli/command/formatter/node_test.go b/components/cli/cli/command/formatter/node_test.go index 768f89f65b..3fa9ceb785 100644 --- a/components/cli/cli/command/formatter/node_test.go +++ b/components/cli/cli/command/formatter/node_test.go @@ -10,8 +10,8 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/swarm" "github.com/docker/docker/pkg/stringid" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" ) func TestNodeContext(t *testing.T) { @@ -203,9 +203,9 @@ foobar_boo Unknown testcase.context.Output = out err := NodeWrite(testcase.context, nodes, types.Info{Swarm: swarm.Info{Cluster: &testcase.clusterInfo}}) if err != nil { - assert.EqualError(t, err, testcase.expected) + assert.Error(t, err, testcase.expected) } else { - assert.Equal(t, testcase.expected, out.String()) + assert.Check(t, is.Equal(testcase.expected, out.String())) } } } @@ -255,8 +255,8 @@ func TestNodeContextWriteJSON(t *testing.T) { msg := fmt.Sprintf("Output: line %d: %s", i, line) var m map[string]interface{} err := json.Unmarshal([]byte(line), &m) - require.NoError(t, err, msg) - assert.Equal(t, testcase.expected[i], m, msg) + assert.NilError(t, err, msg) + assert.Check(t, is.DeepEqual(testcase.expected[i], m), msg) } } } @@ -275,8 +275,8 @@ func TestNodeContextWriteJSONField(t *testing.T) { msg := fmt.Sprintf("Output: line %d: %s", i, line) var s string err := json.Unmarshal([]byte(line), &s) - require.NoError(t, err, msg) - assert.Equal(t, nodes[i].ID, s, msg) + assert.NilError(t, err, msg) + assert.Check(t, is.Equal(nodes[i].ID, s), msg) } } @@ -344,5 +344,5 @@ data Issuer Subject: c3ViamVjdA== Issuer Public Key: cHViS2V5 ` - assert.Equal(t, expected, out.String()) + assert.Check(t, is.Equal(expected, out.String())) } diff --git a/components/cli/cli/command/formatter/plugin_test.go b/components/cli/cli/command/formatter/plugin_test.go index 607262dcc9..ebe615dd13 100644 --- a/components/cli/cli/command/formatter/plugin_test.go +++ b/components/cli/cli/command/formatter/plugin_test.go @@ -8,7 +8,8 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/pkg/stringid" - "github.com/stretchr/testify/assert" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" ) func TestPluginContext(t *testing.T) { @@ -131,9 +132,9 @@ foobar_bar testcase.context.Output = out err := PluginWrite(testcase.context, plugins) if err != nil { - assert.EqualError(t, err, testcase.expected) + assert.Error(t, err, testcase.expected) } else { - assert.Equal(t, testcase.expected, out.String()) + assert.Check(t, is.Equal(testcase.expected, out.String())) } } } @@ -158,7 +159,7 @@ func TestPluginContextWriteJSON(t *testing.T) { if err := json.Unmarshal([]byte(line), &m); err != nil { t.Fatal(err) } - assert.Equal(t, expectedJSONs[i], m) + assert.Check(t, is.DeepEqual(expectedJSONs[i], m)) } } @@ -177,6 +178,6 @@ func TestPluginContextWriteJSONField(t *testing.T) { if err := json.Unmarshal([]byte(line), &s); err != nil { t.Fatal(err) } - assert.Equal(t, plugins[i].ID, s) + assert.Check(t, is.Equal(plugins[i].ID, s)) } } diff --git a/components/cli/cli/command/formatter/search_test.go b/components/cli/cli/command/formatter/search_test.go index 10e4a82ca0..e5a2d1a91a 100644 --- a/components/cli/cli/command/formatter/search_test.go +++ b/components/cli/cli/command/formatter/search_test.go @@ -7,8 +7,9 @@ import ( "testing" registrytypes "github.com/docker/docker/api/types/registry" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" "github.com/gotestyourself/gotestyourself/golden" - "github.com/stretchr/testify/assert" ) func TestSearchContext(t *testing.T) { @@ -154,9 +155,9 @@ result2 5 testcase.context.Output = out err := SearchWrite(testcase.context, results, false, 0) if err != nil { - assert.Error(t, err, testcase.expected) + assert.Check(t, is.ErrorContains(err, testcase.expected)) } else { - assert.Equal(t, out.String(), testcase.expected) + assert.Check(t, is.Equal(out.String(), testcase.expected)) } } } @@ -191,9 +192,9 @@ result2 testcase.context.Output = out err := SearchWrite(testcase.context, results, true, 0) if err != nil { - assert.Error(t, err, testcase.expected) + assert.Check(t, is.ErrorContains(err, testcase.expected)) } else { - assert.Equal(t, out.String(), testcase.expected) + assert.Check(t, is.Equal(out.String(), testcase.expected)) } } } @@ -226,9 +227,9 @@ result1 testcase.context.Output = out err := SearchWrite(testcase.context, results, false, 6) if err != nil { - assert.Error(t, err, testcase.expected) + assert.Check(t, is.ErrorContains(err, testcase.expected)) } else { - assert.Equal(t, out.String(), testcase.expected) + assert.Check(t, is.Equal(out.String(), testcase.expected)) } } } @@ -254,7 +255,7 @@ func TestSearchContextWriteJSON(t *testing.T) { if err := json.Unmarshal([]byte(line), &m); err != nil { t.Fatal(err) } - assert.Equal(t, m, expectedJSONs[i]) + assert.Check(t, is.DeepEqual(m, expectedJSONs[i])) } } @@ -274,6 +275,6 @@ func TestSearchContextWriteJSONField(t *testing.T) { if err := json.Unmarshal([]byte(line), &s); err != nil { t.Fatal(err) } - assert.Equal(t, s, results[i].Name) + assert.Check(t, is.Equal(s, results[i].Name)) } } diff --git a/components/cli/cli/command/formatter/secret_test.go b/components/cli/cli/command/formatter/secret_test.go index 03f6ac1f57..e62793d3b1 100644 --- a/components/cli/cli/command/formatter/secret_test.go +++ b/components/cli/cli/command/formatter/secret_test.go @@ -6,7 +6,8 @@ import ( "time" "github.com/docker/docker/api/types/swarm" - "github.com/stretchr/testify/assert" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" ) func TestSecretContextFormatWrite(t *testing.T) { @@ -55,9 +56,9 @@ id_rsa out := bytes.NewBufferString("") testcase.context.Output = out if err := SecretWrite(testcase.context, secrets); err != nil { - assert.EqualError(t, err, testcase.expected) + assert.Error(t, err, testcase.expected) } else { - assert.Equal(t, testcase.expected, out.String()) + assert.Check(t, is.Equal(testcase.expected, out.String())) } } } diff --git a/components/cli/cli/command/formatter/service_test.go b/components/cli/cli/command/formatter/service_test.go index b05d806dfd..11c00c4b01 100644 --- a/components/cli/cli/command/formatter/service_test.go +++ b/components/cli/cli/command/formatter/service_test.go @@ -8,9 +8,9 @@ import ( "testing" "github.com/docker/docker/api/types/swarm" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" "github.com/gotestyourself/gotestyourself/golden" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" ) func TestServiceContextWrite(t *testing.T) { @@ -126,9 +126,9 @@ bar testcase.context.Output = out err := ServiceListWrite(testcase.context, services, info) if err != nil { - assert.EqualError(t, err, testcase.expected) + assert.Error(t, err, testcase.expected) } else { - assert.Equal(t, testcase.expected, out.String()) + assert.Check(t, is.Equal(testcase.expected, out.String())) } } } @@ -192,8 +192,8 @@ func TestServiceContextWriteJSON(t *testing.T) { msg := fmt.Sprintf("Output: line %d: %s", i, line) var m map[string]interface{} err := json.Unmarshal([]byte(line), &m) - require.NoError(t, err, msg) - assert.Equal(t, expectedJSONs[i], m, msg) + assert.NilError(t, err, msg) + assert.Check(t, is.DeepEqual(expectedJSONs[i], m), msg) } } func TestServiceContextWriteJSONField(t *testing.T) { @@ -220,8 +220,8 @@ func TestServiceContextWriteJSONField(t *testing.T) { msg := fmt.Sprintf("Output: line %d: %s", i, line) var s string err := json.Unmarshal([]byte(line), &s) - require.NoError(t, err, msg) - assert.Equal(t, services[i].Spec.Name, s, msg) + assert.NilError(t, err, msg) + assert.Check(t, is.Equal(services[i].Spec.Name, s), msg) } } @@ -355,5 +355,5 @@ func TestServiceContext_Ports(t *testing.T) { }, } - assert.Equal(t, "*:97-98->97-98/sctp, *:60-61->60-61/tcp, *:62->61/tcp, *:80-81->80/tcp, *:90-95->90-95/tcp, *:90-96->90-96/udp", c.Ports()) + assert.Check(t, is.Equal("*:97-98->97-98/sctp, *:60-61->60-61/tcp, *:62->61/tcp, *:80-81->80/tcp, *:90-95->90-95/tcp, *:90-96->90-96/udp", c.Ports())) } diff --git a/components/cli/cli/command/formatter/stack_test.go b/components/cli/cli/command/formatter/stack_test.go index b18ae7f083..3fa73fefe0 100644 --- a/components/cli/cli/command/formatter/stack_test.go +++ b/components/cli/cli/command/formatter/stack_test.go @@ -4,7 +4,8 @@ import ( "bytes" "testing" - "github.com/stretchr/testify/assert" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" ) func TestStackContextWrite(t *testing.T) { @@ -56,9 +57,9 @@ bar testcase.context.Output = out err := StackWrite(testcase.context, stacks) if err != nil { - assert.Error(t, err, testcase.expected) + assert.Check(t, is.ErrorContains(err, testcase.expected)) } else { - assert.Equal(t, out.String(), testcase.expected) + assert.Check(t, is.Equal(out.String(), testcase.expected)) } } } diff --git a/components/cli/cli/command/formatter/stats_test.go b/components/cli/cli/command/formatter/stats_test.go index fe99d33fd4..6c88fe8771 100644 --- a/components/cli/cli/command/formatter/stats_test.go +++ b/components/cli/cli/command/formatter/stats_test.go @@ -5,7 +5,8 @@ import ( "testing" "github.com/docker/docker/pkg/stringid" - "github.com/stretchr/testify/assert" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" ) func TestContainerStatsContext(t *testing.T) { @@ -116,9 +117,9 @@ container2 -- te.context.Output = &out err := ContainerStatsWrite(te.context, stats, "linux", false) if err != nil { - assert.EqualError(t, err, te.expected) + assert.Error(t, err, te.expected) } else { - assert.Equal(t, te.expected, out.String()) + assert.Check(t, is.Equal(te.expected, out.String())) } } } @@ -182,9 +183,9 @@ container2 -- -- te.context.Output = &out err := ContainerStatsWrite(te.context, stats, "windows", false) if err != nil { - assert.EqualError(t, err, te.expected) + assert.Error(t, err, te.expected) } else { - assert.Equal(t, te.expected, out.String()) + assert.Check(t, is.Equal(te.expected, out.String())) } } } @@ -221,7 +222,7 @@ func TestContainerStatsContextWriteWithNoStats(t *testing.T) { for _, context := range contexts { ContainerStatsWrite(context.context, []StatsEntry{}, "linux", false) - assert.Equal(t, context.expected, out.String()) + assert.Check(t, is.Equal(context.expected, out.String())) // Clean buffer out.Reset() } @@ -259,7 +260,7 @@ func TestContainerStatsContextWriteWithNoStatsWindows(t *testing.T) { for _, context := range contexts { ContainerStatsWrite(context.context, []StatsEntry{}, "windows", false) - assert.Equal(t, context.expected, out.String()) + assert.Check(t, is.Equal(context.expected, out.String())) // Clean buffer out.Reset() } @@ -293,7 +294,7 @@ func TestContainerStatsContextWriteTrunc(t *testing.T) { for _, context := range contexts { ContainerStatsWrite(context.context, []StatsEntry{{ID: "b95a83497c9161c9b444e3d70e1a9dfba0c1840d41720e146a95a08ebf938afc"}}, "linux", context.trunc) - assert.Equal(t, context.expected, out.String()) + assert.Check(t, is.Equal(context.expected, out.String())) // Clean buffer out.Reset() } diff --git a/components/cli/cli/command/formatter/task_test.go b/components/cli/cli/command/formatter/task_test.go index 7e8edd2990..9dc8a18bb8 100644 --- a/components/cli/cli/command/formatter/task_test.go +++ b/components/cli/cli/command/formatter/task_test.go @@ -7,8 +7,9 @@ import ( "testing" "github.com/docker/docker/api/types/swarm" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" "github.com/gotestyourself/gotestyourself/golden" - "github.com/stretchr/testify/assert" ) func TestTaskContextWrite(t *testing.T) { @@ -74,9 +75,9 @@ foobar_bar foo2 testcase.context.Output = out err := TaskWrite(testcase.context, tasks, names, nodes) if err != nil { - assert.EqualError(t, err, testcase.expected) + assert.Error(t, err, testcase.expected) } else { - assert.Equal(t, testcase.expected, out.String()) + assert.Check(t, is.Equal(testcase.expected, out.String())) } } } @@ -100,6 +101,6 @@ func TestTaskContextWriteJSONField(t *testing.T) { if err := json.Unmarshal([]byte(line), &s); err != nil { t.Fatal(err) } - assert.Equal(t, tasks[i].ID, s) + assert.Check(t, is.Equal(tasks[i].ID, s)) } } diff --git a/components/cli/cli/command/formatter/trust_test.go b/components/cli/cli/command/formatter/trust_test.go index dd1a4ff75a..4f97996a7c 100644 --- a/components/cli/cli/command/formatter/trust_test.go +++ b/components/cli/cli/command/formatter/trust_test.go @@ -5,7 +5,8 @@ import ( "testing" "github.com/docker/docker/pkg/stringid" - "github.com/stretchr/testify/assert" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" ) func TestTrustTag(t *testing.T) { @@ -126,9 +127,9 @@ tag3 bbbbbbbb testcase.context.Output = out err := TrustTagWrite(testcase.context, signedTags) if err != nil { - assert.EqualError(t, err, testcase.expected) + assert.Error(t, err, testcase.expected) } else { - assert.Equal(t, testcase.expected, out.String()) + assert.Check(t, is.Equal(testcase.expected, out.String())) } } } @@ -152,8 +153,8 @@ func TestTrustTagContextEmptyWrite(t *testing.T) { out := bytes.NewBufferString("") emptyCase.context.Output = out err := TrustTagWrite(emptyCase.context, emptySignedTags) - assert.NoError(t, err) - assert.Equal(t, emptyCase.expected, out.String()) + assert.NilError(t, err) + assert.Check(t, is.Equal(emptyCase.expected, out.String())) } func TestSignerInfoContextEmptyWrite(t *testing.T) { @@ -171,8 +172,8 @@ func TestSignerInfoContextEmptyWrite(t *testing.T) { out := bytes.NewBufferString("") emptyCase.context.Output = out err := SignerInfoWrite(emptyCase.context, emptySignerInfo) - assert.NoError(t, err) - assert.Equal(t, emptyCase.expected, out.String()) + assert.NilError(t, err) + assert.Check(t, is.Equal(emptyCase.expected, out.String())) } func TestSignerInfoContextWrite(t *testing.T) { @@ -230,9 +231,9 @@ eve foobarbazquxquux, key31, key32 testcase.context.Output = out err := SignerInfoWrite(testcase.context, signerInfo) if err != nil { - assert.EqualError(t, err, testcase.expected) + assert.Error(t, err, testcase.expected) } else { - assert.Equal(t, testcase.expected, out.String()) + assert.Check(t, is.Equal(testcase.expected, out.String())) } } } diff --git a/components/cli/cli/command/formatter/volume_test.go b/components/cli/cli/command/formatter/volume_test.go index f2c21a83c2..425aaed12d 100644 --- a/components/cli/cli/command/formatter/volume_test.go +++ b/components/cli/cli/command/formatter/volume_test.go @@ -9,8 +9,8 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/pkg/stringid" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" ) func TestVolumeContext(t *testing.T) { @@ -133,9 +133,9 @@ foobar_bar testcase.context.Output = out err := VolumeWrite(testcase.context, volumes) if err != nil { - assert.EqualError(t, err, testcase.expected) + assert.Error(t, err, testcase.expected) } else { - assert.Equal(t, testcase.expected, out.String()) + assert.Check(t, is.Equal(testcase.expected, out.String())) } } } @@ -158,8 +158,8 @@ func TestVolumeContextWriteJSON(t *testing.T) { msg := fmt.Sprintf("Output: line %d: %s", i, line) var m map[string]interface{} err := json.Unmarshal([]byte(line), &m) - require.NoError(t, err, msg) - assert.Equal(t, expectedJSONs[i], m, msg) + assert.NilError(t, err, msg) + assert.Check(t, is.DeepEqual(expectedJSONs[i], m), msg) } } @@ -177,7 +177,7 @@ func TestVolumeContextWriteJSONField(t *testing.T) { msg := fmt.Sprintf("Output: line %d: %s", i, line) var s string err := json.Unmarshal([]byte(line), &s) - require.NoError(t, err, msg) - assert.Equal(t, volumes[i].Name, s, msg) + assert.NilError(t, err, msg) + assert.Check(t, is.Equal(volumes[i].Name, s), msg) } } diff --git a/components/cli/cli/command/idresolver/idresolver_test.go b/components/cli/cli/command/idresolver/idresolver_test.go index 98bd306d16..0ef720a95c 100644 --- a/components/cli/cli/command/idresolver/idresolver_test.go +++ b/components/cli/cli/command/idresolver/idresolver_test.go @@ -4,10 +4,11 @@ import ( "testing" "github.com/docker/docker/api/types/swarm" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" // Import builders to get the builder function as package function . "github.com/docker/cli/internal/test/builders" "github.com/pkg/errors" - "github.com/stretchr/testify/assert" "golang.org/x/net/context" ) @@ -21,7 +22,7 @@ func TestResolveError(t *testing.T) { idResolver := New(cli, false) _, err := idResolver.Resolve(context.Background(), struct{}{}, "nodeID") - assert.EqualError(t, err, "unsupported type") + assert.Error(t, err, "unsupported type") } func TestResolveWithNoResolveOption(t *testing.T) { @@ -40,9 +41,9 @@ func TestResolveWithNoResolveOption(t *testing.T) { idResolver := New(cli, true) id, err := idResolver.Resolve(context.Background(), swarm.Node{}, "nodeID") - assert.NoError(t, err) - assert.Equal(t, "nodeID", id) - assert.False(t, resolved) + assert.NilError(t, err) + assert.Check(t, is.Equal("nodeID", id)) + assert.Check(t, !resolved) } func TestResolveWithCache(t *testing.T) { @@ -59,11 +60,11 @@ func TestResolveWithCache(t *testing.T) { ctx := context.Background() for i := 0; i < 2; i++ { id, err := idResolver.Resolve(ctx, swarm.Node{}, "nodeID") - assert.NoError(t, err) - assert.Equal(t, "node-foo", id) + assert.NilError(t, err) + assert.Check(t, is.Equal("node-foo", id)) } - assert.Equal(t, 1, inspectCounter) + assert.Check(t, is.Equal(1, inspectCounter)) } func TestResolveNode(t *testing.T) { @@ -103,8 +104,8 @@ func TestResolveNode(t *testing.T) { idResolver := New(cli, false) id, err := idResolver.Resolve(ctx, swarm.Node{}, tc.nodeID) - assert.NoError(t, err) - assert.Equal(t, tc.expectedID, id) + assert.NilError(t, err) + assert.Check(t, is.Equal(tc.expectedID, id)) } } @@ -138,7 +139,7 @@ func TestResolveService(t *testing.T) { idResolver := New(cli, false) id, err := idResolver.Resolve(ctx, swarm.Service{}, tc.serviceID) - assert.NoError(t, err) - assert.Equal(t, tc.expectedID, id) + assert.NilError(t, err) + assert.Check(t, is.Equal(tc.expectedID, id)) } } diff --git a/components/cli/cli/command/image/build.go b/components/cli/cli/command/image/build.go index dd901bd8ae..1e87f1ee6a 100644 --- a/components/cli/cli/command/image/build.go +++ b/components/cli/cli/command/image/build.go @@ -67,6 +67,7 @@ type buildOptions struct { imageIDFile string stream bool platform string + untrusted bool } // dockerfileFromStdin returns true when the user specified that the Dockerfile @@ -137,7 +138,7 @@ func NewBuildCommand(dockerCli command.Cli) *cobra.Command { flags.StringVar(&options.target, "target", "", "Set the target build stage to build.") flags.StringVar(&options.imageIDFile, "iidfile", "", "Write the image ID to the file") - command.AddTrustVerificationFlags(flags) + command.AddTrustVerificationFlags(flags, &options.untrusted, dockerCli.ContentTrustEnabled()) command.AddPlatformFlag(flags, &options.platform) flags.BoolVar(&options.squash, "squash", false, "Squash newly built layers into a single new layer") @@ -285,7 +286,7 @@ func runBuild(dockerCli command.Cli, options buildOptions) error { defer cancel() var resolvedTags []*resolvedTag - if command.IsTrusted() { + if !options.untrusted { translator := func(ctx context.Context, ref reference.NamedTagged) (reference.Canonical, error) { return TrustedReference(ctx, dockerCli, ref, nil) } @@ -293,10 +294,10 @@ func runBuild(dockerCli command.Cli, options buildOptions) error { if buildCtx != nil { // Wrap the tar archive to replace the Dockerfile entry with the rewritten // Dockerfile which uses trusted pulls. - buildCtx = replaceDockerfileTarWrapper(ctx, buildCtx, relDockerfile, translator, &resolvedTags) + buildCtx = replaceDockerfileForContentTrust(ctx, buildCtx, relDockerfile, translator, &resolvedTags) } else if dockerfileCtx != nil { // if there was not archive context still do the possible replacements in Dockerfile - newDockerfile, _, err := rewriteDockerfileFrom(ctx, dockerfileCtx, translator) + newDockerfile, _, err := rewriteDockerfileFromForContentTrust(ctx, dockerfileCtx, translator) if err != nil { return err } @@ -460,7 +461,7 @@ func runBuild(dockerCli command.Cli, options buildOptions) error { return err } } - if command.IsTrusted() { + if !options.untrusted { // Since the build was successful, now we must tag any of the resolved // images from the above Dockerfile rewrite. for _, resolved := range resolvedTags { @@ -499,11 +500,12 @@ type resolvedTag struct { tagRef reference.NamedTagged } -// rewriteDockerfileFrom rewrites the given Dockerfile by resolving images in +// rewriteDockerfileFromForContentTrust rewrites the given Dockerfile by resolving images in // "FROM " instructions to a digest reference. `translator` is a // function that takes a repository name and tag reference and returns a // trusted digest reference. -func rewriteDockerfileFrom(ctx context.Context, dockerfile io.Reader, translator translatorFunc) (newDockerfile []byte, resolvedTags []*resolvedTag, err error) { +// This should be called *only* when content trust is enabled +func rewriteDockerfileFromForContentTrust(ctx context.Context, dockerfile io.Reader, translator translatorFunc) (newDockerfile []byte, resolvedTags []*resolvedTag, err error) { scanner := bufio.NewScanner(dockerfile) buf := bytes.NewBuffer(nil) @@ -520,7 +522,7 @@ func rewriteDockerfileFrom(ctx context.Context, dockerfile io.Reader, translator return nil, nil, err } ref = reference.TagNameOnly(ref) - if ref, ok := ref.(reference.NamedTagged); ok && command.IsTrusted() { + if ref, ok := ref.(reference.NamedTagged); ok { trustedRef, err := translator(ctx, ref) if err != nil { return nil, nil, err @@ -543,11 +545,10 @@ func rewriteDockerfileFrom(ctx context.Context, dockerfile io.Reader, translator return buf.Bytes(), resolvedTags, scanner.Err() } -// replaceDockerfileTarWrapper wraps the given input tar archive stream and -// replaces the entry with the given Dockerfile name with the contents of the -// new Dockerfile. Returns a new tar archive stream with the replaced -// Dockerfile. -func replaceDockerfileTarWrapper(ctx context.Context, inputTarStream io.ReadCloser, dockerfileName string, translator translatorFunc, resolvedTags *[]*resolvedTag) io.ReadCloser { +// replaceDockerfileForContentTrust wraps the given input tar archive stream and +// uses the translator to replace the Dockerfile which uses a trusted reference. +// Returns a new tar archive stream with the replaced Dockerfile. +func replaceDockerfileForContentTrust(ctx context.Context, inputTarStream io.ReadCloser, dockerfileName string, translator translatorFunc, resolvedTags *[]*resolvedTag) io.ReadCloser { pipeReader, pipeWriter := io.Pipe() go func() { tarReader := tar.NewReader(inputTarStream) @@ -574,7 +575,7 @@ func replaceDockerfileTarWrapper(ctx context.Context, inputTarStream io.ReadClos // generated from a directory on the local filesystem, the // Dockerfile will only appear once in the archive. var newDockerfile []byte - newDockerfile, *resolvedTags, err = rewriteDockerfileFrom(ctx, content, translator) + newDockerfile, *resolvedTags, err = rewriteDockerfileFromForContentTrust(ctx, content, translator) if err != nil { pipeWriter.CloseWithError(err) return diff --git a/components/cli/cli/command/image/build/context_test.go b/components/cli/cli/command/image/build/context_test.go index a15b535ad7..493eda014c 100644 --- a/components/cli/cli/command/image/build/context_test.go +++ b/components/cli/cli/command/image/build/context_test.go @@ -11,10 +11,9 @@ import ( "strings" "testing" - "github.com/docker/cli/internal/test/testutil" "github.com/docker/docker/pkg/archive" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" ) const dockerfileContents = "FROM busybox" @@ -38,7 +37,7 @@ func testValidateContextDirectory(t *testing.T, prepare func(t *testing.T) (stri defer cleanup() err := ValidateContextDirectory(contextDir, excludes) - require.NoError(t, err) + assert.NilError(t, err) } func TestGetContextFromLocalDirNoDockerfile(t *testing.T) { @@ -46,7 +45,7 @@ func TestGetContextFromLocalDirNoDockerfile(t *testing.T) { defer cleanup() _, _, err := GetContextFromLocalDir(contextDir, "") - testutil.ErrorContains(t, err, "Dockerfile") + assert.ErrorContains(t, err, "Dockerfile") } func TestGetContextFromLocalDirNotExistingDir(t *testing.T) { @@ -56,7 +55,7 @@ func TestGetContextFromLocalDirNotExistingDir(t *testing.T) { fakePath := filepath.Join(contextDir, "fake") _, _, err := GetContextFromLocalDir(fakePath, "") - testutil.ErrorContains(t, err, "fake") + assert.ErrorContains(t, err, "fake") } func TestGetContextFromLocalDirNotExistingDockerfile(t *testing.T) { @@ -66,7 +65,7 @@ func TestGetContextFromLocalDirNotExistingDockerfile(t *testing.T) { fakePath := filepath.Join(contextDir, "fake") _, _, err := GetContextFromLocalDir(contextDir, fakePath) - testutil.ErrorContains(t, err, "fake") + assert.ErrorContains(t, err, "fake") } func TestGetContextFromLocalDirWithNoDirectory(t *testing.T) { @@ -79,10 +78,10 @@ func TestGetContextFromLocalDirWithNoDirectory(t *testing.T) { defer chdirCleanup() absContextDir, relDockerfile, err := GetContextFromLocalDir(contextDir, "") - require.NoError(t, err) + assert.NilError(t, err) - assert.Equal(t, contextDir, absContextDir) - assert.Equal(t, DefaultDockerfileName, relDockerfile) + assert.Check(t, is.Equal(contextDir, absContextDir)) + assert.Check(t, is.Equal(DefaultDockerfileName, relDockerfile)) } func TestGetContextFromLocalDirWithDockerfile(t *testing.T) { @@ -92,10 +91,10 @@ func TestGetContextFromLocalDirWithDockerfile(t *testing.T) { createTestTempFile(t, contextDir, DefaultDockerfileName, dockerfileContents, 0777) absContextDir, relDockerfile, err := GetContextFromLocalDir(contextDir, "") - require.NoError(t, err) + assert.NilError(t, err) - assert.Equal(t, contextDir, absContextDir) - assert.Equal(t, DefaultDockerfileName, relDockerfile) + assert.Check(t, is.Equal(contextDir, absContextDir)) + assert.Check(t, is.Equal(DefaultDockerfileName, relDockerfile)) } func TestGetContextFromLocalDirLocalFile(t *testing.T) { @@ -130,10 +129,10 @@ func TestGetContextFromLocalDirWithCustomDockerfile(t *testing.T) { createTestTempFile(t, contextDir, DefaultDockerfileName, dockerfileContents, 0777) absContextDir, relDockerfile, err := GetContextFromLocalDir(contextDir, DefaultDockerfileName) - require.NoError(t, err) + assert.NilError(t, err) - assert.Equal(t, contextDir, absContextDir) - assert.Equal(t, DefaultDockerfileName, relDockerfile) + assert.Check(t, is.Equal(contextDir, absContextDir)) + assert.Check(t, is.Equal(DefaultDockerfileName, relDockerfile)) } func TestGetContextFromReaderString(t *testing.T) { @@ -161,7 +160,7 @@ func TestGetContextFromReaderString(t *testing.T) { t.Fatalf("Tar stream too long: %s", err) } - require.NoError(t, tarArchive.Close()) + assert.NilError(t, tarArchive.Close()) if dockerfileContents != contents { t.Fatalf("Uncompressed tar archive does not equal: %s, got: %s", dockerfileContents, contents) @@ -179,15 +178,15 @@ func TestGetContextFromReaderTar(t *testing.T) { createTestTempFile(t, contextDir, DefaultDockerfileName, dockerfileContents, 0777) tarStream, err := archive.Tar(contextDir, archive.Uncompressed) - require.NoError(t, err) + assert.NilError(t, err) tarArchive, relDockerfile, err := GetContextFromReader(tarStream, DefaultDockerfileName) - require.NoError(t, err) + assert.NilError(t, err) tarReader := tar.NewReader(tarArchive) header, err := tarReader.Next() - require.NoError(t, err) + assert.NilError(t, err) if header.Name != DefaultDockerfileName { t.Fatalf("Dockerfile name should be: %s, got: %s", DefaultDockerfileName, header.Name) @@ -203,7 +202,7 @@ func TestGetContextFromReaderTar(t *testing.T) { t.Fatalf("Tar stream too long: %s", err) } - require.NoError(t, tarArchive.Close()) + assert.NilError(t, tarArchive.Close()) if dockerfileContents != contents { t.Fatalf("Uncompressed tar archive does not equal: %s, got: %s", dockerfileContents, contents) @@ -243,8 +242,8 @@ func TestValidateContextDirectoryWithOneFileExcludes(t *testing.T) { // When an error occurs, it terminates the test. func createTestTempDir(t *testing.T, dir, prefix string) (string, func()) { path, err := ioutil.TempDir(dir, prefix) - require.NoError(t, err) - return path, func() { require.NoError(t, os.RemoveAll(path)) } + assert.NilError(t, err) + return path, func() { assert.NilError(t, os.RemoveAll(path)) } } // createTestTempFile creates a temporary file within dir with specific contents and permissions. @@ -252,7 +251,7 @@ func createTestTempDir(t *testing.T, dir, prefix string) (string, func()) { func createTestTempFile(t *testing.T, dir, filename, contents string, perm os.FileMode) string { filePath := filepath.Join(dir, filename) err := ioutil.WriteFile(filePath, []byte(contents), perm) - require.NoError(t, err) + assert.NilError(t, err) return filePath } @@ -262,9 +261,9 @@ func createTestTempFile(t *testing.T, dir, filename, contents string, perm os.Fi // When an error occurs, it terminates the test. func chdir(t *testing.T, dir string) func() { workingDirectory, err := os.Getwd() - require.NoError(t, err) - require.NoError(t, os.Chdir(dir)) - return func() { require.NoError(t, os.Chdir(workingDirectory)) } + assert.NilError(t, err) + assert.NilError(t, os.Chdir(dir)) + return func() { assert.NilError(t, os.Chdir(workingDirectory)) } } func TestIsArchive(t *testing.T) { @@ -295,6 +294,6 @@ func TestIsArchive(t *testing.T) { }, } for _, testcase := range testcases { - assert.Equal(t, testcase.expected, IsArchive(testcase.header), testcase.doc) + assert.Check(t, is.Equal(testcase.expected, IsArchive(testcase.header)), testcase.doc) } } diff --git a/components/cli/cli/command/image/build_linux_test.go b/components/cli/cli/command/image/build_linux_test.go new file mode 100644 index 0000000000..30815321fa --- /dev/null +++ b/components/cli/cli/command/image/build_linux_test.go @@ -0,0 +1,55 @@ +//+build linux + +package image + +import ( + "bytes" + "io" + "io/ioutil" + "syscall" + "testing" + + "github.com/docker/cli/internal/test" + "github.com/docker/docker/api/types" + "github.com/docker/docker/pkg/archive" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" + "github.com/gotestyourself/gotestyourself/fs" + "golang.org/x/net/context" +) + +func TestRunBuildResetsUidAndGidInContext(t *testing.T) { + dest := fs.NewDir(t, "test-build-context-dest") + defer dest.Remove() + + fakeImageBuild := func(_ context.Context, context io.Reader, options types.ImageBuildOptions) (types.ImageBuildResponse, error) { + assert.NilError(t, archive.Untar(context, dest.Path(), nil)) + + body := new(bytes.Buffer) + return types.ImageBuildResponse{Body: ioutil.NopCloser(body)}, nil + } + cli := test.NewFakeCli(&fakeClient{imageBuildFunc: fakeImageBuild}) + + dir := fs.NewDir(t, "test-build-context", + fs.WithFile("foo", "some content", fs.AsUser(65534, 65534)), + fs.WithFile("Dockerfile", ` + FROM alpine:3.6 + COPY foo bar / + `), + ) + defer dir.Remove() + + options := newBuildOptions() + options.context = dir.Path() + options.untrusted = true + + err := runBuild(cli, options) + assert.NilError(t, err) + + files, err := ioutil.ReadDir(dest.Path()) + assert.NilError(t, err) + for _, fileInfo := range files { + assert.Check(t, is.Equal(uint32(0), fileInfo.Sys().(*syscall.Stat_t).Uid)) + assert.Check(t, is.Equal(uint32(0), fileInfo.Sys().(*syscall.Stat_t).Gid)) + } +} diff --git a/components/cli/cli/command/image/build_test.go b/components/cli/cli/command/image/build_test.go index 2b672fdc1d..e66e01772b 100644 --- a/components/cli/cli/command/image/build_test.go +++ b/components/cli/cli/command/image/build_test.go @@ -1,65 +1,28 @@ package image import ( + "archive/tar" "bytes" "io" "io/ioutil" "os" "path/filepath" - "runtime" "sort" - "syscall" "testing" "github.com/docker/cli/cli/command" "github.com/docker/cli/internal/test" "github.com/docker/docker/api/types" "github.com/docker/docker/pkg/archive" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" "github.com/gotestyourself/gotestyourself/fs" - "github.com/gotestyourself/gotestyourself/skip" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" "golang.org/x/net/context" ) -func TestRunBuildResetsUidAndGidInContext(t *testing.T) { - skip.IfCondition(t, runtime.GOOS == "windows", "uid and gid not relevant on windows") - dest := fs.NewDir(t, "test-build-context-dest") - defer dest.Remove() - - fakeImageBuild := func(_ context.Context, context io.Reader, options types.ImageBuildOptions) (types.ImageBuildResponse, error) { - assert.NoError(t, archive.Untar(context, dest.Path(), nil)) - - body := new(bytes.Buffer) - return types.ImageBuildResponse{Body: ioutil.NopCloser(body)}, nil - } - cli := test.NewFakeCli(&fakeClient{imageBuildFunc: fakeImageBuild}) - - dir := fs.NewDir(t, "test-build-context", - fs.WithFile("foo", "some content", fs.AsUser(65534, 65534)), - fs.WithFile("Dockerfile", ` - FROM alpine:3.6 - COPY foo bar / - `), - ) - defer dir.Remove() - - options := newBuildOptions() - options.context = dir.Path() - - err := runBuild(cli, options) - require.NoError(t, err) - - files, err := ioutil.ReadDir(dest.Path()) - require.NoError(t, err) - for _, fileInfo := range files { - assert.Equal(t, uint32(0), fileInfo.Sys().(*syscall.Stat_t).Uid) - assert.Equal(t, uint32(0), fileInfo.Sys().(*syscall.Stat_t).Gid) - } -} func TestRunBuildDockerfileFromStdinWithCompress(t *testing.T) { dest, err := ioutil.TempDir("", "test-build-compress-dest") - require.NoError(t, err) + assert.NilError(t, err) defer os.RemoveAll(dest) var dockerfileName string @@ -67,11 +30,11 @@ func TestRunBuildDockerfileFromStdinWithCompress(t *testing.T) { buffer := new(bytes.Buffer) tee := io.TeeReader(context, buffer) - assert.NoError(t, archive.Untar(tee, dest, nil)) + assert.NilError(t, archive.Untar(tee, dest, nil)) dockerfileName = options.Dockerfile header := buffer.Bytes()[:10] - assert.Equal(t, archive.Gzip, archive.DetectCompression(header)) + assert.Check(t, is.Equal(archive.Gzip, archive.DetectCompression(header))) body := new(bytes.Buffer) return types.ImageBuildResponse{Body: ioutil.NopCloser(body)}, nil @@ -85,7 +48,7 @@ func TestRunBuildDockerfileFromStdinWithCompress(t *testing.T) { cli.SetIn(command.NewInStream(ioutil.NopCloser(dockerfile))) dir, err := ioutil.TempDir("", "test-build-compress") - require.NoError(t, err) + assert.NilError(t, err) defer os.RemoveAll(dir) ioutil.WriteFile(filepath.Join(dir, "foo"), []byte("some content"), 0644) @@ -94,18 +57,19 @@ func TestRunBuildDockerfileFromStdinWithCompress(t *testing.T) { options.compress = true options.dockerfileName = "-" options.context = dir + options.untrusted = true err = runBuild(cli, options) - require.NoError(t, err) + assert.NilError(t, err) files, err := ioutil.ReadDir(dest) - require.NoError(t, err) + assert.NilError(t, err) actual := []string{} for _, fileInfo := range files { actual = append(actual, fileInfo.Name()) } sort.Strings(actual) - assert.Equal(t, []string{dockerfileName, ".dockerignore", "foo"}, actual) + assert.Check(t, is.DeepEqual([]string{dockerfileName, ".dockerignore", "foo"}, actual)) } func TestRunBuildDockerfileOutsideContext(t *testing.T) { @@ -124,7 +88,7 @@ COPY data /data defer df.Remove() dest, err := ioutil.TempDir("", t.Name()) - require.NoError(t, err) + assert.NilError(t, err) defer os.RemoveAll(dest) var dockerfileName string @@ -132,7 +96,7 @@ COPY data /data buffer := new(bytes.Buffer) tee := io.TeeReader(context, buffer) - assert.NoError(t, archive.Untar(tee, dest, nil)) + assert.NilError(t, archive.Untar(tee, dest, nil)) dockerfileName = options.Dockerfile body := new(bytes.Buffer) @@ -144,30 +108,34 @@ COPY data /data options := newBuildOptions() options.context = dir.Path() options.dockerfileName = df.Path() + options.untrusted = true err = runBuild(cli, options) - require.NoError(t, err) + assert.NilError(t, err) files, err := ioutil.ReadDir(dest) - require.NoError(t, err) + assert.NilError(t, err) var actual []string for _, fileInfo := range files { actual = append(actual, fileInfo.Name()) } sort.Strings(actual) - assert.Equal(t, []string{dockerfileName, ".dockerignore", "data"}, actual) + assert.Check(t, is.DeepEqual([]string{dockerfileName, ".dockerignore", "data"}, actual)) } // TestRunBuildFromLocalGitHubDirNonExistingRepo tests that build contexts // starting with `github.com/` are special-cased, and the build command attempts // to clone the remote repo. +// TODO: test "context selection" logic directly when runBuild is refactored +// to support testing (ex: docker/cli#294) func TestRunBuildFromGitHubSpecialCase(t *testing.T) { cmd := NewBuildCommand(test.NewFakeCli(nil)) - cmd.SetArgs([]string{"github.com/docker/no-such-repository"}) + // Clone a small repo that exists so git doesn't prompt for credentials + cmd.SetArgs([]string{"github.com/docker/for-win"}) cmd.SetOutput(ioutil.Discard) err := cmd.Execute() - assert.Error(t, err) - assert.Contains(t, err.Error(), "unable to prepare context: unable to 'git clone'") + assert.ErrorContains(t, err, "unable to prepare context") + assert.ErrorContains(t, err, "docker-build-git") } // TestRunBuildFromLocalGitHubDirNonExistingRepo tests that a local directory @@ -175,19 +143,57 @@ func TestRunBuildFromGitHubSpecialCase(t *testing.T) { // case. func TestRunBuildFromLocalGitHubDir(t *testing.T) { tmpDir, err := ioutil.TempDir("", "docker-build-from-local-dir-") - require.NoError(t, err) + assert.NilError(t, err) defer os.RemoveAll(tmpDir) buildDir := filepath.Join(tmpDir, "github.com", "docker", "no-such-repository") err = os.MkdirAll(buildDir, 0777) - require.NoError(t, err) + assert.NilError(t, err) err = ioutil.WriteFile(filepath.Join(buildDir, "Dockerfile"), []byte("FROM busybox\n"), 0644) - require.NoError(t, err) + assert.NilError(t, err) client := test.NewFakeCli(&fakeClient{}) cmd := NewBuildCommand(client) cmd.SetArgs([]string{buildDir}) cmd.SetOutput(ioutil.Discard) err = cmd.Execute() - require.NoError(t, err) + assert.NilError(t, err) +} + +func TestRunBuildWithSymlinkedContext(t *testing.T) { + dockerfile := ` +FROM alpine:3.6 +RUN echo hello world +` + + tmpDir := fs.NewDir(t, t.Name(), + fs.WithDir("context", + fs.WithFile("Dockerfile", dockerfile)), + fs.WithSymlink("context-link", "context")) + defer tmpDir.Remove() + + files := []string{} + fakeImageBuild := func(_ context.Context, context io.Reader, options types.ImageBuildOptions) (types.ImageBuildResponse, error) { + tarReader := tar.NewReader(context) + for { + hdr, err := tarReader.Next() + switch err { + case io.EOF: + body := new(bytes.Buffer) + return types.ImageBuildResponse{Body: ioutil.NopCloser(body)}, nil + case nil: + files = append(files, hdr.Name) + default: + return types.ImageBuildResponse{}, err + } + } + } + + cli := test.NewFakeCli(&fakeClient{imageBuildFunc: fakeImageBuild}) + options := newBuildOptions() + options.context = tmpDir.Join("context-link") + options.untrusted = true + assert.NilError(t, runBuild(cli, options)) + + assert.DeepEqual(t, files, []string{"Dockerfile"}) } diff --git a/components/cli/cli/command/image/history_test.go b/components/cli/cli/command/image/history_test.go index eb656e5cf4..ce03c8c64d 100644 --- a/components/cli/cli/command/image/history_test.go +++ b/components/cli/cli/command/image/history_test.go @@ -7,11 +7,10 @@ import ( "time" "github.com/docker/cli/internal/test" - "github.com/docker/cli/internal/test/testutil" "github.com/docker/docker/api/types/image" + "github.com/gotestyourself/gotestyourself/assert" "github.com/gotestyourself/gotestyourself/golden" "github.com/pkg/errors" - "github.com/stretchr/testify/assert" ) func TestNewHistoryCommandErrors(t *testing.T) { @@ -39,7 +38,7 @@ func TestNewHistoryCommandErrors(t *testing.T) { cmd := NewHistoryCommand(test.NewFakeCli(&fakeClient{imageHistoryFunc: tc.imageHistoryFunc})) cmd.SetOutput(ioutil.Discard) cmd.SetArgs(tc.args) - testutil.ErrorContains(t, cmd.Execute(), tc.expectedError) + assert.ErrorContains(t, cmd.Execute(), tc.expectedError) } } @@ -47,7 +46,6 @@ func TestNewHistoryCommandSuccess(t *testing.T) { testCases := []struct { name string args []string - outputRegex string imageHistoryFunc func(img string) ([]image.HistoryResponseItem, error) }{ { @@ -64,16 +62,17 @@ func TestNewHistoryCommandSuccess(t *testing.T) { name: "quiet", args: []string{"--quiet", "image:tag"}, }, - // TODO: This test is failing since the output does not contain an RFC3339 date - //{ - // name: "non-human", - // args: []string{"--human=false", "image:tag"}, - // outputRegex: "\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}", // RFC3339 date format match - //}, { - name: "non-human-header", - args: []string{"--human=false", "image:tag"}, - outputRegex: "CREATED\\sAT", + name: "non-human", + args: []string{"--human=false", "image:tag"}, + imageHistoryFunc: func(img string) ([]image.HistoryResponseItem, error) { + return []image.HistoryResponseItem{{ + ID: "abcdef", + Created: time.Date(2017, 1, 1, 12, 0, 3, 0, time.UTC).Unix(), + CreatedBy: "rose", + Comment: "new history item!", + }}, nil + }, }, { name: "quiet-no-trunc", @@ -92,12 +91,8 @@ func TestNewHistoryCommandSuccess(t *testing.T) { cmd.SetOutput(ioutil.Discard) cmd.SetArgs(tc.args) err := cmd.Execute() - assert.NoError(t, err) + assert.NilError(t, err) actual := cli.OutBuffer().String() - if tc.outputRegex == "" { - golden.Assert(t, actual, fmt.Sprintf("history-command-success.%s.golden", tc.name)) - } else { - assert.Regexp(t, tc.outputRegex, actual) - } + golden.Assert(t, actual, fmt.Sprintf("history-command-success.%s.golden", tc.name)) } } diff --git a/components/cli/cli/command/image/import_test.go b/components/cli/cli/command/image/import_test.go index 7f9bc2d8dc..2eefe72479 100644 --- a/components/cli/cli/command/image/import_test.go +++ b/components/cli/cli/command/image/import_test.go @@ -7,10 +7,10 @@ import ( "testing" "github.com/docker/cli/internal/test" - "github.com/docker/cli/internal/test/testutil" "github.com/docker/docker/api/types" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" "github.com/pkg/errors" - "github.com/stretchr/testify/assert" ) func TestNewImportCommandErrors(t *testing.T) { @@ -38,7 +38,7 @@ func TestNewImportCommandErrors(t *testing.T) { cmd := NewImportCommand(test.NewFakeCli(&fakeClient{imageImportFunc: tc.imageImportFunc})) cmd.SetOutput(ioutil.Discard) cmd.SetArgs(tc.args) - testutil.ErrorContains(t, cmd.Execute(), tc.expectedError) + assert.ErrorContains(t, cmd.Execute(), tc.expectedError) } } @@ -46,7 +46,7 @@ func TestNewImportCommandInvalidFile(t *testing.T) { cmd := NewImportCommand(test.NewFakeCli(&fakeClient{})) cmd.SetOutput(ioutil.Discard) cmd.SetArgs([]string{"testdata/import-command-success.unexistent-file"}) - testutil.ErrorContains(t, cmd.Execute(), "testdata/import-command-success.unexistent-file") + assert.ErrorContains(t, cmd.Execute(), "testdata/import-command-success.unexistent-file") } func TestNewImportCommandSuccess(t *testing.T) { @@ -67,7 +67,7 @@ func TestNewImportCommandSuccess(t *testing.T) { name: "double", args: []string{"-", "image:local"}, imageImportFunc: func(source types.ImageImportSource, ref string, options types.ImageImportOptions) (io.ReadCloser, error) { - assert.Equal(t, "image:local", ref) + assert.Check(t, is.Equal("image:local", ref)) return ioutil.NopCloser(strings.NewReader("")), nil }, }, @@ -75,7 +75,7 @@ func TestNewImportCommandSuccess(t *testing.T) { name: "message", args: []string{"--message", "test message", "-"}, imageImportFunc: func(source types.ImageImportSource, ref string, options types.ImageImportOptions) (io.ReadCloser, error) { - assert.Equal(t, "test message", options.Message) + assert.Check(t, is.Equal("test message", options.Message)) return ioutil.NopCloser(strings.NewReader("")), nil }, }, @@ -83,7 +83,7 @@ func TestNewImportCommandSuccess(t *testing.T) { name: "change", args: []string{"--change", "ENV DEBUG true", "-"}, imageImportFunc: func(source types.ImageImportSource, ref string, options types.ImageImportOptions) (io.ReadCloser, error) { - assert.Equal(t, "ENV DEBUG true", options.Changes[0]) + assert.Check(t, is.Equal("ENV DEBUG true", options.Changes[0])) return ioutil.NopCloser(strings.NewReader("")), nil }, }, @@ -92,6 +92,6 @@ func TestNewImportCommandSuccess(t *testing.T) { cmd := NewImportCommand(test.NewFakeCli(&fakeClient{imageImportFunc: tc.imageImportFunc})) cmd.SetOutput(ioutil.Discard) cmd.SetArgs(tc.args) - assert.NoError(t, cmd.Execute()) + assert.NilError(t, cmd.Execute()) } } diff --git a/components/cli/cli/command/image/inspect_test.go b/components/cli/cli/command/image/inspect_test.go index 4e94d06231..4bf0547416 100644 --- a/components/cli/cli/command/image/inspect_test.go +++ b/components/cli/cli/command/image/inspect_test.go @@ -6,10 +6,10 @@ import ( "testing" "github.com/docker/cli/internal/test" - "github.com/docker/cli/internal/test/testutil" "github.com/docker/docker/api/types" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" "github.com/gotestyourself/gotestyourself/golden" - "github.com/stretchr/testify/assert" ) func TestNewInspectCommandErrors(t *testing.T) { @@ -28,7 +28,7 @@ func TestNewInspectCommandErrors(t *testing.T) { cmd := newInspectCommand(test.NewFakeCli(&fakeClient{})) cmd.SetOutput(ioutil.Discard) cmd.SetArgs(tc.args) - testutil.ErrorContains(t, cmd.Execute(), tc.expectedError) + assert.ErrorContains(t, cmd.Execute(), tc.expectedError) } } @@ -46,7 +46,7 @@ func TestNewInspectCommandSuccess(t *testing.T) { imageCount: 1, imageInspectFunc: func(image string) (types.ImageInspect, []byte, error) { imageInspectInvocationCount++ - assert.Equal(t, "image", image) + assert.Check(t, is.Equal("image", image)) return types.ImageInspect{}, nil, nil }, }, @@ -66,9 +66,9 @@ func TestNewInspectCommandSuccess(t *testing.T) { imageInspectFunc: func(image string) (types.ImageInspect, []byte, error) { imageInspectInvocationCount++ if imageInspectInvocationCount == 1 { - assert.Equal(t, "image1", image) + assert.Check(t, is.Equal("image1", image)) } else { - assert.Equal(t, "image2", image) + assert.Check(t, is.Equal("image2", image)) } return types.ImageInspect{}, nil, nil }, @@ -81,8 +81,8 @@ func TestNewInspectCommandSuccess(t *testing.T) { cmd.SetOutput(ioutil.Discard) cmd.SetArgs(tc.args) err := cmd.Execute() - assert.NoError(t, err) + assert.NilError(t, err) golden.Assert(t, cli.OutBuffer().String(), fmt.Sprintf("inspect-command-success.%s.golden", tc.name)) - assert.Equal(t, imageInspectInvocationCount, tc.imageCount) + assert.Check(t, is.Equal(imageInspectInvocationCount, tc.imageCount)) } } diff --git a/components/cli/cli/command/image/list_test.go b/components/cli/cli/command/image/list_test.go index 1755bb3e3d..f4ce847262 100644 --- a/components/cli/cli/command/image/list_test.go +++ b/components/cli/cli/command/image/list_test.go @@ -7,11 +7,11 @@ import ( "github.com/docker/cli/cli/config/configfile" "github.com/docker/cli/internal/test" - "github.com/docker/cli/internal/test/testutil" "github.com/docker/docker/api/types" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" "github.com/gotestyourself/gotestyourself/golden" "github.com/pkg/errors" - "github.com/stretchr/testify/assert" ) func TestNewImagesCommandErrors(t *testing.T) { @@ -38,7 +38,7 @@ func TestNewImagesCommandErrors(t *testing.T) { cmd := NewImagesCommand(test.NewFakeCli(&fakeClient{imageListFunc: tc.imageListFunc})) cmd.SetOutput(ioutil.Discard) cmd.SetArgs(tc.args) - testutil.ErrorContains(t, cmd.Execute(), tc.expectedError) + assert.ErrorContains(t, cmd.Execute(), tc.expectedError) } } @@ -65,7 +65,7 @@ func TestNewImagesCommandSuccess(t *testing.T) { name: "match-name", args: []string{"image"}, imageListFunc: func(options types.ImageListOptions) ([]types.ImageSummary, error) { - assert.Equal(t, "image", options.Filters.Get("reference")[0]) + assert.Check(t, is.Equal("image", options.Filters.Get("reference")[0])) return []types.ImageSummary{{}}, nil }, }, @@ -73,7 +73,7 @@ func TestNewImagesCommandSuccess(t *testing.T) { name: "filters", args: []string{"--filter", "name=value"}, imageListFunc: func(options types.ImageListOptions) ([]types.ImageSummary, error) { - assert.Equal(t, "value", options.Filters.Get("name")[0]) + assert.Check(t, is.Equal("value", options.Filters.Get("name")[0])) return []types.ImageSummary{{}}, nil }, }, @@ -85,14 +85,14 @@ func TestNewImagesCommandSuccess(t *testing.T) { cmd.SetOutput(ioutil.Discard) cmd.SetArgs(tc.args) err := cmd.Execute() - assert.NoError(t, err) + assert.NilError(t, err) golden.Assert(t, cli.OutBuffer().String(), fmt.Sprintf("list-command-success.%s.golden", tc.name)) } } func TestNewListCommandAlias(t *testing.T) { cmd := newListCommand(test.NewFakeCli(&fakeClient{})) - assert.True(t, cmd.HasAlias("images")) - assert.True(t, cmd.HasAlias("list")) - assert.False(t, cmd.HasAlias("other")) + assert.Check(t, cmd.HasAlias("images")) + assert.Check(t, cmd.HasAlias("list")) + assert.Check(t, !cmd.HasAlias("other")) } diff --git a/components/cli/cli/command/image/load_test.go b/components/cli/cli/command/image/load_test.go index 5f05bca47b..d3ec89e36c 100644 --- a/components/cli/cli/command/image/load_test.go +++ b/components/cli/cli/command/image/load_test.go @@ -8,11 +8,10 @@ import ( "testing" "github.com/docker/cli/internal/test" - "github.com/docker/cli/internal/test/testutil" "github.com/docker/docker/api/types" + "github.com/gotestyourself/gotestyourself/assert" "github.com/gotestyourself/gotestyourself/golden" "github.com/pkg/errors" - "github.com/stretchr/testify/assert" ) func TestNewLoadCommandErrors(t *testing.T) { @@ -47,7 +46,7 @@ func TestNewLoadCommandErrors(t *testing.T) { cmd := NewLoadCommand(cli) cmd.SetOutput(ioutil.Discard) cmd.SetArgs(tc.args) - testutil.ErrorContains(t, cmd.Execute(), tc.expectedError) + assert.ErrorContains(t, cmd.Execute(), tc.expectedError) } } @@ -57,7 +56,7 @@ func TestNewLoadCommandInvalidInput(t *testing.T) { cmd.SetOutput(ioutil.Discard) cmd.SetArgs([]string{"--input", "*"}) err := cmd.Execute() - testutil.ErrorContains(t, err, expectedError) + assert.ErrorContains(t, err, expectedError) } func TestNewLoadCommandSuccess(t *testing.T) { @@ -96,7 +95,7 @@ func TestNewLoadCommandSuccess(t *testing.T) { cmd.SetOutput(ioutil.Discard) cmd.SetArgs(tc.args) err := cmd.Execute() - assert.NoError(t, err) + assert.NilError(t, err) golden.Assert(t, cli.OutBuffer().String(), fmt.Sprintf("load-command-success.%s.golden", tc.name)) } } diff --git a/components/cli/cli/command/image/prune_test.go b/components/cli/cli/command/image/prune_test.go index 12f51b60fe..9c03d83d72 100644 --- a/components/cli/cli/command/image/prune_test.go +++ b/components/cli/cli/command/image/prune_test.go @@ -6,12 +6,12 @@ import ( "testing" "github.com/docker/cli/internal/test" - "github.com/docker/cli/internal/test/testutil" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/filters" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" "github.com/gotestyourself/gotestyourself/golden" "github.com/pkg/errors" - "github.com/stretchr/testify/assert" ) func TestNewPruneCommandErrors(t *testing.T) { @@ -41,7 +41,7 @@ func TestNewPruneCommandErrors(t *testing.T) { })) cmd.SetOutput(ioutil.Discard) cmd.SetArgs(tc.args) - testutil.ErrorContains(t, cmd.Execute(), tc.expectedError) + assert.ErrorContains(t, cmd.Execute(), tc.expectedError) } } @@ -55,7 +55,7 @@ func TestNewPruneCommandSuccess(t *testing.T) { name: "all", args: []string{"--all"}, imagesPruneFunc: func(pruneFilter filters.Args) (types.ImagesPruneReport, error) { - assert.Equal(t, "false", pruneFilter.Get("dangling")[0]) + assert.Check(t, is.Equal("false", pruneFilter.Get("dangling")[0])) return types.ImagesPruneReport{}, nil }, }, @@ -63,7 +63,7 @@ func TestNewPruneCommandSuccess(t *testing.T) { name: "force-deleted", args: []string{"--force"}, imagesPruneFunc: func(pruneFilter filters.Args) (types.ImagesPruneReport, error) { - assert.Equal(t, "true", pruneFilter.Get("dangling")[0]) + assert.Check(t, is.Equal("true", pruneFilter.Get("dangling")[0])) return types.ImagesPruneReport{ ImagesDeleted: []types.ImageDeleteResponseItem{{Deleted: "image1"}}, SpaceReclaimed: 1, @@ -74,7 +74,7 @@ func TestNewPruneCommandSuccess(t *testing.T) { name: "force-untagged", args: []string{"--force"}, imagesPruneFunc: func(pruneFilter filters.Args) (types.ImagesPruneReport, error) { - assert.Equal(t, "true", pruneFilter.Get("dangling")[0]) + assert.Check(t, is.Equal("true", pruneFilter.Get("dangling")[0])) return types.ImagesPruneReport{ ImagesDeleted: []types.ImageDeleteResponseItem{{Untagged: "image1"}}, SpaceReclaimed: 2, @@ -88,7 +88,7 @@ func TestNewPruneCommandSuccess(t *testing.T) { cmd.SetOutput(ioutil.Discard) cmd.SetArgs(tc.args) err := cmd.Execute() - assert.NoError(t, err) + assert.NilError(t, err) golden.Assert(t, cli.OutBuffer().String(), fmt.Sprintf("prune-command-success.%s.golden", tc.name)) } } diff --git a/components/cli/cli/command/image/pull.go b/components/cli/cli/command/image/pull.go index be653ec926..60db336e4d 100644 --- a/components/cli/cli/command/image/pull.go +++ b/components/cli/cli/command/image/pull.go @@ -14,9 +14,10 @@ import ( ) type pullOptions struct { - remote string - all bool - platform string + remote string + all bool + platform string + untrusted bool } // NewPullCommand creates a new `docker pull` command @@ -38,7 +39,7 @@ func NewPullCommand(dockerCli command.Cli) *cobra.Command { flags.BoolVarP(&opts.all, "all-tags", "a", false, "Download all tagged images in the repository") command.AddPlatformFlag(flags, &opts.platform) - command.AddTrustVerificationFlags(flags) + command.AddTrustVerificationFlags(flags, &opts.untrusted, dockerCli.ContentTrustEnabled()) return cmd } @@ -58,14 +59,14 @@ func runPull(cli command.Cli, opts pullOptions) error { } ctx := context.Background() - imgRefAndAuth, err := trust.GetImageReferencesAndAuth(ctx, AuthResolver(cli), distributionRef.String()) + imgRefAndAuth, err := trust.GetImageReferencesAndAuth(ctx, nil, AuthResolver(cli), distributionRef.String()) if err != nil { return err } // Check if reference has a digest _, isCanonical := distributionRef.(reference.Canonical) - if command.IsTrusted() && !isCanonical { + if !opts.untrusted && !isCanonical { err = trustedPull(ctx, cli, imgRefAndAuth, opts.platform) } else { err = imagePullPrivileged(ctx, cli, imgRefAndAuth, opts.all, opts.platform) diff --git a/components/cli/cli/command/image/pull_test.go b/components/cli/cli/command/image/pull_test.go index b0e9929abe..df639232fa 100644 --- a/components/cli/cli/command/image/pull_test.go +++ b/components/cli/cli/command/image/pull_test.go @@ -8,10 +8,11 @@ import ( "testing" "github.com/docker/cli/internal/test" - "github.com/docker/cli/internal/test/testutil" + "github.com/docker/cli/internal/test/notary" "github.com/docker/docker/api/types" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" "github.com/gotestyourself/gotestyourself/golden" - "github.com/stretchr/testify/assert" ) func TestNewPullCommandErrors(t *testing.T) { @@ -41,7 +42,7 @@ func TestNewPullCommandErrors(t *testing.T) { cmd := NewPullCommand(cli) cmd.SetOutput(ioutil.Discard) cmd.SetArgs(tc.args) - testutil.ErrorContains(t, cmd.Execute(), tc.expectedError) + assert.ErrorContains(t, cmd.Execute(), tc.expectedError) } } @@ -65,7 +66,7 @@ func TestNewPullCommandSuccess(t *testing.T) { for _, tc := range testCases { cli := test.NewFakeCli(&fakeClient{ imagePullFunc: func(ref string, options types.ImagePullOptions) (io.ReadCloser, error) { - assert.Equal(t, tc.expectedTag, ref, tc.name) + assert.Check(t, is.Equal(tc.expectedTag, ref), tc.name) return ioutil.NopCloser(strings.NewReader("")), nil }, }) @@ -73,7 +74,48 @@ func TestNewPullCommandSuccess(t *testing.T) { cmd.SetOutput(ioutil.Discard) cmd.SetArgs(tc.args) err := cmd.Execute() - assert.NoError(t, err) + assert.NilError(t, err) golden.Assert(t, cli.OutBuffer().String(), fmt.Sprintf("pull-command-success.%s.golden", tc.name)) } } + +func TestNewPullCommandWithContentTrustErrors(t *testing.T) { + testCases := []struct { + name string + args []string + expectedError string + notaryFunc test.NotaryClientFuncType + }{ + { + name: "offline-notary-server", + notaryFunc: notary.GetOfflineNotaryRepository, + expectedError: "client is offline", + args: []string{"image:tag"}, + }, + { + name: "uninitialized-notary-server", + notaryFunc: notary.GetUninitializedNotaryRepository, + expectedError: "remote trust data does not exist", + args: []string{"image:tag"}, + }, + { + name: "empty-notary-server", + notaryFunc: notary.GetEmptyTargetsNotaryRepository, + expectedError: "No valid trust data for tag", + args: []string{"image:tag"}, + }, + } + for _, tc := range testCases { + cli := test.NewFakeCli(&fakeClient{ + imagePullFunc: func(ref string, options types.ImagePullOptions) (io.ReadCloser, error) { + return ioutil.NopCloser(strings.NewReader("")), fmt.Errorf("shouldn't try to pull image") + }, + }, test.EnableContentTrust) + cli.SetNotaryClient(tc.notaryFunc) + cmd := NewPullCommand(cli) + cmd.SetOutput(ioutil.Discard) + cmd.SetArgs(tc.args) + err := cmd.Execute() + assert.ErrorContains(t, err, tc.expectedError) + } +} diff --git a/components/cli/cli/command/image/push.go b/components/cli/cli/command/image/push.go index 741ef67c59..61e1b09d62 100644 --- a/components/cli/cli/command/image/push.go +++ b/components/cli/cli/command/image/push.go @@ -11,26 +11,34 @@ import ( "github.com/spf13/cobra" ) +type pushOptions struct { + remote string + untrusted bool +} + // NewPushCommand creates a new `docker push` command func NewPushCommand(dockerCli command.Cli) *cobra.Command { + var opts pushOptions + cmd := &cobra.Command{ Use: "push [OPTIONS] NAME[:TAG]", Short: "Push an image or a repository to a registry", Args: cli.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { - return runPush(dockerCli, args[0]) + opts.remote = args[0] + return runPush(dockerCli, opts) }, } flags := cmd.Flags() - command.AddTrustSigningFlags(flags) + command.AddTrustSigningFlags(flags, &opts.untrusted, dockerCli.ContentTrustEnabled()) return cmd } -func runPush(dockerCli command.Cli, remote string) error { - ref, err := reference.ParseNormalizedNamed(remote) +func runPush(dockerCli command.Cli, opts pushOptions) error { + ref, err := reference.ParseNormalizedNamed(opts.remote) if err != nil { return err } @@ -47,7 +55,7 @@ func runPush(dockerCli command.Cli, remote string) error { authConfig := command.ResolveAuthConfig(ctx, dockerCli, repoInfo.Index) requestPrivilege := command.RegistryAuthenticationPrivilegedFunc(dockerCli, repoInfo.Index, "push") - if command.IsTrusted() { + if !opts.untrusted { return TrustedPush(ctx, dockerCli, repoInfo, ref, authConfig, requestPrivilege) } diff --git a/components/cli/cli/command/image/push_test.go b/components/cli/cli/command/image/push_test.go index 48d78b7d03..10d5cb3a81 100644 --- a/components/cli/cli/command/image/push_test.go +++ b/components/cli/cli/command/image/push_test.go @@ -7,10 +7,9 @@ import ( "testing" "github.com/docker/cli/internal/test" - "github.com/docker/cli/internal/test/testutil" "github.com/docker/docker/api/types" + "github.com/gotestyourself/gotestyourself/assert" "github.com/pkg/errors" - "github.com/stretchr/testify/assert" ) func TestNewPushCommandErrors(t *testing.T) { @@ -38,18 +37,13 @@ func TestNewPushCommandErrors(t *testing.T) { return ioutil.NopCloser(strings.NewReader("")), errors.Errorf("Failed to push") }, }, - { - name: "trust-error", - args: []string{"--disable-content-trust=false", "image:repo"}, - expectedError: "you are not authorized to perform this operation: server returned 401.", - }, } for _, tc := range testCases { cli := test.NewFakeCli(&fakeClient{imagePushFunc: tc.imagePushFunc}) cmd := NewPushCommand(cli) cmd.SetOutput(ioutil.Discard) cmd.SetArgs(tc.args) - testutil.ErrorContains(t, cmd.Execute(), tc.expectedError) + assert.ErrorContains(t, cmd.Execute(), tc.expectedError) } } @@ -72,6 +66,6 @@ func TestNewPushCommandSuccess(t *testing.T) { cmd := NewPushCommand(cli) cmd.SetOutput(ioutil.Discard) cmd.SetArgs(tc.args) - assert.NoError(t, cmd.Execute()) + assert.NilError(t, cmd.Execute()) } } diff --git a/components/cli/cli/command/image/remove_test.go b/components/cli/cli/command/image/remove_test.go index ec8b34945d..2c0cc9b0ac 100644 --- a/components/cli/cli/command/image/remove_test.go +++ b/components/cli/cli/command/image/remove_test.go @@ -6,11 +6,11 @@ import ( "testing" "github.com/docker/cli/internal/test" - "github.com/docker/cli/internal/test/testutil" "github.com/docker/docker/api/types" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" "github.com/gotestyourself/gotestyourself/golden" "github.com/pkg/errors" - "github.com/stretchr/testify/assert" ) type notFound struct { @@ -27,9 +27,9 @@ func (n notFound) NotFound() bool { func TestNewRemoveCommandAlias(t *testing.T) { cmd := newRemoveCommand(test.NewFakeCli(&fakeClient{})) - assert.True(t, cmd.HasAlias("rmi")) - assert.True(t, cmd.HasAlias("remove")) - assert.False(t, cmd.HasAlias("other")) + assert.Check(t, cmd.HasAlias("rmi")) + assert.Check(t, cmd.HasAlias("remove")) + assert.Check(t, !cmd.HasAlias("other")) } func TestNewRemoveCommandErrors(t *testing.T) { @@ -48,7 +48,7 @@ func TestNewRemoveCommandErrors(t *testing.T) { args: []string{"-f", "image1"}, expectedError: "error removing image", imageRemoveFunc: func(image string, options types.ImageRemoveOptions) ([]types.ImageDeleteResponseItem, error) { - assert.Equal(t, "image1", image) + assert.Check(t, is.Equal("image1", image)) return []types.ImageDeleteResponseItem{}, errors.Errorf("error removing image") }, }, @@ -57,8 +57,8 @@ func TestNewRemoveCommandErrors(t *testing.T) { args: []string{"arg1"}, expectedError: "error removing image", imageRemoveFunc: func(image string, options types.ImageRemoveOptions) ([]types.ImageDeleteResponseItem, error) { - assert.False(t, options.Force) - assert.True(t, options.PruneChildren) + assert.Check(t, !options.Force) + assert.Check(t, options.PruneChildren) return []types.ImageDeleteResponseItem{}, errors.Errorf("error removing image") }, }, @@ -70,7 +70,7 @@ func TestNewRemoveCommandErrors(t *testing.T) { })) cmd.SetOutput(ioutil.Discard) cmd.SetArgs(tc.args) - testutil.ErrorContains(t, cmd.Execute(), tc.expectedError) + assert.ErrorContains(t, cmd.Execute(), tc.expectedError) }) } } @@ -86,7 +86,7 @@ func TestNewRemoveCommandSuccess(t *testing.T) { name: "Image Deleted", args: []string{"image1"}, imageRemoveFunc: func(image string, options types.ImageRemoveOptions) ([]types.ImageDeleteResponseItem, error) { - assert.Equal(t, "image1", image) + assert.Check(t, is.Equal("image1", image)) return []types.ImageDeleteResponseItem{{Deleted: image}}, nil }, }, @@ -94,8 +94,8 @@ func TestNewRemoveCommandSuccess(t *testing.T) { name: "Image not found with force option", args: []string{"-f", "image1"}, imageRemoveFunc: func(image string, options types.ImageRemoveOptions) ([]types.ImageDeleteResponseItem, error) { - assert.Equal(t, "image1", image) - assert.Equal(t, true, options.Force) + assert.Check(t, is.Equal("image1", image)) + assert.Check(t, is.Equal(true, options.Force)) return []types.ImageDeleteResponseItem{}, notFound{"image1"} }, expectedStderr: "Error: No such image: image1\n", @@ -105,7 +105,7 @@ func TestNewRemoveCommandSuccess(t *testing.T) { name: "Image Untagged", args: []string{"image1"}, imageRemoveFunc: func(image string, options types.ImageRemoveOptions) ([]types.ImageDeleteResponseItem, error) { - assert.Equal(t, "image1", image) + assert.Check(t, is.Equal("image1", image)) return []types.ImageDeleteResponseItem{{Untagged: image}}, nil }, }, @@ -126,8 +126,8 @@ func TestNewRemoveCommandSuccess(t *testing.T) { cmd := NewRemoveCommand(cli) cmd.SetOutput(ioutil.Discard) cmd.SetArgs(tc.args) - assert.NoError(t, cmd.Execute()) - assert.Equal(t, tc.expectedStderr, cli.ErrBuffer().String()) + assert.NilError(t, cmd.Execute()) + assert.Check(t, is.Equal(tc.expectedStderr, cli.ErrBuffer().String())) golden.Assert(t, cli.OutBuffer().String(), fmt.Sprintf("remove-command-success.%s.golden", tc.name)) }) } diff --git a/components/cli/cli/command/image/save_test.go b/components/cli/cli/command/image/save_test.go index f402da7e84..b87be3c8af 100644 --- a/components/cli/cli/command/image/save_test.go +++ b/components/cli/cli/command/image/save_test.go @@ -8,10 +8,9 @@ import ( "testing" "github.com/docker/cli/internal/test" - "github.com/docker/cli/internal/test/testutil" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" "github.com/pkg/errors" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" ) func TestNewSaveCommandErrors(t *testing.T) { @@ -54,7 +53,7 @@ func TestNewSaveCommandErrors(t *testing.T) { cmd := NewSaveCommand(cli) cmd.SetOutput(ioutil.Discard) cmd.SetArgs(tc.args) - testutil.ErrorContains(t, cmd.Execute(), tc.expectedError) + assert.ErrorContains(t, cmd.Execute(), tc.expectedError) } } @@ -69,8 +68,8 @@ func TestNewSaveCommandSuccess(t *testing.T) { args: []string{"-o", "save_tmp_file", "arg1"}, isTerminal: true, imageSaveFunc: func(images []string) (io.ReadCloser, error) { - require.Len(t, images, 1) - assert.Equal(t, "arg1", images[0]) + assert.Assert(t, is.Len(images, 1)) + assert.Check(t, is.Equal("arg1", images[0])) return ioutil.NopCloser(strings.NewReader("")), nil }, deferredFunc: func() { @@ -81,9 +80,9 @@ func TestNewSaveCommandSuccess(t *testing.T) { args: []string{"arg1", "arg2"}, isTerminal: false, imageSaveFunc: func(images []string) (io.ReadCloser, error) { - require.Len(t, images, 2) - assert.Equal(t, "arg1", images[0]) - assert.Equal(t, "arg2", images[1]) + assert.Assert(t, is.Len(images, 2)) + assert.Check(t, is.Equal("arg1", images[0])) + assert.Check(t, is.Equal("arg2", images[1])) return ioutil.NopCloser(strings.NewReader("")), nil }, }, @@ -96,7 +95,7 @@ func TestNewSaveCommandSuccess(t *testing.T) { })) cmd.SetOutput(ioutil.Discard) cmd.SetArgs(tc.args) - assert.NoError(t, cmd.Execute()) + assert.NilError(t, cmd.Execute()) if tc.deferredFunc != nil { tc.deferredFunc() } diff --git a/components/cli/cli/command/image/tag_test.go b/components/cli/cli/command/image/tag_test.go index 0698522969..0319cb0522 100644 --- a/components/cli/cli/command/image/tag_test.go +++ b/components/cli/cli/command/image/tag_test.go @@ -5,8 +5,8 @@ import ( "testing" "github.com/docker/cli/internal/test" - "github.com/docker/cli/internal/test/testutil" - "github.com/stretchr/testify/assert" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" ) func TestCliNewTagCommandErrors(t *testing.T) { @@ -20,7 +20,7 @@ func TestCliNewTagCommandErrors(t *testing.T) { cmd := NewTagCommand(test.NewFakeCli(&fakeClient{})) cmd.SetArgs(args) cmd.SetOutput(ioutil.Discard) - testutil.ErrorContains(t, cmd.Execute(), expectedError) + assert.ErrorContains(t, cmd.Execute(), expectedError) } } @@ -28,14 +28,14 @@ func TestCliNewTagCommand(t *testing.T) { cmd := NewTagCommand( test.NewFakeCli(&fakeClient{ imageTagFunc: func(image string, ref string) error { - assert.Equal(t, "image1", image) - assert.Equal(t, "image2", ref) + assert.Check(t, is.Equal("image1", image)) + assert.Check(t, is.Equal("image2", ref)) return nil }, })) cmd.SetArgs([]string{"image1", "image2"}) cmd.SetOutput(ioutil.Discard) - assert.NoError(t, cmd.Execute()) + assert.NilError(t, cmd.Execute()) value, _ := cmd.Flags().GetBool("interspersed") - assert.False(t, value) + assert.Check(t, !value) } diff --git a/components/cli/cli/command/image/testdata/history-command-success.non-human.golden b/components/cli/cli/command/image/testdata/history-command-success.non-human.golden new file mode 100644 index 0000000000..4a83a3d837 --- /dev/null +++ b/components/cli/cli/command/image/testdata/history-command-success.non-human.golden @@ -0,0 +1,2 @@ +IMAGE CREATED AT CREATED BY SIZE COMMENT +abcdef 2017-01-01T12:00:03Z rose 0 new history item! diff --git a/components/cli/cli/command/image/trust.go b/components/cli/cli/command/image/trust.go index 7a70ef96c0..4dffff1289 100644 --- a/components/cli/cli/command/image/trust.go +++ b/components/cli/cli/command/image/trust.go @@ -198,7 +198,7 @@ func trustedPull(ctx context.Context, cli command.Cli, imgRefAndAuth trust.Image if err != nil { return err } - updatedImgRefAndAuth, err := trust.GetImageReferencesAndAuth(ctx, AuthResolver(cli), trustedRef.String()) + updatedImgRefAndAuth, err := trust.GetImageReferencesAndAuth(ctx, nil, AuthResolver(cli), trustedRef.String()) if err != nil { return err } @@ -293,35 +293,24 @@ func imagePullPrivileged(ctx context.Context, cli command.Cli, imgRefAndAuth tru // TrustedReference returns the canonical trusted reference for an image reference func TrustedReference(ctx context.Context, cli command.Cli, ref reference.NamedTagged, rs registry.Service) (reference.Canonical, error) { - var ( - repoInfo *registry.RepositoryInfo - err error - ) - if rs != nil { - repoInfo, err = rs.ResolveRepository(ref) - } else { - repoInfo, err = registry.ParseRepositoryInfo(ref) - } + imgRefAndAuth, err := trust.GetImageReferencesAndAuth(ctx, rs, AuthResolver(cli), ref.String()) if err != nil { return nil, err } - // Resolve the Auth config relevant for this server - authConfig := command.ResolveAuthConfig(ctx, cli, repoInfo.Index) - - notaryRepo, err := trust.GetNotaryRepository(cli.In(), cli.Out(), command.UserAgent(), repoInfo, &authConfig, "pull") + notaryRepo, err := cli.NotaryClient(imgRefAndAuth, []string{"pull"}) if err != nil { return nil, errors.Wrap(err, "error establishing connection to trust repository") } t, err := notaryRepo.GetTargetByName(ref.Tag(), trust.ReleasesRole, data.CanonicalTargetsRole) if err != nil { - return nil, trust.NotaryError(repoInfo.Name.Name(), err) + return nil, trust.NotaryError(imgRefAndAuth.RepoInfo().Name.Name(), err) } // Only list tags in the top level targets role or the releases delegation role - ignore // all other delegation roles if t.Role != trust.ReleasesRole && t.Role != data.CanonicalTargetsRole { - return nil, trust.NotaryError(repoInfo.Name.Name(), client.ErrNoSuchTarget(ref.Tag())) + return nil, trust.NotaryError(imgRefAndAuth.RepoInfo().Name.Name(), client.ErrNoSuchTarget(ref.Tag())) } r, err := convertTarget(t.Target) if err != nil { diff --git a/components/cli/cli/command/image/trust_test.go b/components/cli/cli/command/image/trust_test.go index 6071ebdea5..9aaa60396f 100644 --- a/components/cli/cli/command/image/trust_test.go +++ b/components/cli/cli/command/image/trust_test.go @@ -8,8 +8,7 @@ import ( "github.com/docker/cli/cli/trust" registrytypes "github.com/docker/docker/api/types/registry" "github.com/docker/docker/registry" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" + "github.com/gotestyourself/gotestyourself/assert" "github.com/theupdateframework/notary/client" "github.com/theupdateframework/notary/passphrase" "github.com/theupdateframework/notary/trustpinning" @@ -64,12 +63,12 @@ func TestNonOfficialTrustServer(t *testing.T) { func TestAddTargetToAllSignableRolesError(t *testing.T) { tmpDir, err := ioutil.TempDir("", "notary-test-") - assert.NoError(t, err) + assert.NilError(t, err) defer os.RemoveAll(tmpDir) notaryRepo, err := client.NewFileCachedRepository(tmpDir, "gun", "https://localhost", nil, passphrase.ConstantRetriever("password"), trustpinning.TrustPinConfig{}) - require.NoError(t, err) + assert.NilError(t, err) target := client.Target{} err = AddTargetToAllSignableRoles(notaryRepo, &target) - assert.EqualError(t, err, "client is offline") + assert.Error(t, err, "client is offline") } diff --git a/components/cli/cli/command/inspect/inspector_test.go b/components/cli/cli/command/inspect/inspector_test.go index 7d19fceda2..8eb818ee7d 100644 --- a/components/cli/cli/command/inspect/inspector_test.go +++ b/components/cli/cli/command/inspect/inspector_test.go @@ -6,8 +6,8 @@ import ( "testing" "github.com/docker/cli/templates" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" ) type testElement struct { @@ -243,17 +243,17 @@ func TestTemplateInspectorRawFallbackNumber(t *testing.T) { } b := new(bytes.Buffer) tmpl, err := templates.Parse("{{.Size}} {{.Id}}") - require.NoError(t, err) + assert.NilError(t, err) i := NewTemplateInspector(b, tmpl) for _, tc := range testcases { err = i.Inspect(typedElem, tc.raw) - require.NoError(t, err) + assert.NilError(t, err) err = i.Flush() - require.NoError(t, err) + assert.NilError(t, err) - assert.Equal(t, tc.exp, b.String()) + assert.Check(t, is.Equal(tc.exp, b.String())) b.Reset() } } diff --git a/components/cli/cli/command/manifest/annotate_test.go b/components/cli/cli/command/manifest/annotate_test.go index ad80bf6e0a..ab6f3bbf3a 100644 --- a/components/cli/cli/command/manifest/annotate_test.go +++ b/components/cli/cli/command/manifest/annotate_test.go @@ -5,10 +5,9 @@ import ( "testing" "github.com/docker/cli/internal/test" - "github.com/docker/cli/internal/test/testutil" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" "github.com/gotestyourself/gotestyourself/golden" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" ) func TestManifestAnnotateError(t *testing.T) { @@ -35,7 +34,7 @@ func TestManifestAnnotateError(t *testing.T) { cmd := newAnnotateCommand(cli) cmd.SetArgs(tc.args) cmd.SetOutput(ioutil.Discard) - testutil.ErrorContains(t, cmd.Execute(), tc.expectedError) + assert.ErrorContains(t, cmd.Execute(), tc.expectedError) } } @@ -48,13 +47,13 @@ func TestManifestAnnotate(t *testing.T) { namedRef := ref(t, "alpine:3.0") imageManifest := fullImageManifest(t, namedRef) err := store.Save(ref(t, "list:v1"), namedRef, imageManifest) - require.NoError(t, err) + assert.NilError(t, err) cmd := newAnnotateCommand(cli) cmd.SetArgs([]string{"example.com/list:v1", "example.com/fake:0.0"}) cmd.SetOutput(ioutil.Discard) expectedError := "manifest for image example.com/fake:0.0 does not exist" - testutil.ErrorContains(t, cmd.Execute(), expectedError) + assert.ErrorContains(t, cmd.Execute(), expectedError) cmd.SetArgs([]string{"example.com/list:v1", "example.com/alpine:3.0"}) cmd.Flags().Set("os", "freebsd") @@ -62,17 +61,17 @@ func TestManifestAnnotate(t *testing.T) { cmd.Flags().Set("os-features", "feature1") cmd.Flags().Set("variant", "v7") expectedError = "manifest entry for image has unsupported os/arch combination" - testutil.ErrorContains(t, cmd.Execute(), expectedError) + assert.ErrorContains(t, cmd.Execute(), expectedError) cmd.Flags().Set("arch", "arm") - require.NoError(t, cmd.Execute()) + assert.NilError(t, cmd.Execute()) cmd = newInspectCommand(cli) err = cmd.Flags().Set("verbose", "true") - require.NoError(t, err) + assert.NilError(t, err) cmd.SetArgs([]string{"example.com/list:v1", "example.com/alpine:3.0"}) - require.NoError(t, cmd.Execute()) + assert.NilError(t, cmd.Execute()) actual := cli.OutBuffer() expected := golden.Get(t, "inspect-annotate.golden") - assert.Equal(t, string(expected), actual.String()) + assert.Check(t, is.Equal(string(expected), actual.String())) } diff --git a/components/cli/cli/command/manifest/create_test.go b/components/cli/cli/command/manifest/create_test.go index a16ab5a073..378aa86041 100644 --- a/components/cli/cli/command/manifest/create_test.go +++ b/components/cli/cli/command/manifest/create_test.go @@ -6,12 +6,11 @@ import ( manifesttypes "github.com/docker/cli/cli/manifest/types" "github.com/docker/cli/internal/test" - "github.com/docker/cli/internal/test/testutil" "github.com/docker/distribution/reference" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" "github.com/gotestyourself/gotestyourself/golden" "github.com/pkg/errors" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" "golang.org/x/net/context" ) @@ -35,7 +34,7 @@ func TestManifestCreateErrors(t *testing.T) { cmd := newCreateListCommand(cli) cmd.SetArgs(tc.args) cmd.SetOutput(ioutil.Discard) - testutil.ErrorContains(t, cmd.Execute(), tc.expectedError) + assert.ErrorContains(t, cmd.Execute(), tc.expectedError) } } @@ -50,28 +49,28 @@ func TestManifestCreateAmend(t *testing.T) { namedRef := ref(t, "alpine:3.0") imageManifest := fullImageManifest(t, namedRef) err := store.Save(ref(t, "list:v1"), namedRef, imageManifest) - require.NoError(t, err) + assert.NilError(t, err) namedRef = ref(t, "alpine:3.1") imageManifest = fullImageManifest(t, namedRef) err = store.Save(ref(t, "list:v1"), namedRef, imageManifest) - require.NoError(t, err) + assert.NilError(t, err) cmd := newCreateListCommand(cli) cmd.SetArgs([]string{"example.com/list:v1", "example.com/alpine:3.1"}) cmd.Flags().Set("amend", "true") cmd.SetOutput(ioutil.Discard) err = cmd.Execute() - require.NoError(t, err) + assert.NilError(t, err) // make a new cli to clear the buffers cli = test.NewFakeCli(nil) cli.SetManifestStore(store) inspectCmd := newInspectCommand(cli) inspectCmd.SetArgs([]string{"example.com/list:v1"}) - require.NoError(t, inspectCmd.Execute()) + assert.NilError(t, inspectCmd.Execute()) actual := cli.OutBuffer() expected := golden.Get(t, "inspect-manifest-list.golden") - assert.Equal(t, string(expected), actual.String()) + assert.Check(t, is.Equal(string(expected), actual.String())) } // attempt to overwrite a saved manifest and get refused @@ -84,13 +83,13 @@ func TestManifestCreateRefuseAmend(t *testing.T) { namedRef := ref(t, "alpine:3.0") imageManifest := fullImageManifest(t, namedRef) err := store.Save(ref(t, "list:v1"), namedRef, imageManifest) - require.NoError(t, err) + assert.NilError(t, err) cmd := newCreateListCommand(cli) cmd.SetArgs([]string{"example.com/list:v1", "example.com/alpine:3.0"}) cmd.SetOutput(ioutil.Discard) err = cmd.Execute() - assert.EqualError(t, err, "refusing to amend an existing manifest list with no --amend flag") + assert.Error(t, err, "refusing to amend an existing manifest list with no --amend flag") } // attempt to make a manifest list without valid images @@ -113,5 +112,5 @@ func TestManifestCreateNoManifest(t *testing.T) { cmd.SetArgs([]string{"example.com/list:v1", "example.com/alpine:3.0"}) cmd.SetOutput(ioutil.Discard) err := cmd.Execute() - assert.EqualError(t, err, "No such image: example.com/alpine:3.0") + assert.Error(t, err, "No such image: example.com/alpine:3.0") } diff --git a/components/cli/cli/command/manifest/inspect_test.go b/components/cli/cli/command/manifest/inspect_test.go index 6dcf6deadd..9ba503a80e 100644 --- a/components/cli/cli/command/manifest/inspect_test.go +++ b/components/cli/cli/command/manifest/inspect_test.go @@ -12,24 +12,24 @@ import ( "github.com/docker/distribution" "github.com/docker/distribution/manifest/schema2" "github.com/docker/distribution/reference" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" "github.com/gotestyourself/gotestyourself/golden" - "github.com/opencontainers/go-digest" + digest "github.com/opencontainers/go-digest" "github.com/pkg/errors" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" "golang.org/x/net/context" ) func newTempManifestStore(t *testing.T) (store.Store, func()) { tmpdir, err := ioutil.TempDir("", "test-manifest-storage") - require.NoError(t, err) + assert.NilError(t, err) return store.NewStore(tmpdir), func() { os.RemoveAll(tmpdir) } } func ref(t *testing.T, name string) reference.Named { named, err := reference.ParseNamed("example.com/" + name) - require.NoError(t, err) + assert.NilError(t, err) return named } @@ -49,7 +49,7 @@ func fullImageManifest(t *testing.T, ref reference.Named) types.ImageManifest { }, }, }) - require.NoError(t, err) + assert.NilError(t, err) // TODO: include image data for verbose inspect return types.NewImageManifest(ref, digest.Digest("sha256:7328f6f8b41890597575cbaadc884e7386ae0acc53b747401ebce5cf0d62abcd"), types.Image{OS: "linux", Architecture: "amd64"}, man) } @@ -65,7 +65,7 @@ func TestInspectCommandLocalManifestNotFound(t *testing.T) { cmd.SetOutput(ioutil.Discard) cmd.SetArgs([]string{"example.com/list:v1", "example.com/alpine:3.0"}) err := cmd.Execute() - assert.EqualError(t, err, "No such manifest: example.com/alpine:3.0") + assert.Error(t, err, "No such manifest: example.com/alpine:3.0") } func TestInspectCommandNotFound(t *testing.T) { @@ -87,7 +87,7 @@ func TestInspectCommandNotFound(t *testing.T) { cmd.SetOutput(ioutil.Discard) cmd.SetArgs([]string{"example.com/alpine:3.0"}) err := cmd.Execute() - assert.EqualError(t, err, "No such manifest: example.com/alpine:3.0") + assert.Error(t, err, "No such manifest: example.com/alpine:3.0") } func TestInspectCommandLocalManifest(t *testing.T) { @@ -99,14 +99,14 @@ func TestInspectCommandLocalManifest(t *testing.T) { namedRef := ref(t, "alpine:3.0") imageManifest := fullImageManifest(t, namedRef) err := store.Save(ref(t, "list:v1"), namedRef, imageManifest) - require.NoError(t, err) + assert.NilError(t, err) cmd := newInspectCommand(cli) cmd.SetArgs([]string{"example.com/list:v1", "example.com/alpine:3.0"}) - require.NoError(t, cmd.Execute()) + assert.NilError(t, cmd.Execute()) actual := cli.OutBuffer() expected := golden.Get(t, "inspect-manifest.golden") - assert.Equal(t, string(expected), actual.String()) + assert.Check(t, is.Equal(string(expected), actual.String())) } func TestInspectcommandRemoteManifest(t *testing.T) { @@ -124,8 +124,8 @@ func TestInspectcommandRemoteManifest(t *testing.T) { cmd := newInspectCommand(cli) cmd.SetOutput(ioutil.Discard) cmd.SetArgs([]string{"example.com/alpine:3.0"}) - require.NoError(t, cmd.Execute()) + assert.NilError(t, cmd.Execute()) actual := cli.OutBuffer() expected := golden.Get(t, "inspect-manifest.golden") - assert.Equal(t, string(expected), actual.String()) + assert.Check(t, is.Equal(string(expected), actual.String())) } diff --git a/components/cli/cli/command/manifest/push_test.go b/components/cli/cli/command/manifest/push_test.go index 608dd2c23b..f339ea0de6 100644 --- a/components/cli/cli/command/manifest/push_test.go +++ b/components/cli/cli/command/manifest/push_test.go @@ -6,15 +6,14 @@ import ( manifesttypes "github.com/docker/cli/cli/manifest/types" "github.com/docker/cli/internal/test" - "github.com/docker/cli/internal/test/testutil" "github.com/docker/distribution/reference" + "github.com/gotestyourself/gotestyourself/assert" "github.com/pkg/errors" - "github.com/stretchr/testify/require" "golang.org/x/net/context" ) func newFakeRegistryClient(t *testing.T) *fakeRegistryClient { - require.NoError(t, nil) + assert.NilError(t, nil) return &fakeRegistryClient{ getManifestFunc: func(_ context.Context, _ reference.Named) (manifesttypes.ImageManifest, error) { @@ -46,7 +45,7 @@ func TestManifestPushErrors(t *testing.T) { cmd := newPushListCommand(cli) cmd.SetArgs(tc.args) cmd.SetOutput(ioutil.Discard) - testutil.ErrorContains(t, cmd.Execute(), tc.expectedError) + assert.ErrorContains(t, cmd.Execute(), tc.expectedError) } } @@ -64,10 +63,10 @@ func TestManifestPush(t *testing.T) { namedRef := ref(t, "alpine:3.0") imageManifest := fullImageManifest(t, namedRef) err := store.Save(ref(t, "list:v1"), namedRef, imageManifest) - require.NoError(t, err) + assert.NilError(t, err) cmd := newPushListCommand(cli) cmd.SetArgs([]string{"example.com/list:v1"}) err = cmd.Execute() - require.NoError(t, err) + assert.NilError(t, err) } diff --git a/components/cli/cli/command/network/connect_test.go b/components/cli/cli/command/network/connect_test.go index 064aa041c8..d4c9654eaf 100644 --- a/components/cli/cli/command/network/connect_test.go +++ b/components/cli/cli/command/network/connect_test.go @@ -5,10 +5,10 @@ import ( "testing" "github.com/docker/cli/internal/test" - "github.com/docker/cli/internal/test/testutil" "github.com/docker/docker/api/types/network" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" "github.com/pkg/errors" - "github.com/stretchr/testify/assert" "golang.org/x/net/context" ) @@ -38,7 +38,7 @@ func TestNetworkConnectErrors(t *testing.T) { ) cmd.SetArgs(tc.args) cmd.SetOutput(ioutil.Discard) - testutil.ErrorContains(t, cmd.Execute(), tc.expectedError) + assert.ErrorContains(t, cmd.Execute(), tc.expectedError) } } @@ -54,7 +54,7 @@ func TestNetworkConnectWithFlags(t *testing.T) { } cli := test.NewFakeCli(&fakeClient{ networkConnectFunc: func(ctx context.Context, networkID, container string, config *network.EndpointSettings) error { - assert.Equal(t, expectedOpts, config.IPAMConfig, "not expected driver error") + assert.Check(t, is.DeepEqual(expectedOpts, config.IPAMConfig), "not expected driver error") return nil }, }) @@ -66,5 +66,5 @@ func TestNetworkConnectWithFlags(t *testing.T) { cmd.Flags().Set("ip-range", "192.168.4.0/24") cmd.Flags().Set("gateway", "192.168.4.1/24") cmd.Flags().Set("subnet", "192.168.4.0/24") - assert.NoError(t, cmd.Execute()) + assert.NilError(t, cmd.Execute()) } diff --git a/components/cli/cli/command/network/create_test.go b/components/cli/cli/command/network/create_test.go index dda68046db..6a76695c91 100644 --- a/components/cli/cli/command/network/create_test.go +++ b/components/cli/cli/command/network/create_test.go @@ -6,12 +6,11 @@ import ( "testing" "github.com/docker/cli/internal/test" - "github.com/docker/cli/internal/test/testutil" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/network" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" "github.com/pkg/errors" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" "golang.org/x/net/context" ) @@ -136,10 +135,10 @@ func TestNetworkCreateErrors(t *testing.T) { ) cmd.SetArgs(tc.args) for key, value := range tc.flags { - require.NoError(t, cmd.Flags().Set(key, value)) + assert.NilError(t, cmd.Flags().Set(key, value)) } cmd.SetOutput(ioutil.Discard) - testutil.ErrorContains(t, cmd.Execute(), tc.expectedError) + assert.ErrorContains(t, cmd.Execute(), tc.expectedError) } } @@ -155,8 +154,8 @@ func TestNetworkCreateWithFlags(t *testing.T) { } cli := test.NewFakeCli(&fakeClient{ networkCreateFunc: func(ctx context.Context, name string, createBody types.NetworkCreate) (types.NetworkCreateResponse, error) { - assert.Equal(t, expectedDriver, createBody.Driver, "not expected driver error") - assert.Equal(t, expectedOpts, createBody.IPAM.Config, "not expected driver error") + assert.Check(t, is.Equal(expectedDriver, createBody.Driver), "not expected driver error") + assert.Check(t, is.DeepEqual(expectedOpts, createBody.IPAM.Config), "not expected driver error") return types.NetworkCreateResponse{ ID: name, }, nil @@ -170,6 +169,6 @@ func TestNetworkCreateWithFlags(t *testing.T) { cmd.Flags().Set("ip-range", "192.168.4.0/24") cmd.Flags().Set("gateway", "192.168.4.1/24") cmd.Flags().Set("subnet", "192.168.4.0/24") - assert.NoError(t, cmd.Execute()) - assert.Equal(t, "banana", strings.TrimSpace(cli.OutBuffer().String())) + assert.NilError(t, cmd.Execute()) + assert.Check(t, is.Equal("banana", strings.TrimSpace(cli.OutBuffer().String()))) } diff --git a/components/cli/cli/command/network/disconnect_test.go b/components/cli/cli/command/network/disconnect_test.go index e3920e9777..9c1bc0906f 100644 --- a/components/cli/cli/command/network/disconnect_test.go +++ b/components/cli/cli/command/network/disconnect_test.go @@ -5,7 +5,7 @@ import ( "testing" "github.com/docker/cli/internal/test" - "github.com/docker/cli/internal/test/testutil" + "github.com/gotestyourself/gotestyourself/assert" "github.com/pkg/errors" "golang.org/x/net/context" ) @@ -36,6 +36,6 @@ func TestNetworkDisconnectErrors(t *testing.T) { ) cmd.SetArgs(tc.args) cmd.SetOutput(ioutil.Discard) - testutil.ErrorContains(t, cmd.Execute(), tc.expectedError) + assert.ErrorContains(t, cmd.Execute(), tc.expectedError) } } diff --git a/components/cli/cli/command/network/list_test.go b/components/cli/cli/command/network/list_test.go index 63fc0a13cb..558f6cb2ba 100644 --- a/components/cli/cli/command/network/list_test.go +++ b/components/cli/cli/command/network/list_test.go @@ -1,20 +1,19 @@ package network import ( - "testing" - "io/ioutil" - "strings" + "testing" "github.com/docker/cli/internal/test" . "github.com/docker/cli/internal/test/builders" - "github.com/docker/cli/internal/test/testutil" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/filters" + "github.com/google/go-cmp/cmp" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" "github.com/gotestyourself/gotestyourself/golden" "github.com/pkg/errors" - "github.com/stretchr/testify/assert" "golang.org/x/net/context" ) @@ -38,23 +37,18 @@ func TestNetworkListErrors(t *testing.T) { }), ) cmd.SetOutput(ioutil.Discard) - testutil.ErrorContains(t, cmd.Execute(), tc.expectedError) - + assert.ErrorContains(t, cmd.Execute(), tc.expectedError) } } func TestNetworkListWithFlags(t *testing.T) { - - filterArgs := filters.NewArgs() - filterArgs.Add("image.name", "ubuntu") - expectedOpts := types.NetworkListOptions{ - Filters: filterArgs, + Filters: filters.NewArgs(filters.Arg("image.name", "ubuntu")), } cli := test.NewFakeCli(&fakeClient{ networkListFunc: func(ctx context.Context, options types.NetworkListOptions) ([]types.NetworkResource, error) { - assert.Equal(t, expectedOpts, options, "not expected options error") + assert.Check(t, is.DeepEqual(expectedOpts, options, cmp.AllowUnexported(filters.Args{}))) return []types.NetworkResource{*NetworkResource(NetworkResourceID("123454321"), NetworkResourceName("network_1"), NetworkResourceDriver("09.7.01"), @@ -64,6 +58,6 @@ func TestNetworkListWithFlags(t *testing.T) { cmd := newListCommand(cli) cmd.Flags().Set("filter", "image.name=ubuntu") - assert.NoError(t, cmd.Execute()) + assert.NilError(t, cmd.Execute()) golden.Assert(t, strings.TrimSpace(cli.OutBuffer().String()), "network-list.golden") } diff --git a/components/cli/cli/command/node/demote_test.go b/components/cli/cli/command/node/demote_test.go index bf86e5227e..5bbc7dae36 100644 --- a/components/cli/cli/command/node/demote_test.go +++ b/components/cli/cli/command/node/demote_test.go @@ -6,11 +6,10 @@ import ( "github.com/docker/cli/internal/test" "github.com/docker/docker/api/types/swarm" + "github.com/gotestyourself/gotestyourself/assert" "github.com/pkg/errors" // Import builders to get the builder function as package function . "github.com/docker/cli/internal/test/builders" - "github.com/docker/cli/internal/test/testutil" - "github.com/stretchr/testify/assert" ) func TestNodeDemoteErrors(t *testing.T) { @@ -46,7 +45,7 @@ func TestNodeDemoteErrors(t *testing.T) { })) cmd.SetArgs(tc.args) cmd.SetOutput(ioutil.Discard) - testutil.ErrorContains(t, cmd.Execute(), tc.expectedError) + assert.ErrorContains(t, cmd.Execute(), tc.expectedError) } } @@ -64,7 +63,7 @@ func TestNodeDemoteNoChange(t *testing.T) { }, })) cmd.SetArgs([]string{"nodeID"}) - assert.NoError(t, cmd.Execute()) + assert.NilError(t, cmd.Execute()) } func TestNodeDemoteMultipleNode(t *testing.T) { @@ -81,5 +80,5 @@ func TestNodeDemoteMultipleNode(t *testing.T) { }, })) cmd.SetArgs([]string{"nodeID1", "nodeID2"}) - assert.NoError(t, cmd.Execute()) + assert.NilError(t, cmd.Execute()) } diff --git a/components/cli/cli/command/node/inspect_test.go b/components/cli/cli/command/node/inspect_test.go index 739a4783be..73f0a9e872 100644 --- a/components/cli/cli/command/node/inspect_test.go +++ b/components/cli/cli/command/node/inspect_test.go @@ -11,9 +11,8 @@ import ( "github.com/pkg/errors" // Import builders to get the builder function as package function . "github.com/docker/cli/internal/test/builders" - "github.com/docker/cli/internal/test/testutil" + "github.com/gotestyourself/gotestyourself/assert" "github.com/gotestyourself/gotestyourself/golden" - "github.com/stretchr/testify/assert" ) func TestNodeInspectErrors(t *testing.T) { @@ -76,7 +75,7 @@ func TestNodeInspectErrors(t *testing.T) { cmd.Flags().Set(key, value) } cmd.SetOutput(ioutil.Discard) - testutil.ErrorContains(t, cmd.Execute(), tc.expectedError) + assert.ErrorContains(t, cmd.Execute(), tc.expectedError) } } @@ -113,7 +112,7 @@ func TestNodeInspectPretty(t *testing.T) { cmd := newInspectCommand(cli) cmd.SetArgs([]string{"nodeID"}) cmd.Flags().Set("pretty", "true") - assert.NoError(t, cmd.Execute()) + assert.NilError(t, cmd.Execute()) golden.Assert(t, cli.OutBuffer().String(), fmt.Sprintf("node-inspect-pretty.%s.golden", tc.name)) } } diff --git a/components/cli/cli/command/node/list_test.go b/components/cli/cli/command/node/list_test.go index 2cbe96e766..2d56af8377 100644 --- a/components/cli/cli/command/node/list_test.go +++ b/components/cli/cli/command/node/list_test.go @@ -8,11 +8,12 @@ import ( "github.com/docker/cli/internal/test" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/swarm" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" "github.com/gotestyourself/gotestyourself/golden" "github.com/pkg/errors" // Import builders to get the builder function as package function . "github.com/docker/cli/internal/test/builders" - "github.com/stretchr/testify/assert" ) func TestNodeListErrorOnAPIFailure(t *testing.T) { @@ -48,7 +49,7 @@ func TestNodeListErrorOnAPIFailure(t *testing.T) { }) cmd := newListCommand(cli) cmd.SetOutput(ioutil.Discard) - assert.EqualError(t, cmd.Execute(), tc.expectedError) + assert.Error(t, cmd.Execute(), tc.expectedError) } } @@ -71,7 +72,7 @@ func TestNodeList(t *testing.T) { }) cmd := newListCommand(cli) - assert.NoError(t, cmd.Execute()) + assert.NilError(t, cmd.Execute()) golden.Assert(t, cli.OutBuffer().String(), "node-list-sort.golden") } @@ -85,8 +86,8 @@ func TestNodeListQuietShouldOnlyPrintIDs(t *testing.T) { }) cmd := newListCommand(cli) cmd.Flags().Set("quiet", "true") - assert.NoError(t, cmd.Execute()) - assert.Equal(t, cli.OutBuffer().String(), "nodeID1\n") + assert.NilError(t, cmd.Execute()) + assert.Check(t, is.Equal(cli.OutBuffer().String(), "nodeID1\n")) } func TestNodeListDefaultFormatFromConfig(t *testing.T) { @@ -110,7 +111,7 @@ func TestNodeListDefaultFormatFromConfig(t *testing.T) { NodesFormat: "{{.ID}}: {{.Hostname}} {{.Status}}/{{.ManagerStatus}}", }) cmd := newListCommand(cli) - assert.NoError(t, cmd.Execute()) + assert.NilError(t, cmd.Execute()) golden.Assert(t, cli.OutBuffer().String(), "node-list-format-from-config.golden") } @@ -135,6 +136,6 @@ func TestNodeListFormat(t *testing.T) { }) cmd := newListCommand(cli) cmd.Flags().Set("format", "{{.Hostname}}: {{.ManagerStatus}}") - assert.NoError(t, cmd.Execute()) + assert.NilError(t, cmd.Execute()) golden.Assert(t, cli.OutBuffer().String(), "node-list-format-flag.golden") } diff --git a/components/cli/cli/command/node/promote_test.go b/components/cli/cli/command/node/promote_test.go index 4c66342346..5d5c105dfe 100644 --- a/components/cli/cli/command/node/promote_test.go +++ b/components/cli/cli/command/node/promote_test.go @@ -6,11 +6,10 @@ import ( "github.com/docker/cli/internal/test" "github.com/docker/docker/api/types/swarm" + "github.com/gotestyourself/gotestyourself/assert" "github.com/pkg/errors" // Import builders to get the builder function as package function . "github.com/docker/cli/internal/test/builders" - "github.com/docker/cli/internal/test/testutil" - "github.com/stretchr/testify/assert" ) func TestNodePromoteErrors(t *testing.T) { @@ -46,7 +45,7 @@ func TestNodePromoteErrors(t *testing.T) { })) cmd.SetArgs(tc.args) cmd.SetOutput(ioutil.Discard) - testutil.ErrorContains(t, cmd.Execute(), tc.expectedError) + assert.ErrorContains(t, cmd.Execute(), tc.expectedError) } } @@ -64,7 +63,7 @@ func TestNodePromoteNoChange(t *testing.T) { }, })) cmd.SetArgs([]string{"nodeID"}) - assert.NoError(t, cmd.Execute()) + assert.NilError(t, cmd.Execute()) } func TestNodePromoteMultipleNode(t *testing.T) { @@ -81,5 +80,5 @@ func TestNodePromoteMultipleNode(t *testing.T) { }, })) cmd.SetArgs([]string{"nodeID1", "nodeID2"}) - assert.NoError(t, cmd.Execute()) + assert.NilError(t, cmd.Execute()) } diff --git a/components/cli/cli/command/node/ps_test.go b/components/cli/cli/command/node/ps_test.go index 836a130f76..2a8533fb08 100644 --- a/components/cli/cli/command/node/ps_test.go +++ b/components/cli/cli/command/node/ps_test.go @@ -12,8 +12,8 @@ import ( "github.com/pkg/errors" // Import builders to get the builder function as package function . "github.com/docker/cli/internal/test/builders" + "github.com/gotestyourself/gotestyourself/assert" "github.com/gotestyourself/gotestyourself/golden" - "github.com/stretchr/testify/assert" ) func TestNodePsErrors(t *testing.T) { @@ -60,7 +60,7 @@ func TestNodePsErrors(t *testing.T) { cmd.Flags().Set(key, value) } cmd.SetOutput(ioutil.Discard) - assert.EqualError(t, cmd.Execute(), tc.expectedError) + assert.Error(t, cmd.Execute(), tc.expectedError) } } @@ -122,7 +122,7 @@ func TestNodePs(t *testing.T) { for key, value := range tc.flags { cmd.Flags().Set(key, value) } - assert.NoError(t, cmd.Execute()) + assert.NilError(t, cmd.Execute()) golden.Assert(t, cli.OutBuffer().String(), fmt.Sprintf("node-ps.%s.golden", tc.name)) } } diff --git a/components/cli/cli/command/node/remove_test.go b/components/cli/cli/command/node/remove_test.go index 78fc4f76fa..17ebb46f75 100644 --- a/components/cli/cli/command/node/remove_test.go +++ b/components/cli/cli/command/node/remove_test.go @@ -5,9 +5,8 @@ import ( "testing" "github.com/docker/cli/internal/test" - "github.com/docker/cli/internal/test/testutil" + "github.com/gotestyourself/gotestyourself/assert" "github.com/pkg/errors" - "github.com/stretchr/testify/assert" ) func TestNodeRemoveErrors(t *testing.T) { @@ -34,12 +33,12 @@ func TestNodeRemoveErrors(t *testing.T) { })) cmd.SetArgs(tc.args) cmd.SetOutput(ioutil.Discard) - testutil.ErrorContains(t, cmd.Execute(), tc.expectedError) + assert.ErrorContains(t, cmd.Execute(), tc.expectedError) } } func TestNodeRemoveMultiple(t *testing.T) { cmd := newRemoveCommand(test.NewFakeCli(&fakeClient{})) cmd.SetArgs([]string{"nodeID1", "nodeID2"}) - assert.NoError(t, cmd.Execute()) + assert.NilError(t, cmd.Execute()) } diff --git a/components/cli/cli/command/node/update_test.go b/components/cli/cli/command/node/update_test.go index e1aa28a806..6d3f6f0bd4 100644 --- a/components/cli/cli/command/node/update_test.go +++ b/components/cli/cli/command/node/update_test.go @@ -6,11 +6,10 @@ import ( "github.com/docker/cli/internal/test" "github.com/docker/docker/api/types/swarm" + "github.com/gotestyourself/gotestyourself/assert" "github.com/pkg/errors" // Import builders to get the builder function as package function . "github.com/docker/cli/internal/test/builders" - "github.com/docker/cli/internal/test/testutil" - "github.com/stretchr/testify/assert" ) func TestNodeUpdateErrors(t *testing.T) { @@ -66,7 +65,7 @@ func TestNodeUpdateErrors(t *testing.T) { cmd.Flags().Set(key, value) } cmd.SetOutput(ioutil.Discard) - testutil.ErrorContains(t, cmd.Execute(), tc.expectedError) + assert.ErrorContains(t, cmd.Execute(), tc.expectedError) } } @@ -165,6 +164,6 @@ func TestNodeUpdate(t *testing.T) { for key, value := range tc.flags { cmd.Flags().Set(key, value) } - assert.NoError(t, cmd.Execute()) + assert.NilError(t, cmd.Execute()) } } diff --git a/components/cli/cli/command/plugin/create_test.go b/components/cli/cli/command/plugin/create_test.go index 739e4197eb..41191cab35 100644 --- a/components/cli/cli/command/plugin/create_test.go +++ b/components/cli/cli/command/plugin/create_test.go @@ -4,17 +4,21 @@ import ( "fmt" "io" "io/ioutil" + "runtime" "testing" "github.com/docker/cli/internal/test" - "github.com/docker/cli/internal/test/testutil" "github.com/docker/docker/api/types" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" "github.com/gotestyourself/gotestyourself/fs" - "github.com/stretchr/testify/assert" ) func TestCreateErrors(t *testing.T) { - + noSuchFile := "no such file or directory" + if runtime.GOOS == "windows" { + noSuchFile = "The system cannot find the file specified." + } testCases := []struct { args []string expectedError string @@ -29,7 +33,7 @@ func TestCreateErrors(t *testing.T) { }, { args: []string{"plugin-foo", "nonexistent_context_dir"}, - expectedError: "no such file or directory", + expectedError: noSuchFile, }, } @@ -38,7 +42,7 @@ func TestCreateErrors(t *testing.T) { cmd := newCreateCommand(cli) cmd.SetArgs(tc.args) cmd.SetOutput(ioutil.Discard) - testutil.ErrorContains(t, cmd.Execute(), tc.expectedError) + assert.ErrorContains(t, cmd.Execute(), tc.expectedError) } } @@ -50,7 +54,7 @@ func TestCreateErrorOnFileAsContextDir(t *testing.T) { cmd := newCreateCommand(cli) cmd.SetArgs([]string{"plugin-foo", tmpFile.Path()}) cmd.SetOutput(ioutil.Discard) - testutil.ErrorContains(t, cmd.Execute(), "context must be a directory") + assert.ErrorContains(t, cmd.Execute(), "context must be a directory") } func TestCreateErrorOnContextDirWithoutConfig(t *testing.T) { @@ -61,7 +65,12 @@ func TestCreateErrorOnContextDirWithoutConfig(t *testing.T) { cmd := newCreateCommand(cli) cmd.SetArgs([]string{"plugin-foo", tmpDir.Path()}) cmd.SetOutput(ioutil.Discard) - testutil.ErrorContains(t, cmd.Execute(), "config.json: no such file or directory") + + expectedErr := "config.json: no such file or directory" + if runtime.GOOS == "windows" { + expectedErr = "config.json: The system cannot find the file specified." + } + assert.ErrorContains(t, cmd.Execute(), expectedErr) } func TestCreateErrorOnInvalidConfig(t *testing.T) { @@ -74,7 +83,7 @@ func TestCreateErrorOnInvalidConfig(t *testing.T) { cmd := newCreateCommand(cli) cmd.SetArgs([]string{"plugin-foo", tmpDir.Path()}) cmd.SetOutput(ioutil.Discard) - testutil.ErrorContains(t, cmd.Execute(), "invalid") + assert.ErrorContains(t, cmd.Execute(), "invalid") } func TestCreateErrorFromDaemon(t *testing.T) { @@ -92,7 +101,7 @@ func TestCreateErrorFromDaemon(t *testing.T) { cmd := newCreateCommand(cli) cmd.SetArgs([]string{"plugin-foo", tmpDir.Path()}) cmd.SetOutput(ioutil.Discard) - testutil.ErrorContains(t, cmd.Execute(), "Error creating plugin") + assert.ErrorContains(t, cmd.Execute(), "Error creating plugin") } func TestCreatePlugin(t *testing.T) { @@ -109,6 +118,6 @@ func TestCreatePlugin(t *testing.T) { cmd := newCreateCommand(cli) cmd.SetArgs([]string{"plugin-foo", tmpDir.Path()}) - assert.NoError(t, cmd.Execute()) - assert.Equal(t, "plugin-foo\n", cli.OutBuffer().String()) + assert.NilError(t, cmd.Execute()) + assert.Check(t, is.Equal("plugin-foo\n", cli.OutBuffer().String())) } diff --git a/components/cli/cli/command/plugin/disable_test.go b/components/cli/cli/command/plugin/disable_test.go index 96fce34646..592e0e4951 100644 --- a/components/cli/cli/command/plugin/disable_test.go +++ b/components/cli/cli/command/plugin/disable_test.go @@ -6,9 +6,9 @@ import ( "testing" "github.com/docker/cli/internal/test" - "github.com/docker/cli/internal/test/testutil" "github.com/docker/docker/api/types" - "github.com/stretchr/testify/assert" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" ) func TestPluginDisableErrors(t *testing.T) { @@ -41,7 +41,7 @@ func TestPluginDisableErrors(t *testing.T) { })) cmd.SetArgs(tc.args) cmd.SetOutput(ioutil.Discard) - testutil.ErrorContains(t, cmd.Execute(), tc.expectedError) + assert.ErrorContains(t, cmd.Execute(), tc.expectedError) } } @@ -53,6 +53,6 @@ func TestPluginDisable(t *testing.T) { }) cmd := newDisableCommand(cli) cmd.SetArgs([]string{"plugin-foo"}) - assert.NoError(t, cmd.Execute()) - assert.Equal(t, "plugin-foo\n", cli.OutBuffer().String()) + assert.NilError(t, cmd.Execute()) + assert.Check(t, is.Equal("plugin-foo\n", cli.OutBuffer().String())) } diff --git a/components/cli/cli/command/plugin/enable_test.go b/components/cli/cli/command/plugin/enable_test.go index 68b50eae6f..42ce53576a 100644 --- a/components/cli/cli/command/plugin/enable_test.go +++ b/components/cli/cli/command/plugin/enable_test.go @@ -6,9 +6,9 @@ import ( "testing" "github.com/docker/cli/internal/test" - "github.com/docker/cli/internal/test/testutil" "github.com/docker/docker/api/types" - "github.com/stretchr/testify/assert" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" ) func TestPluginEnableErrors(t *testing.T) { @@ -52,7 +52,7 @@ func TestPluginEnableErrors(t *testing.T) { cmd.Flags().Set(key, value) } cmd.SetOutput(ioutil.Discard) - testutil.ErrorContains(t, cmd.Execute(), tc.expectedError) + assert.ErrorContains(t, cmd.Execute(), tc.expectedError) } } @@ -65,6 +65,6 @@ func TestPluginEnable(t *testing.T) { cmd := newEnableCommand(cli) cmd.SetArgs([]string{"plugin-foo"}) - assert.NoError(t, cmd.Execute()) - assert.Equal(t, "plugin-foo\n", cli.OutBuffer().String()) + assert.NilError(t, cmd.Execute()) + assert.Check(t, is.Equal("plugin-foo\n", cli.OutBuffer().String())) } diff --git a/components/cli/cli/command/plugin/install.go b/components/cli/cli/command/plugin/install.go index f7537670eb..40be773bc3 100644 --- a/components/cli/cli/command/plugin/install.go +++ b/components/cli/cli/command/plugin/install.go @@ -24,11 +24,12 @@ type pluginOptions struct { disable bool args []string skipRemoteCheck bool + untrusted bool } -func loadPullFlags(opts *pluginOptions, flags *pflag.FlagSet) { +func loadPullFlags(dockerCli command.Cli, opts *pluginOptions, flags *pflag.FlagSet) { flags.BoolVar(&opts.grantPerms, "grant-all-permissions", false, "Grant all permissions necessary to run the plugin") - command.AddTrustVerificationFlags(flags) + command.AddTrustVerificationFlags(flags, &opts.untrusted, dockerCli.ContentTrustEnabled()) } func newInstallCommand(dockerCli command.Cli) *cobra.Command { @@ -47,7 +48,7 @@ func newInstallCommand(dockerCli command.Cli) *cobra.Command { } flags := cmd.Flags() - loadPullFlags(&options, flags) + loadPullFlags(dockerCli, &options, flags) flags.BoolVar(&options.disable, "disable", false, "Do not enable the plugin on install") flags.StringVar(&options.localName, "alias", "", "Local name for plugin") return cmd @@ -90,7 +91,7 @@ func buildPullConfig(ctx context.Context, dockerCli command.Cli, opts pluginOpti remote := ref.String() _, isCanonical := ref.(reference.Canonical) - if command.IsTrusted() && !isCanonical { + if !opts.untrusted && !isCanonical { ref = reference.TagNameOnly(ref) nt, ok := ref.(reference.NamedTagged) if !ok { diff --git a/components/cli/cli/command/plugin/push.go b/components/cli/cli/command/plugin/push.go index 68d25157c6..5dd039f8bf 100644 --- a/components/cli/cli/command/plugin/push.go +++ b/components/cli/cli/command/plugin/push.go @@ -13,30 +13,37 @@ import ( "github.com/spf13/cobra" ) +type pushOptions struct { + name string + untrusted bool +} + func newPushCommand(dockerCli command.Cli) *cobra.Command { + var opts pushOptions cmd := &cobra.Command{ Use: "push [OPTIONS] PLUGIN[:TAG]", Short: "Push a plugin to a registry", Args: cli.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { - return runPush(dockerCli, args[0]) + opts.name = args[0] + return runPush(dockerCli, opts) }, } flags := cmd.Flags() - command.AddTrustSigningFlags(flags) + command.AddTrustSigningFlags(flags, &opts.untrusted, dockerCli.ContentTrustEnabled()) return cmd } -func runPush(dockerCli command.Cli, name string) error { - named, err := reference.ParseNormalizedNamed(name) +func runPush(dockerCli command.Cli, opts pushOptions) error { + named, err := reference.ParseNormalizedNamed(opts.name) if err != nil { return err } if _, ok := named.(reference.Canonical); ok { - return errors.Errorf("invalid name: %s", name) + return errors.Errorf("invalid name: %s", opts.name) } named = reference.TagNameOnly(named) @@ -60,7 +67,7 @@ func runPush(dockerCli command.Cli, name string) error { } defer responseBody.Close() - if command.IsTrusted() { + if !opts.untrusted { repoInfo.Class = "plugin" return image.PushTrustedReference(dockerCli, repoInfo, named, authConfig, responseBody) } diff --git a/components/cli/cli/command/plugin/remove_test.go b/components/cli/cli/command/plugin/remove_test.go index cc179091b9..813c7615f6 100644 --- a/components/cli/cli/command/plugin/remove_test.go +++ b/components/cli/cli/command/plugin/remove_test.go @@ -6,9 +6,9 @@ import ( "testing" "github.com/docker/cli/internal/test" - "github.com/docker/cli/internal/test/testutil" "github.com/docker/docker/api/types" - "github.com/stretchr/testify/assert" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" ) func TestRemoveErrors(t *testing.T) { @@ -38,7 +38,7 @@ func TestRemoveErrors(t *testing.T) { cmd := newRemoveCommand(cli) cmd.SetArgs(tc.args) cmd.SetOutput(ioutil.Discard) - testutil.ErrorContains(t, cmd.Execute(), tc.expectedError) + assert.ErrorContains(t, cmd.Execute(), tc.expectedError) } } @@ -50,8 +50,8 @@ func TestRemove(t *testing.T) { }) cmd := newRemoveCommand(cli) cmd.SetArgs([]string{"plugin-foo"}) - assert.NoError(t, cmd.Execute()) - assert.Equal(t, "plugin-foo\n", cli.OutBuffer().String()) + assert.NilError(t, cmd.Execute()) + assert.Check(t, is.Equal("plugin-foo\n", cli.OutBuffer().String())) } func TestRemoveWithForceOption(t *testing.T) { @@ -65,7 +65,7 @@ func TestRemoveWithForceOption(t *testing.T) { cmd := newRemoveCommand(cli) cmd.SetArgs([]string{"plugin-foo"}) cmd.Flags().Set("force", "true") - assert.NoError(t, cmd.Execute()) - assert.True(t, force) - assert.Equal(t, "plugin-foo\n", cli.OutBuffer().String()) + assert.NilError(t, cmd.Execute()) + assert.Check(t, force) + assert.Check(t, is.Equal("plugin-foo\n", cli.OutBuffer().String())) } diff --git a/components/cli/cli/command/plugin/upgrade.go b/components/cli/cli/command/plugin/upgrade.go index f1a67edc40..f5afb509d5 100644 --- a/components/cli/cli/command/plugin/upgrade.go +++ b/components/cli/cli/command/plugin/upgrade.go @@ -30,7 +30,7 @@ func newUpgradeCommand(dockerCli command.Cli) *cobra.Command { } flags := cmd.Flags() - loadPullFlags(&options, flags) + loadPullFlags(dockerCli, &options, flags) flags.BoolVar(&options.skipRemoteCheck, "skip-remote-check", false, "Do not check if specified remote plugin matches existing plugin image") return cmd } diff --git a/components/cli/cli/command/registry_test.go b/components/cli/cli/command/registry_test.go index 3f3d5f579f..d621926ce8 100644 --- a/components/cli/cli/command/registry_test.go +++ b/components/cli/cli/command/registry_test.go @@ -3,8 +3,9 @@ package command_test import ( "testing" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" "github.com/pkg/errors" - "github.com/stretchr/testify/assert" "golang.org/x/net/context" // Prevents a circular import with "github.com/docker/cli/internal/test" @@ -64,12 +65,12 @@ func TestElectAuthServer(t *testing.T) { for _, tc := range testCases { cli := test.NewFakeCli(&fakeClient{infoFunc: tc.infoFunc}) server := ElectAuthServer(context.Background(), cli) - assert.Equal(t, tc.expectedAuthServer, server) + assert.Check(t, is.Equal(tc.expectedAuthServer, server)) actual := cli.ErrBuffer().String() if tc.expectedWarning == "" { - assert.Empty(t, actual) + assert.Check(t, is.Len(actual, 0)) } else { - assert.Contains(t, actual, tc.expectedWarning) + assert.Check(t, is.Contains(actual, tc.expectedWarning)) } } } diff --git a/components/cli/cli/command/secret/create_test.go b/components/cli/cli/command/secret/create_test.go index 383ed77660..7a9492ca0f 100644 --- a/components/cli/cli/command/secret/create_test.go +++ b/components/cli/cli/command/secret/create_test.go @@ -8,11 +8,11 @@ import ( "testing" "github.com/docker/cli/internal/test" - "github.com/docker/cli/internal/test/testutil" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/swarm" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" "github.com/pkg/errors" - "github.com/stretchr/testify/assert" ) const secretDataFile = "secret-create-with-name.golden" @@ -45,14 +45,14 @@ func TestSecretCreateErrors(t *testing.T) { ) cmd.SetArgs(tc.args) cmd.SetOutput(ioutil.Discard) - testutil.ErrorContains(t, cmd.Execute(), tc.expectedError) + assert.ErrorContains(t, cmd.Execute(), tc.expectedError) } } func TestSecretCreateWithName(t *testing.T) { name := "foo" data, err := ioutil.ReadFile(filepath.Join("testdata", secretDataFile)) - assert.NoError(t, err) + assert.NilError(t, err) expected := swarm.SecretSpec{ Annotations: swarm.Annotations{ @@ -75,8 +75,8 @@ func TestSecretCreateWithName(t *testing.T) { cmd := newSecretCreateCommand(cli) cmd.SetArgs([]string{name, filepath.Join("testdata", secretDataFile)}) - assert.NoError(t, cmd.Execute()) - assert.Equal(t, "ID-"+name, strings.TrimSpace(cli.OutBuffer().String())) + assert.NilError(t, cmd.Execute()) + assert.Check(t, is.Equal("ID-"+name, strings.TrimSpace(cli.OutBuffer().String()))) } func TestSecretCreateWithDriver(t *testing.T) { @@ -104,8 +104,8 @@ func TestSecretCreateWithDriver(t *testing.T) { cmd := newSecretCreateCommand(cli) cmd.SetArgs([]string{name}) cmd.Flags().Set("driver", expectedDriver.Name) - assert.NoError(t, cmd.Execute()) - assert.Equal(t, "ID-"+name, strings.TrimSpace(cli.OutBuffer().String())) + assert.NilError(t, cmd.Execute()) + assert.Check(t, is.Equal("ID-"+name, strings.TrimSpace(cli.OutBuffer().String()))) } func TestSecretCreateWithTemplatingDriver(t *testing.T) { @@ -133,8 +133,8 @@ func TestSecretCreateWithTemplatingDriver(t *testing.T) { cmd := newSecretCreateCommand(cli) cmd.SetArgs([]string{name}) cmd.Flags().Set("template-driver", expectedDriver.Name) - assert.NoError(t, cmd.Execute()) - assert.Equal(t, "ID-"+name, strings.TrimSpace(cli.OutBuffer().String())) + assert.NilError(t, cmd.Execute()) + assert.Check(t, is.Equal("ID-"+name, strings.TrimSpace(cli.OutBuffer().String()))) } func TestSecretCreateWithLabels(t *testing.T) { @@ -164,6 +164,6 @@ func TestSecretCreateWithLabels(t *testing.T) { cmd.SetArgs([]string{name, filepath.Join("testdata", secretDataFile)}) cmd.Flags().Set("label", "lbl1=Label-foo") cmd.Flags().Set("label", "lbl2=Label-bar") - assert.NoError(t, cmd.Execute()) - assert.Equal(t, "ID-"+name, strings.TrimSpace(cli.OutBuffer().String())) + assert.NilError(t, cmd.Execute()) + assert.Check(t, is.Equal("ID-"+name, strings.TrimSpace(cli.OutBuffer().String()))) } diff --git a/components/cli/cli/command/secret/inspect_test.go b/components/cli/cli/command/secret/inspect_test.go index cb74e11ae2..b23d78beb9 100644 --- a/components/cli/cli/command/secret/inspect_test.go +++ b/components/cli/cli/command/secret/inspect_test.go @@ -11,9 +11,8 @@ import ( "github.com/pkg/errors" // Import builders to get the builder function as package function . "github.com/docker/cli/internal/test/builders" - "github.com/docker/cli/internal/test/testutil" + "github.com/gotestyourself/gotestyourself/assert" "github.com/gotestyourself/gotestyourself/golden" - "github.com/stretchr/testify/assert" ) func TestSecretInspectErrors(t *testing.T) { @@ -62,7 +61,7 @@ func TestSecretInspectErrors(t *testing.T) { cmd.Flags().Set(key, value) } cmd.SetOutput(ioutil.Discard) - testutil.ErrorContains(t, cmd.Execute(), tc.expectedError) + assert.ErrorContains(t, cmd.Execute(), tc.expectedError) } } @@ -98,7 +97,7 @@ func TestSecretInspectWithoutFormat(t *testing.T) { }) cmd := newSecretInspectCommand(cli) cmd.SetArgs(tc.args) - assert.NoError(t, cmd.Execute()) + assert.NilError(t, cmd.Execute()) golden.Assert(t, cli.OutBuffer().String(), fmt.Sprintf("secret-inspect-without-format.%s.golden", tc.name)) } } @@ -135,7 +134,7 @@ func TestSecretInspectWithFormat(t *testing.T) { cmd := newSecretInspectCommand(cli) cmd.SetArgs(tc.args) cmd.Flags().Set("format", tc.format) - assert.NoError(t, cmd.Execute()) + assert.NilError(t, cmd.Execute()) golden.Assert(t, cli.OutBuffer().String(), fmt.Sprintf("secret-inspect-with-format.%s.golden", tc.name)) } } @@ -168,7 +167,7 @@ func TestSecretInspectPretty(t *testing.T) { cmd := newSecretInspectCommand(cli) cmd.SetArgs([]string{"secretID"}) cmd.Flags().Set("pretty", "true") - assert.NoError(t, cmd.Execute()) + assert.NilError(t, cmd.Execute()) golden.Assert(t, cli.OutBuffer().String(), fmt.Sprintf("secret-inspect-pretty.%s.golden", tc.name)) } } diff --git a/components/cli/cli/command/secret/ls_test.go b/components/cli/cli/command/secret/ls_test.go index 28b087a5ae..371218926c 100644 --- a/components/cli/cli/command/secret/ls_test.go +++ b/components/cli/cli/command/secret/ls_test.go @@ -12,9 +12,9 @@ import ( "github.com/pkg/errors" // Import builders to get the builder function as package function . "github.com/docker/cli/internal/test/builders" - "github.com/docker/cli/internal/test/testutil" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" "github.com/gotestyourself/gotestyourself/golden" - "github.com/stretchr/testify/assert" ) func TestSecretListErrors(t *testing.T) { @@ -42,7 +42,7 @@ func TestSecretListErrors(t *testing.T) { ) cmd.SetArgs(tc.args) cmd.SetOutput(ioutil.Discard) - testutil.ErrorContains(t, cmd.Execute(), tc.expectedError) + assert.ErrorContains(t, cmd.Execute(), tc.expectedError) } } @@ -74,7 +74,7 @@ func TestSecretList(t *testing.T) { }, }) cmd := newSecretListCommand(cli) - assert.NoError(t, cmd.Execute()) + assert.NilError(t, cmd.Execute()) golden.Assert(t, cli.OutBuffer().String(), "secret-list-sort.golden") } @@ -91,7 +91,7 @@ func TestSecretListWithQuietOption(t *testing.T) { }) cmd := newSecretListCommand(cli) cmd.Flags().Set("quiet", "true") - assert.NoError(t, cmd.Execute()) + assert.NilError(t, cmd.Execute()) golden.Assert(t, cli.OutBuffer().String(), "secret-list-with-quiet-option.golden") } @@ -110,7 +110,7 @@ func TestSecretListWithConfigFormat(t *testing.T) { SecretFormat: "{{ .Name }} {{ .Labels }}", }) cmd := newSecretListCommand(cli) - assert.NoError(t, cmd.Execute()) + assert.NilError(t, cmd.Execute()) golden.Assert(t, cli.OutBuffer().String(), "secret-list-with-config-format.golden") } @@ -127,15 +127,15 @@ func TestSecretListWithFormat(t *testing.T) { }) cmd := newSecretListCommand(cli) cmd.Flags().Set("format", "{{ .Name }} {{ .Labels }}") - assert.NoError(t, cmd.Execute()) + assert.NilError(t, cmd.Execute()) golden.Assert(t, cli.OutBuffer().String(), "secret-list-with-format.golden") } func TestSecretListWithFilter(t *testing.T) { cli := test.NewFakeCli(&fakeClient{ secretListFunc: func(options types.SecretListOptions) ([]swarm.Secret, error) { - assert.Equal(t, "foo", options.Filters.Get("name")[0], "foo") - assert.Equal(t, "lbl1=Label-bar", options.Filters.Get("label")[0]) + assert.Check(t, is.Equal("foo", options.Filters.Get("name")[0]), "foo") + assert.Check(t, is.Equal("lbl1=Label-bar", options.Filters.Get("label")[0])) return []swarm.Secret{ *Secret(SecretID("ID-foo"), SecretName("foo"), @@ -155,6 +155,6 @@ func TestSecretListWithFilter(t *testing.T) { cmd := newSecretListCommand(cli) cmd.Flags().Set("filter", "name=foo") cmd.Flags().Set("filter", "label=lbl1=Label-bar") - assert.NoError(t, cmd.Execute()) + assert.NilError(t, cmd.Execute()) golden.Assert(t, cli.OutBuffer().String(), "secret-list-with-filter.golden") } diff --git a/components/cli/cli/command/secret/remove_test.go b/components/cli/cli/command/secret/remove_test.go index accb3ea042..349f47b387 100644 --- a/components/cli/cli/command/secret/remove_test.go +++ b/components/cli/cli/command/secret/remove_test.go @@ -6,9 +6,9 @@ import ( "testing" "github.com/docker/cli/internal/test" - "github.com/docker/cli/internal/test/testutil" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" "github.com/pkg/errors" - "github.com/stretchr/testify/assert" ) func TestSecretRemoveErrors(t *testing.T) { @@ -37,7 +37,7 @@ func TestSecretRemoveErrors(t *testing.T) { ) cmd.SetArgs(tc.args) cmd.SetOutput(ioutil.Discard) - testutil.ErrorContains(t, cmd.Execute(), tc.expectedError) + assert.ErrorContains(t, cmd.Execute(), tc.expectedError) } } @@ -52,9 +52,9 @@ func TestSecretRemoveWithName(t *testing.T) { }) cmd := newSecretRemoveCommand(cli) cmd.SetArgs(names) - assert.NoError(t, cmd.Execute()) - assert.Equal(t, names, strings.Split(strings.TrimSpace(cli.OutBuffer().String()), "\n")) - assert.Equal(t, names, removedSecrets) + assert.NilError(t, cmd.Execute()) + assert.Check(t, is.DeepEqual(names, strings.Split(strings.TrimSpace(cli.OutBuffer().String()), "\n"))) + assert.Check(t, is.DeepEqual(names, removedSecrets)) } func TestSecretRemoveContinueAfterError(t *testing.T) { @@ -74,6 +74,6 @@ func TestSecretRemoveContinueAfterError(t *testing.T) { cmd := newSecretRemoveCommand(cli) cmd.SetOutput(ioutil.Discard) cmd.SetArgs(names) - assert.EqualError(t, cmd.Execute(), "error removing secret: foo") - assert.Equal(t, names, removedSecrets) + assert.Error(t, cmd.Execute(), "error removing secret: foo") + assert.Check(t, is.DeepEqual(names, removedSecrets)) } diff --git a/components/cli/cli/command/service/generic_resource_opts_test.go b/components/cli/cli/command/service/generic_resource_opts_test.go index 99217e9f36..ffc7f4ca64 100644 --- a/components/cli/cli/command/service/generic_resource_opts_test.go +++ b/components/cli/cli/command/service/generic_resource_opts_test.go @@ -3,7 +3,8 @@ package service import ( "testing" - "github.com/stretchr/testify/assert" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" ) func TestValidateSingleGenericResource(t *testing.T) { @@ -12,11 +13,11 @@ func TestValidateSingleGenericResource(t *testing.T) { for _, v := range incorrect { _, err := ValidateSingleGenericResource(v) - assert.Error(t, err) + assert.Check(t, is.ErrorContains(err, "")) } for _, v := range correct { _, err := ValidateSingleGenericResource(v) - assert.NoError(t, err) + assert.NilError(t, err) } } diff --git a/components/cli/cli/command/service/inspect_test.go b/components/cli/cli/command/service/inspect_test.go index d464197202..7339dc968f 100644 --- a/components/cli/cli/command/service/inspect_test.go +++ b/components/cli/cli/command/service/inspect_test.go @@ -10,7 +10,8 @@ import ( "github.com/docker/cli/cli/command/formatter" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/swarm" - "github.com/stretchr/testify/assert" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" ) func formatServiceInspect(t *testing.T, format formatter.Format, now time.Time) string { @@ -130,5 +131,5 @@ func TestJSONFormatWithNoUpdateConfig(t *testing.T) { if err := json.Unmarshal([]byte(s2), &m2); err != nil { t.Fatal(err) } - assert.Equal(t, m1, m2) + assert.Check(t, is.DeepEqual(m1, m2)) } diff --git a/components/cli/cli/command/service/list_test.go b/components/cli/cli/command/service/list_test.go index 679c733701..6286906f32 100644 --- a/components/cli/cli/command/service/list_test.go +++ b/components/cli/cli/command/service/list_test.go @@ -6,8 +6,8 @@ import ( "github.com/docker/cli/internal/test" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/swarm" + "github.com/gotestyourself/gotestyourself/assert" "github.com/gotestyourself/gotestyourself/golden" - "github.com/stretchr/testify/assert" "golang.org/x/net/context" ) @@ -23,6 +23,6 @@ func TestServiceListOrder(t *testing.T) { }) cmd := newListCommand(cli) cmd.Flags().Set("format", "{{.Name}}") - assert.NoError(t, cmd.Execute()) + assert.NilError(t, cmd.Execute()) golden.Assert(t, cli.OutBuffer().String(), "service-list-sort.golden") } diff --git a/components/cli/cli/command/service/opts_test.go b/components/cli/cli/command/service/opts_test.go index f68cbb3c5a..a84072382b 100644 --- a/components/cli/cli/command/service/opts_test.go +++ b/components/cli/cli/command/service/opts_test.go @@ -10,45 +10,45 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/swarm" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" ) func TestMemBytesString(t *testing.T) { var mem opts.MemBytes = 1048576 - assert.Equal(t, "1MiB", mem.String()) + assert.Check(t, is.Equal("1MiB", mem.String())) } func TestMemBytesSetAndValue(t *testing.T) { var mem opts.MemBytes - assert.NoError(t, mem.Set("5kb")) - assert.Equal(t, int64(5120), mem.Value()) + assert.NilError(t, mem.Set("5kb")) + assert.Check(t, is.Equal(int64(5120), mem.Value())) } func TestNanoCPUsString(t *testing.T) { var cpus opts.NanoCPUs = 6100000000 - assert.Equal(t, "6.100", cpus.String()) + assert.Check(t, is.Equal("6.100", cpus.String())) } func TestNanoCPUsSetAndValue(t *testing.T) { var cpus opts.NanoCPUs - assert.NoError(t, cpus.Set("0.35")) - assert.Equal(t, int64(350000000), cpus.Value()) + assert.NilError(t, cpus.Set("0.35")) + assert.Check(t, is.Equal(int64(350000000), cpus.Value())) } func TestUint64OptString(t *testing.T) { value := uint64(2345678) opt := Uint64Opt{value: &value} - assert.Equal(t, "2345678", opt.String()) + assert.Check(t, is.Equal("2345678", opt.String())) opt = Uint64Opt{} - assert.Equal(t, "", opt.String()) + assert.Check(t, is.Equal("", opt.String())) } func TestUint64OptSetAndValue(t *testing.T) { var opt Uint64Opt - assert.NoError(t, opt.Set("14445")) - assert.Equal(t, uint64(14445), *opt.Value()) + assert.NilError(t, opt.Set("14445")) + assert.Check(t, is.Equal(uint64(14445), *opt.Value())) } func TestHealthCheckOptionsToHealthConfig(t *testing.T) { @@ -61,14 +61,14 @@ func TestHealthCheckOptionsToHealthConfig(t *testing.T) { retries: 10, } config, err := opt.toHealthConfig() - assert.NoError(t, err) - assert.Equal(t, &container.HealthConfig{ + assert.NilError(t, err) + assert.Check(t, is.DeepEqual(&container.HealthConfig{ Test: []string{"CMD-SHELL", "curl"}, Interval: time.Second, Timeout: time.Second, StartPeriod: time.Second, Retries: 10, - }, config) + }, config)) } func TestHealthCheckOptionsToHealthConfigNoHealthcheck(t *testing.T) { @@ -76,10 +76,10 @@ func TestHealthCheckOptionsToHealthConfigNoHealthcheck(t *testing.T) { noHealthcheck: true, } config, err := opt.toHealthConfig() - assert.NoError(t, err) - assert.Equal(t, &container.HealthConfig{ + assert.NilError(t, err) + assert.Check(t, is.DeepEqual(&container.HealthConfig{ Test: []string{"NONE"}, - }, config) + }, config)) } func TestHealthCheckOptionsToHealthConfigConflict(t *testing.T) { @@ -88,7 +88,7 @@ func TestHealthCheckOptionsToHealthConfigConflict(t *testing.T) { noHealthcheck: true, } _, err := opt.toHealthConfig() - assert.EqualError(t, err, "--no-healthcheck conflicts with --health-* options") + assert.Error(t, err, "--no-healthcheck conflicts with --health-* options") } func TestResourceOptionsToResourceRequirements(t *testing.T) { @@ -109,7 +109,7 @@ func TestResourceOptionsToResourceRequirements(t *testing.T) { for _, opt := range incorrectOptions { _, err := opt.ToResourceRequirements() - assert.Error(t, err) + assert.Check(t, is.ErrorContains(err, "")) } correctOptions := []resourceOptions{ @@ -123,8 +123,8 @@ func TestResourceOptionsToResourceRequirements(t *testing.T) { for _, opt := range correctOptions { r, err := opt.ToResourceRequirements() - assert.NoError(t, err) - assert.Len(t, r.Reservations.GenericResources, len(opt.resGenericResources)) + assert.NilError(t, err) + assert.Check(t, is.Len(r.Reservations.GenericResources, len(opt.resGenericResources))) } } @@ -159,6 +159,6 @@ func TestToServiceNetwork(t *testing.T) { ctx := context.Background() flags := newCreateCommand(nil).Flags() service, err := o.ToService(ctx, client, flags) - require.NoError(t, err) - assert.Equal(t, []swarm.NetworkAttachmentConfig{{Target: "id111"}, {Target: "id555"}, {Target: "id999"}}, service.TaskTemplate.Networks) + assert.NilError(t, err) + assert.Check(t, is.DeepEqual([]swarm.NetworkAttachmentConfig{{Target: "id111"}, {Target: "id555"}, {Target: "id999"}}, service.TaskTemplate.Networks)) } diff --git a/components/cli/cli/command/service/progress/progress_test.go b/components/cli/cli/command/service/progress/progress_test.go index d1c118f7d7..198d29acf6 100644 --- a/components/cli/cli/command/service/progress/progress_test.go +++ b/components/cli/cli/command/service/progress/progress_test.go @@ -7,7 +7,8 @@ import ( "github.com/docker/docker/api/types/swarm" "github.com/docker/docker/pkg/progress" - "github.com/stretchr/testify/assert" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" ) type mockProgress struct { @@ -36,9 +37,9 @@ func (u updaterTester) testUpdater(tasks []swarm.Task, expectedConvergence bool, u.p.clear() converged, err := u.updater.update(u.service, tasks, u.activeNodes, u.rollback) - assert.NoError(u.t, err) - assert.Equal(u.t, expectedConvergence, converged) - assert.Equal(u.t, expectedProgress, u.p.p) + assert.Check(u.t, err) + assert.Check(u.t, is.Equal(expectedConvergence, converged)) + assert.Check(u.t, is.DeepEqual(expectedProgress, u.p.p)) } func TestReplicatedProgressUpdaterOneReplica(t *testing.T) { diff --git a/components/cli/cli/command/service/ps_test.go b/components/cli/cli/command/service/ps_test.go index e3f30dcb5b..bb66849f55 100644 --- a/components/cli/cli/command/service/ps_test.go +++ b/components/cli/cli/command/service/ps_test.go @@ -8,8 +8,9 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/filters" "github.com/docker/docker/api/types/swarm" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" + "github.com/google/go-cmp/cmp" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" "golang.org/x/net/context" ) @@ -26,22 +27,23 @@ func TestCreateFilter(t *testing.T) { } filter := opts.NewFilterOpt() - require.NoError(t, filter.Set("node=somenode")) + assert.NilError(t, filter.Set("node=somenode")) options := psOptions{ services: []string{"idmatch", "idprefix", "namematch", "notfound"}, filter: filter, } actual, notfound, err := createFilter(context.Background(), client, options) - require.NoError(t, err) - assert.Equal(t, notfound, []string{"no such service: notfound"}) + assert.NilError(t, err) + assert.Check(t, is.DeepEqual(notfound, []string{"no such service: notfound"})) - expected := filters.NewArgs() - expected.Add("service", "idmatch") - expected.Add("service", "idprefixmatch") - expected.Add("service", "cccccccc") - expected.Add("node", "somenode") - assert.Equal(t, expected, actual) + expected := filters.NewArgs( + filters.Arg("service", "idmatch"), + filters.Arg("service", "idprefixmatch"), + filters.Arg("service", "cccccccc"), + filters.Arg("node", "somenode"), + ) + assert.DeepEqual(t, expected, actual, cmpFilters) } func TestCreateFilterWithAmbiguousIDPrefixError(t *testing.T) { @@ -58,7 +60,7 @@ func TestCreateFilterWithAmbiguousIDPrefixError(t *testing.T) { filter: opts.NewFilterOpt(), } _, _, err := createFilter(context.Background(), client, options) - assert.EqualError(t, err, "multiple services found with provided prefix: aaa") + assert.Error(t, err, "multiple services found with provided prefix: aaa") } func TestCreateFilterNoneFound(t *testing.T) { @@ -68,7 +70,7 @@ func TestCreateFilterNoneFound(t *testing.T) { filter: opts.NewFilterOpt(), } _, _, err := createFilter(context.Background(), client, options) - assert.EqualError(t, err, "no such service: foo\nno such service: notfound") + assert.Error(t, err, "no such service: foo\nno such service: notfound") } func TestRunPSWarnsOnNotFound(t *testing.T) { @@ -87,7 +89,7 @@ func TestRunPSWarnsOnNotFound(t *testing.T) { format: "{{.ID}}", } err := runPS(cli, options) - assert.EqualError(t, err, "no such service: bar") + assert.Error(t, err, "no such service: bar") } func TestRunPSQuiet(t *testing.T) { @@ -102,16 +104,17 @@ func TestRunPSQuiet(t *testing.T) { cli := test.NewFakeCli(client) err := runPS(cli, psOptions{services: []string{"foo"}, quiet: true, filter: opts.NewFilterOpt()}) - require.NoError(t, err) - assert.Equal(t, "sxabyp0obqokwekpun4rjo0b3\n", cli.OutBuffer().String()) + assert.NilError(t, err) + assert.Check(t, is.Equal("sxabyp0obqokwekpun4rjo0b3\n", cli.OutBuffer().String())) } func TestUpdateNodeFilter(t *testing.T) { selfNodeID := "foofoo" - filter := filters.NewArgs() - filter.Add("node", "one") - filter.Add("node", "two") - filter.Add("node", "self") + filter := filters.NewArgs( + filters.Arg("node", "one"), + filters.Arg("node", "two"), + filters.Arg("node", "self"), + ) client := &fakeClient{ infoFunc: func(_ context.Context) (types.Info, error) { @@ -121,9 +124,12 @@ func TestUpdateNodeFilter(t *testing.T) { updateNodeFilter(context.Background(), client, filter) - expected := filters.NewArgs() - expected.Add("node", "one") - expected.Add("node", "two") - expected.Add("node", selfNodeID) - assert.Equal(t, expected, filter) + expected := filters.NewArgs( + filters.Arg("node", "one"), + filters.Arg("node", "two"), + filters.Arg("node", selfNodeID), + ) + assert.DeepEqual(t, expected, filter, cmpFilters) } + +var cmpFilters = cmp.AllowUnexported(filters.Args{}) diff --git a/components/cli/cli/command/service/rollback_test.go b/components/cli/cli/command/service/rollback_test.go index c063676645..6e5620cbe7 100644 --- a/components/cli/cli/command/service/rollback_test.go +++ b/components/cli/cli/command/service/rollback_test.go @@ -7,10 +7,10 @@ import ( "testing" "github.com/docker/cli/internal/test" - "github.com/docker/cli/internal/test/testutil" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/swarm" - "github.com/stretchr/testify/assert" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" "golang.org/x/net/context" ) @@ -50,8 +50,8 @@ func TestRollback(t *testing.T) { cmd.SetArgs(tc.args) cmd.Flags().Set("quiet", "true") cmd.SetOutput(ioutil.Discard) - assert.NoError(t, cmd.Execute()) - assert.Equal(t, strings.TrimSpace(cli.ErrBuffer().String()), tc.expectedDockerCliErr) + assert.NilError(t, cmd.Execute()) + assert.Check(t, is.Equal(strings.TrimSpace(cli.ErrBuffer().String()), tc.expectedDockerCliErr)) } } @@ -99,6 +99,6 @@ func TestRollbackWithErrors(t *testing.T) { cmd.SetArgs(tc.args) cmd.Flags().Set("quiet", "true") cmd.SetOutput(ioutil.Discard) - testutil.ErrorContains(t, cmd.Execute(), tc.expectedError) + assert.ErrorContains(t, cmd.Execute(), tc.expectedError) } } diff --git a/components/cli/cli/command/service/trust.go b/components/cli/cli/command/service/trust.go index 8bf9149764..c304a99124 100644 --- a/components/cli/cli/command/service/trust.go +++ b/components/cli/cli/command/service/trust.go @@ -16,7 +16,7 @@ import ( ) func resolveServiceImageDigestContentTrust(dockerCli command.Cli, service *swarm.ServiceSpec) error { - if !command.IsTrusted() { + if !dockerCli.ContentTrustEnabled() { // When not using content trust, digest resolution happens later when // contacting the registry to retrieve image information. return nil diff --git a/components/cli/cli/command/service/update_test.go b/components/cli/cli/command/service/update_test.go index e7e428192f..d647093254 100644 --- a/components/cli/cli/command/service/update_test.go +++ b/components/cli/cli/command/service/update_test.go @@ -7,13 +7,12 @@ import ( "testing" "time" - "github.com/docker/cli/internal/test/testutil" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/container" mounttypes "github.com/docker/docker/api/types/mount" "github.com/docker/docker/api/types/swarm" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" "golang.org/x/net/context" ) @@ -30,7 +29,7 @@ func TestUpdateServiceArgs(t *testing.T) { cspec.Args = []string{"old", "args"} updateService(nil, nil, flags, spec) - assert.Equal(t, []string{"the", "new args"}, cspec.Args) + assert.Check(t, is.DeepEqual([]string{"the", "new args"}, cspec.Args)) } func TestUpdateLabels(t *testing.T) { @@ -44,9 +43,9 @@ func TestUpdateLabels(t *testing.T) { } updateLabels(flags, &labels) - assert.Len(t, labels, 2) - assert.Equal(t, "value", labels["tokeep"]) - assert.Equal(t, "newlabel", labels["toadd"]) + assert.Check(t, is.Len(labels, 2)) + assert.Check(t, is.Equal("value", labels["tokeep"])) + assert.Check(t, is.Equal("newlabel", labels["toadd"])) } func TestUpdateLabelsRemoveALabelThatDoesNotExist(t *testing.T) { @@ -55,7 +54,7 @@ func TestUpdateLabelsRemoveALabelThatDoesNotExist(t *testing.T) { labels := map[string]string{"foo": "theoldlabel"} updateLabels(flags, &labels) - assert.Len(t, labels, 1) + assert.Check(t, is.Len(labels, 1)) } func TestUpdatePlacementConstraints(t *testing.T) { @@ -68,9 +67,9 @@ func TestUpdatePlacementConstraints(t *testing.T) { } updatePlacementConstraints(flags, placement) - require.Len(t, placement.Constraints, 2) - assert.Equal(t, "container=tokeep", placement.Constraints[0]) - assert.Equal(t, "node=toadd", placement.Constraints[1]) + assert.Assert(t, is.Len(placement.Constraints, 2)) + assert.Check(t, is.Equal("container=tokeep", placement.Constraints[0])) + assert.Check(t, is.Equal("node=toadd", placement.Constraints[1])) } func TestUpdatePlacementPrefs(t *testing.T) { @@ -94,9 +93,9 @@ func TestUpdatePlacementPrefs(t *testing.T) { } updatePlacementPreferences(flags, placement) - require.Len(t, placement.Preferences, 2) - assert.Equal(t, "node.labels.row", placement.Preferences[0].Spread.SpreadDescriptor) - assert.Equal(t, "node.labels.dc", placement.Preferences[1].Spread.SpreadDescriptor) + assert.Assert(t, is.Len(placement.Preferences, 2)) + assert.Check(t, is.Equal("node.labels.row", placement.Preferences[0].Spread.SpreadDescriptor)) + assert.Check(t, is.Equal("node.labels.dc", placement.Preferences[1].Spread.SpreadDescriptor)) } func TestUpdateEnvironment(t *testing.T) { @@ -107,11 +106,11 @@ func TestUpdateEnvironment(t *testing.T) { envs := []string{"toremove=theenvtoremove", "tokeep=value"} updateEnvironment(flags, &envs) - require.Len(t, envs, 2) + assert.Assert(t, is.Len(envs, 2)) // Order has been removed in updateEnvironment (map) sort.Strings(envs) - assert.Equal(t, "toadd=newenv", envs[0]) - assert.Equal(t, "tokeep=value", envs[1]) + assert.Check(t, is.Equal("toadd=newenv", envs[0])) + assert.Check(t, is.Equal("tokeep=value", envs[1])) } func TestUpdateEnvironmentWithDuplicateValues(t *testing.T) { @@ -123,7 +122,7 @@ func TestUpdateEnvironmentWithDuplicateValues(t *testing.T) { envs := []string{"foo=value"} updateEnvironment(flags, &envs) - assert.Len(t, envs, 0) + assert.Check(t, is.Len(envs, 0)) } func TestUpdateEnvironmentWithDuplicateKeys(t *testing.T) { @@ -134,8 +133,8 @@ func TestUpdateEnvironmentWithDuplicateKeys(t *testing.T) { envs := []string{"A=c"} updateEnvironment(flags, &envs) - require.Len(t, envs, 1) - assert.Equal(t, "A=b", envs[0]) + assert.Assert(t, is.Len(envs, 1)) + assert.Check(t, is.Equal("A=b", envs[0])) } func TestUpdateGroups(t *testing.T) { @@ -149,10 +148,10 @@ func TestUpdateGroups(t *testing.T) { groups := []string{"bar", "root"} updateGroups(flags, &groups) - require.Len(t, groups, 3) - assert.Equal(t, "bar", groups[0]) - assert.Equal(t, "foo", groups[1]) - assert.Equal(t, "wheel", groups[2]) + assert.Assert(t, is.Len(groups, 3)) + assert.Check(t, is.Equal("bar", groups[0])) + assert.Check(t, is.Equal("foo", groups[1])) + assert.Check(t, is.Equal("wheel", groups[2])) } func TestUpdateDNSConfig(t *testing.T) { @@ -167,7 +166,7 @@ func TestUpdateDNSConfig(t *testing.T) { // IPv6 flags.Set("dns-add", "2001:db8:abc8::1") // Invalid dns record - testutil.ErrorContains(t, flags.Set("dns-add", "x.y.z.w"), "x.y.z.w is not an ip address") + assert.ErrorContains(t, flags.Set("dns-add", "x.y.z.w"), "x.y.z.w is not an ip address") // domains with duplicates flags.Set("dns-search-add", "example.com") @@ -175,7 +174,7 @@ func TestUpdateDNSConfig(t *testing.T) { flags.Set("dns-search-add", "example.org") flags.Set("dns-search-rm", "example.org") // Invalid dns search domain - testutil.ErrorContains(t, flags.Set("dns-search-add", "example$com"), "example$com is not a valid domain") + assert.ErrorContains(t, flags.Set("dns-search-add", "example$com"), "example$com is not a valid domain") flags.Set("dns-option-add", "ndots:9") flags.Set("dns-option-rm", "timeout:3") @@ -188,17 +187,17 @@ func TestUpdateDNSConfig(t *testing.T) { updateDNSConfig(flags, &config) - require.Len(t, config.Nameservers, 3) - assert.Equal(t, "1.1.1.1", config.Nameservers[0]) - assert.Equal(t, "2001:db8:abc8::1", config.Nameservers[1]) - assert.Equal(t, "5.5.5.5", config.Nameservers[2]) + assert.Assert(t, is.Len(config.Nameservers, 3)) + assert.Check(t, is.Equal("1.1.1.1", config.Nameservers[0])) + assert.Check(t, is.Equal("2001:db8:abc8::1", config.Nameservers[1])) + assert.Check(t, is.Equal("5.5.5.5", config.Nameservers[2])) - require.Len(t, config.Search, 2) - assert.Equal(t, "example.com", config.Search[0]) - assert.Equal(t, "localdomain", config.Search[1]) + assert.Assert(t, is.Len(config.Search, 2)) + assert.Check(t, is.Equal("example.com", config.Search[0])) + assert.Check(t, is.Equal("localdomain", config.Search[1])) - require.Len(t, config.Options, 1) - assert.Equal(t, config.Options[0], "ndots:9") + assert.Assert(t, is.Len(config.Options, 1)) + assert.Check(t, is.Equal(config.Options[0], "ndots:9")) } func TestUpdateMounts(t *testing.T) { @@ -212,9 +211,9 @@ func TestUpdateMounts(t *testing.T) { } updateMounts(flags, &mounts) - require.Len(t, mounts, 2) - assert.Equal(t, "/toadd", mounts[0].Target) - assert.Equal(t, "/tokeep", mounts[1].Target) + assert.Assert(t, is.Len(mounts, 2)) + assert.Check(t, is.Equal("/toadd", mounts[0].Target)) + assert.Check(t, is.Equal("/tokeep", mounts[1].Target)) } func TestUpdateMountsWithDuplicateMounts(t *testing.T) { @@ -228,10 +227,10 @@ func TestUpdateMountsWithDuplicateMounts(t *testing.T) { } updateMounts(flags, &mounts) - require.Len(t, mounts, 3) - assert.Equal(t, "/tokeep1", mounts[0].Target) - assert.Equal(t, "/tokeep2", mounts[1].Target) - assert.Equal(t, "/toadd", mounts[2].Target) + assert.Assert(t, is.Len(mounts, 3)) + assert.Check(t, is.Equal("/tokeep1", mounts[0].Target)) + assert.Check(t, is.Equal("/tokeep2", mounts[1].Target)) + assert.Check(t, is.Equal("/toadd", mounts[2].Target)) } func TestUpdatePorts(t *testing.T) { @@ -245,13 +244,13 @@ func TestUpdatePorts(t *testing.T) { } err := updatePorts(flags, &portConfigs) - assert.NoError(t, err) - require.Len(t, portConfigs, 2) + assert.NilError(t, err) + assert.Assert(t, is.Len(portConfigs, 2)) // Do a sort to have the order (might have changed by map) targetPorts := []int{int(portConfigs[0].TargetPort), int(portConfigs[1].TargetPort)} sort.Ints(targetPorts) - assert.Equal(t, 555, targetPorts[0]) - assert.Equal(t, 1000, targetPorts[1]) + assert.Check(t, is.Equal(555, targetPorts[0])) + assert.Check(t, is.Equal(1000, targetPorts[1])) } func TestUpdatePortsDuplicate(t *testing.T) { @@ -269,9 +268,9 @@ func TestUpdatePortsDuplicate(t *testing.T) { } err := updatePorts(flags, &portConfigs) - assert.NoError(t, err) - require.Len(t, portConfigs, 1) - assert.Equal(t, uint32(80), portConfigs[0].TargetPort) + assert.NilError(t, err) + assert.Assert(t, is.Len(portConfigs, 1)) + assert.Check(t, is.Equal(uint32(80), portConfigs[0].TargetPort)) } func TestUpdateHealthcheckTable(t *testing.T) { @@ -345,9 +344,9 @@ func TestUpdateHealthcheckTable(t *testing.T) { } err := updateHealthcheck(flags, cspec) if c.err != "" { - assert.EqualError(t, err, c.err) + assert.Error(t, err, c.err) } else { - assert.NoError(t, err) + assert.NilError(t, err) if !reflect.DeepEqual(cspec.Healthcheck, c.expected) { t.Errorf("incorrect result for test %d, expected health config:\n\t%#v\ngot:\n\t%#v", i, c.expected, cspec.Healthcheck) } @@ -364,14 +363,14 @@ func TestUpdateHosts(t *testing.T) { // just hostname should work as well flags.Set("host-rm", "example.net") // bad format error - testutil.ErrorContains(t, flags.Set("host-add", "$example.com$"), `bad format for add-host: "$example.com$"`) + assert.ErrorContains(t, flags.Set("host-add", "$example.com$"), `bad format for add-host: "$example.com$"`) hosts := []string{"1.2.3.4 example.com", "4.3.2.1 example.org", "2001:db8:abc8::1 example.net"} expected := []string{"1.2.3.4 example.com", "4.3.2.1 example.org", "2001:db8:abc8::1 ipv6.net"} err := updateHosts(flags, &hosts) - assert.NoError(t, err) - assert.Equal(t, expected, hosts) + assert.NilError(t, err) + assert.Check(t, is.DeepEqual(expected, hosts)) } func TestUpdateHostsPreservesOrder(t *testing.T) { @@ -382,8 +381,8 @@ func TestUpdateHostsPreservesOrder(t *testing.T) { hosts := []string{} err := updateHosts(flags, &hosts) - assert.NoError(t, err) - assert.Equal(t, []string{"127.0.0.2 foobar", "127.0.0.1 foobar", "127.0.0.3 foobar"}, hosts) + assert.NilError(t, err) + assert.Check(t, is.DeepEqual([]string{"127.0.0.2 foobar", "127.0.0.1 foobar", "127.0.0.3 foobar"}, hosts)) } func TestUpdatePortsRmWithProtocol(t *testing.T) { @@ -404,10 +403,10 @@ func TestUpdatePortsRmWithProtocol(t *testing.T) { } err := updatePorts(flags, &portConfigs) - assert.NoError(t, err) - require.Len(t, portConfigs, 2) - assert.Equal(t, uint32(81), portConfigs[0].TargetPort) - assert.Equal(t, uint32(82), portConfigs[1].TargetPort) + assert.NilError(t, err) + assert.Assert(t, is.Len(portConfigs, 2)) + assert.Check(t, is.Equal(uint32(81), portConfigs[0].TargetPort)) + assert.Check(t, is.Equal(uint32(82), portConfigs[1].TargetPort)) } type secretAPIClientMock struct { @@ -461,11 +460,11 @@ func TestUpdateSecretUpdateInPlace(t *testing.T) { updatedSecrets, err := getUpdatedSecrets(apiClient, flags, secrets) - assert.NoError(t, err) - require.Len(t, updatedSecrets, 1) - assert.Equal(t, "tn9qiblgnuuut11eufquw5dev", updatedSecrets[0].SecretID) - assert.Equal(t, "foo", updatedSecrets[0].SecretName) - assert.Equal(t, "foo2", updatedSecrets[0].File.Name) + assert.NilError(t, err) + assert.Assert(t, is.Len(updatedSecrets, 1)) + assert.Check(t, is.Equal("tn9qiblgnuuut11eufquw5dev", updatedSecrets[0].SecretID)) + assert.Check(t, is.Equal("foo", updatedSecrets[0].SecretName)) + assert.Check(t, is.Equal("foo2", updatedSecrets[0].File.Name)) } func TestUpdateReadOnly(t *testing.T) { @@ -480,18 +479,18 @@ func TestUpdateReadOnly(t *testing.T) { flags := newUpdateCommand(nil).Flags() flags.Set("read-only", "true") updateService(nil, nil, flags, spec) - assert.True(t, cspec.ReadOnly) + assert.Check(t, cspec.ReadOnly) // Update without --read-only, no change flags = newUpdateCommand(nil).Flags() updateService(nil, nil, flags, spec) - assert.True(t, cspec.ReadOnly) + assert.Check(t, cspec.ReadOnly) // Update with --read-only=false, changed to false flags = newUpdateCommand(nil).Flags() flags.Set("read-only", "false") updateService(nil, nil, flags, spec) - assert.False(t, cspec.ReadOnly) + assert.Check(t, !cspec.ReadOnly) } func TestUpdateStopSignal(t *testing.T) { @@ -506,74 +505,74 @@ func TestUpdateStopSignal(t *testing.T) { flags := newUpdateCommand(nil).Flags() flags.Set("stop-signal", "SIGUSR1") updateService(nil, nil, flags, spec) - assert.Equal(t, "SIGUSR1", cspec.StopSignal) + assert.Check(t, is.Equal("SIGUSR1", cspec.StopSignal)) // Update without --stop-signal, no change flags = newUpdateCommand(nil).Flags() updateService(nil, nil, flags, spec) - assert.Equal(t, "SIGUSR1", cspec.StopSignal) + assert.Check(t, is.Equal("SIGUSR1", cspec.StopSignal)) // Update with --stop-signal=SIGWINCH flags = newUpdateCommand(nil).Flags() flags.Set("stop-signal", "SIGWINCH") updateService(nil, nil, flags, spec) - assert.Equal(t, "SIGWINCH", cspec.StopSignal) + assert.Check(t, is.Equal("SIGWINCH", cspec.StopSignal)) } func TestUpdateIsolationValid(t *testing.T) { flags := newUpdateCommand(nil).Flags() err := flags.Set("isolation", "process") - require.NoError(t, err) + assert.NilError(t, err) spec := swarm.ServiceSpec{ TaskTemplate: swarm.TaskSpec{ ContainerSpec: &swarm.ContainerSpec{}, }, } err = updateService(context.Background(), nil, flags, &spec) - require.NoError(t, err) - assert.Equal(t, container.IsolationProcess, spec.TaskTemplate.ContainerSpec.Isolation) + assert.NilError(t, err) + assert.Check(t, is.Equal(container.IsolationProcess, spec.TaskTemplate.ContainerSpec.Isolation)) } func TestUpdateIsolationInvalid(t *testing.T) { // validation depends on daemon os / version so validation should be done on the daemon side flags := newUpdateCommand(nil).Flags() err := flags.Set("isolation", "test") - require.NoError(t, err) + assert.NilError(t, err) spec := swarm.ServiceSpec{ TaskTemplate: swarm.TaskSpec{ ContainerSpec: &swarm.ContainerSpec{}, }, } err = updateService(context.Background(), nil, flags, &spec) - require.NoError(t, err) - assert.Equal(t, container.Isolation("test"), spec.TaskTemplate.ContainerSpec.Isolation) + assert.NilError(t, err) + assert.Check(t, is.Equal(container.Isolation("test"), spec.TaskTemplate.ContainerSpec.Isolation)) } func TestAddGenericResources(t *testing.T) { task := &swarm.TaskSpec{} flags := newUpdateCommand(nil).Flags() - assert.Nil(t, addGenericResources(flags, task)) + assert.Check(t, addGenericResources(flags, task)) flags.Set(flagGenericResourcesAdd, "foo=1") - assert.NoError(t, addGenericResources(flags, task)) - assert.Len(t, task.Resources.Reservations.GenericResources, 1) + assert.Check(t, addGenericResources(flags, task)) + assert.Check(t, is.Len(task.Resources.Reservations.GenericResources, 1)) // Checks that foo isn't added a 2nd time flags = newUpdateCommand(nil).Flags() flags.Set(flagGenericResourcesAdd, "bar=1") - assert.NoError(t, addGenericResources(flags, task)) - assert.Len(t, task.Resources.Reservations.GenericResources, 2) + assert.Check(t, addGenericResources(flags, task)) + assert.Check(t, is.Len(task.Resources.Reservations.GenericResources, 2)) } func TestRemoveGenericResources(t *testing.T) { task := &swarm.TaskSpec{} flags := newUpdateCommand(nil).Flags() - assert.Nil(t, removeGenericResources(flags, task)) + assert.Check(t, removeGenericResources(flags, task)) flags.Set(flagGenericResourcesRemove, "foo") - assert.Error(t, removeGenericResources(flags, task)) + assert.Check(t, is.ErrorContains(removeGenericResources(flags, task), "")) flags = newUpdateCommand(nil).Flags() flags.Set(flagGenericResourcesAdd, "foo=1") @@ -584,8 +583,8 @@ func TestRemoveGenericResources(t *testing.T) { flags = newUpdateCommand(nil).Flags() flags.Set(flagGenericResourcesRemove, "foo") - assert.NoError(t, removeGenericResources(flags, task)) - assert.Len(t, task.Resources.Reservations.GenericResources, 1) + assert.Check(t, removeGenericResources(flags, task)) + assert.Check(t, is.Len(task.Resources.Reservations.GenericResources, 1)) } func TestUpdateNetworks(t *testing.T) { @@ -618,38 +617,38 @@ func TestUpdateNetworks(t *testing.T) { flags := newUpdateCommand(nil).Flags() err := flags.Set(flagNetworkAdd, "aaa-network") - require.NoError(t, err) + assert.NilError(t, err) err = updateService(ctx, client, flags, &svc) - require.NoError(t, err) - assert.Equal(t, []swarm.NetworkAttachmentConfig{{Target: "id555"}, {Target: "id999"}}, svc.TaskTemplate.Networks) + assert.NilError(t, err) + assert.Check(t, is.DeepEqual([]swarm.NetworkAttachmentConfig{{Target: "id555"}, {Target: "id999"}}, svc.TaskTemplate.Networks)) flags = newUpdateCommand(nil).Flags() err = flags.Set(flagNetworkAdd, "aaa-network") - require.NoError(t, err) + assert.NilError(t, err) err = updateService(ctx, client, flags, &svc) - assert.EqualError(t, err, "service is already attached to network aaa-network") - assert.Equal(t, []swarm.NetworkAttachmentConfig{{Target: "id555"}, {Target: "id999"}}, svc.TaskTemplate.Networks) + assert.Error(t, err, "service is already attached to network aaa-network") + assert.Check(t, is.DeepEqual([]swarm.NetworkAttachmentConfig{{Target: "id555"}, {Target: "id999"}}, svc.TaskTemplate.Networks)) flags = newUpdateCommand(nil).Flags() err = flags.Set(flagNetworkAdd, "id555") - require.NoError(t, err) + assert.NilError(t, err) err = updateService(ctx, client, flags, &svc) - assert.EqualError(t, err, "service is already attached to network id555") - assert.Equal(t, []swarm.NetworkAttachmentConfig{{Target: "id555"}, {Target: "id999"}}, svc.TaskTemplate.Networks) + assert.Error(t, err, "service is already attached to network id555") + assert.Check(t, is.DeepEqual([]swarm.NetworkAttachmentConfig{{Target: "id555"}, {Target: "id999"}}, svc.TaskTemplate.Networks)) flags = newUpdateCommand(nil).Flags() err = flags.Set(flagNetworkRemove, "id999") - require.NoError(t, err) + assert.NilError(t, err) err = updateService(ctx, client, flags, &svc) - assert.NoError(t, err) - assert.Equal(t, []swarm.NetworkAttachmentConfig{{Target: "id555"}}, svc.TaskTemplate.Networks) + assert.NilError(t, err) + assert.Check(t, is.DeepEqual([]swarm.NetworkAttachmentConfig{{Target: "id555"}}, svc.TaskTemplate.Networks)) flags = newUpdateCommand(nil).Flags() err = flags.Set(flagNetworkAdd, "mmm-network") - require.NoError(t, err) + assert.NilError(t, err) err = flags.Set(flagNetworkRemove, "aaa-network") - require.NoError(t, err) + assert.NilError(t, err) err = updateService(ctx, client, flags, &svc) - assert.NoError(t, err) - assert.Equal(t, []swarm.NetworkAttachmentConfig{{Target: "id999"}}, svc.TaskTemplate.Networks) + assert.NilError(t, err) + assert.Check(t, is.DeepEqual([]swarm.NetworkAttachmentConfig{{Target: "id999"}}, svc.TaskTemplate.Networks)) } diff --git a/components/cli/cli/command/stack/kubernetes/loader_test.go b/components/cli/cli/command/stack/kubernetes/loader_test.go index a96e66d47b..84a53da6fe 100644 --- a/components/cli/cli/command/stack/kubernetes/loader_test.go +++ b/components/cli/cli/command/stack/kubernetes/loader_test.go @@ -5,7 +5,8 @@ import ( composetypes "github.com/docker/cli/cli/compose/types" apiv1beta1 "github.com/docker/cli/kubernetes/compose/v1beta1" - "github.com/stretchr/testify/require" + "github.com/google/go-cmp/cmp" + "github.com/gotestyourself/gotestyourself/assert" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -24,13 +25,13 @@ func TestLoadStack(t *testing.T) { }, }, }) - require.NoError(t, err) - require.Equal(t, &apiv1beta1.Stack{ + assert.NilError(t, err) + assert.DeepEqual(t, &apiv1beta1.Stack{ ObjectMeta: metav1.ObjectMeta{ Name: "foo", }, Spec: apiv1beta1.StackSpec{ - ComposeFile: string(`version: "3.1" + ComposeFile: `version: "3.1" services: bar: image: bar @@ -40,7 +41,15 @@ networks: {} volumes: {} secrets: {} configs: {} -`), +`, }, - }, s) + }, s, cmpKubeAPITime) } + +// TODO: this can be removed when k8s.io/apimachinery is updated to > 1.9.0 +var cmpKubeAPITime = cmp.Comparer(func(x, y *metav1.Time) bool { + if x == nil || y == nil { + return x == y + } + return x.Time.Equal(y.Time) +}) diff --git a/components/cli/cli/command/stack/list_test.go b/components/cli/cli/command/stack/list_test.go index 90d32e0f99..b3a9bdc3b7 100644 --- a/components/cli/cli/command/stack/list_test.go +++ b/components/cli/cli/command/stack/list_test.go @@ -7,12 +7,11 @@ import ( "github.com/docker/cli/internal/test" // Import builders to get the builder function as package function . "github.com/docker/cli/internal/test/builders" - "github.com/docker/cli/internal/test/testutil" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/swarm" + "github.com/gotestyourself/gotestyourself/assert" "github.com/gotestyourself/gotestyourself/golden" "github.com/pkg/errors" - "github.com/stretchr/testify/assert" ) func TestListErrors(t *testing.T) { @@ -55,7 +54,7 @@ func TestListErrors(t *testing.T) { for key, value := range tc.flags { cmd.Flags().Set(key, value) } - testutil.ErrorContains(t, cmd.Execute(), tc.expectedError) + assert.ErrorContains(t, cmd.Execute(), tc.expectedError) } } @@ -72,7 +71,7 @@ func TestListWithFormat(t *testing.T) { }) cmd := newListCommand(cli) cmd.Flags().Set("format", "{{ .Name }}") - assert.NoError(t, cmd.Execute()) + assert.NilError(t, cmd.Execute()) golden.Assert(t, cli.OutBuffer().String(), "stack-list-with-format.golden") } @@ -88,7 +87,7 @@ func TestListWithoutFormat(t *testing.T) { }, }) cmd := newListCommand(cli) - assert.NoError(t, cmd.Execute()) + assert.NilError(t, cmd.Execute()) golden.Assert(t, cli.OutBuffer().String(), "stack-list-without-format.golden") } @@ -141,7 +140,7 @@ func TestListOrder(t *testing.T) { }, }) cmd := newListCommand(cli) - assert.NoError(t, cmd.Execute()) + assert.NilError(t, cmd.Execute()) golden.Assert(t, cli.OutBuffer().String(), uc.golden) } } diff --git a/components/cli/cli/command/stack/loader/loader_test.go b/components/cli/cli/command/stack/loader/loader_test.go index 29ccd4e7f5..c027371dda 100644 --- a/components/cli/cli/command/stack/loader/loader_test.go +++ b/components/cli/cli/command/stack/loader/loader_test.go @@ -6,9 +6,9 @@ import ( "strings" "testing" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" "github.com/gotestyourself/gotestyourself/fs" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" ) func TestGetConfigDetails(t *testing.T) { @@ -22,11 +22,11 @@ services: defer file.Remove() details, err := getConfigDetails([]string{file.Path()}, nil) - require.NoError(t, err) - assert.Equal(t, filepath.Dir(file.Path()), details.WorkingDir) - require.Len(t, details.ConfigFiles, 1) - assert.Equal(t, "3.0", details.ConfigFiles[0].Config["version"]) - assert.Len(t, details.Environment, len(os.Environ())) + assert.NilError(t, err) + assert.Check(t, is.Equal(filepath.Dir(file.Path()), details.WorkingDir)) + assert.Assert(t, is.Len(details.ConfigFiles, 1)) + assert.Check(t, is.Equal("3.0", details.ConfigFiles[0].Config["version"])) + assert.Check(t, is.Len(details.Environment, len(os.Environ()))) } func TestGetConfigDetailsStdin(t *testing.T) { @@ -37,11 +37,11 @@ services: image: alpine:3.5 ` details, err := getConfigDetails([]string{"-"}, strings.NewReader(content)) - require.NoError(t, err) + assert.NilError(t, err) cwd, err := os.Getwd() - require.NoError(t, err) - assert.Equal(t, cwd, details.WorkingDir) - require.Len(t, details.ConfigFiles, 1) - assert.Equal(t, "3.0", details.ConfigFiles[0].Config["version"]) - assert.Len(t, details.Environment, len(os.Environ())) + assert.NilError(t, err) + assert.Check(t, is.Equal(cwd, details.WorkingDir)) + assert.Assert(t, is.Len(details.ConfigFiles, 1)) + assert.Check(t, is.Equal("3.0", details.ConfigFiles[0].Config["version"])) + assert.Check(t, is.Len(details.Environment, len(os.Environ()))) } diff --git a/components/cli/cli/command/stack/ps_test.go b/components/cli/cli/command/stack/ps_test.go index fb7055f09c..b3cce8d58a 100644 --- a/components/cli/cli/command/stack/ps_test.go +++ b/components/cli/cli/command/stack/ps_test.go @@ -9,12 +9,12 @@ import ( "github.com/docker/cli/internal/test" // Import builders to get the builder function as package function . "github.com/docker/cli/internal/test/builders" - "github.com/docker/cli/internal/test/testutil" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/swarm" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" "github.com/gotestyourself/gotestyourself/golden" "github.com/pkg/errors" - "github.com/stretchr/testify/assert" ) func TestStackPsErrors(t *testing.T) { @@ -47,7 +47,7 @@ func TestStackPsErrors(t *testing.T) { })) cmd.SetArgs(tc.args) cmd.SetOutput(ioutil.Discard) - testutil.ErrorContains(t, cmd.Execute(), tc.expectedError) + assert.ErrorContains(t, cmd.Execute(), tc.expectedError) } } @@ -61,9 +61,8 @@ func TestStackPsEmptyStack(t *testing.T) { cmd.SetArgs([]string{"foo"}) cmd.SetOutput(ioutil.Discard) - assert.Error(t, cmd.Execute()) - assert.EqualError(t, cmd.Execute(), "nothing found in stack: foo") - assert.Equal(t, "", fakeCli.OutBuffer().String()) + assert.Error(t, cmd.Execute(), "nothing found in stack: foo") + assert.Check(t, is.Equal("", fakeCli.OutBuffer().String())) } func TestStackPsWithQuietOption(t *testing.T) { @@ -75,7 +74,7 @@ func TestStackPsWithQuietOption(t *testing.T) { cmd := newPsCommand(cli) cmd.SetArgs([]string{"foo"}) cmd.Flags().Set("quiet", "true") - assert.NoError(t, cmd.Execute()) + assert.NilError(t, cmd.Execute()) golden.Assert(t, cli.OutBuffer().String(), "stack-ps-with-quiet-option.golden") } @@ -90,7 +89,7 @@ func TestStackPsWithNoTruncOption(t *testing.T) { cmd.SetArgs([]string{"foo"}) cmd.Flags().Set("no-trunc", "true") cmd.Flags().Set("format", "{{ .ID }}") - assert.NoError(t, cmd.Execute()) + assert.NilError(t, cmd.Execute()) golden.Assert(t, cli.OutBuffer().String(), "stack-ps-with-no-trunc-option.golden") } @@ -109,7 +108,7 @@ func TestStackPsWithNoResolveOption(t *testing.T) { cmd.SetArgs([]string{"foo"}) cmd.Flags().Set("no-resolve", "true") cmd.Flags().Set("format", "{{ .Node }}") - assert.NoError(t, cmd.Execute()) + assert.NilError(t, cmd.Execute()) golden.Assert(t, cli.OutBuffer().String(), "stack-ps-with-no-resolve-option.golden") } @@ -122,7 +121,7 @@ func TestStackPsWithFormat(t *testing.T) { cmd := newPsCommand(cli) cmd.SetArgs([]string{"foo"}) cmd.Flags().Set("format", "{{ .Name }}") - assert.NoError(t, cmd.Execute()) + assert.NilError(t, cmd.Execute()) golden.Assert(t, cli.OutBuffer().String(), "stack-ps-with-format.golden") } @@ -137,7 +136,7 @@ func TestStackPsWithConfigFormat(t *testing.T) { }) cmd := newPsCommand(cli) cmd.SetArgs([]string{"foo"}) - assert.NoError(t, cmd.Execute()) + assert.NilError(t, cmd.Execute()) golden.Assert(t, cli.OutBuffer().String(), "stack-ps-with-config-format.golden") } @@ -159,6 +158,6 @@ func TestStackPsWithoutFormat(t *testing.T) { }) cmd := newPsCommand(cli) cmd.SetArgs([]string{"foo"}) - assert.NoError(t, cmd.Execute()) + assert.NilError(t, cmd.Execute()) golden.Assert(t, cli.OutBuffer().String(), "stack-ps-without-format.golden") } diff --git a/components/cli/cli/command/stack/remove_test.go b/components/cli/cli/command/stack/remove_test.go index 0ce5cdacfc..5f27a9b32a 100644 --- a/components/cli/cli/command/stack/remove_test.go +++ b/components/cli/cli/command/stack/remove_test.go @@ -7,7 +7,8 @@ import ( "testing" "github.com/docker/cli/internal/test" - "github.com/stretchr/testify/assert" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" ) func fakeClientForRemoveStackTest(version string) *fakeClient { @@ -45,11 +46,11 @@ func TestRemoveStackVersion124DoesNotRemoveConfigsOrSecrets(t *testing.T) { cmd := newRemoveCommand(test.NewFakeCli(client)) cmd.SetArgs([]string{"foo", "bar"}) - assert.NoError(t, cmd.Execute()) - assert.Equal(t, buildObjectIDs(client.services), client.removedServices) - assert.Equal(t, buildObjectIDs(client.networks), client.removedNetworks) - assert.Nil(t, client.removedSecrets) - assert.Nil(t, client.removedConfigs) + assert.NilError(t, cmd.Execute()) + assert.Check(t, is.DeepEqual(buildObjectIDs(client.services), client.removedServices)) + assert.Check(t, is.DeepEqual(buildObjectIDs(client.networks), client.removedNetworks)) + assert.Check(t, is.Len(client.removedSecrets, 0)) + assert.Check(t, is.Len(client.removedConfigs, 0)) } func TestRemoveStackVersion125DoesNotRemoveConfigs(t *testing.T) { @@ -57,11 +58,11 @@ func TestRemoveStackVersion125DoesNotRemoveConfigs(t *testing.T) { cmd := newRemoveCommand(test.NewFakeCli(client)) cmd.SetArgs([]string{"foo", "bar"}) - assert.NoError(t, cmd.Execute()) - assert.Equal(t, buildObjectIDs(client.services), client.removedServices) - assert.Equal(t, buildObjectIDs(client.networks), client.removedNetworks) - assert.Equal(t, buildObjectIDs(client.secrets), client.removedSecrets) - assert.Nil(t, client.removedConfigs) + assert.NilError(t, cmd.Execute()) + assert.Check(t, is.DeepEqual(buildObjectIDs(client.services), client.removedServices)) + assert.Check(t, is.DeepEqual(buildObjectIDs(client.networks), client.removedNetworks)) + assert.Check(t, is.DeepEqual(buildObjectIDs(client.secrets), client.removedSecrets)) + assert.Check(t, is.Len(client.removedConfigs, 0)) } func TestRemoveStackVersion130RemovesEverything(t *testing.T) { @@ -69,11 +70,11 @@ func TestRemoveStackVersion130RemovesEverything(t *testing.T) { cmd := newRemoveCommand(test.NewFakeCli(client)) cmd.SetArgs([]string{"foo", "bar"}) - assert.NoError(t, cmd.Execute()) - assert.Equal(t, buildObjectIDs(client.services), client.removedServices) - assert.Equal(t, buildObjectIDs(client.networks), client.removedNetworks) - assert.Equal(t, buildObjectIDs(client.secrets), client.removedSecrets) - assert.Equal(t, buildObjectIDs(client.configs), client.removedConfigs) + assert.NilError(t, cmd.Execute()) + assert.Check(t, is.DeepEqual(buildObjectIDs(client.services), client.removedServices)) + assert.Check(t, is.DeepEqual(buildObjectIDs(client.networks), client.removedNetworks)) + assert.Check(t, is.DeepEqual(buildObjectIDs(client.secrets), client.removedSecrets)) + assert.Check(t, is.DeepEqual(buildObjectIDs(client.configs), client.removedConfigs)) } func TestRemoveStackSkipEmpty(t *testing.T) { @@ -100,19 +101,19 @@ func TestRemoveStackSkipEmpty(t *testing.T) { cmd := newRemoveCommand(fakeCli) cmd.SetArgs([]string{"foo", "bar"}) - assert.NoError(t, cmd.Execute()) + assert.NilError(t, cmd.Execute()) expectedList := []string{"Removing service bar_service1", "Removing service bar_service2", "Removing secret bar_secret1", "Removing config bar_config1", "Removing network bar_network1\n", } - assert.Equal(t, strings.Join(expectedList, "\n"), fakeCli.OutBuffer().String()) - assert.Contains(t, fakeCli.ErrBuffer().String(), "Nothing found in stack: foo\n") - assert.Equal(t, allServiceIDs, fakeClient.removedServices) - assert.Equal(t, allNetworkIDs, fakeClient.removedNetworks) - assert.Equal(t, allSecretIDs, fakeClient.removedSecrets) - assert.Equal(t, allConfigIDs, fakeClient.removedConfigs) + assert.Check(t, is.Equal(strings.Join(expectedList, "\n"), fakeCli.OutBuffer().String())) + assert.Check(t, is.Contains(fakeCli.ErrBuffer().String(), "Nothing found in stack: foo\n")) + assert.Check(t, is.DeepEqual(allServiceIDs, fakeClient.removedServices)) + assert.Check(t, is.DeepEqual(allNetworkIDs, fakeClient.removedNetworks)) + assert.Check(t, is.DeepEqual(allSecretIDs, fakeClient.removedSecrets)) + assert.Check(t, is.DeepEqual(allConfigIDs, fakeClient.removedConfigs)) } func TestRemoveContinueAfterError(t *testing.T) { @@ -149,9 +150,9 @@ func TestRemoveContinueAfterError(t *testing.T) { cmd.SetOutput(ioutil.Discard) cmd.SetArgs([]string{"foo", "bar"}) - assert.EqualError(t, cmd.Execute(), "Failed to remove some resources from stack: foo") - assert.Equal(t, allServiceIDs, removedServices) - assert.Equal(t, allNetworkIDs, cli.removedNetworks) - assert.Equal(t, allSecretIDs, cli.removedSecrets) - assert.Equal(t, allConfigIDs, cli.removedConfigs) + assert.Error(t, cmd.Execute(), "Failed to remove some resources from stack: foo") + assert.Check(t, is.DeepEqual(allServiceIDs, removedServices)) + assert.Check(t, is.DeepEqual(allNetworkIDs, cli.removedNetworks)) + assert.Check(t, is.DeepEqual(allSecretIDs, cli.removedSecrets)) + assert.Check(t, is.DeepEqual(allConfigIDs, cli.removedConfigs)) } diff --git a/components/cli/cli/command/stack/services_test.go b/components/cli/cli/command/stack/services_test.go index 2f6d6e0e00..195d65c346 100644 --- a/components/cli/cli/command/stack/services_test.go +++ b/components/cli/cli/command/stack/services_test.go @@ -8,12 +8,12 @@ import ( "github.com/docker/cli/internal/test" // Import builders to get the builder function as package function . "github.com/docker/cli/internal/test/builders" - "github.com/docker/cli/internal/test/testutil" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/swarm" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" "github.com/gotestyourself/gotestyourself/golden" "github.com/pkg/errors" - "github.com/stretchr/testify/assert" ) func TestStackServicesErrors(t *testing.T) { @@ -76,7 +76,7 @@ func TestStackServicesErrors(t *testing.T) { cmd.Flags().Set(key, value) } cmd.SetOutput(ioutil.Discard) - testutil.ErrorContains(t, cmd.Execute(), tc.expectedError) + assert.ErrorContains(t, cmd.Execute(), tc.expectedError) } } @@ -88,9 +88,9 @@ func TestStackServicesEmptyServiceList(t *testing.T) { }) cmd := newServicesCommand(fakeCli) cmd.SetArgs([]string{"foo"}) - assert.NoError(t, cmd.Execute()) - assert.Equal(t, "", fakeCli.OutBuffer().String()) - assert.Equal(t, "Nothing found in stack: foo\n", fakeCli.ErrBuffer().String()) + assert.NilError(t, cmd.Execute()) + assert.Check(t, is.Equal("", fakeCli.OutBuffer().String())) + assert.Check(t, is.Equal("Nothing found in stack: foo\n", fakeCli.ErrBuffer().String())) } func TestStackServicesWithQuietOption(t *testing.T) { @@ -102,7 +102,7 @@ func TestStackServicesWithQuietOption(t *testing.T) { cmd := newServicesCommand(cli) cmd.Flags().Set("quiet", "true") cmd.SetArgs([]string{"foo"}) - assert.NoError(t, cmd.Execute()) + assert.NilError(t, cmd.Execute()) golden.Assert(t, cli.OutBuffer().String(), "stack-services-with-quiet-option.golden") } @@ -117,7 +117,7 @@ func TestStackServicesWithFormat(t *testing.T) { cmd := newServicesCommand(cli) cmd.SetArgs([]string{"foo"}) cmd.Flags().Set("format", "{{ .Name }}") - assert.NoError(t, cmd.Execute()) + assert.NilError(t, cmd.Execute()) golden.Assert(t, cli.OutBuffer().String(), "stack-services-with-format.golden") } @@ -134,7 +134,7 @@ func TestStackServicesWithConfigFormat(t *testing.T) { }) cmd := newServicesCommand(cli) cmd.SetArgs([]string{"foo"}) - assert.NoError(t, cmd.Execute()) + assert.NilError(t, cmd.Execute()) golden.Assert(t, cli.OutBuffer().String(), "stack-services-with-config-format.golden") } @@ -157,6 +157,6 @@ func TestStackServicesWithoutFormat(t *testing.T) { }) cmd := newServicesCommand(cli) cmd.SetArgs([]string{"foo"}) - assert.NoError(t, cmd.Execute()) + assert.NilError(t, cmd.Execute()) golden.Assert(t, cli.OutBuffer().String(), "stack-services-without-format.golden") } diff --git a/components/cli/cli/command/stack/swarm/deploy_bundlefile_test.go b/components/cli/cli/command/stack/swarm/deploy_bundlefile_test.go index c259c60d2a..4e66caabed 100644 --- a/components/cli/cli/command/stack/swarm/deploy_bundlefile_test.go +++ b/components/cli/cli/command/stack/swarm/deploy_bundlefile_test.go @@ -2,38 +2,39 @@ package swarm import ( "bytes" - "fmt" "path/filepath" "testing" - "github.com/stretchr/testify/assert" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" ) func TestLoadBundlefileErrors(t *testing.T) { testCases := []struct { namespace string path string - expectedError error + expectedError string }{ { namespace: "namespace_foo", - expectedError: fmt.Errorf("Bundle %s.dab not found", "namespace_foo"), + expectedError: "Bundle namespace_foo.dab not found", }, { namespace: "namespace_foo", path: "invalid_path", - expectedError: fmt.Errorf("Bundle %s not found", "invalid_path"), - }, - { - namespace: "namespace_foo", - path: filepath.Join("testdata", "bundlefile_with_invalid_syntax"), - expectedError: fmt.Errorf("Error reading"), + expectedError: "Bundle invalid_path not found", }, + // FIXME: this test never working, testdata file is missing from repo + //{ + // namespace: "namespace_foo", + // path: string(golden.Get(t, "bundlefile_with_invalid_syntax")), + // expectedError: "Error reading", + //}, } for _, tc := range testCases { _, err := loadBundlefile(&bytes.Buffer{}, tc.namespace, tc.path) - assert.Error(t, err, tc.expectedError) + assert.ErrorContains(t, err, tc.expectedError) } } @@ -44,6 +45,6 @@ func TestLoadBundlefile(t *testing.T) { path := filepath.Join("testdata", "bundlefile_with_two_services.dab") bundleFile, err := loadBundlefile(buf, namespace, path) - assert.NoError(t, err) - assert.Equal(t, len(bundleFile.Services), 2) + assert.NilError(t, err) + assert.Check(t, is.Equal(len(bundleFile.Services), 2)) } diff --git a/components/cli/cli/command/stack/swarm/deploy_composefile_test.go b/components/cli/cli/command/stack/swarm/deploy_composefile_test.go index 7f3133148c..2903fd3c42 100644 --- a/components/cli/cli/command/stack/swarm/deploy_composefile_test.go +++ b/components/cli/cli/command/stack/swarm/deploy_composefile_test.go @@ -4,10 +4,9 @@ import ( "testing" "github.com/docker/cli/internal/test/network" - "github.com/docker/cli/internal/test/testutil" "github.com/docker/docker/api/types" + "github.com/gotestyourself/gotestyourself/assert" "github.com/pkg/errors" - "github.com/stretchr/testify/assert" "golang.org/x/net/context" ) @@ -34,10 +33,13 @@ func TestValidateExternalNetworks(t *testing.T) { inspectError: errors.New("Unexpected"), expectedMsg: "Unexpected", }, - { - inspectError: errors.New("host net does not exist on swarm classic"), - network: "host", - }, + // FIXME(vdemeester) that doesn't work under windows, the check needs to be smarter + /* + { + inspectError: errors.New("host net does not exist on swarm classic"), + network: "host", + }, + */ { network: "user", expectedMsg: "is not in the right scope", @@ -57,9 +59,9 @@ func TestValidateExternalNetworks(t *testing.T) { networks := []string{testcase.network} err := validateExternalNetworks(context.Background(), fakeClient, networks) if testcase.expectedMsg == "" { - assert.NoError(t, err) + assert.NilError(t, err) } else { - testutil.ErrorContains(t, err, testcase.expectedMsg) + assert.ErrorContains(t, err, testcase.expectedMsg) } } } diff --git a/components/cli/cli/command/stack/swarm/deploy_test.go b/components/cli/cli/command/stack/swarm/deploy_test.go index 73c91a0392..3fdbbdbee7 100644 --- a/components/cli/cli/command/stack/swarm/deploy_test.go +++ b/components/cli/cli/command/stack/swarm/deploy_test.go @@ -7,7 +7,8 @@ import ( "github.com/docker/cli/internal/test" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/swarm" - "github.com/stretchr/testify/assert" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" "golang.org/x/net/context" ) @@ -22,7 +23,7 @@ func TestPruneServices(t *testing.T) { dockerCli := test.NewFakeCli(client) pruneServices(ctx, dockerCli, namespace, services) - assert.Equal(t, buildObjectIDs([]string{objectName("foo", "remove")}), client.removedServices) + assert.Check(t, is.DeepEqual(buildObjectIDs([]string{objectName("foo", "remove")}), client.removedServices)) } // TestServiceUpdateResolveImageChanged tests that the service's @@ -93,9 +94,9 @@ func TestServiceUpdateResolveImageChanged(t *testing.T) { }, } err := deployServices(ctx, client, spec, namespace, false, ResolveImageChanged) - assert.NoError(t, err) - assert.Equal(t, testcase.expectedQueryRegistry, receivedOptions.QueryRegistry) - assert.Equal(t, testcase.expectedImage, receivedService.TaskTemplate.ContainerSpec.Image) + assert.NilError(t, err) + assert.Check(t, is.Equal(testcase.expectedQueryRegistry, receivedOptions.QueryRegistry)) + assert.Check(t, is.Equal(testcase.expectedImage, receivedService.TaskTemplate.ContainerSpec.Image)) receivedService = swarm.ServiceSpec{} receivedOptions = types.ServiceUpdateOptions{} diff --git a/components/cli/cli/command/swarm/ca_test.go b/components/cli/cli/command/swarm/ca_test.go index cb2668293a..d11739ec37 100644 --- a/components/cli/cli/command/swarm/ca_test.go +++ b/components/cli/cli/command/swarm/ca_test.go @@ -8,10 +8,9 @@ import ( "time" "github.com/docker/cli/internal/test" - "github.com/docker/cli/internal/test/testutil" "github.com/docker/docker/api/types/swarm" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" ) func swarmSpecWithFullCAConfig() *swarm.Spec { @@ -35,13 +34,13 @@ func swarmSpecWithFullCAConfig() *swarm.Spec { func TestDisplayTrustRootNoRoot(t *testing.T) { buffer := new(bytes.Buffer) err := displayTrustRoot(buffer, swarm.Swarm{}) - assert.EqualError(t, err, "No CA information available") + assert.Error(t, err, "No CA information available") } func TestDisplayTrustRootInvalidFlags(t *testing.T) { // we need an actual PEMfile to test tmpfile, err := ioutil.TempFile("", "pemfile") - assert.NoError(t, err) + assert.NilError(t, err) defer os.Remove(tmpfile.Name()) tmpfile.Write([]byte(` -----BEGIN CERTIFICATE----- @@ -95,9 +94,9 @@ PQQDAgNIADBFAiEAqD3Kb2rgsy6NoTk+zEgcUi/aGBCsvQDG3vML1PXN8j0CIBjj }, nil }, })) - assert.NoError(t, cmd.Flags().Parse(args)) + assert.Check(t, cmd.Flags().Parse(args)) cmd.SetOutput(ioutil.Discard) - testutil.ErrorContains(t, cmd.Execute(), "flag requires the `--rotate` flag to update the CA") + assert.ErrorContains(t, cmd.Execute(), "flag requires the `--rotate` flag to update the CA") } } @@ -109,8 +108,8 @@ func TestDisplayTrustRoot(t *testing.T) { TLSInfo: swarm.TLSInfo{TrustRoot: trustRoot}, }, }) - require.NoError(t, err) - assert.Equal(t, trustRoot+"\n", buffer.String()) + assert.NilError(t, err) + assert.Check(t, is.Equal(trustRoot+"\n", buffer.String())) } func TestUpdateSwarmSpecDefaultRotate(t *testing.T) { @@ -122,7 +121,7 @@ func TestUpdateSwarmSpecDefaultRotate(t *testing.T) { expected.CAConfig.ForceRotate = 2 expected.CAConfig.SigningCACert = "" expected.CAConfig.SigningCAKey = "" - assert.Equal(t, expected, spec) + assert.Check(t, is.DeepEqual(expected, spec)) } func TestUpdateSwarmSpecPartial(t *testing.T) { @@ -134,7 +133,7 @@ func TestUpdateSwarmSpecPartial(t *testing.T) { expected := swarmSpecWithFullCAConfig() expected.CAConfig.SigningCACert = "cacert" - assert.Equal(t, expected, spec) + assert.Check(t, is.DeepEqual(expected, spec)) } func TestUpdateSwarmSpecFullFlags(t *testing.T) { @@ -151,5 +150,5 @@ func TestUpdateSwarmSpecFullFlags(t *testing.T) { expected.CAConfig.SigningCACert = "cacert" expected.CAConfig.SigningCAKey = "cakey" expected.CAConfig.NodeCertExpiry = 3 * time.Minute - assert.Equal(t, expected, spec) + assert.Check(t, is.DeepEqual(expected, spec)) } diff --git a/components/cli/cli/command/swarm/init_test.go b/components/cli/cli/command/swarm/init_test.go index 24a1b90717..22712726ca 100644 --- a/components/cli/cli/command/swarm/init_test.go +++ b/components/cli/cli/command/swarm/init_test.go @@ -8,9 +8,9 @@ import ( "github.com/docker/cli/internal/test" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/swarm" + "github.com/gotestyourself/gotestyourself/assert" "github.com/gotestyourself/gotestyourself/golden" "github.com/pkg/errors" - "github.com/stretchr/testify/assert" ) func TestSwarmInitErrorOnAPIFailure(t *testing.T) { @@ -74,7 +74,7 @@ func TestSwarmInitErrorOnAPIFailure(t *testing.T) { cmd.Flags().Set(key, value) } cmd.SetOutput(ioutil.Discard) - assert.EqualError(t, cmd.Execute(), tc.expectedError) + assert.Error(t, cmd.Execute(), tc.expectedError) } } @@ -119,7 +119,7 @@ func TestSwarmInit(t *testing.T) { for key, value := range tc.flags { cmd.Flags().Set(key, value) } - assert.NoError(t, cmd.Execute()) + assert.NilError(t, cmd.Execute()) golden.Assert(t, cli.OutBuffer().String(), fmt.Sprintf("init-%s.golden", tc.name)) } } diff --git a/components/cli/cli/command/swarm/join_test.go b/components/cli/cli/command/swarm/join_test.go index 69546cd8cf..7817df1440 100644 --- a/components/cli/cli/command/swarm/join_test.go +++ b/components/cli/cli/command/swarm/join_test.go @@ -6,11 +6,11 @@ import ( "testing" "github.com/docker/cli/internal/test" - "github.com/docker/cli/internal/test/testutil" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/swarm" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" "github.com/pkg/errors" - "github.com/stretchr/testify/assert" ) func TestSwarmJoinErrors(t *testing.T) { @@ -55,7 +55,7 @@ func TestSwarmJoinErrors(t *testing.T) { })) cmd.SetArgs(tc.args) cmd.SetOutput(ioutil.Discard) - testutil.ErrorContains(t, cmd.Execute(), tc.expectedError) + assert.ErrorContains(t, cmd.Execute(), tc.expectedError) } } @@ -94,7 +94,7 @@ func TestSwarmJoin(t *testing.T) { }) cmd := newJoinCommand(cli) cmd.SetArgs([]string{"remote"}) - assert.NoError(t, cmd.Execute()) - assert.Equal(t, strings.TrimSpace(cli.OutBuffer().String()), tc.expected) + assert.NilError(t, cmd.Execute()) + assert.Check(t, is.Equal(strings.TrimSpace(cli.OutBuffer().String()), tc.expected)) } } diff --git a/components/cli/cli/command/swarm/join_token_test.go b/components/cli/cli/command/swarm/join_token_test.go index 6ab503113a..ca042671b2 100644 --- a/components/cli/cli/command/swarm/join_token_test.go +++ b/components/cli/cli/command/swarm/join_token_test.go @@ -11,9 +11,8 @@ import ( "github.com/pkg/errors" // Import builders to get the builder function as package function . "github.com/docker/cli/internal/test/builders" - "github.com/docker/cli/internal/test/testutil" + "github.com/gotestyourself/gotestyourself/assert" "github.com/gotestyourself/gotestyourself/golden" - "github.com/stretchr/testify/assert" ) func TestSwarmJoinTokenErrors(t *testing.T) { @@ -101,7 +100,7 @@ func TestSwarmJoinTokenErrors(t *testing.T) { cmd.Flags().Set(key, value) } cmd.SetOutput(ioutil.Discard) - testutil.ErrorContains(t, cmd.Execute(), tc.expectedError) + assert.ErrorContains(t, cmd.Execute(), tc.expectedError) } } @@ -206,7 +205,7 @@ func TestSwarmJoinToken(t *testing.T) { for key, value := range tc.flags { cmd.Flags().Set(key, value) } - assert.NoError(t, cmd.Execute()) + assert.NilError(t, cmd.Execute()) golden.Assert(t, cli.OutBuffer().String(), fmt.Sprintf("jointoken-%s.golden", tc.name)) } } diff --git a/components/cli/cli/command/swarm/leave_test.go b/components/cli/cli/command/swarm/leave_test.go index cd8aeb8297..07eac2c4da 100644 --- a/components/cli/cli/command/swarm/leave_test.go +++ b/components/cli/cli/command/swarm/leave_test.go @@ -6,9 +6,9 @@ import ( "testing" "github.com/docker/cli/internal/test" - "github.com/docker/cli/internal/test/testutil" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" "github.com/pkg/errors" - "github.com/stretchr/testify/assert" ) func TestSwarmLeaveErrors(t *testing.T) { @@ -38,13 +38,13 @@ func TestSwarmLeaveErrors(t *testing.T) { })) cmd.SetArgs(tc.args) cmd.SetOutput(ioutil.Discard) - testutil.ErrorContains(t, cmd.Execute(), tc.expectedError) + assert.ErrorContains(t, cmd.Execute(), tc.expectedError) } } func TestSwarmLeave(t *testing.T) { cli := test.NewFakeCli(&fakeClient{}) cmd := newLeaveCommand(cli) - assert.NoError(t, cmd.Execute()) - assert.Equal(t, "Node left the swarm.", strings.TrimSpace(cli.OutBuffer().String())) + assert.NilError(t, cmd.Execute()) + assert.Check(t, is.Equal("Node left the swarm.", strings.TrimSpace(cli.OutBuffer().String()))) } diff --git a/components/cli/cli/command/swarm/opts_test.go b/components/cli/cli/command/swarm/opts_test.go index c694cc1bdd..6aa12b89c4 100644 --- a/components/cli/cli/command/swarm/opts_test.go +++ b/components/cli/cli/command/swarm/opts_test.go @@ -3,37 +3,38 @@ package swarm import ( "testing" - "github.com/stretchr/testify/assert" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" ) func TestNodeAddrOptionSetHostAndPort(t *testing.T) { opt := NewNodeAddrOption("old:123") addr := "newhost:5555" - assert.NoError(t, opt.Set(addr)) - assert.Equal(t, addr, opt.Value()) + assert.NilError(t, opt.Set(addr)) + assert.Check(t, is.Equal(addr, opt.Value())) } func TestNodeAddrOptionSetHostOnly(t *testing.T) { opt := NewListenAddrOption() - assert.NoError(t, opt.Set("newhost")) - assert.Equal(t, "newhost:2377", opt.Value()) + assert.NilError(t, opt.Set("newhost")) + assert.Check(t, is.Equal("newhost:2377", opt.Value())) } func TestNodeAddrOptionSetHostOnlyIPv6(t *testing.T) { opt := NewListenAddrOption() - assert.NoError(t, opt.Set("::1")) - assert.Equal(t, "[::1]:2377", opt.Value()) + assert.NilError(t, opt.Set("::1")) + assert.Check(t, is.Equal("[::1]:2377", opt.Value())) } func TestNodeAddrOptionSetPortOnly(t *testing.T) { opt := NewListenAddrOption() - assert.NoError(t, opt.Set(":4545")) - assert.Equal(t, "0.0.0.0:4545", opt.Value()) + assert.NilError(t, opt.Set(":4545")) + assert.Check(t, is.Equal("0.0.0.0:4545", opt.Value())) } func TestNodeAddrOptionSetInvalidFormat(t *testing.T) { opt := NewListenAddrOption() - assert.EqualError(t, opt.Set("http://localhost:4545"), "Invalid proto, expected tcp: http://localhost:4545") + assert.Error(t, opt.Set("http://localhost:4545"), "Invalid proto, expected tcp: http://localhost:4545") } func TestExternalCAOptionErrors(t *testing.T) { @@ -64,7 +65,7 @@ func TestExternalCAOptionErrors(t *testing.T) { } for _, tc := range testCases { opt := &ExternalCAOption{} - assert.EqualError(t, opt.Set(tc.externalCA), tc.expectedError) + assert.Error(t, opt.Set(tc.externalCA), tc.expectedError) } } @@ -96,15 +97,15 @@ func TestExternalCAOption(t *testing.T) { } for _, tc := range testCases { opt := &ExternalCAOption{} - assert.NoError(t, opt.Set(tc.externalCA)) - assert.Equal(t, tc.expected, opt.String()) + assert.NilError(t, opt.Set(tc.externalCA)) + assert.Check(t, is.Equal(tc.expected, opt.String())) } } func TestExternalCAOptionMultiple(t *testing.T) { opt := &ExternalCAOption{} - assert.NoError(t, opt.Set("protocol=cfssl,url=https://example.com")) - assert.NoError(t, opt.Set("protocol=CFSSL,url=anything")) - assert.Len(t, opt.Value(), 2) - assert.Equal(t, "cfssl: https://example.com, cfssl: anything", opt.String()) + assert.NilError(t, opt.Set("protocol=cfssl,url=https://example.com")) + assert.NilError(t, opt.Set("protocol=CFSSL,url=anything")) + assert.Check(t, is.Len(opt.Value(), 2)) + assert.Check(t, is.Equal("cfssl: https://example.com, cfssl: anything", opt.String())) } diff --git a/components/cli/cli/command/swarm/unlock_key_test.go b/components/cli/cli/command/swarm/unlock_key_test.go index 97c04fc628..aaade6fe9c 100644 --- a/components/cli/cli/command/swarm/unlock_key_test.go +++ b/components/cli/cli/command/swarm/unlock_key_test.go @@ -11,9 +11,8 @@ import ( "github.com/pkg/errors" // Import builders to get the builder function as package function . "github.com/docker/cli/internal/test/builders" - "github.com/docker/cli/internal/test/testutil" + "github.com/gotestyourself/gotestyourself/assert" "github.com/gotestyourself/gotestyourself/golden" - "github.com/stretchr/testify/assert" ) func TestSwarmUnlockKeyErrors(t *testing.T) { @@ -93,7 +92,7 @@ func TestSwarmUnlockKeyErrors(t *testing.T) { cmd.Flags().Set(key, value) } cmd.SetOutput(ioutil.Discard) - testutil.ErrorContains(t, cmd.Execute(), tc.expectedError) + assert.ErrorContains(t, cmd.Execute(), tc.expectedError) } } @@ -166,7 +165,7 @@ func TestSwarmUnlockKey(t *testing.T) { for key, value := range tc.flags { cmd.Flags().Set(key, value) } - assert.NoError(t, cmd.Execute()) + assert.NilError(t, cmd.Execute()) golden.Assert(t, cli.OutBuffer().String(), fmt.Sprintf("unlockkeys-%s.golden", tc.name)) } } diff --git a/components/cli/cli/command/swarm/unlock_test.go b/components/cli/cli/command/swarm/unlock_test.go index c7aee0a3c7..eb481d13ca 100644 --- a/components/cli/cli/command/swarm/unlock_test.go +++ b/components/cli/cli/command/swarm/unlock_test.go @@ -7,11 +7,10 @@ import ( "github.com/docker/cli/cli/command" "github.com/docker/cli/internal/test" - "github.com/docker/cli/internal/test/testutil" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/swarm" + "github.com/gotestyourself/gotestyourself/assert" "github.com/pkg/errors" - "github.com/stretchr/testify/assert" ) func TestSwarmUnlockErrors(t *testing.T) { @@ -72,7 +71,7 @@ func TestSwarmUnlockErrors(t *testing.T) { })) cmd.SetArgs(tc.args) cmd.SetOutput(ioutil.Discard) - testutil.ErrorContains(t, cmd.Execute(), tc.expectedError) + assert.ErrorContains(t, cmd.Execute(), tc.expectedError) } } @@ -95,5 +94,5 @@ func TestSwarmUnlock(t *testing.T) { }) dockerCli.SetIn(command.NewInStream(ioutil.NopCloser(strings.NewReader(input)))) cmd := newUnlockCommand(dockerCli) - assert.NoError(t, cmd.Execute()) + assert.NilError(t, cmd.Execute()) } diff --git a/components/cli/cli/command/swarm/update_test.go b/components/cli/cli/command/swarm/update_test.go index e2a0033916..60e44ddfe0 100644 --- a/components/cli/cli/command/swarm/update_test.go +++ b/components/cli/cli/command/swarm/update_test.go @@ -12,9 +12,8 @@ import ( "github.com/pkg/errors" // Import builders to get the builder function as package function . "github.com/docker/cli/internal/test/builders" - "github.com/docker/cli/internal/test/testutil" + "github.com/gotestyourself/gotestyourself/assert" "github.com/gotestyourself/gotestyourself/golden" - "github.com/stretchr/testify/assert" ) func TestSwarmUpdateErrors(t *testing.T) { @@ -78,7 +77,7 @@ func TestSwarmUpdateErrors(t *testing.T) { cmd.Flags().Set(key, value) } cmd.SetOutput(ioutil.Discard) - testutil.ErrorContains(t, cmd.Execute(), tc.expectedError) + assert.ErrorContains(t, cmd.Execute(), tc.expectedError) } } @@ -173,7 +172,7 @@ func TestSwarmUpdate(t *testing.T) { cmd.Flags().Set(key, value) } cmd.SetOutput(cli.OutBuffer()) - assert.NoError(t, cmd.Execute()) + assert.NilError(t, cmd.Execute()) golden.Assert(t, cli.OutBuffer().String(), fmt.Sprintf("update-%s.golden", tc.name)) } } diff --git a/components/cli/cli/command/system/info_test.go b/components/cli/cli/command/system/info_test.go index 4291623d1a..7e2dd6bff8 100644 --- a/components/cli/cli/command/system/info_test.go +++ b/components/cli/cli/command/system/info_test.go @@ -10,8 +10,9 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/registry" "github.com/docker/docker/api/types/swarm" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" "github.com/gotestyourself/gotestyourself/golden" - "github.com/stretchr/testify/assert" ) // helper function that base64 decodes a string and ignores the error @@ -226,12 +227,12 @@ func TestPrettyPrintInfo(t *testing.T) { }, } { cli := test.NewFakeCli(&fakeClient{}) - assert.NoError(t, prettyPrintInfo(cli, tc.dockerInfo)) + assert.NilError(t, prettyPrintInfo(cli, tc.dockerInfo)) golden.Assert(t, cli.OutBuffer().String(), tc.expectedGolden+".golden") if tc.warningsGolden != "" { golden.Assert(t, cli.ErrBuffer().String(), tc.warningsGolden+".golden") } else { - assert.Equal(t, "", cli.ErrBuffer().String()) + assert.Check(t, is.Equal("", cli.ErrBuffer().String())) } } } diff --git a/components/cli/cli/command/system/prune_test.go b/components/cli/cli/command/system/prune_test.go index 0d694ce066..b763c480ab 100644 --- a/components/cli/cli/command/system/prune_test.go +++ b/components/cli/cli/command/system/prune_test.go @@ -4,12 +4,19 @@ import ( "testing" "github.com/docker/cli/internal/test" - "github.com/stretchr/testify/assert" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" ) -func TestPrunePromptPre131(t *testing.T) { +func TestPrunePromptPre131DoesNotIncludeBuildCache(t *testing.T) { cli := test.NewFakeCli(&fakeClient{version: "1.30"}) cmd := newPruneCommand(cli) - assert.NoError(t, cmd.Execute()) - assert.NotContains(t, cli.OutBuffer().String(), "all build cache") + assert.NilError(t, cmd.Execute()) + expected := `WARNING! This will remove: + - all stopped containers + - all networks not used by at least one container + - all dangling images +Are you sure you want to continue? [y/N] ` + assert.Check(t, is.Equal(expected, cli.OutBuffer().String())) + } diff --git a/components/cli/cli/command/system/version_test.go b/components/cli/cli/command/system/version_test.go index 98c0c1167d..c704ce2051 100644 --- a/components/cli/cli/command/system/version_test.go +++ b/components/cli/cli/command/system/version_test.go @@ -6,11 +6,12 @@ import ( "testing" "github.com/docker/cli/cli/command" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" "github.com/docker/cli/internal/test" "github.com/docker/docker/api" "github.com/docker/docker/api/types" - "github.com/stretchr/testify/assert" "golang.org/x/net/context" ) @@ -22,12 +23,15 @@ func TestVersionWithoutServer(t *testing.T) { }) cmd := NewVersionCommand(cli) cmd.SetOutput(cli.Err()) - assert.Error(t, cmd.Execute()) - assert.Contains(t, cleanTabs(cli.OutBuffer().String()), "Client:") - assert.NotContains(t, cleanTabs(cli.OutBuffer().String()), "Server:") + assert.ErrorContains(t, cmd.Execute(), "no server") + out := cli.OutBuffer().String() + // TODO: use an assertion like e2e/image/build_test.go:assertBuildOutput() + // instead of contains/not contains + assert.Check(t, is.Contains(out, "Client:")) + assert.Assert(t, !strings.Contains(out, "Server:"), "actual: %s", out) } -func fakeServerVersion(ctx context.Context) (types.Version, error) { +func fakeServerVersion(_ context.Context) (types.Version, error) { return types.Version{ Version: "docker-dev", APIVersion: api.DefaultVersion, @@ -38,8 +42,8 @@ func TestVersionWithOrchestrator(t *testing.T) { cli := test.NewFakeCli(&fakeClient{serverVersion: fakeServerVersion}) cli.SetClientInfo(func() command.ClientInfo { return command.ClientInfo{Orchestrator: "swarm"} }) cmd := NewVersionCommand(cli) - assert.NoError(t, cmd.Execute()) - assert.Contains(t, cleanTabs(cli.OutBuffer().String()), "Orchestrator: swarm") + assert.NilError(t, cmd.Execute()) + assert.Check(t, is.Contains(cleanTabs(cli.OutBuffer().String()), "Orchestrator: swarm")) } func cleanTabs(line string) string { diff --git a/components/cli/cli/command/task/print_test.go b/components/cli/cli/command/task/print_test.go index 04ae1bb0ef..731d2de16f 100644 --- a/components/cli/cli/command/task/print_test.go +++ b/components/cli/cli/command/task/print_test.go @@ -12,8 +12,8 @@ import ( . "github.com/docker/cli/internal/test/builders" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/swarm" + "github.com/gotestyourself/gotestyourself/assert" "github.com/gotestyourself/gotestyourself/golden" - "github.com/stretchr/testify/assert" ) func TestTaskPrintWithQuietOption(t *testing.T) { @@ -24,7 +24,7 @@ func TestTaskPrintWithQuietOption(t *testing.T) { cli := test.NewFakeCli(apiClient) tasks := []swarm.Task{*Task(TaskID("id-foo"))} err := Print(context.Background(), cli, tasks, idresolver.New(apiClient, noResolve), trunc, quiet, formatter.TableFormatKey) - assert.NoError(t, err) + assert.NilError(t, err) golden.Assert(t, cli.OutBuffer().String(), "task-print-with-quiet-option.golden") } @@ -38,7 +38,7 @@ func TestTaskPrintWithNoTruncOption(t *testing.T) { *Task(TaskID("id-foo-yov6omdek8fg3k5stosyp2m50")), } err := Print(context.Background(), cli, tasks, idresolver.New(apiClient, noResolve), trunc, quiet, "{{ .ID }}") - assert.NoError(t, err) + assert.NilError(t, err) golden.Assert(t, cli.OutBuffer().String(), "task-print-with-no-trunc-option.golden") } @@ -52,7 +52,7 @@ func TestTaskPrintWithGlobalService(t *testing.T) { *Task(TaskServiceID("service-id-foo"), TaskNodeID("node-id-bar"), TaskSlot(0)), } err := Print(context.Background(), cli, tasks, idresolver.New(apiClient, noResolve), trunc, quiet, "{{ .Name }}") - assert.NoError(t, err) + assert.NilError(t, err) golden.Assert(t, cli.OutBuffer().String(), "task-print-with-global-service.golden") } @@ -66,7 +66,7 @@ func TestTaskPrintWithReplicatedService(t *testing.T) { *Task(TaskServiceID("service-id-foo"), TaskSlot(1)), } err := Print(context.Background(), cli, tasks, idresolver.New(apiClient, noResolve), trunc, quiet, "{{ .Name }}") - assert.NoError(t, err) + assert.NilError(t, err) golden.Assert(t, cli.OutBuffer().String(), "task-print-with-replicated-service.golden") } @@ -102,7 +102,7 @@ func TestTaskPrintWithIndentation(t *testing.T) { ), } err := Print(context.Background(), cli, tasks, idresolver.New(apiClient, noResolve), trunc, quiet, formatter.TableFormatKey) - assert.NoError(t, err) + assert.NilError(t, err) golden.Assert(t, cli.OutBuffer().String(), "task-print-with-indentation.golden") } @@ -123,6 +123,6 @@ func TestTaskPrintWithResolution(t *testing.T) { *Task(TaskServiceID("service-id-foo"), TaskSlot(1)), } err := Print(context.Background(), cli, tasks, idresolver.New(apiClient, noResolve), trunc, quiet, "{{ .Name }} {{ .Node }}") - assert.NoError(t, err) + assert.NilError(t, err) golden.Assert(t, cli.OutBuffer().String(), "task-print-with-resolution.golden") } diff --git a/components/cli/cli/command/trust.go b/components/cli/cli/command/trust.go index 51f760ac5b..65f2408585 100644 --- a/components/cli/cli/command/trust.go +++ b/components/cli/cli/command/trust.go @@ -1,47 +1,15 @@ package command import ( - "os" - "strconv" - "github.com/spf13/pflag" ) -var ( - // TODO: make this not global - untrusted bool -) - -func init() { - untrusted = !getDefaultTrustState() -} - // AddTrustVerificationFlags adds content trust flags to the provided flagset -func AddTrustVerificationFlags(fs *pflag.FlagSet) { - trusted := getDefaultTrustState() - fs.BoolVar(&untrusted, "disable-content-trust", !trusted, "Skip image verification") +func AddTrustVerificationFlags(fs *pflag.FlagSet, v *bool, trusted bool) { + fs.BoolVar(v, "disable-content-trust", !trusted, "Skip image verification") } // AddTrustSigningFlags adds "signing" flags to the provided flagset -func AddTrustSigningFlags(fs *pflag.FlagSet) { - trusted := getDefaultTrustState() - fs.BoolVar(&untrusted, "disable-content-trust", !trusted, "Skip image signing") -} - -// getDefaultTrustState returns true if content trust is enabled through the $DOCKER_CONTENT_TRUST environment variable. -func getDefaultTrustState() bool { - var trusted bool - if e := os.Getenv("DOCKER_CONTENT_TRUST"); e != "" { - if t, err := strconv.ParseBool(e); t || err != nil { - // treat any other value as true - trusted = true - } - } - return trusted -} - -// IsTrusted returns true if content trust is enabled, either through the $DOCKER_CONTENT_TRUST environment variable, -// or through `--disabled-content-trust=false` on a command. -func IsTrusted() bool { - return !untrusted +func AddTrustSigningFlags(fs *pflag.FlagSet, v *bool, trusted bool) { + fs.BoolVar(v, "disable-content-trust", !trusted, "Skip image signing") } diff --git a/components/cli/cli/command/trust/common.go b/components/cli/cli/command/trust/common.go index 29b0ee622e..9173e6eeeb 100644 --- a/components/cli/cli/command/trust/common.go +++ b/components/cli/cli/command/trust/common.go @@ -66,7 +66,7 @@ type trustKey struct { // This information is to be pretty printed or serialized into a machine-readable format. func lookupTrustInfo(cli command.Cli, remote string) (trustTagRowList, []client.RoleWithSignatures, []data.Role, error) { ctx := context.Background() - imgRefAndAuth, err := trust.GetImageReferencesAndAuth(ctx, image.AuthResolver(cli), remote) + imgRefAndAuth, err := trust.GetImageReferencesAndAuth(ctx, nil, image.AuthResolver(cli), remote) if err != nil { return trustTagRowList{}, []client.RoleWithSignatures{}, []data.Role{}, err } diff --git a/components/cli/cli/command/trust/helpers_test.go b/components/cli/cli/command/trust/helpers_test.go index a13eae2d6e..216ae5ce9d 100644 --- a/components/cli/cli/command/trust/helpers_test.go +++ b/components/cli/cli/command/trust/helpers_test.go @@ -5,21 +5,20 @@ import ( "os" "testing" + "github.com/gotestyourself/gotestyourself/assert" "github.com/theupdateframework/notary/client" "github.com/theupdateframework/notary/passphrase" "github.com/theupdateframework/notary/trustpinning" - - "github.com/stretchr/testify/assert" ) func TestGetOrGenerateNotaryKeyAndInitRepo(t *testing.T) { tmpDir, err := ioutil.TempDir("", "notary-test-") - assert.NoError(t, err) + assert.NilError(t, err) defer os.RemoveAll(tmpDir) notaryRepo, err := client.NewFileCachedRepository(tmpDir, "gun", "https://localhost", nil, passphrase.ConstantRetriever(passwd), trustpinning.TrustPinConfig{}) - assert.NoError(t, err) + assert.NilError(t, err) err = getOrGenerateRootKeyAndInitRepo(notaryRepo) - assert.EqualError(t, err, "client is offline") + assert.Error(t, err, "client is offline") } diff --git a/components/cli/cli/command/trust/inspect_pretty_test.go b/components/cli/cli/command/trust/inspect_pretty_test.go index 93e5686958..bc2ea67928 100644 --- a/components/cli/cli/command/trust/inspect_pretty_test.go +++ b/components/cli/cli/command/trust/inspect_pretty_test.go @@ -7,10 +7,11 @@ import ( "github.com/docker/cli/cli/trust" "github.com/docker/cli/internal/test" - "github.com/docker/cli/internal/test/testutil" + notaryfake "github.com/docker/cli/internal/test/notary" dockerClient "github.com/docker/docker/client" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" "github.com/gotestyourself/gotestyourself/golden" - "github.com/stretchr/testify/assert" "github.com/theupdateframework/notary" "github.com/theupdateframework/notary/client" "github.com/theupdateframework/notary/tuf/data" @@ -49,137 +50,137 @@ func TestTrustInspectPrettyCommandErrors(t *testing.T) { cmd.SetArgs(tc.args) cmd.SetOutput(ioutil.Discard) cmd.Flags().Set("pretty", "true") - testutil.ErrorContains(t, cmd.Execute(), tc.expectedError) + assert.ErrorContains(t, cmd.Execute(), tc.expectedError) } } func TestTrustInspectPrettyCommandOfflineErrors(t *testing.T) { cli := test.NewFakeCli(&fakeClient{}) - cli.SetNotaryClient(getOfflineNotaryRepository) + cli.SetNotaryClient(notaryfake.GetOfflineNotaryRepository) cmd := newInspectCommand(cli) cmd.Flags().Set("pretty", "true") cmd.SetArgs([]string{"nonexistent-reg-name.io/image"}) cmd.SetOutput(ioutil.Discard) - testutil.ErrorContains(t, cmd.Execute(), "No signatures or cannot access nonexistent-reg-name.io/image") + assert.ErrorContains(t, cmd.Execute(), "No signatures or cannot access nonexistent-reg-name.io/image") cli = test.NewFakeCli(&fakeClient{}) - cli.SetNotaryClient(getOfflineNotaryRepository) + cli.SetNotaryClient(notaryfake.GetOfflineNotaryRepository) cmd = newInspectCommand(cli) cmd.Flags().Set("pretty", "true") cmd.SetArgs([]string{"nonexistent-reg-name.io/image:tag"}) cmd.SetOutput(ioutil.Discard) - testutil.ErrorContains(t, cmd.Execute(), "No signatures or cannot access nonexistent-reg-name.io/image") + assert.ErrorContains(t, cmd.Execute(), "No signatures or cannot access nonexistent-reg-name.io/image") } func TestTrustInspectPrettyCommandUninitializedErrors(t *testing.T) { cli := test.NewFakeCli(&fakeClient{}) - cli.SetNotaryClient(getUninitializedNotaryRepository) + cli.SetNotaryClient(notaryfake.GetUninitializedNotaryRepository) cmd := newInspectCommand(cli) cmd.Flags().Set("pretty", "true") cmd.SetArgs([]string{"reg/unsigned-img"}) cmd.SetOutput(ioutil.Discard) - testutil.ErrorContains(t, cmd.Execute(), "No signatures or cannot access reg/unsigned-img") + assert.ErrorContains(t, cmd.Execute(), "No signatures or cannot access reg/unsigned-img") cli = test.NewFakeCli(&fakeClient{}) - cli.SetNotaryClient(getUninitializedNotaryRepository) + cli.SetNotaryClient(notaryfake.GetUninitializedNotaryRepository) cmd = newInspectCommand(cli) cmd.Flags().Set("pretty", "true") cmd.SetArgs([]string{"reg/unsigned-img:tag"}) cmd.SetOutput(ioutil.Discard) - testutil.ErrorContains(t, cmd.Execute(), "No signatures or cannot access reg/unsigned-img:tag") + assert.ErrorContains(t, cmd.Execute(), "No signatures or cannot access reg/unsigned-img:tag") } func TestTrustInspectPrettyCommandEmptyNotaryRepoErrors(t *testing.T) { cli := test.NewFakeCli(&fakeClient{}) - cli.SetNotaryClient(getEmptyTargetsNotaryRepository) + cli.SetNotaryClient(notaryfake.GetEmptyTargetsNotaryRepository) cmd := newInspectCommand(cli) cmd.Flags().Set("pretty", "true") cmd.SetArgs([]string{"reg/img:unsigned-tag"}) cmd.SetOutput(ioutil.Discard) - assert.NoError(t, cmd.Execute()) - assert.Contains(t, cli.OutBuffer().String(), "No signatures for reg/img:unsigned-tag") - assert.Contains(t, cli.OutBuffer().String(), "Administrative keys for reg/img") + assert.NilError(t, cmd.Execute()) + assert.Check(t, is.Contains(cli.OutBuffer().String(), "No signatures for reg/img:unsigned-tag")) + assert.Check(t, is.Contains(cli.OutBuffer().String(), "Administrative keys for reg/img")) cli = test.NewFakeCli(&fakeClient{}) - cli.SetNotaryClient(getEmptyTargetsNotaryRepository) + cli.SetNotaryClient(notaryfake.GetEmptyTargetsNotaryRepository) cmd = newInspectCommand(cli) cmd.Flags().Set("pretty", "true") cmd.SetArgs([]string{"reg/img"}) cmd.SetOutput(ioutil.Discard) - assert.NoError(t, cmd.Execute()) - assert.Contains(t, cli.OutBuffer().String(), "No signatures for reg/img") - assert.Contains(t, cli.OutBuffer().String(), "Administrative keys for reg/img") + assert.NilError(t, cmd.Execute()) + assert.Check(t, is.Contains(cli.OutBuffer().String(), "No signatures for reg/img")) + assert.Check(t, is.Contains(cli.OutBuffer().String(), "Administrative keys for reg/img")) } func TestTrustInspectPrettyCommandFullRepoWithoutSigners(t *testing.T) { cli := test.NewFakeCli(&fakeClient{}) - cli.SetNotaryClient(getLoadedWithNoSignersNotaryRepository) + cli.SetNotaryClient(notaryfake.GetLoadedWithNoSignersNotaryRepository) cmd := newInspectCommand(cli) cmd.Flags().Set("pretty", "true") cmd.SetArgs([]string{"signed-repo"}) - assert.NoError(t, cmd.Execute()) + assert.NilError(t, cmd.Execute()) golden.Assert(t, cli.OutBuffer().String(), "trust-inspect-pretty-full-repo-no-signers.golden") } func TestTrustInspectPrettyCommandOneTagWithoutSigners(t *testing.T) { cli := test.NewFakeCli(&fakeClient{}) - cli.SetNotaryClient(getLoadedWithNoSignersNotaryRepository) + cli.SetNotaryClient(notaryfake.GetLoadedWithNoSignersNotaryRepository) cmd := newInspectCommand(cli) cmd.Flags().Set("pretty", "true") cmd.SetArgs([]string{"signed-repo:green"}) - assert.NoError(t, cmd.Execute()) + assert.NilError(t, cmd.Execute()) golden.Assert(t, cli.OutBuffer().String(), "trust-inspect-pretty-one-tag-no-signers.golden") } func TestTrustInspectPrettyCommandFullRepoWithSigners(t *testing.T) { cli := test.NewFakeCli(&fakeClient{}) - cli.SetNotaryClient(getLoadedNotaryRepository) + cli.SetNotaryClient(notaryfake.GetLoadedNotaryRepository) cmd := newInspectCommand(cli) cmd.Flags().Set("pretty", "true") cmd.SetArgs([]string{"signed-repo"}) - assert.NoError(t, cmd.Execute()) + assert.NilError(t, cmd.Execute()) golden.Assert(t, cli.OutBuffer().String(), "trust-inspect-pretty-full-repo-with-signers.golden") } func TestTrustInspectPrettyCommandUnsignedTagInSignedRepo(t *testing.T) { cli := test.NewFakeCli(&fakeClient{}) - cli.SetNotaryClient(getLoadedNotaryRepository) + cli.SetNotaryClient(notaryfake.GetLoadedNotaryRepository) cmd := newInspectCommand(cli) cmd.Flags().Set("pretty", "true") cmd.SetArgs([]string{"signed-repo:unsigned"}) - assert.NoError(t, cmd.Execute()) + assert.NilError(t, cmd.Execute()) golden.Assert(t, cli.OutBuffer().String(), "trust-inspect-pretty-unsigned-tag-with-signers.golden") } func TestNotaryRoleToSigner(t *testing.T) { - assert.Equal(t, releasedRoleName, notaryRoleToSigner(data.CanonicalTargetsRole)) - assert.Equal(t, releasedRoleName, notaryRoleToSigner(trust.ReleasesRole)) - assert.Equal(t, "signer", notaryRoleToSigner("targets/signer")) - assert.Equal(t, "docker/signer", notaryRoleToSigner("targets/docker/signer")) + assert.Check(t, is.Equal(releasedRoleName, notaryRoleToSigner(data.CanonicalTargetsRole))) + assert.Check(t, is.Equal(releasedRoleName, notaryRoleToSigner(trust.ReleasesRole))) + assert.Check(t, is.Equal("signer", notaryRoleToSigner("targets/signer"))) + assert.Check(t, is.Equal("docker/signer", notaryRoleToSigner("targets/docker/signer"))) // It's nonsense for other base roles to have signed off on a target, but this function leaves role names intact for _, role := range data.BaseRoles { if role == data.CanonicalTargetsRole { continue } - assert.Equal(t, role.String(), notaryRoleToSigner(role)) + assert.Check(t, is.Equal(role.String(), notaryRoleToSigner(role))) } - assert.Equal(t, "notarole", notaryRoleToSigner(data.RoleName("notarole"))) + assert.Check(t, is.Equal("notarole", notaryRoleToSigner(data.RoleName("notarole")))) } // check if a role name is "released": either targets/releases or targets TUF roles func TestIsReleasedTarget(t *testing.T) { - assert.True(t, isReleasedTarget(trust.ReleasesRole)) + assert.Check(t, isReleasedTarget(trust.ReleasesRole)) for _, role := range data.BaseRoles { - assert.Equal(t, role == data.CanonicalTargetsRole, isReleasedTarget(role)) + assert.Check(t, is.Equal(role == data.CanonicalTargetsRole, isReleasedTarget(role))) } - assert.False(t, isReleasedTarget(data.RoleName("targets/not-releases"))) - assert.False(t, isReleasedTarget(data.RoleName("random"))) - assert.False(t, isReleasedTarget(data.RoleName("targets/releases/subrole"))) + assert.Check(t, !isReleasedTarget(data.RoleName("targets/not-releases"))) + assert.Check(t, !isReleasedTarget(data.RoleName("random"))) + assert.Check(t, !isReleasedTarget(data.RoleName("targets/releases/subrole"))) } // creates a mock delegation with a given name and no keys @@ -196,7 +197,7 @@ func TestMatchEmptySignatures(t *testing.T) { emptyTgts := []client.TargetSignedStruct{} matchedSigRows := matchReleasedSignatures(emptyTgts) - assert.Empty(t, matchedSigRows) + assert.Check(t, is.Len(matchedSigRows, 0)) } func TestMatchUnreleasedSignatures(t *testing.T) { @@ -209,7 +210,7 @@ func TestMatchUnreleasedSignatures(t *testing.T) { } matchedSigRows := matchReleasedSignatures(unreleasedTgts) - assert.Empty(t, matchedSigRows) + assert.Check(t, is.Len(matchedSigRows, 0)) } func TestMatchOneReleasedSingleSignature(t *testing.T) { @@ -227,13 +228,13 @@ func TestMatchOneReleasedSingleSignature(t *testing.T) { } matchedSigRows := matchReleasedSignatures(oneReleasedTgt) - assert.Len(t, matchedSigRows, 1) + assert.Check(t, is.Len(matchedSigRows, 1)) outputRow := matchedSigRows[0] // Empty signers because "targets/releases" doesn't show up - assert.Empty(t, outputRow.Signers) - assert.Equal(t, releasedTgt.Name, outputRow.SignedTag) - assert.Equal(t, hex.EncodeToString(releasedTgt.Hashes[notary.SHA256]), outputRow.Digest) + assert.Check(t, is.Len(outputRow.Signers, 0)) + assert.Check(t, is.Equal(releasedTgt.Name, outputRow.SignedTag)) + assert.Check(t, is.Equal(hex.EncodeToString(releasedTgt.Hashes[notary.SHA256]), outputRow.Digest)) } func TestMatchOneReleasedMultiSignature(t *testing.T) { @@ -252,13 +253,13 @@ func TestMatchOneReleasedMultiSignature(t *testing.T) { } matchedSigRows := matchReleasedSignatures(oneReleasedTgt) - assert.Len(t, matchedSigRows, 1) + assert.Check(t, is.Len(matchedSigRows, 1)) outputRow := matchedSigRows[0] // We should have three signers - assert.Equal(t, outputRow.Signers, []string{"a", "b", "c"}) - assert.Equal(t, releasedTgt.Name, outputRow.SignedTag) - assert.Equal(t, hex.EncodeToString(releasedTgt.Hashes[notary.SHA256]), outputRow.Digest) + assert.Check(t, is.DeepEqual(outputRow.Signers, []string{"a", "b", "c"})) + assert.Check(t, is.Equal(releasedTgt.Name, outputRow.SignedTag)) + assert.Check(t, is.Equal(hex.EncodeToString(releasedTgt.Hashes[notary.SHA256]), outputRow.Digest)) } func TestMatchMultiReleasedMultiSignature(t *testing.T) { @@ -291,23 +292,23 @@ func TestMatchMultiReleasedMultiSignature(t *testing.T) { multiReleasedTgts = append(multiReleasedTgts, client.TargetSignedStruct{Role: mockDelegationRoleWithName("targets/c"), Target: targetC}) matchedSigRows := matchReleasedSignatures(multiReleasedTgts) - assert.Len(t, matchedSigRows, 3) + assert.Check(t, is.Len(matchedSigRows, 3)) // note that the output is sorted by tag name, so we can reliably index to validate data: outputTargetA := matchedSigRows[0] - assert.Equal(t, outputTargetA.Signers, []string{"a"}) - assert.Equal(t, targetA.Name, outputTargetA.SignedTag) - assert.Equal(t, hex.EncodeToString(targetA.Hashes[notary.SHA256]), outputTargetA.Digest) + assert.Check(t, is.DeepEqual(outputTargetA.Signers, []string{"a"})) + assert.Check(t, is.Equal(targetA.Name, outputTargetA.SignedTag)) + assert.Check(t, is.Equal(hex.EncodeToString(targetA.Hashes[notary.SHA256]), outputTargetA.Digest)) outputTargetB := matchedSigRows[1] - assert.Equal(t, outputTargetB.Signers, []string{"a", "b"}) - assert.Equal(t, targetB.Name, outputTargetB.SignedTag) - assert.Equal(t, hex.EncodeToString(targetB.Hashes[notary.SHA256]), outputTargetB.Digest) + assert.Check(t, is.DeepEqual(outputTargetB.Signers, []string{"a", "b"})) + assert.Check(t, is.Equal(targetB.Name, outputTargetB.SignedTag)) + assert.Check(t, is.Equal(hex.EncodeToString(targetB.Hashes[notary.SHA256]), outputTargetB.Digest)) outputTargetC := matchedSigRows[2] - assert.Equal(t, outputTargetC.Signers, []string{"a", "b", "c"}) - assert.Equal(t, targetC.Name, outputTargetC.SignedTag) - assert.Equal(t, hex.EncodeToString(targetC.Hashes[notary.SHA256]), outputTargetC.Digest) + assert.Check(t, is.DeepEqual(outputTargetC.Signers, []string{"a", "b", "c"})) + assert.Check(t, is.Equal(targetC.Name, outputTargetC.SignedTag)) + assert.Check(t, is.Equal(hex.EncodeToString(targetC.Hashes[notary.SHA256]), outputTargetC.Digest)) } func TestMatchReleasedSignatureFromTargets(t *testing.T) { @@ -317,12 +318,12 @@ func TestMatchReleasedSignatureFromTargets(t *testing.T) { releasedTgt := client.Target{Name: "released", Hashes: data.Hashes{notary.SHA256: []byte("released-hash")}} oneReleasedTgt = append(oneReleasedTgt, client.TargetSignedStruct{Role: mockDelegationRoleWithName(data.CanonicalTargetsRole.String()), Target: releasedTgt}) matchedSigRows := matchReleasedSignatures(oneReleasedTgt) - assert.Len(t, matchedSigRows, 1) + assert.Check(t, is.Len(matchedSigRows, 1)) outputRow := matchedSigRows[0] // Empty signers because "targets" doesn't show up - assert.Empty(t, outputRow.Signers) - assert.Equal(t, releasedTgt.Name, outputRow.SignedTag) - assert.Equal(t, hex.EncodeToString(releasedTgt.Hashes[notary.SHA256]), outputRow.Digest) + assert.Check(t, is.Len(outputRow.Signers, 0)) + assert.Check(t, is.Equal(releasedTgt.Name, outputRow.SignedTag)) + assert.Check(t, is.Equal(hex.EncodeToString(releasedTgt.Hashes[notary.SHA256]), outputRow.Digest)) } func TestGetSignerRolesWithKeyIDs(t *testing.T) { @@ -381,7 +382,7 @@ func TestGetSignerRolesWithKeyIDs(t *testing.T) { roleWithSigs = append(roleWithSigs, roleWithSig) } signerRoleToKeyIDs := getDelegationRoleToKeyMap(roles) - assert.Equal(t, expectedSignerRoleToKeyIDs, signerRoleToKeyIDs) + assert.Check(t, is.DeepEqual(expectedSignerRoleToKeyIDs, signerRoleToKeyIDs)) } func TestFormatAdminRole(t *testing.T) { @@ -392,7 +393,7 @@ func TestFormatAdminRole(t *testing.T) { Name: "targets/alice", } aliceRoleWithSigs := client.RoleWithSignatures{Role: aliceRole, Signatures: nil} - assert.Equal(t, "", formatAdminRole(aliceRoleWithSigs)) + assert.Check(t, is.Equal("", formatAdminRole(aliceRoleWithSigs))) releasesRole := data.Role{ RootRole: data.RootRole{ @@ -401,7 +402,7 @@ func TestFormatAdminRole(t *testing.T) { Name: "targets/releases", } releasesRoleWithSigs := client.RoleWithSignatures{Role: releasesRole, Signatures: nil} - assert.Equal(t, "", formatAdminRole(releasesRoleWithSigs)) + assert.Check(t, is.Equal("", formatAdminRole(releasesRoleWithSigs))) timestampRole := data.Role{ RootRole: data.RootRole{ @@ -410,7 +411,7 @@ func TestFormatAdminRole(t *testing.T) { Name: data.CanonicalTimestampRole, } timestampRoleWithSigs := client.RoleWithSignatures{Role: timestampRole, Signatures: nil} - assert.Equal(t, "", formatAdminRole(timestampRoleWithSigs)) + assert.Check(t, is.Equal("", formatAdminRole(timestampRoleWithSigs))) snapshotRole := data.Role{ RootRole: data.RootRole{ @@ -419,7 +420,7 @@ func TestFormatAdminRole(t *testing.T) { Name: data.CanonicalSnapshotRole, } snapshotRoleWithSigs := client.RoleWithSignatures{Role: snapshotRole, Signatures: nil} - assert.Equal(t, "", formatAdminRole(snapshotRoleWithSigs)) + assert.Check(t, is.Equal("", formatAdminRole(snapshotRoleWithSigs))) rootRole := data.Role{ RootRole: data.RootRole{ @@ -428,7 +429,7 @@ func TestFormatAdminRole(t *testing.T) { Name: data.CanonicalRootRole, } rootRoleWithSigs := client.RoleWithSignatures{Role: rootRole, Signatures: nil} - assert.Equal(t, "Root Key:\tkey11\n", formatAdminRole(rootRoleWithSigs)) + assert.Check(t, is.Equal("Root Key:\tkey11\n", formatAdminRole(rootRoleWithSigs))) targetsRole := data.Role{ RootRole: data.RootRole{ @@ -437,5 +438,5 @@ func TestFormatAdminRole(t *testing.T) { Name: data.CanonicalTargetsRole, } targetsRoleWithSigs := client.RoleWithSignatures{Role: targetsRole, Signatures: nil} - assert.Equal(t, "Repository Key:\tabc, key11, key99\n", formatAdminRole(targetsRoleWithSigs)) + assert.Check(t, is.Equal("Repository Key:\tabc, key11, key99\n", formatAdminRole(targetsRoleWithSigs))) } diff --git a/components/cli/cli/command/trust/inspect_test.go b/components/cli/cli/command/trust/inspect_test.go index 7073f10bcf..6e0e402bbd 100644 --- a/components/cli/cli/command/trust/inspect_test.go +++ b/components/cli/cli/command/trust/inspect_test.go @@ -5,9 +5,9 @@ import ( "testing" "github.com/docker/cli/internal/test" - "github.com/docker/cli/internal/test/testutil" + "github.com/docker/cli/internal/test/notary" + "github.com/gotestyourself/gotestyourself/assert" "github.com/gotestyourself/gotestyourself/golden" - "github.com/stretchr/testify/assert" ) func TestTrustInspectCommandErrors(t *testing.T) { @@ -37,95 +37,95 @@ func TestTrustInspectCommandErrors(t *testing.T) { cmd.Flags().Set("pretty", "true") cmd.SetArgs(tc.args) cmd.SetOutput(ioutil.Discard) - testutil.ErrorContains(t, cmd.Execute(), tc.expectedError) + assert.ErrorContains(t, cmd.Execute(), tc.expectedError) } } func TestTrustInspectCommandOfflineErrors(t *testing.T) { cli := test.NewFakeCli(&fakeClient{}) - cli.SetNotaryClient(getOfflineNotaryRepository) + cli.SetNotaryClient(notary.GetOfflineNotaryRepository) cmd := newInspectCommand(cli) cmd.SetArgs([]string{"nonexistent-reg-name.io/image"}) cmd.SetOutput(ioutil.Discard) - testutil.ErrorContains(t, cmd.Execute(), "No signatures or cannot access nonexistent-reg-name.io/image") + assert.ErrorContains(t, cmd.Execute(), "No signatures or cannot access nonexistent-reg-name.io/image") cli = test.NewFakeCli(&fakeClient{}) - cli.SetNotaryClient(getOfflineNotaryRepository) + cli.SetNotaryClient(notary.GetOfflineNotaryRepository) cmd = newInspectCommand(cli) cmd.SetArgs([]string{"nonexistent-reg-name.io/image:tag"}) cmd.SetOutput(ioutil.Discard) - testutil.ErrorContains(t, cmd.Execute(), "No signatures or cannot access nonexistent-reg-name.io/image") + assert.ErrorContains(t, cmd.Execute(), "No signatures or cannot access nonexistent-reg-name.io/image") } func TestTrustInspectCommandUninitializedErrors(t *testing.T) { cli := test.NewFakeCli(&fakeClient{}) - cli.SetNotaryClient(getUninitializedNotaryRepository) + cli.SetNotaryClient(notary.GetUninitializedNotaryRepository) cmd := newInspectCommand(cli) cmd.SetArgs([]string{"reg/unsigned-img"}) cmd.SetOutput(ioutil.Discard) - testutil.ErrorContains(t, cmd.Execute(), "No signatures or cannot access reg/unsigned-img") + assert.ErrorContains(t, cmd.Execute(), "No signatures or cannot access reg/unsigned-img") golden.Assert(t, cli.OutBuffer().String(), "trust-inspect-uninitialized.golden") cli = test.NewFakeCli(&fakeClient{}) - cli.SetNotaryClient(getUninitializedNotaryRepository) + cli.SetNotaryClient(notary.GetUninitializedNotaryRepository) cmd = newInspectCommand(cli) cmd.SetArgs([]string{"reg/unsigned-img:tag"}) cmd.SetOutput(ioutil.Discard) - testutil.ErrorContains(t, cmd.Execute(), "No signatures or cannot access reg/unsigned-img:tag") + assert.ErrorContains(t, cmd.Execute(), "No signatures or cannot access reg/unsigned-img:tag") golden.Assert(t, cli.OutBuffer().String(), "trust-inspect-uninitialized.golden") } func TestTrustInspectCommandEmptyNotaryRepo(t *testing.T) { cli := test.NewFakeCli(&fakeClient{}) - cli.SetNotaryClient(getEmptyTargetsNotaryRepository) + cli.SetNotaryClient(notary.GetEmptyTargetsNotaryRepository) cmd := newInspectCommand(cli) cmd.SetArgs([]string{"reg/img:unsigned-tag"}) cmd.SetOutput(ioutil.Discard) - assert.NoError(t, cmd.Execute()) + assert.NilError(t, cmd.Execute()) golden.Assert(t, cli.OutBuffer().String(), "trust-inspect-empty-repo.golden") } func TestTrustInspectCommandFullRepoWithoutSigners(t *testing.T) { cli := test.NewFakeCli(&fakeClient{}) - cli.SetNotaryClient(getLoadedWithNoSignersNotaryRepository) + cli.SetNotaryClient(notary.GetLoadedWithNoSignersNotaryRepository) cmd := newInspectCommand(cli) cmd.SetArgs([]string{"signed-repo"}) - assert.NoError(t, cmd.Execute()) + assert.NilError(t, cmd.Execute()) golden.Assert(t, cli.OutBuffer().String(), "trust-inspect-full-repo-no-signers.golden") } func TestTrustInspectCommandOneTagWithoutSigners(t *testing.T) { cli := test.NewFakeCli(&fakeClient{}) - cli.SetNotaryClient(getLoadedWithNoSignersNotaryRepository) + cli.SetNotaryClient(notary.GetLoadedWithNoSignersNotaryRepository) cmd := newInspectCommand(cli) cmd.SetArgs([]string{"signed-repo:green"}) - assert.NoError(t, cmd.Execute()) + assert.NilError(t, cmd.Execute()) golden.Assert(t, cli.OutBuffer().String(), "trust-inspect-one-tag-no-signers.golden") } func TestTrustInspectCommandFullRepoWithSigners(t *testing.T) { cli := test.NewFakeCli(&fakeClient{}) - cli.SetNotaryClient(getLoadedNotaryRepository) + cli.SetNotaryClient(notary.GetLoadedNotaryRepository) cmd := newInspectCommand(cli) cmd.SetArgs([]string{"signed-repo"}) - assert.NoError(t, cmd.Execute()) + assert.NilError(t, cmd.Execute()) golden.Assert(t, cli.OutBuffer().String(), "trust-inspect-full-repo-with-signers.golden") } func TestTrustInspectCommandMultipleFullReposWithSigners(t *testing.T) { cli := test.NewFakeCli(&fakeClient{}) - cli.SetNotaryClient(getLoadedNotaryRepository) + cli.SetNotaryClient(notary.GetLoadedNotaryRepository) cmd := newInspectCommand(cli) cmd.SetArgs([]string{"signed-repo", "signed-repo"}) - assert.NoError(t, cmd.Execute()) + assert.NilError(t, cmd.Execute()) golden.Assert(t, cli.OutBuffer().String(), "trust-inspect-multiple-repos-with-signers.golden") } func TestTrustInspectCommandUnsignedTagInSignedRepo(t *testing.T) { cli := test.NewFakeCli(&fakeClient{}) - cli.SetNotaryClient(getLoadedNotaryRepository) + cli.SetNotaryClient(notary.GetLoadedNotaryRepository) cmd := newInspectCommand(cli) cmd.SetArgs([]string{"signed-repo:unsigned"}) - assert.NoError(t, cmd.Execute()) + assert.NilError(t, cmd.Execute()) golden.Assert(t, cli.OutBuffer().String(), "trust-inspect-unsigned-tag-with-signers.golden") } diff --git a/components/cli/cli/command/trust/key_generate_test.go b/components/cli/cli/command/trust/key_generate_test.go index faf6f31873..6e26f98d1f 100644 --- a/components/cli/cli/command/trust/key_generate_test.go +++ b/components/cli/cli/command/trust/key_generate_test.go @@ -10,8 +10,8 @@ import ( "github.com/docker/cli/cli/config" "github.com/docker/cli/internal/test" - "github.com/docker/cli/internal/test/testutil" - "github.com/stretchr/testify/assert" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" "github.com/theupdateframework/notary" "github.com/theupdateframework/notary/passphrase" "github.com/theupdateframework/notary/trustmanager" @@ -36,7 +36,7 @@ func TestTrustKeyGenerateErrors(t *testing.T) { } tmpDir, err := ioutil.TempDir("", "docker-key-generate-test-") - assert.NoError(t, err) + assert.NilError(t, err) defer os.RemoveAll(tmpDir) config.SetDir(tmpDir) @@ -45,17 +45,17 @@ func TestTrustKeyGenerateErrors(t *testing.T) { cmd := newKeyGenerateCommand(cli) cmd.SetArgs(tc.args) cmd.SetOutput(ioutil.Discard) - testutil.ErrorContains(t, cmd.Execute(), tc.expectedError) + assert.ErrorContains(t, cmd.Execute(), tc.expectedError) } } func TestGenerateKeySuccess(t *testing.T) { pubKeyCWD, err := ioutil.TempDir("", "pub-keys-") - assert.NoError(t, err) + assert.NilError(t, err) defer os.RemoveAll(pubKeyCWD) privKeyStorageDir, err := ioutil.TempDir("", "priv-keys-") - assert.NoError(t, err) + assert.NilError(t, err) defer os.RemoveAll(privKeyStorageDir) passwd := "password" @@ -63,25 +63,25 @@ func TestGenerateKeySuccess(t *testing.T) { // generate a single key keyName := "alice" privKeyFileStore, err := trustmanager.NewKeyFileStore(privKeyStorageDir, cannedPasswordRetriever) - assert.NoError(t, err) + assert.NilError(t, err) pubKeyPEM, err := generateKeyAndOutputPubPEM(keyName, privKeyFileStore) - assert.NoError(t, err) + assert.NilError(t, err) - assert.Equal(t, keyName, pubKeyPEM.Headers["role"]) + assert.Check(t, is.Equal(keyName, pubKeyPEM.Headers["role"])) // the default GUN is empty - assert.Equal(t, "", pubKeyPEM.Headers["gun"]) + assert.Check(t, is.Equal("", pubKeyPEM.Headers["gun"])) // assert public key header - assert.Equal(t, "PUBLIC KEY", pubKeyPEM.Type) + assert.Check(t, is.Equal("PUBLIC KEY", pubKeyPEM.Type)) // check that an appropriate ~//private/.key file exists expectedPrivKeyDir := filepath.Join(privKeyStorageDir, notary.PrivDir) _, err = os.Stat(expectedPrivKeyDir) - assert.NoError(t, err) + assert.NilError(t, err) keyFiles, err := ioutil.ReadDir(expectedPrivKeyDir) - assert.NoError(t, err) - assert.Len(t, keyFiles, 1) + assert.NilError(t, err) + assert.Check(t, is.Len(keyFiles, 1)) privKeyFilePath := filepath.Join(expectedPrivKeyDir, keyFiles[0].Name()) // verify the key content @@ -89,50 +89,46 @@ func TestGenerateKeySuccess(t *testing.T) { defer privFrom.Close() fromBytes, _ := ioutil.ReadAll(privFrom) privKeyPEM, _ := pem.Decode(fromBytes) - assert.Equal(t, keyName, privKeyPEM.Headers["role"]) + assert.Check(t, is.Equal(keyName, privKeyPEM.Headers["role"])) // the default GUN is empty - assert.Equal(t, "", privKeyPEM.Headers["gun"]) + assert.Check(t, is.Equal("", privKeyPEM.Headers["gun"])) // assert encrypted header - assert.Equal(t, "ENCRYPTED PRIVATE KEY", privKeyPEM.Type) + assert.Check(t, is.Equal("ENCRYPTED PRIVATE KEY", privKeyPEM.Type)) // check that the passphrase matches _, err = tufutils.ParsePKCS8ToTufKey(privKeyPEM.Bytes, []byte(passwd)) - assert.NoError(t, err) + assert.NilError(t, err) // check that the public key exists at the correct path if we use the helper: returnedPath, err := writePubKeyPEMToDir(pubKeyPEM, keyName, pubKeyCWD) - assert.NoError(t, err) + assert.NilError(t, err) expectedPubKeyPath := filepath.Join(pubKeyCWD, keyName+".pub") - assert.Equal(t, returnedPath, expectedPubKeyPath) + assert.Check(t, is.Equal(returnedPath, expectedPubKeyPath)) _, err = os.Stat(expectedPubKeyPath) - assert.NoError(t, err) + assert.NilError(t, err) // check that the public key is the only file output in CWD cwdKeyFiles, err := ioutil.ReadDir(pubKeyCWD) - assert.NoError(t, err) - assert.Len(t, cwdKeyFiles, 1) + assert.NilError(t, err) + assert.Check(t, is.Len(cwdKeyFiles, 1)) } func TestValidateKeyArgs(t *testing.T) { pubKeyCWD, err := ioutil.TempDir("", "pub-keys-") - assert.NoError(t, err) + assert.NilError(t, err) defer os.RemoveAll(pubKeyCWD) err = validateKeyArgs("a", pubKeyCWD) - assert.NoError(t, err) + assert.NilError(t, err) err = validateKeyArgs("a/b", pubKeyCWD) - assert.Error(t, err) - assert.Equal(t, err.Error(), "key name \"a/b\" must start with lowercase alphanumeric characters and can include \"-\" or \"_\" after the first character") + assert.Error(t, err, "key name \"a/b\" must start with lowercase alphanumeric characters and can include \"-\" or \"_\" after the first character") err = validateKeyArgs("-", pubKeyCWD) - assert.Error(t, err) - assert.Equal(t, err.Error(), "key name \"-\" must start with lowercase alphanumeric characters and can include \"-\" or \"_\" after the first character") + assert.Error(t, err, "key name \"-\" must start with lowercase alphanumeric characters and can include \"-\" or \"_\" after the first character") - assert.NoError(t, ioutil.WriteFile(filepath.Join(pubKeyCWD, "a.pub"), []byte("abc"), notary.PrivExecPerms)) + assert.NilError(t, ioutil.WriteFile(filepath.Join(pubKeyCWD, "a.pub"), []byte("abc"), notary.PrivExecPerms)) err = validateKeyArgs("a", pubKeyCWD) - assert.Error(t, err) - assert.Equal(t, err.Error(), fmt.Sprintf("public key file already exists: \"%s/a.pub\"", pubKeyCWD)) + assert.Error(t, err, fmt.Sprintf("public key file already exists: \"%s\"", filepath.Join(pubKeyCWD, "a.pub"))) err = validateKeyArgs("a", "/random/dir/") - assert.Error(t, err) - assert.Equal(t, err.Error(), "public key path does not exist: \"/random/dir/\"") + assert.Error(t, err, "public key path does not exist: \"/random/dir/\"") } diff --git a/components/cli/cli/command/trust/key_load_test.go b/components/cli/cli/command/trust/key_load_test.go index 5902b36667..c4da320a59 100644 --- a/components/cli/cli/command/trust/key_load_test.go +++ b/components/cli/cli/command/trust/key_load_test.go @@ -6,12 +6,14 @@ import ( "io/ioutil" "os" "path/filepath" + "runtime" "testing" "github.com/docker/cli/cli/config" "github.com/docker/cli/internal/test" - "github.com/docker/cli/internal/test/testutil" - "github.com/stretchr/testify/assert" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" + "github.com/gotestyourself/gotestyourself/skip" "github.com/theupdateframework/notary" "github.com/theupdateframework/notary/passphrase" "github.com/theupdateframework/notary/storage" @@ -20,6 +22,10 @@ import ( ) func TestTrustKeyLoadErrors(t *testing.T) { + noSuchFile := "stat iamnotakey: no such file or directory" + if runtime.GOOS == "windows" { + noSuchFile = "CreateFile iamnotakey: The system cannot find the file specified." + } testCases := []struct { name string args []string @@ -40,7 +46,7 @@ func TestTrustKeyLoadErrors(t *testing.T) { { name: "not-a-key", args: []string{"iamnotakey"}, - expectedError: "refusing to load key from iamnotakey: stat iamnotakey: no such file or directory", + expectedError: "refusing to load key from iamnotakey: " + noSuchFile, expectedOutput: "Loading key from \"iamnotakey\"...\n", }, { @@ -51,7 +57,7 @@ func TestTrustKeyLoadErrors(t *testing.T) { }, } tmpDir, err := ioutil.TempDir("", "docker-key-load-test-") - assert.NoError(t, err) + assert.NilError(t, err) defer os.RemoveAll(tmpDir) config.SetDir(tmpDir) @@ -60,8 +66,8 @@ func TestTrustKeyLoadErrors(t *testing.T) { cmd := newKeyLoadCommand(cli) cmd.SetArgs(tc.args) cmd.SetOutput(ioutil.Discard) - testutil.ErrorContains(t, cmd.Execute(), tc.expectedError) - assert.Contains(t, cli.OutBuffer().String(), tc.expectedOutput) + assert.ErrorContains(t, cmd.Execute(), tc.expectedError) + assert.Check(t, is.Contains(cli.OutBuffer().String(), tc.expectedOutput)) } } @@ -109,6 +115,7 @@ var testKeys = map[string][]byte{ } func TestLoadKeyFromPath(t *testing.T) { + skip.If(t, runtime.GOOS == "windows") for keyID, keyBytes := range testKeys { t.Run(fmt.Sprintf("load-key-id-%s-from-path", keyID), func(t *testing.T) { testLoadKeyFromPath(t, keyID, keyBytes) @@ -118,51 +125,52 @@ func TestLoadKeyFromPath(t *testing.T) { func testLoadKeyFromPath(t *testing.T, privKeyID string, privKeyFixture []byte) { privKeyDir, err := ioutil.TempDir("", "key-load-test-") - assert.NoError(t, err) + assert.NilError(t, err) defer os.RemoveAll(privKeyDir) privKeyFilepath := filepath.Join(privKeyDir, "privkey.pem") - assert.NoError(t, ioutil.WriteFile(privKeyFilepath, privKeyFixture, notary.PrivNoExecPerms)) + assert.NilError(t, ioutil.WriteFile(privKeyFilepath, privKeyFixture, notary.PrivNoExecPerms)) keyStorageDir, err := ioutil.TempDir("", "loaded-keys-") - assert.NoError(t, err) + assert.NilError(t, err) defer os.RemoveAll(keyStorageDir) passwd := "password" cannedPasswordRetriever := passphrase.ConstantRetriever(passwd) keyFileStore, err := storage.NewPrivateKeyFileStorage(keyStorageDir, notary.KeyExtension) - assert.NoError(t, err) + assert.NilError(t, err) privKeyImporters := []trustmanager.Importer{keyFileStore} // get the privKeyBytes privKeyBytes, err := getPrivKeyBytesFromPath(privKeyFilepath) - assert.NoError(t, err) + assert.NilError(t, err) // import the key to our keyStorageDir - assert.NoError(t, loadPrivKeyBytesToStore(privKeyBytes, privKeyImporters, privKeyFilepath, "signer-name", cannedPasswordRetriever)) + assert.Check(t, loadPrivKeyBytesToStore(privKeyBytes, privKeyImporters, privKeyFilepath, "signer-name", cannedPasswordRetriever)) // check that the appropriate ~//private/.key file exists expectedImportKeyPath := filepath.Join(keyStorageDir, notary.PrivDir, privKeyID+"."+notary.KeyExtension) _, err = os.Stat(expectedImportKeyPath) - assert.NoError(t, err) + assert.NilError(t, err) // verify the key content from, _ := os.OpenFile(expectedImportKeyPath, os.O_RDONLY, notary.PrivExecPerms) defer from.Close() fromBytes, _ := ioutil.ReadAll(from) keyPEM, _ := pem.Decode(fromBytes) - assert.Equal(t, "signer-name", keyPEM.Headers["role"]) + assert.Check(t, is.Equal("signer-name", keyPEM.Headers["role"])) // the default GUN is empty - assert.Equal(t, "", keyPEM.Headers["gun"]) + assert.Check(t, is.Equal("", keyPEM.Headers["gun"])) // assert encrypted header - assert.Equal(t, "ENCRYPTED PRIVATE KEY", keyPEM.Type) + assert.Check(t, is.Equal("ENCRYPTED PRIVATE KEY", keyPEM.Type)) decryptedKey, err := tufutils.ParsePKCS8ToTufKey(keyPEM.Bytes, []byte(passwd)) - assert.NoError(t, err) + assert.NilError(t, err) fixturePEM, _ := pem.Decode(privKeyFixture) - assert.Equal(t, fixturePEM.Bytes, decryptedKey.Private()) + assert.Check(t, is.DeepEqual(fixturePEM.Bytes, decryptedKey.Private())) } func TestLoadKeyTooPermissive(t *testing.T) { + skip.If(t, runtime.GOOS == "windows") for keyID, keyBytes := range testKeys { t.Run(fmt.Sprintf("load-key-id-%s-too-permissive", keyID), func(t *testing.T) { testLoadKeyTooPermissive(t, keyBytes) @@ -172,45 +180,45 @@ func TestLoadKeyTooPermissive(t *testing.T) { func testLoadKeyTooPermissive(t *testing.T, privKeyFixture []byte) { privKeyDir, err := ioutil.TempDir("", "key-load-test-") - assert.NoError(t, err) + assert.NilError(t, err) defer os.RemoveAll(privKeyDir) privKeyFilepath := filepath.Join(privKeyDir, "privkey477.pem") - assert.NoError(t, ioutil.WriteFile(privKeyFilepath, privKeyFixture, 0477)) + assert.NilError(t, ioutil.WriteFile(privKeyFilepath, privKeyFixture, 0477)) keyStorageDir, err := ioutil.TempDir("", "loaded-keys-") - assert.NoError(t, err) + assert.NilError(t, err) defer os.RemoveAll(keyStorageDir) // import the key to our keyStorageDir _, err = getPrivKeyBytesFromPath(privKeyFilepath) - assert.Error(t, err) - assert.Contains(t, fmt.Sprintf("private key file %s must not be readable or writable by others", privKeyFilepath), err.Error()) + expected := fmt.Sprintf("private key file %s must not be readable or writable by others", privKeyFilepath) + assert.Error(t, err, expected) privKeyFilepath = filepath.Join(privKeyDir, "privkey667.pem") - assert.NoError(t, ioutil.WriteFile(privKeyFilepath, privKeyFixture, 0677)) + assert.NilError(t, ioutil.WriteFile(privKeyFilepath, privKeyFixture, 0677)) _, err = getPrivKeyBytesFromPath(privKeyFilepath) - assert.Error(t, err) - assert.Contains(t, fmt.Sprintf("private key file %s must not be readable or writable by others", privKeyFilepath), err.Error()) + expected = fmt.Sprintf("private key file %s must not be readable or writable by others", privKeyFilepath) + assert.Error(t, err, expected) privKeyFilepath = filepath.Join(privKeyDir, "privkey777.pem") - assert.NoError(t, ioutil.WriteFile(privKeyFilepath, privKeyFixture, 0777)) + assert.NilError(t, ioutil.WriteFile(privKeyFilepath, privKeyFixture, 0777)) _, err = getPrivKeyBytesFromPath(privKeyFilepath) - assert.Error(t, err) - assert.Contains(t, fmt.Sprintf("private key file %s must not be readable or writable by others", privKeyFilepath), err.Error()) + expected = fmt.Sprintf("private key file %s must not be readable or writable by others", privKeyFilepath) + assert.Error(t, err, expected) privKeyFilepath = filepath.Join(privKeyDir, "privkey400.pem") - assert.NoError(t, ioutil.WriteFile(privKeyFilepath, privKeyFixture, 0400)) + assert.NilError(t, ioutil.WriteFile(privKeyFilepath, privKeyFixture, 0400)) _, err = getPrivKeyBytesFromPath(privKeyFilepath) - assert.NoError(t, err) + assert.NilError(t, err) privKeyFilepath = filepath.Join(privKeyDir, "privkey600.pem") - assert.NoError(t, ioutil.WriteFile(privKeyFilepath, privKeyFixture, 0600)) + assert.NilError(t, ioutil.WriteFile(privKeyFilepath, privKeyFixture, 0600)) _, err = getPrivKeyBytesFromPath(privKeyFilepath) - assert.NoError(t, err) + assert.NilError(t, err) } var pubKeyFixture = []byte(`-----BEGIN PUBLIC KEY----- @@ -219,26 +227,27 @@ H3nzy2O6Q/ct4BjOBKa+WCdRtPo78bA+C/7t81ADQO8Jqaj59W50rwoqDQ== -----END PUBLIC KEY-----`) func TestLoadPubKeyFailure(t *testing.T) { + skip.If(t, runtime.GOOS == "windows") pubKeyDir, err := ioutil.TempDir("", "key-load-test-pubkey-") - assert.NoError(t, err) + assert.NilError(t, err) defer os.RemoveAll(pubKeyDir) pubKeyFilepath := filepath.Join(pubKeyDir, "pubkey.pem") - assert.NoError(t, ioutil.WriteFile(pubKeyFilepath, pubKeyFixture, notary.PrivNoExecPerms)) + assert.NilError(t, ioutil.WriteFile(pubKeyFilepath, pubKeyFixture, notary.PrivNoExecPerms)) keyStorageDir, err := ioutil.TempDir("", "loaded-keys-") - assert.NoError(t, err) + assert.NilError(t, err) defer os.RemoveAll(keyStorageDir) passwd := "password" cannedPasswordRetriever := passphrase.ConstantRetriever(passwd) keyFileStore, err := storage.NewPrivateKeyFileStorage(keyStorageDir, notary.KeyExtension) - assert.NoError(t, err) + assert.NilError(t, err) privKeyImporters := []trustmanager.Importer{keyFileStore} pubKeyBytes, err := getPrivKeyBytesFromPath(pubKeyFilepath) - assert.NoError(t, err) + assert.NilError(t, err) // import the key to our keyStorageDir - it should fail err = loadPrivKeyBytesToStore(pubKeyBytes, privKeyImporters, pubKeyFilepath, "signer-name", cannedPasswordRetriever) - assert.Error(t, err) - assert.Contains(t, fmt.Sprintf("provided file %s is not a supported private key - to add a signer's public key use docker trust signer add", pubKeyFilepath), err.Error()) + expected := fmt.Sprintf("provided file %s is not a supported private key - to add a signer's public key use docker trust signer add", pubKeyFilepath) + assert.Error(t, err, expected) } diff --git a/components/cli/cli/command/trust/revoke.go b/components/cli/cli/command/trust/revoke.go index df2a22aa4d..31437b03f1 100644 --- a/components/cli/cli/command/trust/revoke.go +++ b/components/cli/cli/command/trust/revoke.go @@ -36,7 +36,7 @@ func newRevokeCommand(dockerCli command.Cli) *cobra.Command { func revokeTrust(cli command.Cli, remote string, options revokeOptions) error { ctx := context.Background() - imgRefAndAuth, err := trust.GetImageReferencesAndAuth(ctx, image.AuthResolver(cli), remote) + imgRefAndAuth, err := trust.GetImageReferencesAndAuth(ctx, nil, image.AuthResolver(cli), remote) if err != nil { return err } diff --git a/components/cli/cli/command/trust/revoke_test.go b/components/cli/cli/command/trust/revoke_test.go index ea93accdf9..289fd3f173 100644 --- a/components/cli/cli/command/trust/revoke_test.go +++ b/components/cli/cli/command/trust/revoke_test.go @@ -6,9 +6,9 @@ import ( "testing" "github.com/docker/cli/internal/test" - "github.com/docker/cli/internal/test/testutil" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" + "github.com/docker/cli/internal/test/notary" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" "github.com/theupdateframework/notary/client" "github.com/theupdateframework/notary/passphrase" "github.com/theupdateframework/notary/trustpinning" @@ -50,99 +50,99 @@ func TestTrustRevokeCommandErrors(t *testing.T) { test.NewFakeCli(&fakeClient{})) cmd.SetArgs(tc.args) cmd.SetOutput(ioutil.Discard) - testutil.ErrorContains(t, cmd.Execute(), tc.expectedError) + assert.ErrorContains(t, cmd.Execute(), tc.expectedError) } } func TestTrustRevokeCommandOfflineErrors(t *testing.T) { cli := test.NewFakeCli(&fakeClient{}) - cli.SetNotaryClient(getOfflineNotaryRepository) + cli.SetNotaryClient(notary.GetOfflineNotaryRepository) cmd := newRevokeCommand(cli) cmd.SetArgs([]string{"reg-name.io/image"}) cmd.SetOutput(ioutil.Discard) - assert.NoError(t, cmd.Execute()) - assert.Contains(t, cli.OutBuffer().String(), "Please confirm you would like to delete all signature data for reg-name.io/image? [y/N] \nAborting action.") + assert.NilError(t, cmd.Execute()) + assert.Check(t, is.Contains(cli.OutBuffer().String(), "Please confirm you would like to delete all signature data for reg-name.io/image? [y/N] \nAborting action.")) cli = test.NewFakeCli(&fakeClient{}) - cli.SetNotaryClient(getOfflineNotaryRepository) + cli.SetNotaryClient(notary.GetOfflineNotaryRepository) cmd = newRevokeCommand(cli) cmd.SetArgs([]string{"reg-name.io/image", "-y"}) cmd.SetOutput(ioutil.Discard) cli = test.NewFakeCli(&fakeClient{}) - cli.SetNotaryClient(getOfflineNotaryRepository) + cli.SetNotaryClient(notary.GetOfflineNotaryRepository) cmd = newRevokeCommand(cli) cmd.SetArgs([]string{"reg-name.io/image:tag"}) cmd.SetOutput(ioutil.Discard) - testutil.ErrorContains(t, cmd.Execute(), "could not remove signature for reg-name.io/image:tag: client is offline") + assert.ErrorContains(t, cmd.Execute(), "could not remove signature for reg-name.io/image:tag: client is offline") } func TestTrustRevokeCommandUninitializedErrors(t *testing.T) { cli := test.NewFakeCli(&fakeClient{}) - cli.SetNotaryClient(getUninitializedNotaryRepository) + cli.SetNotaryClient(notary.GetUninitializedNotaryRepository) cmd := newRevokeCommand(cli) cmd.SetArgs([]string{"reg-name.io/image"}) cmd.SetOutput(ioutil.Discard) - assert.NoError(t, cmd.Execute()) - assert.Contains(t, cli.OutBuffer().String(), "Please confirm you would like to delete all signature data for reg-name.io/image? [y/N] \nAborting action.") + assert.NilError(t, cmd.Execute()) + assert.Check(t, is.Contains(cli.OutBuffer().String(), "Please confirm you would like to delete all signature data for reg-name.io/image? [y/N] \nAborting action.")) cli = test.NewFakeCli(&fakeClient{}) - cli.SetNotaryClient(getUninitializedNotaryRepository) + cli.SetNotaryClient(notary.GetUninitializedNotaryRepository) cmd = newRevokeCommand(cli) cmd.SetArgs([]string{"reg-name.io/image", "-y"}) cmd.SetOutput(ioutil.Discard) - testutil.ErrorContains(t, cmd.Execute(), "could not remove signature for reg-name.io/image: does not have trust data for") + assert.ErrorContains(t, cmd.Execute(), "could not remove signature for reg-name.io/image: does not have trust data for") cli = test.NewFakeCli(&fakeClient{}) - cli.SetNotaryClient(getUninitializedNotaryRepository) + cli.SetNotaryClient(notary.GetUninitializedNotaryRepository) cmd = newRevokeCommand(cli) cmd.SetArgs([]string{"reg-name.io/image:tag"}) cmd.SetOutput(ioutil.Discard) - testutil.ErrorContains(t, cmd.Execute(), "could not remove signature for reg-name.io/image:tag: does not have trust data for") + assert.ErrorContains(t, cmd.Execute(), "could not remove signature for reg-name.io/image:tag: does not have trust data for") } func TestTrustRevokeCommandEmptyNotaryRepo(t *testing.T) { cli := test.NewFakeCli(&fakeClient{}) - cli.SetNotaryClient(getEmptyTargetsNotaryRepository) + cli.SetNotaryClient(notary.GetEmptyTargetsNotaryRepository) cmd := newRevokeCommand(cli) cmd.SetArgs([]string{"reg-name.io/image"}) cmd.SetOutput(ioutil.Discard) - assert.NoError(t, cmd.Execute()) - assert.Contains(t, cli.OutBuffer().String(), "Please confirm you would like to delete all signature data for reg-name.io/image? [y/N] \nAborting action.") + assert.NilError(t, cmd.Execute()) + assert.Check(t, is.Contains(cli.OutBuffer().String(), "Please confirm you would like to delete all signature data for reg-name.io/image? [y/N] \nAborting action.")) cli = test.NewFakeCli(&fakeClient{}) - cli.SetNotaryClient(getEmptyTargetsNotaryRepository) + cli.SetNotaryClient(notary.GetEmptyTargetsNotaryRepository) cmd = newRevokeCommand(cli) cmd.SetArgs([]string{"reg-name.io/image", "-y"}) cmd.SetOutput(ioutil.Discard) - testutil.ErrorContains(t, cmd.Execute(), "could not remove signature for reg-name.io/image: no signed tags to remove") + assert.ErrorContains(t, cmd.Execute(), "could not remove signature for reg-name.io/image: no signed tags to remove") cli = test.NewFakeCli(&fakeClient{}) - cli.SetNotaryClient(getEmptyTargetsNotaryRepository) + cli.SetNotaryClient(notary.GetEmptyTargetsNotaryRepository) cmd = newRevokeCommand(cli) cmd.SetArgs([]string{"reg-name.io/image:tag"}) cmd.SetOutput(ioutil.Discard) - testutil.ErrorContains(t, cmd.Execute(), "could not remove signature for reg-name.io/image:tag: No valid trust data for tag") + assert.ErrorContains(t, cmd.Execute(), "could not remove signature for reg-name.io/image:tag: No valid trust data for tag") } func TestNewRevokeTrustAllSigConfirmation(t *testing.T) { cli := test.NewFakeCli(&fakeClient{}) - cli.SetNotaryClient(getEmptyTargetsNotaryRepository) + cli.SetNotaryClient(notary.GetEmptyTargetsNotaryRepository) cmd := newRevokeCommand(cli) cmd.SetArgs([]string{"alpine"}) - assert.NoError(t, cmd.Execute()) + assert.NilError(t, cmd.Execute()) - assert.Contains(t, cli.OutBuffer().String(), "Please confirm you would like to delete all signature data for alpine? [y/N] \nAborting action.") + assert.Check(t, is.Contains(cli.OutBuffer().String(), "Please confirm you would like to delete all signature data for alpine? [y/N] \nAborting action.")) } func TestGetSignableRolesForTargetAndRemoveError(t *testing.T) { tmpDir, err := ioutil.TempDir("", "notary-test-") - assert.NoError(t, err) + assert.NilError(t, err) defer os.RemoveAll(tmpDir) notaryRepo, err := client.NewFileCachedRepository(tmpDir, "gun", "https://localhost", nil, passphrase.ConstantRetriever("password"), trustpinning.TrustPinConfig{}) - require.NoError(t, err) + assert.NilError(t, err) target := client.Target{} err = getSignableRolesForTargetAndRemove(target, notaryRepo) - assert.EqualError(t, err, "client is offline") + assert.Error(t, err, "client is offline") } diff --git a/components/cli/cli/command/trust/sign.go b/components/cli/cli/command/trust/sign.go index df5c088c9d..234a057c32 100644 --- a/components/cli/cli/command/trust/sign.go +++ b/components/cli/cli/command/trust/sign.go @@ -42,7 +42,7 @@ func newSignCommand(dockerCli command.Cli) *cobra.Command { func runSignImage(cli command.Cli, options signOptions) error { imageName := options.imageName ctx := context.Background() - imgRefAndAuth, err := trust.GetImageReferencesAndAuth(ctx, image.AuthResolver(cli), imageName) + imgRefAndAuth, err := trust.GetImageReferencesAndAuth(ctx, nil, image.AuthResolver(cli), imageName) if err != nil { return err } diff --git a/components/cli/cli/command/trust/sign_test.go b/components/cli/cli/command/trust/sign_test.go index fdfaec739f..e441f7bef2 100644 --- a/components/cli/cli/command/trust/sign_test.go +++ b/components/cli/cli/command/trust/sign_test.go @@ -5,14 +5,16 @@ import ( "encoding/json" "io/ioutil" "os" + "runtime" "testing" "github.com/docker/cli/cli/config" "github.com/docker/cli/cli/trust" "github.com/docker/cli/internal/test" - "github.com/docker/cli/internal/test/testutil" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" + notaryfake "github.com/docker/cli/internal/test/notary" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" + "github.com/gotestyourself/gotestyourself/skip" "github.com/theupdateframework/notary" "github.com/theupdateframework/notary/client" "github.com/theupdateframework/notary/client/changelist" @@ -61,7 +63,7 @@ func TestTrustSignCommandErrors(t *testing.T) { } // change to a tmpdir tmpDir, err := ioutil.TempDir("", "docker-sign-test-") - assert.NoError(t, err) + assert.NilError(t, err) defer os.RemoveAll(tmpDir) config.SetDir(tmpDir) for _, tc := range testCases { @@ -69,83 +71,83 @@ func TestTrustSignCommandErrors(t *testing.T) { test.NewFakeCli(&fakeClient{})) cmd.SetArgs(tc.args) cmd.SetOutput(ioutil.Discard) - testutil.ErrorContains(t, cmd.Execute(), tc.expectedError) + assert.ErrorContains(t, cmd.Execute(), tc.expectedError) } } func TestTrustSignCommandOfflineErrors(t *testing.T) { cli := test.NewFakeCli(&fakeClient{}) - cli.SetNotaryClient(getOfflineNotaryRepository) + cli.SetNotaryClient(notaryfake.GetOfflineNotaryRepository) cmd := newSignCommand(cli) cmd.SetArgs([]string{"reg-name.io/image:tag"}) cmd.SetOutput(ioutil.Discard) - assert.Error(t, cmd.Execute()) - testutil.ErrorContains(t, cmd.Execute(), "client is offline") + assert.ErrorContains(t, cmd.Execute(), "client is offline") } func TestGetOrGenerateNotaryKey(t *testing.T) { tmpDir, err := ioutil.TempDir("", "notary-test-") - assert.NoError(t, err) + assert.NilError(t, err) defer os.RemoveAll(tmpDir) notaryRepo, err := client.NewFileCachedRepository(tmpDir, "gun", "https://localhost", nil, passphrase.ConstantRetriever(passwd), trustpinning.TrustPinConfig{}) - assert.NoError(t, err) + assert.NilError(t, err) // repo is empty, try making a root key rootKeyA, err := getOrGenerateNotaryKey(notaryRepo, data.CanonicalRootRole) - assert.NoError(t, err) - assert.NotNil(t, rootKeyA) + assert.NilError(t, err) + assert.Check(t, rootKeyA != nil) // we should only have one newly generated key allKeys := notaryRepo.GetCryptoService().ListAllKeys() - assert.Len(t, allKeys, 1) - assert.NotNil(t, notaryRepo.GetCryptoService().GetKey(rootKeyA.ID())) + assert.Check(t, is.Len(allKeys, 1)) + assert.Check(t, notaryRepo.GetCryptoService().GetKey(rootKeyA.ID()) != nil) // this time we should get back the same key if we ask for another root key rootKeyB, err := getOrGenerateNotaryKey(notaryRepo, data.CanonicalRootRole) - assert.NoError(t, err) - assert.NotNil(t, rootKeyB) + assert.NilError(t, err) + assert.Check(t, rootKeyB != nil) // we should only have one newly generated key allKeys = notaryRepo.GetCryptoService().ListAllKeys() - assert.Len(t, allKeys, 1) - assert.NotNil(t, notaryRepo.GetCryptoService().GetKey(rootKeyB.ID())) + assert.Check(t, is.Len(allKeys, 1)) + assert.Check(t, notaryRepo.GetCryptoService().GetKey(rootKeyB.ID()) != nil) // The key we retrieved should be identical to the one we generated - assert.Equal(t, rootKeyA, rootKeyB) + assert.Check(t, is.DeepEqual(rootKeyA.Public(), rootKeyB.Public())) // Now also try with a delegation key releasesKey, err := getOrGenerateNotaryKey(notaryRepo, data.RoleName(trust.ReleasesRole)) - assert.NoError(t, err) - assert.NotNil(t, releasesKey) + assert.NilError(t, err) + assert.Check(t, releasesKey != nil) // we should now have two keys allKeys = notaryRepo.GetCryptoService().ListAllKeys() - assert.Len(t, allKeys, 2) - assert.NotNil(t, notaryRepo.GetCryptoService().GetKey(releasesKey.ID())) + assert.Check(t, is.Len(allKeys, 2)) + assert.Check(t, notaryRepo.GetCryptoService().GetKey(releasesKey.ID()) != nil) // The key we retrieved should be identical to the one we generated - assert.NotEqual(t, releasesKey, rootKeyA) - assert.NotEqual(t, releasesKey, rootKeyB) + assert.Check(t, releasesKey != rootKeyA) + assert.Check(t, releasesKey != rootKeyB) } func TestAddStageSigners(t *testing.T) { + skip.If(t, runtime.GOOS == "windows", "FIXME: not supported currently") tmpDir, err := ioutil.TempDir("", "notary-test-") - assert.NoError(t, err) + assert.NilError(t, err) defer os.RemoveAll(tmpDir) notaryRepo, err := client.NewFileCachedRepository(tmpDir, "gun", "https://localhost", nil, passphrase.ConstantRetriever(passwd), trustpinning.TrustPinConfig{}) - assert.NoError(t, err) + assert.NilError(t, err) // stage targets/user userRole := data.RoleName("targets/user") userKey := data.NewPublicKey("algoA", []byte("a")) err = addStagedSigner(notaryRepo, userRole, []data.PublicKey{userKey}) - assert.NoError(t, err) + assert.NilError(t, err) // check the changelist for four total changes: two on targets/releases and two on targets/user cl, err := notaryRepo.GetChangelist() - assert.NoError(t, err) + assert.NilError(t, err) changeList := cl.List() - assert.Len(t, changeList, 4) + assert.Check(t, is.Len(changeList, 4)) // ordering is determinstic: // first change is for targets/user key creation @@ -154,7 +156,7 @@ func TestAddStageSigners(t *testing.T) { NewThreshold: notary.MinThreshold, AddKeys: data.KeyList([]data.PublicKey{userKey}), }) - require.NoError(t, err) + assert.NilError(t, err) expectedChange := changelist.NewTUFChange( changelist.ActionCreate, userRole, @@ -162,14 +164,14 @@ func TestAddStageSigners(t *testing.T) { "", // no path for delegations expectedJSON, ) - assert.Equal(t, expectedChange, newSignerKeyChange) + assert.Check(t, is.DeepEqual(expectedChange, newSignerKeyChange)) // second change is for targets/user getting all paths newSignerPathsChange := changeList[1] expectedJSON, err = json.Marshal(&changelist.TUFDelegation{ AddPaths: []string{""}, }) - require.NoError(t, err) + assert.NilError(t, err) expectedChange = changelist.NewTUFChange( changelist.ActionCreate, userRole, @@ -177,7 +179,7 @@ func TestAddStageSigners(t *testing.T) { "", // no path for delegations expectedJSON, ) - assert.Equal(t, expectedChange, newSignerPathsChange) + assert.Check(t, is.DeepEqual(expectedChange, newSignerPathsChange)) releasesRole := data.RoleName("targets/releases") @@ -187,7 +189,7 @@ func TestAddStageSigners(t *testing.T) { NewThreshold: notary.MinThreshold, AddKeys: data.KeyList([]data.PublicKey{userKey}), }) - require.NoError(t, err) + assert.NilError(t, err) expectedChange = changelist.NewTUFChange( changelist.ActionCreate, releasesRole, @@ -195,14 +197,14 @@ func TestAddStageSigners(t *testing.T) { "", // no path for delegations expectedJSON, ) - assert.Equal(t, expectedChange, releasesKeyChange) + assert.Check(t, is.DeepEqual(expectedChange, releasesKeyChange)) // fourth change is for targets/releases getting all paths releasesPathsChange := changeList[3] expectedJSON, err = json.Marshal(&changelist.TUFDelegation{ AddPaths: []string{""}, }) - require.NoError(t, err) + assert.NilError(t, err) expectedChange = changelist.NewTUFChange( changelist.ActionCreate, releasesRole, @@ -210,19 +212,19 @@ func TestAddStageSigners(t *testing.T) { "", // no path for delegations expectedJSON, ) - assert.Equal(t, expectedChange, releasesPathsChange) + assert.Check(t, is.DeepEqual(expectedChange, releasesPathsChange)) } func TestGetSignedManifestHashAndSize(t *testing.T) { tmpDir, err := ioutil.TempDir("", "notary-test-") - assert.NoError(t, err) + assert.NilError(t, err) defer os.RemoveAll(tmpDir) notaryRepo, err := client.NewFileCachedRepository(tmpDir, "gun", "https://localhost", nil, passphrase.ConstantRetriever(passwd), trustpinning.TrustPinConfig{}) - assert.NoError(t, err) + assert.NilError(t, err) target := &client.Target{} target.Hashes, target.Length, err = getSignedManifestHashAndSize(notaryRepo, "test") - assert.EqualError(t, err, "client is offline") + assert.Error(t, err, "client is offline") } func TestGetReleasedTargetHashAndSize(t *testing.T) { @@ -233,36 +235,36 @@ func TestGetReleasedTargetHashAndSize(t *testing.T) { oneReleasedTgt = append(oneReleasedTgt, client.TargetSignedStruct{Role: mockDelegationRoleWithName(unreleasedRole), Target: unreleasedTgt}) } _, _, err := getReleasedTargetHashAndSize(oneReleasedTgt, "unreleased") - assert.EqualError(t, err, "No valid trust data for unreleased") + assert.Error(t, err, "No valid trust data for unreleased") releasedTgt := client.Target{Name: "released", Hashes: data.Hashes{notary.SHA256: []byte("released-hash")}} oneReleasedTgt = append(oneReleasedTgt, client.TargetSignedStruct{Role: mockDelegationRoleWithName("targets/releases"), Target: releasedTgt}) hash, _, _ := getReleasedTargetHashAndSize(oneReleasedTgt, "unreleased") - assert.Equal(t, data.Hashes{notary.SHA256: []byte("released-hash")}, hash) + assert.Check(t, is.DeepEqual(data.Hashes{notary.SHA256: []byte("released-hash")}, hash)) } func TestCreateTarget(t *testing.T) { tmpDir, err := ioutil.TempDir("", "notary-test-") - assert.NoError(t, err) + assert.NilError(t, err) defer os.RemoveAll(tmpDir) notaryRepo, err := client.NewFileCachedRepository(tmpDir, "gun", "https://localhost", nil, passphrase.ConstantRetriever(passwd), trustpinning.TrustPinConfig{}) - assert.NoError(t, err) + assert.NilError(t, err) _, err = createTarget(notaryRepo, "") - assert.EqualError(t, err, "No tag specified") + assert.Error(t, err, "No tag specified") _, err = createTarget(notaryRepo, "1") - assert.EqualError(t, err, "client is offline") + assert.Error(t, err, "client is offline") } func TestGetExistingSignatureInfoForReleasedTag(t *testing.T) { tmpDir, err := ioutil.TempDir("", "notary-test-") - assert.NoError(t, err) + assert.NilError(t, err) defer os.RemoveAll(tmpDir) notaryRepo, err := client.NewFileCachedRepository(tmpDir, "gun", "https://localhost", nil, passphrase.ConstantRetriever(passwd), trustpinning.TrustPinConfig{}) - assert.NoError(t, err) + assert.NilError(t, err) _, err = getExistingSignatureInfoForReleasedTag(notaryRepo, "test") - assert.EqualError(t, err, "client is offline") + assert.Error(t, err, "client is offline") } func TestPrettyPrintExistingSignatureInfo(t *testing.T) { @@ -271,37 +273,37 @@ func TestPrettyPrintExistingSignatureInfo(t *testing.T) { existingSig := trustTagRow{trustTagKey{"tagName", "abc123"}, signers} prettyPrintExistingSignatureInfo(buf, existingSig) - assert.Contains(t, buf.String(), "Existing signatures for tag tagName digest abc123 from:\nAlice, Bob, Carol") + assert.Check(t, is.Contains(buf.String(), "Existing signatures for tag tagName digest abc123 from:\nAlice, Bob, Carol")) } func TestSignCommandChangeListIsCleanedOnError(t *testing.T) { tmpDir, err := ioutil.TempDir("", "docker-sign-test-") - assert.NoError(t, err) + assert.NilError(t, err) defer os.RemoveAll(tmpDir) config.SetDir(tmpDir) cli := test.NewFakeCli(&fakeClient{}) - cli.SetNotaryClient(getLoadedNotaryRepository) + cli.SetNotaryClient(notaryfake.GetLoadedNotaryRepository) cmd := newSignCommand(cli) cmd.SetArgs([]string{"ubuntu:latest"}) cmd.SetOutput(ioutil.Discard) err = cmd.Execute() - require.Error(t, err) + assert.Assert(t, err != nil) notaryRepo, err := client.NewFileCachedRepository(tmpDir, "docker.io/library/ubuntu", "https://localhost", nil, passphrase.ConstantRetriever(passwd), trustpinning.TrustPinConfig{}) - assert.NoError(t, err) + assert.NilError(t, err) cl, err := notaryRepo.GetChangelist() - require.NoError(t, err) - assert.Equal(t, len(cl.List()), 0) + assert.NilError(t, err) + assert.Check(t, is.Equal(len(cl.List()), 0)) } func TestSignCommandLocalFlag(t *testing.T) { cli := test.NewFakeCli(&fakeClient{}) - cli.SetNotaryClient(getEmptyTargetsNotaryRepository) + cli.SetNotaryClient(notaryfake.GetEmptyTargetsNotaryRepository) cmd := newSignCommand(cli) cmd.SetArgs([]string{"--local", "reg-name.io/image:red"}) cmd.SetOutput(ioutil.Discard) - testutil.ErrorContains(t, cmd.Execute(), "error during connect: Get /images/reg-name.io/image:red/json: unsupported protocol scheme") + assert.ErrorContains(t, cmd.Execute(), "error during connect: Get /images/reg-name.io/image:red/json: unsupported protocol scheme") } diff --git a/components/cli/cli/command/trust/signer_add.go b/components/cli/cli/command/trust/signer_add.go index 874915cb1e..304aeec92f 100644 --- a/components/cli/cli/command/trust/signer_add.go +++ b/components/cli/cli/command/trust/signer_add.go @@ -82,7 +82,7 @@ func addSigner(cli command.Cli, options signerAddOptions) error { func addSignerToRepo(cli command.Cli, signerName string, repoName string, signerPubKeys []data.PublicKey) error { ctx := context.Background() - imgRefAndAuth, err := trust.GetImageReferencesAndAuth(ctx, image.AuthResolver(cli), repoName) + imgRefAndAuth, err := trust.GetImageReferencesAndAuth(ctx, nil, image.AuthResolver(cli), repoName) if err != nil { return err } diff --git a/components/cli/cli/command/trust/signer_add_test.go b/components/cli/cli/command/trust/signer_add_test.go index a27f161cb7..643a31b75a 100644 --- a/components/cli/cli/command/trust/signer_add_test.go +++ b/components/cli/cli/command/trust/signer_add_test.go @@ -5,12 +5,14 @@ import ( "io/ioutil" "os" "path/filepath" + "runtime" "testing" "github.com/docker/cli/cli/config" "github.com/docker/cli/internal/test" - "github.com/docker/cli/internal/test/testutil" - "github.com/stretchr/testify/assert" + notaryfake "github.com/docker/cli/internal/test/notary" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" "github.com/theupdateframework/notary" ) @@ -51,87 +53,95 @@ func TestTrustSignerAddErrors(t *testing.T) { }, } tmpDir, err := ioutil.TempDir("", "docker-sign-test-") - assert.NoError(t, err) + assert.NilError(t, err) defer os.RemoveAll(tmpDir) config.SetDir(tmpDir) for _, tc := range testCases { cli := test.NewFakeCli(&fakeClient{}) - cli.SetNotaryClient(getOfflineNotaryRepository) + cli.SetNotaryClient(notaryfake.GetOfflineNotaryRepository) cmd := newSignerAddCommand(cli) cmd.SetArgs(tc.args) cmd.SetOutput(ioutil.Discard) - testutil.ErrorContains(t, cmd.Execute(), tc.expectedError) + assert.ErrorContains(t, cmd.Execute(), tc.expectedError) } } func TestSignerAddCommandNoTargetsKey(t *testing.T) { tmpDir, err := ioutil.TempDir("", "docker-sign-test-") - assert.NoError(t, err) + assert.NilError(t, err) defer os.RemoveAll(tmpDir) config.SetDir(tmpDir) tmpfile, err := ioutil.TempFile("", "pemfile") - assert.NoError(t, err) + assert.NilError(t, err) defer os.Remove(tmpfile.Name()) cli := test.NewFakeCli(&fakeClient{}) - cli.SetNotaryClient(getEmptyTargetsNotaryRepository) + cli.SetNotaryClient(notaryfake.GetEmptyTargetsNotaryRepository) cmd := newSignerAddCommand(cli) cmd.SetArgs([]string{"--key", tmpfile.Name(), "alice", "alpine", "linuxkit/alpine"}) cmd.SetOutput(ioutil.Discard) - assert.EqualError(t, cmd.Execute(), fmt.Sprintf("could not parse public key from file: %s: no valid public key found", tmpfile.Name())) + assert.Error(t, cmd.Execute(), fmt.Sprintf("could not parse public key from file: %s: no valid public key found", tmpfile.Name())) } func TestSignerAddCommandBadKeyPath(t *testing.T) { tmpDir, err := ioutil.TempDir("", "docker-sign-test-") - assert.NoError(t, err) + assert.NilError(t, err) defer os.RemoveAll(tmpDir) config.SetDir(tmpDir) cli := test.NewFakeCli(&fakeClient{}) - cli.SetNotaryClient(getEmptyTargetsNotaryRepository) + cli.SetNotaryClient(notaryfake.GetEmptyTargetsNotaryRepository) cmd := newSignerAddCommand(cli) cmd.SetArgs([]string{"--key", "/path/to/key.pem", "alice", "alpine"}) cmd.SetOutput(ioutil.Discard) - assert.EqualError(t, cmd.Execute(), "unable to read public key from file: open /path/to/key.pem: no such file or directory") + expectedError := "unable to read public key from file: open /path/to/key.pem: no such file or directory" + if runtime.GOOS == "windows" { + expectedError = "unable to read public key from file: open /path/to/key.pem: The system cannot find the path specified." + } + assert.Error(t, cmd.Execute(), expectedError) } func TestSignerAddCommandInvalidRepoName(t *testing.T) { tmpDir, err := ioutil.TempDir("", "docker-sign-test-") - assert.NoError(t, err) + assert.NilError(t, err) defer os.RemoveAll(tmpDir) config.SetDir(tmpDir) pubKeyDir, err := ioutil.TempDir("", "key-load-test-pubkey-") - assert.NoError(t, err) + assert.NilError(t, err) defer os.RemoveAll(pubKeyDir) pubKeyFilepath := filepath.Join(pubKeyDir, "pubkey.pem") - assert.NoError(t, ioutil.WriteFile(pubKeyFilepath, pubKeyFixture, notary.PrivNoExecPerms)) + assert.NilError(t, ioutil.WriteFile(pubKeyFilepath, pubKeyFixture, notary.PrivNoExecPerms)) cli := test.NewFakeCli(&fakeClient{}) - cli.SetNotaryClient(getUninitializedNotaryRepository) + cli.SetNotaryClient(notaryfake.GetUninitializedNotaryRepository) cmd := newSignerAddCommand(cli) imageName := "870d292919d01a0af7e7f056271dc78792c05f55f49b9b9012b6d89725bd9abd" cmd.SetArgs([]string{"--key", pubKeyFilepath, "alice", imageName}) cmd.SetOutput(ioutil.Discard) - assert.EqualError(t, cmd.Execute(), "Failed to add signer to: 870d292919d01a0af7e7f056271dc78792c05f55f49b9b9012b6d89725bd9abd") + assert.Error(t, cmd.Execute(), "Failed to add signer to: 870d292919d01a0af7e7f056271dc78792c05f55f49b9b9012b6d89725bd9abd") expectedErr := fmt.Sprintf("invalid repository name (%s), cannot specify 64-byte hexadecimal strings\n\n", imageName) - assert.Equal(t, expectedErr, cli.ErrBuffer().String()) + assert.Check(t, is.Equal(expectedErr, cli.ErrBuffer().String())) } func TestIngestPublicKeys(t *testing.T) { // Call with a bad path _, err := ingestPublicKeys([]string{"foo", "bar"}) - assert.EqualError(t, err, "unable to read public key from file: open foo: no such file or directory") + expectedError := "unable to read public key from file: open foo: no such file or directory" + if runtime.GOOS == "windows" { + expectedError = "unable to read public key from file: open foo: The system cannot find the file specified." + } + assert.Error(t, err, expectedError) // Call with real file path tmpfile, err := ioutil.TempFile("", "pemfile") - assert.NoError(t, err) + assert.NilError(t, err) defer os.Remove(tmpfile.Name()) _, err = ingestPublicKeys([]string{tmpfile.Name()}) - assert.EqualError(t, err, fmt.Sprintf("could not parse public key from file: %s: no valid public key found", tmpfile.Name())) + assert.Error(t, err, fmt.Sprintf("could not parse public key from file: %s: no valid public key found", tmpfile.Name())) } diff --git a/components/cli/cli/command/trust/signer_remove.go b/components/cli/cli/command/trust/signer_remove.go index 4aa2825c4f..27774632bf 100644 --- a/components/cli/cli/command/trust/signer_remove.go +++ b/components/cli/cli/command/trust/signer_remove.go @@ -80,7 +80,7 @@ func isLastSignerForReleases(roleWithSig data.Role, allRoles []client.RoleWithSi func removeSingleSigner(cli command.Cli, repoName, signerName string, forceYes bool) error { ctx := context.Background() - imgRefAndAuth, err := trust.GetImageReferencesAndAuth(ctx, image.AuthResolver(cli), repoName) + imgRefAndAuth, err := trust.GetImageReferencesAndAuth(ctx, nil, image.AuthResolver(cli), repoName) if err != nil { return err } diff --git a/components/cli/cli/command/trust/signer_remove_test.go b/components/cli/cli/command/trust/signer_remove_test.go index 890fbf37a8..4f11634be2 100644 --- a/components/cli/cli/command/trust/signer_remove_test.go +++ b/components/cli/cli/command/trust/signer_remove_test.go @@ -5,8 +5,9 @@ import ( "testing" "github.com/docker/cli/internal/test" - "github.com/docker/cli/internal/test/testutil" - "github.com/stretchr/testify/assert" + notaryfake "github.com/docker/cli/internal/test/notary" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" "github.com/theupdateframework/notary/client" "github.com/theupdateframework/notary/tuf/data" ) @@ -32,7 +33,7 @@ func TestTrustSignerRemoveErrors(t *testing.T) { test.NewFakeCli(&fakeClient{})) cmd.SetArgs(tc.args) cmd.SetOutput(ioutil.Discard) - testutil.ErrorContains(t, cmd.Execute(), tc.expectedError) + assert.ErrorContains(t, cmd.Execute(), tc.expectedError) } testCasesWithOutput := []struct { name string @@ -57,44 +58,44 @@ func TestTrustSignerRemoveErrors(t *testing.T) { } for _, tc := range testCasesWithOutput { cli := test.NewFakeCli(&fakeClient{}) - cli.SetNotaryClient(getOfflineNotaryRepository) + cli.SetNotaryClient(notaryfake.GetOfflineNotaryRepository) cmd := newSignerRemoveCommand(cli) cmd.SetArgs(tc.args) cmd.SetOutput(ioutil.Discard) cmd.Execute() - assert.Contains(t, cli.ErrBuffer().String(), tc.expectedError) + assert.Check(t, is.Contains(cli.ErrBuffer().String(), tc.expectedError)) } } func TestRemoveSingleSigner(t *testing.T) { cli := test.NewFakeCli(&fakeClient{}) - cli.SetNotaryClient(getLoadedNotaryRepository) + cli.SetNotaryClient(notaryfake.GetLoadedNotaryRepository) err := removeSingleSigner(cli, "signed-repo", "test", true) - assert.EqualError(t, err, "No signer test for repository signed-repo") + assert.Error(t, err, "No signer test for repository signed-repo") err = removeSingleSigner(cli, "signed-repo", "releases", true) - assert.EqualError(t, err, "releases is a reserved keyword and cannot be removed") + assert.Error(t, err, "releases is a reserved keyword and cannot be removed") } func TestRemoveMultipleSigners(t *testing.T) { cli := test.NewFakeCli(&fakeClient{}) - cli.SetNotaryClient(getLoadedNotaryRepository) + cli.SetNotaryClient(notaryfake.GetLoadedNotaryRepository) err := removeSigner(cli, signerRemoveOptions{signer: "test", repos: []string{"signed-repo", "signed-repo"}, forceYes: true}) - assert.EqualError(t, err, "Error removing signer from: signed-repo, signed-repo") - assert.Contains(t, cli.ErrBuffer().String(), - "No signer test for repository signed-repo") - assert.Contains(t, cli.OutBuffer().String(), "Removing signer \"test\" from signed-repo...\n") + assert.Error(t, err, "Error removing signer from: signed-repo, signed-repo") + assert.Check(t, is.Contains(cli.ErrBuffer().String(), + "No signer test for repository signed-repo")) + assert.Check(t, is.Contains(cli.OutBuffer().String(), "Removing signer \"test\" from signed-repo...\n")) } func TestRemoveLastSignerWarning(t *testing.T) { cli := test.NewFakeCli(&fakeClient{}) - cli.SetNotaryClient(getLoadedNotaryRepository) + cli.SetNotaryClient(notaryfake.GetLoadedNotaryRepository) err := removeSigner(cli, signerRemoveOptions{signer: "alice", repos: []string{"signed-repo"}, forceYes: false}) - assert.NoError(t, err) - assert.Contains(t, cli.OutBuffer().String(), + assert.NilError(t, err) + assert.Check(t, is.Contains(cli.OutBuffer().String(), "The signer \"alice\" signed the last released version of signed-repo. "+ "Removing this signer will make signed-repo unpullable. "+ - "Are you sure you want to continue? [y/N]") + "Are you sure you want to continue? [y/N]")) } func TestIsLastSignerForReleases(t *testing.T) { @@ -104,7 +105,7 @@ func TestIsLastSignerForReleases(t *testing.T) { releaserole.Threshold = 1 allrole := []client.RoleWithSignatures{releaserole} lastsigner, _ := isLastSignerForReleases(role, allrole) - assert.Equal(t, false, lastsigner) + assert.Check(t, is.Equal(false, lastsigner)) role.KeyIDs = []string{"deadbeef"} sig := data.Signature{} @@ -113,12 +114,12 @@ func TestIsLastSignerForReleases(t *testing.T) { releaserole.Threshold = 1 allrole = []client.RoleWithSignatures{releaserole} lastsigner, _ = isLastSignerForReleases(role, allrole) - assert.Equal(t, true, lastsigner) + assert.Check(t, is.Equal(true, lastsigner)) sig.KeyID = "8badf00d" releaserole.Signatures = []data.Signature{sig} releaserole.Threshold = 1 allrole = []client.RoleWithSignatures{releaserole} lastsigner, _ = isLastSignerForReleases(role, allrole) - assert.Equal(t, false, lastsigner) + assert.Check(t, is.Equal(false, lastsigner)) } diff --git a/components/cli/cli/command/volume/create_test.go b/components/cli/cli/command/volume/create_test.go index d7c6cb5857..d68d065e27 100644 --- a/components/cli/cli/command/volume/create_test.go +++ b/components/cli/cli/command/volume/create_test.go @@ -7,11 +7,11 @@ import ( "testing" "github.com/docker/cli/internal/test" - "github.com/docker/cli/internal/test/testutil" "github.com/docker/docker/api/types" volumetypes "github.com/docker/docker/api/types/volume" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" "github.com/pkg/errors" - "github.com/stretchr/testify/assert" ) func TestVolumeCreateErrors(t *testing.T) { @@ -50,7 +50,7 @@ func TestVolumeCreateErrors(t *testing.T) { cmd.Flags().Set(key, value) } cmd.SetOutput(ioutil.Discard) - testutil.ErrorContains(t, cmd.Execute(), tc.expectedError) + assert.ErrorContains(t, cmd.Execute(), tc.expectedError) } } @@ -72,15 +72,15 @@ func TestVolumeCreateWithName(t *testing.T) { // Test by flags cmd := newCreateCommand(cli) cmd.Flags().Set("name", name) - assert.NoError(t, cmd.Execute()) - assert.Equal(t, name, strings.TrimSpace(buf.String())) + assert.NilError(t, cmd.Execute()) + assert.Check(t, is.Equal(name, strings.TrimSpace(buf.String()))) // Then by args buf.Reset() cmd = newCreateCommand(cli) cmd.SetArgs([]string{name}) - assert.NoError(t, cmd.Execute()) - assert.Equal(t, name, strings.TrimSpace(buf.String())) + assert.NilError(t, cmd.Execute()) + assert.Check(t, is.Equal(name, strings.TrimSpace(buf.String()))) } func TestVolumeCreateWithFlags(t *testing.T) { @@ -121,6 +121,6 @@ func TestVolumeCreateWithFlags(t *testing.T) { cmd.Flags().Set("opt", "baz=baz") cmd.Flags().Set("label", "lbl1=v1") cmd.Flags().Set("label", "lbl2=v2") - assert.NoError(t, cmd.Execute()) - assert.Equal(t, name, strings.TrimSpace(cli.OutBuffer().String())) + assert.NilError(t, cmd.Execute()) + assert.Check(t, is.Equal(name, strings.TrimSpace(cli.OutBuffer().String()))) } diff --git a/components/cli/cli/command/volume/inspect_test.go b/components/cli/cli/command/volume/inspect_test.go index 934e9b27d8..c827b7281c 100644 --- a/components/cli/cli/command/volume/inspect_test.go +++ b/components/cli/cli/command/volume/inspect_test.go @@ -10,9 +10,8 @@ import ( "github.com/pkg/errors" // Import builders to get the builder function as package function . "github.com/docker/cli/internal/test/builders" - "github.com/docker/cli/internal/test/testutil" + "github.com/gotestyourself/gotestyourself/assert" "github.com/gotestyourself/gotestyourself/golden" - "github.com/stretchr/testify/assert" ) func TestVolumeInspectErrors(t *testing.T) { @@ -63,7 +62,7 @@ func TestVolumeInspectErrors(t *testing.T) { cmd.Flags().Set(key, value) } cmd.SetOutput(ioutil.Discard) - testutil.ErrorContains(t, cmd.Execute(), tc.expectedError) + assert.ErrorContains(t, cmd.Execute(), tc.expectedError) } } @@ -99,7 +98,7 @@ func TestVolumeInspectWithoutFormat(t *testing.T) { }) cmd := newInspectCommand(cli) cmd.SetArgs(tc.args) - assert.NoError(t, cmd.Execute()) + assert.NilError(t, cmd.Execute()) golden.Assert(t, cli.OutBuffer().String(), fmt.Sprintf("volume-inspect-without-format.%s.golden", tc.name)) } } @@ -136,7 +135,7 @@ func TestVolumeInspectWithFormat(t *testing.T) { cmd := newInspectCommand(cli) cmd.SetArgs(tc.args) cmd.Flags().Set("format", tc.format) - assert.NoError(t, cmd.Execute()) + assert.NilError(t, cmd.Execute()) golden.Assert(t, cli.OutBuffer().String(), fmt.Sprintf("volume-inspect-with-format.%s.golden", tc.name)) } } diff --git a/components/cli/cli/command/volume/list_test.go b/components/cli/cli/command/volume/list_test.go index 264e1010a1..52b3e46603 100644 --- a/components/cli/cli/command/volume/list_test.go +++ b/components/cli/cli/command/volume/list_test.go @@ -12,9 +12,8 @@ import ( "github.com/pkg/errors" // Import builders to get the builder function as package function . "github.com/docker/cli/internal/test/builders" - "github.com/docker/cli/internal/test/testutil" + "github.com/gotestyourself/gotestyourself/assert" "github.com/gotestyourself/gotestyourself/golden" - "github.com/stretchr/testify/assert" ) func TestVolumeListErrors(t *testing.T) { @@ -46,7 +45,7 @@ func TestVolumeListErrors(t *testing.T) { cmd.Flags().Set(key, value) } cmd.SetOutput(ioutil.Discard) - testutil.ErrorContains(t, cmd.Execute(), tc.expectedError) + assert.ErrorContains(t, cmd.Execute(), tc.expectedError) } } @@ -65,7 +64,7 @@ func TestVolumeListWithoutFormat(t *testing.T) { }, }) cmd := newListCommand(cli) - assert.NoError(t, cmd.Execute()) + assert.NilError(t, cmd.Execute()) golden.Assert(t, cli.OutBuffer().String(), "volume-list-without-format.golden") } @@ -87,7 +86,7 @@ func TestVolumeListWithConfigFormat(t *testing.T) { VolumesFormat: "{{ .Name }} {{ .Driver }} {{ .Labels }}", }) cmd := newListCommand(cli) - assert.NoError(t, cmd.Execute()) + assert.NilError(t, cmd.Execute()) golden.Assert(t, cli.OutBuffer().String(), "volume-list-with-config-format.golden") } @@ -107,6 +106,6 @@ func TestVolumeListWithFormat(t *testing.T) { }) cmd := newListCommand(cli) cmd.Flags().Set("format", "{{ .Name }} {{ .Driver }} {{ .Labels }}") - assert.NoError(t, cmd.Execute()) + assert.NilError(t, cmd.Execute()) golden.Assert(t, cli.OutBuffer().String(), "volume-list-with-format.golden") } diff --git a/components/cli/cli/command/volume/prune_test.go b/components/cli/cli/command/volume/prune_test.go index d913f535db..0d75d3b75d 100644 --- a/components/cli/cli/command/volume/prune_test.go +++ b/components/cli/cli/command/volume/prune_test.go @@ -9,13 +9,12 @@ import ( "github.com/docker/cli/cli/command" "github.com/docker/cli/internal/test" - "github.com/docker/cli/internal/test/testutil" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/filters" + "github.com/gotestyourself/gotestyourself/assert" "github.com/gotestyourself/gotestyourself/golden" "github.com/gotestyourself/gotestyourself/skip" "github.com/pkg/errors" - "github.com/stretchr/testify/assert" ) func TestVolumePruneErrors(t *testing.T) { @@ -50,7 +49,7 @@ func TestVolumePruneErrors(t *testing.T) { cmd.Flags().Set(key, value) } cmd.SetOutput(ioutil.Discard) - testutil.ErrorContains(t, cmd.Execute(), tc.expectedError) + assert.ErrorContains(t, cmd.Execute(), tc.expectedError) } } @@ -73,7 +72,7 @@ func TestVolumePruneForce(t *testing.T) { }) cmd := NewPruneCommand(cli) cmd.Flags().Set("force", "true") - assert.NoError(t, cmd.Execute()) + assert.NilError(t, cmd.Execute()) golden.Assert(t, cli.OutBuffer().String(), fmt.Sprintf("volume-prune.%s.golden", tc.name)) } } @@ -89,7 +88,7 @@ func TestVolumePrunePromptYes(t *testing.T) { cli.SetIn(command.NewInStream(ioutil.NopCloser(strings.NewReader(input)))) cmd := NewPruneCommand(cli) - assert.NoError(t, cmd.Execute()) + assert.NilError(t, cmd.Execute()) golden.Assert(t, cli.OutBuffer().String(), "volume-prune-yes.golden") } } @@ -105,7 +104,7 @@ func TestVolumePrunePromptNo(t *testing.T) { cli.SetIn(command.NewInStream(ioutil.NopCloser(strings.NewReader(input)))) cmd := NewPruneCommand(cli) - assert.NoError(t, cmd.Execute()) + assert.NilError(t, cmd.Execute()) golden.Assert(t, cli.OutBuffer().String(), "volume-prune-no.golden") } } diff --git a/components/cli/cli/command/volume/remove_test.go b/components/cli/cli/command/volume/remove_test.go index fdfb1d788f..f773a3648e 100644 --- a/components/cli/cli/command/volume/remove_test.go +++ b/components/cli/cli/command/volume/remove_test.go @@ -5,9 +5,8 @@ import ( "testing" "github.com/docker/cli/internal/test" - "github.com/docker/cli/internal/test/testutil" + "github.com/gotestyourself/gotestyourself/assert" "github.com/pkg/errors" - "github.com/stretchr/testify/assert" ) func TestVolumeRemoveErrors(t *testing.T) { @@ -34,12 +33,12 @@ func TestVolumeRemoveErrors(t *testing.T) { })) cmd.SetArgs(tc.args) cmd.SetOutput(ioutil.Discard) - testutil.ErrorContains(t, cmd.Execute(), tc.expectedError) + assert.ErrorContains(t, cmd.Execute(), tc.expectedError) } } func TestNodeRemoveMultiple(t *testing.T) { cmd := newRemoveCommand(test.NewFakeCli(&fakeClient{})) cmd.SetArgs([]string{"volume1", "volume2"}) - assert.NoError(t, cmd.Execute()) + assert.NilError(t, cmd.Execute()) } diff --git a/components/cli/cli/compose/convert/compose_test.go b/components/cli/cli/compose/convert/compose_test.go index 1392fce604..a2ff2f1723 100644 --- a/components/cli/cli/compose/convert/compose_test.go +++ b/components/cli/cli/compose/convert/compose_test.go @@ -6,14 +6,14 @@ import ( composetypes "github.com/docker/cli/cli/compose/types" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/network" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" "github.com/gotestyourself/gotestyourself/fs" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" ) func TestNamespaceScope(t *testing.T) { scoped := Namespace{name: "foo"}.Scope("bar") - assert.Equal(t, "foo_bar", scoped) + assert.Check(t, is.Equal("foo_bar", scoped)) } func TestAddStackLabel(t *testing.T) { @@ -25,7 +25,7 @@ func TestAddStackLabel(t *testing.T) { "something": "labeled", LabelNamespace: "foo", } - assert.Equal(t, expected, actual) + assert.Check(t, is.DeepEqual(expected, actual)) } func TestNetworks(t *testing.T) { @@ -97,8 +97,8 @@ func TestNetworks(t *testing.T) { } networks, externals := Networks(namespace, source, serviceNetworks) - assert.Equal(t, expected, networks) - assert.Equal(t, []string{"special"}, externals) + assert.Check(t, is.DeepEqual(expected, networks)) + assert.Check(t, is.DeepEqual([]string{"special"}, externals)) } func TestSecrets(t *testing.T) { @@ -121,15 +121,15 @@ func TestSecrets(t *testing.T) { } specs, err := Secrets(namespace, source) - assert.NoError(t, err) - require.Len(t, specs, 1) + assert.NilError(t, err) + assert.Assert(t, is.Len(specs, 1)) secret := specs[0] - assert.Equal(t, "foo_one", secret.Name) - assert.Equal(t, map[string]string{ + assert.Check(t, is.Equal("foo_one", secret.Name)) + assert.Check(t, is.DeepEqual(map[string]string{ "monster": "mash", LabelNamespace: "foo", - }, secret.Labels) - assert.Equal(t, []byte(secretText), secret.Data) + }, secret.Labels)) + assert.Check(t, is.DeepEqual([]byte(secretText), secret.Data)) } func TestConfigs(t *testing.T) { @@ -152,13 +152,13 @@ func TestConfigs(t *testing.T) { } specs, err := Configs(namespace, source) - assert.NoError(t, err) - require.Len(t, specs, 1) + assert.NilError(t, err) + assert.Assert(t, is.Len(specs, 1)) config := specs[0] - assert.Equal(t, "foo_one", config.Name) - assert.Equal(t, map[string]string{ + assert.Check(t, is.Equal("foo_one", config.Name)) + assert.Check(t, is.DeepEqual(map[string]string{ "monster": "mash", LabelNamespace: "foo", - }, config.Labels) - assert.Equal(t, []byte(configText), config.Data) + }, config.Labels)) + assert.Check(t, is.DeepEqual([]byte(configText), config.Data)) } diff --git a/components/cli/cli/compose/convert/service_test.go b/components/cli/cli/compose/convert/service_test.go index 0f32997077..044b4e3e30 100644 --- a/components/cli/cli/compose/convert/service_test.go +++ b/components/cli/cli/compose/convert/service_test.go @@ -12,21 +12,21 @@ import ( "github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/swarm" "github.com/docker/docker/client" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" "github.com/pkg/errors" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" "golang.org/x/net/context" ) func TestConvertRestartPolicyFromNone(t *testing.T) { policy, err := convertRestartPolicy("no", nil) - assert.NoError(t, err) - assert.Equal(t, (*swarm.RestartPolicy)(nil), policy) + assert.NilError(t, err) + assert.Check(t, is.DeepEqual((*swarm.RestartPolicy)(nil), policy)) } func TestConvertRestartPolicyFromUnknown(t *testing.T) { _, err := convertRestartPolicy("unknown", nil) - assert.EqualError(t, err, "unknown restart policy: unknown") + assert.Error(t, err, "unknown restart policy: unknown") } func TestConvertRestartPolicyFromAlways(t *testing.T) { @@ -34,8 +34,8 @@ func TestConvertRestartPolicyFromAlways(t *testing.T) { expected := &swarm.RestartPolicy{ Condition: swarm.RestartPolicyConditionAny, } - assert.NoError(t, err) - assert.Equal(t, expected, policy) + assert.NilError(t, err) + assert.Check(t, is.DeepEqual(expected, policy)) } func TestConvertRestartPolicyFromFailure(t *testing.T) { @@ -45,8 +45,8 @@ func TestConvertRestartPolicyFromFailure(t *testing.T) { Condition: swarm.RestartPolicyConditionOnFailure, MaxAttempts: &attempts, } - assert.NoError(t, err) - assert.Equal(t, expected, policy) + assert.NilError(t, err) + assert.Check(t, is.DeepEqual(expected, policy)) } func strPtr(val string) *string { @@ -60,7 +60,7 @@ func TestConvertEnvironment(t *testing.T) { } env := convertEnvironment(source) sort.Strings(env) - assert.Equal(t, []string{"foo=bar", "key=value"}, env) + assert.Check(t, is.DeepEqual([]string{"foo=bar", "key=value"}, env)) } func TestConvertExtraHosts(t *testing.T) { @@ -69,7 +69,7 @@ func TestConvertExtraHosts(t *testing.T) { "alpha:127.0.0.1", "zulu:ff02::1", } - assert.Equal(t, []string{"127.0.0.2 zulu", "127.0.0.1 alpha", "ff02::1 zulu"}, convertExtraHosts(source)) + assert.Check(t, is.DeepEqual([]string{"127.0.0.2 zulu", "127.0.0.1 alpha", "ff02::1 zulu"}, convertExtraHosts(source))) } func TestConvertResourcesFull(t *testing.T) { @@ -84,7 +84,7 @@ func TestConvertResourcesFull(t *testing.T) { }, } resources, err := convertResources(source) - assert.NoError(t, err) + assert.NilError(t, err) expected := &swarm.ResourceRequirements{ Limits: &swarm.Resources{ @@ -96,7 +96,7 @@ func TestConvertResourcesFull(t *testing.T) { MemoryBytes: 200000000, }, } - assert.Equal(t, expected, resources) + assert.Check(t, is.DeepEqual(expected, resources)) } func TestConvertResourcesOnlyMemory(t *testing.T) { @@ -109,7 +109,7 @@ func TestConvertResourcesOnlyMemory(t *testing.T) { }, } resources, err := convertResources(source) - assert.NoError(t, err) + assert.NilError(t, err) expected := &swarm.ResourceRequirements{ Limits: &swarm.Resources{ @@ -119,7 +119,7 @@ func TestConvertResourcesOnlyMemory(t *testing.T) { MemoryBytes: 200000000, }, } - assert.Equal(t, expected, resources) + assert.Check(t, is.DeepEqual(expected, resources)) } func TestConvertHealthcheck(t *testing.T) { @@ -140,8 +140,8 @@ func TestConvertHealthcheck(t *testing.T) { } healthcheck, err := convertHealthcheck(source) - assert.NoError(t, err) - assert.Equal(t, expected, healthcheck) + assert.NilError(t, err) + assert.Check(t, is.DeepEqual(expected, healthcheck)) } func TestConvertHealthcheckDisable(t *testing.T) { @@ -151,8 +151,8 @@ func TestConvertHealthcheckDisable(t *testing.T) { } healthcheck, err := convertHealthcheck(source) - assert.NoError(t, err) - assert.Equal(t, expected, healthcheck) + assert.NilError(t, err) + assert.Check(t, is.DeepEqual(expected, healthcheck)) } func TestConvertHealthcheckDisableWithTest(t *testing.T) { @@ -161,7 +161,7 @@ func TestConvertHealthcheckDisableWithTest(t *testing.T) { Test: []string{"EXEC", "touch"}, } _, err := convertHealthcheck(source) - assert.EqualError(t, err, "test and disable can't be set at the same time") + assert.Error(t, err, "test and disable can't be set at the same time") } func TestConvertEndpointSpec(t *testing.T) { @@ -195,8 +195,8 @@ func TestConvertEndpointSpec(t *testing.T) { }, } - assert.NoError(t, err) - assert.Equal(t, expected, *endpoint) + assert.NilError(t, err) + assert.Check(t, is.DeepEqual(expected, *endpoint)) } func TestConvertServiceNetworksOnlyDefault(t *testing.T) { @@ -212,8 +212,8 @@ func TestConvertServiceNetworksOnlyDefault(t *testing.T) { }, } - assert.NoError(t, err) - assert.Equal(t, expected, configs) + assert.NilError(t, err) + assert.Check(t, is.DeepEqual(expected, configs)) } func TestConvertServiceNetworks(t *testing.T) { @@ -250,8 +250,8 @@ func TestConvertServiceNetworks(t *testing.T) { sortedConfigs := byTargetSort(configs) sort.Sort(&sortedConfigs) - assert.NoError(t, err) - assert.Equal(t, expected, []swarm.NetworkAttachmentConfig(sortedConfigs)) + assert.NilError(t, err) + assert.Check(t, is.DeepEqual(expected, []swarm.NetworkAttachmentConfig(sortedConfigs))) } func TestConvertServiceNetworksCustomDefault(t *testing.T) { @@ -273,8 +273,8 @@ func TestConvertServiceNetworksCustomDefault(t *testing.T) { }, } - assert.NoError(t, err) - assert.Equal(t, expected, []swarm.NetworkAttachmentConfig(configs)) + assert.NilError(t, err) + assert.Check(t, is.DeepEqual(expected, []swarm.NetworkAttachmentConfig(configs))) } type byTargetSort []swarm.NetworkAttachmentConfig @@ -294,8 +294,8 @@ func (s byTargetSort) Swap(i, j int) { func TestConvertDNSConfigEmpty(t *testing.T) { dnsConfig, err := convertDNSConfig(nil, nil) - assert.NoError(t, err) - assert.Equal(t, (*swarm.DNSConfig)(nil), dnsConfig) + assert.NilError(t, err) + assert.Check(t, is.DeepEqual((*swarm.DNSConfig)(nil), dnsConfig)) } var ( @@ -305,74 +305,74 @@ var ( func TestConvertDNSConfigAll(t *testing.T) { dnsConfig, err := convertDNSConfig(nameservers, search) - assert.NoError(t, err) - assert.Equal(t, &swarm.DNSConfig{ + assert.NilError(t, err) + assert.Check(t, is.DeepEqual(&swarm.DNSConfig{ Nameservers: nameservers, Search: search, - }, dnsConfig) + }, dnsConfig)) } func TestConvertDNSConfigNameservers(t *testing.T) { dnsConfig, err := convertDNSConfig(nameservers, nil) - assert.NoError(t, err) - assert.Equal(t, &swarm.DNSConfig{ + assert.NilError(t, err) + assert.Check(t, is.DeepEqual(&swarm.DNSConfig{ Nameservers: nameservers, Search: nil, - }, dnsConfig) + }, dnsConfig)) } func TestConvertDNSConfigSearch(t *testing.T) { dnsConfig, err := convertDNSConfig(nil, search) - assert.NoError(t, err) - assert.Equal(t, &swarm.DNSConfig{ + assert.NilError(t, err) + assert.Check(t, is.DeepEqual(&swarm.DNSConfig{ Nameservers: nil, Search: search, - }, dnsConfig) + }, dnsConfig)) } func TestConvertCredentialSpec(t *testing.T) { swarmSpec, err := convertCredentialSpec(composetypes.CredentialSpecConfig{}) - assert.NoError(t, err) - assert.Nil(t, swarmSpec) + assert.NilError(t, err) + assert.Check(t, is.Nil(swarmSpec)) swarmSpec, err = convertCredentialSpec(composetypes.CredentialSpecConfig{ File: "/foo", }) - assert.NoError(t, err) - assert.Equal(t, swarmSpec.File, "/foo") - assert.Equal(t, swarmSpec.Registry, "") + assert.NilError(t, err) + assert.Check(t, is.Equal(swarmSpec.File, "/foo")) + assert.Check(t, is.Equal(swarmSpec.Registry, "")) swarmSpec, err = convertCredentialSpec(composetypes.CredentialSpecConfig{ Registry: "foo", }) - assert.NoError(t, err) - assert.Equal(t, swarmSpec.File, "") - assert.Equal(t, swarmSpec.Registry, "foo") + assert.NilError(t, err) + assert.Check(t, is.Equal(swarmSpec.File, "")) + assert.Check(t, is.Equal(swarmSpec.Registry, "foo")) swarmSpec, err = convertCredentialSpec(composetypes.CredentialSpecConfig{ File: "/asdf", Registry: "foo", }) - assert.Error(t, err) - assert.Nil(t, swarmSpec) + assert.Check(t, is.ErrorContains(err, "")) + assert.Check(t, is.Nil(swarmSpec)) } func TestConvertUpdateConfigOrder(t *testing.T) { // test default behavior updateConfig := convertUpdateConfig(&composetypes.UpdateConfig{}) - assert.Equal(t, "", updateConfig.Order) + assert.Check(t, is.Equal("", updateConfig.Order)) // test start-first updateConfig = convertUpdateConfig(&composetypes.UpdateConfig{ Order: "start-first", }) - assert.Equal(t, updateConfig.Order, "start-first") + assert.Check(t, is.Equal(updateConfig.Order, "start-first")) // test stop-first updateConfig = convertUpdateConfig(&composetypes.UpdateConfig{ Order: "stop-first", }) - assert.Equal(t, updateConfig.Order, "stop-first") + assert.Check(t, is.Equal(updateConfig.Order, "stop-first")) } func TestConvertFileObject(t *testing.T) { @@ -385,7 +385,7 @@ func TestConvertFileObject(t *testing.T) { Mode: uint32Ptr(0644), } swarmRef, err := convertFileObject(namespace, config, lookupConfig) - require.NoError(t, err) + assert.NilError(t, err) expected := swarmReferenceObject{ Name: "testing_source", @@ -396,7 +396,7 @@ func TestConvertFileObject(t *testing.T) { Mode: os.FileMode(0644), }, } - assert.Equal(t, expected, swarmRef) + assert.Check(t, is.DeepEqual(expected, swarmRef)) } func lookupConfig(key string) (composetypes.FileObjectConfig, error) { @@ -410,7 +410,7 @@ func TestConvertFileObjectDefaults(t *testing.T) { namespace := NewNamespace("testing") config := composetypes.FileReferenceConfig{Source: "source"} swarmRef, err := convertFileObject(namespace, config, lookupConfig) - require.NoError(t, err) + assert.NilError(t, err) expected := swarmReferenceObject{ Name: "testing_source", @@ -421,7 +421,7 @@ func TestConvertFileObjectDefaults(t *testing.T) { Mode: os.FileMode(0444), }, } - assert.Equal(t, expected, swarmRef) + assert.Check(t, is.DeepEqual(expected, swarmRef)) } func TestServiceConvertsIsolation(t *testing.T) { @@ -429,8 +429,8 @@ func TestServiceConvertsIsolation(t *testing.T) { Isolation: "hyperv", } result, err := Service("1.35", Namespace{name: "foo"}, src, nil, nil, nil, nil) - require.NoError(t, err) - assert.Equal(t, container.IsolationHyperV, result.TaskTemplate.ContainerSpec.Isolation) + assert.NilError(t, err) + assert.Check(t, is.Equal(container.IsolationHyperV, result.TaskTemplate.ContainerSpec.Isolation)) } func TestConvertServiceSecrets(t *testing.T) { @@ -449,8 +449,8 @@ func TestConvertServiceSecrets(t *testing.T) { } client := &fakeClient{ secretListFunc: func(opts types.SecretListOptions) ([]swarm.Secret, error) { - assert.Contains(t, opts.Filters.Get("name"), "foo_secret") - assert.Contains(t, opts.Filters.Get("name"), "bar_secret") + assert.Check(t, is.Contains(opts.Filters.Get("name"), "foo_secret")) + assert.Check(t, is.Contains(opts.Filters.Get("name"), "bar_secret")) return []swarm.Secret{ {Spec: swarm.SecretSpec{Annotations: swarm.Annotations{Name: "foo_secret"}}}, {Spec: swarm.SecretSpec{Annotations: swarm.Annotations{Name: "bar_secret"}}}, @@ -458,7 +458,7 @@ func TestConvertServiceSecrets(t *testing.T) { }, } refs, err := convertServiceSecrets(client, namespace, secrets, secretSpecs) - require.NoError(t, err) + assert.NilError(t, err) expected := []*swarm.SecretReference{ { SecretName: "bar_secret", @@ -479,7 +479,7 @@ func TestConvertServiceSecrets(t *testing.T) { }, }, } - require.Equal(t, expected, refs) + assert.DeepEqual(t, expected, refs) } func TestConvertServiceConfigs(t *testing.T) { @@ -498,8 +498,8 @@ func TestConvertServiceConfigs(t *testing.T) { } client := &fakeClient{ configListFunc: func(opts types.ConfigListOptions) ([]swarm.Config, error) { - assert.Contains(t, opts.Filters.Get("name"), "foo_config") - assert.Contains(t, opts.Filters.Get("name"), "bar_config") + assert.Check(t, is.Contains(opts.Filters.Get("name"), "foo_config")) + assert.Check(t, is.Contains(opts.Filters.Get("name"), "bar_config")) return []swarm.Config{ {Spec: swarm.ConfigSpec{Annotations: swarm.Annotations{Name: "foo_config"}}}, {Spec: swarm.ConfigSpec{Annotations: swarm.Annotations{Name: "bar_config"}}}, @@ -507,7 +507,7 @@ func TestConvertServiceConfigs(t *testing.T) { }, } refs, err := convertServiceConfigObjs(client, namespace, configs, configSpecs) - require.NoError(t, err) + assert.NilError(t, err) expected := []*swarm.ConfigReference{ { ConfigName: "bar_config", @@ -528,7 +528,7 @@ func TestConvertServiceConfigs(t *testing.T) { }, }, } - require.Equal(t, expected, refs) + assert.DeepEqual(t, expected, refs) } type fakeClient struct { diff --git a/components/cli/cli/compose/convert/volume_test.go b/components/cli/cli/compose/convert/volume_test.go index 1603e8453a..d7d45eedf5 100644 --- a/components/cli/cli/compose/convert/volume_test.go +++ b/components/cli/cli/compose/convert/volume_test.go @@ -5,7 +5,8 @@ import ( composetypes "github.com/docker/cli/cli/compose/types" "github.com/docker/docker/api/types/mount" - "github.com/stretchr/testify/assert" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" ) func TestConvertVolumeToMountAnonymousVolume(t *testing.T) { @@ -18,8 +19,8 @@ func TestConvertVolumeToMountAnonymousVolume(t *testing.T) { Target: "/foo/bar", } mount, err := convertVolumeToMount(config, volumes{}, NewNamespace("foo")) - assert.NoError(t, err) - assert.Equal(t, expected, mount) + assert.NilError(t, err) + assert.Check(t, is.DeepEqual(expected, mount)) } func TestConvertVolumeToMountAnonymousBind(t *testing.T) { @@ -31,7 +32,7 @@ func TestConvertVolumeToMountAnonymousBind(t *testing.T) { }, } _, err := convertVolumeToMount(config, volumes{}, NewNamespace("foo")) - assert.EqualError(t, err, "invalid bind source, source cannot be empty") + assert.Error(t, err, "invalid bind source, source cannot be empty") } func TestConvertVolumeToMountUnapprovedType(t *testing.T) { @@ -40,7 +41,7 @@ func TestConvertVolumeToMountUnapprovedType(t *testing.T) { Target: "/foo/bar", } _, err := convertVolumeToMount(config, volumes{}, NewNamespace("foo")) - assert.EqualError(t, err, "volume type must be volume, bind, or tmpfs") + assert.Error(t, err, "volume type must be volume, bind, or tmpfs") } func TestConvertVolumeToMountConflictingOptionsBindInVolume(t *testing.T) { @@ -55,7 +56,7 @@ func TestConvertVolumeToMountConflictingOptionsBindInVolume(t *testing.T) { }, } _, err := convertVolumeToMount(config, volumes{}, namespace) - assert.EqualError(t, err, "bind options are incompatible with type volume") + assert.Error(t, err, "bind options are incompatible with type volume") } func TestConvertVolumeToMountConflictingOptionsTmpfsInVolume(t *testing.T) { @@ -70,7 +71,7 @@ func TestConvertVolumeToMountConflictingOptionsTmpfsInVolume(t *testing.T) { }, } _, err := convertVolumeToMount(config, volumes{}, namespace) - assert.EqualError(t, err, "tmpfs options are incompatible with type volume") + assert.Error(t, err, "tmpfs options are incompatible with type volume") } func TestConvertVolumeToMountConflictingOptionsVolumeInBind(t *testing.T) { @@ -85,7 +86,7 @@ func TestConvertVolumeToMountConflictingOptionsVolumeInBind(t *testing.T) { }, } _, err := convertVolumeToMount(config, volumes{}, namespace) - assert.EqualError(t, err, "volume options are incompatible with type bind") + assert.Error(t, err, "volume options are incompatible with type bind") } func TestConvertVolumeToMountConflictingOptionsTmpfsInBind(t *testing.T) { @@ -100,7 +101,7 @@ func TestConvertVolumeToMountConflictingOptionsTmpfsInBind(t *testing.T) { }, } _, err := convertVolumeToMount(config, volumes{}, namespace) - assert.EqualError(t, err, "tmpfs options are incompatible with type bind") + assert.Error(t, err, "tmpfs options are incompatible with type bind") } func TestConvertVolumeToMountConflictingOptionsBindInTmpfs(t *testing.T) { @@ -114,7 +115,7 @@ func TestConvertVolumeToMountConflictingOptionsBindInTmpfs(t *testing.T) { }, } _, err := convertVolumeToMount(config, volumes{}, namespace) - assert.EqualError(t, err, "bind options are incompatible with type tmpfs") + assert.Error(t, err, "bind options are incompatible with type tmpfs") } func TestConvertVolumeToMountConflictingOptionsVolumeInTmpfs(t *testing.T) { @@ -128,7 +129,7 @@ func TestConvertVolumeToMountConflictingOptionsVolumeInTmpfs(t *testing.T) { }, } _, err := convertVolumeToMount(config, volumes{}, namespace) - assert.EqualError(t, err, "volume options are incompatible with type tmpfs") + assert.Error(t, err, "volume options are incompatible with type tmpfs") } func TestConvertVolumeToMountNamedVolume(t *testing.T) { @@ -173,8 +174,8 @@ func TestConvertVolumeToMountNamedVolume(t *testing.T) { }, } mount, err := convertVolumeToMount(config, stackVolumes, namespace) - assert.NoError(t, err) - assert.Equal(t, expected, mount) + assert.NilError(t, err) + assert.Check(t, is.DeepEqual(expected, mount)) } func TestConvertVolumeToMountNamedVolumeWithNameCustomizd(t *testing.T) { @@ -220,8 +221,8 @@ func TestConvertVolumeToMountNamedVolumeWithNameCustomizd(t *testing.T) { }, } mount, err := convertVolumeToMount(config, stackVolumes, namespace) - assert.NoError(t, err) - assert.Equal(t, expected, mount) + assert.NilError(t, err) + assert.Check(t, is.DeepEqual(expected, mount)) } func TestConvertVolumeToMountNamedVolumeExternal(t *testing.T) { @@ -244,8 +245,8 @@ func TestConvertVolumeToMountNamedVolumeExternal(t *testing.T) { Target: "/foo", } mount, err := convertVolumeToMount(config, stackVolumes, namespace) - assert.NoError(t, err) - assert.Equal(t, expected, mount) + assert.NilError(t, err) + assert.Check(t, is.DeepEqual(expected, mount)) } func TestConvertVolumeToMountNamedVolumeExternalNoCopy(t *testing.T) { @@ -273,8 +274,8 @@ func TestConvertVolumeToMountNamedVolumeExternalNoCopy(t *testing.T) { }, } mount, err := convertVolumeToMount(config, stackVolumes, namespace) - assert.NoError(t, err) - assert.Equal(t, expected, mount) + assert.NilError(t, err) + assert.Check(t, is.DeepEqual(expected, mount)) } func TestConvertVolumeToMountBind(t *testing.T) { @@ -295,8 +296,8 @@ func TestConvertVolumeToMountBind(t *testing.T) { Bind: &composetypes.ServiceVolumeBind{Propagation: "shared"}, } mount, err := convertVolumeToMount(config, stackVolumes, namespace) - assert.NoError(t, err) - assert.Equal(t, expected, mount) + assert.NilError(t, err) + assert.Check(t, is.DeepEqual(expected, mount)) } func TestConvertVolumeToMountVolumeDoesNotExist(t *testing.T) { @@ -308,7 +309,7 @@ func TestConvertVolumeToMountVolumeDoesNotExist(t *testing.T) { ReadOnly: true, } _, err := convertVolumeToMount(config, volumes{}, namespace) - assert.EqualError(t, err, "undefined volume \"unknown\"") + assert.Error(t, err, "undefined volume \"unknown\"") } func TestConvertTmpfsToMountVolume(t *testing.T) { @@ -325,8 +326,8 @@ func TestConvertTmpfsToMountVolume(t *testing.T) { TmpfsOptions: &mount.TmpfsOptions{SizeBytes: 1000}, } mount, err := convertVolumeToMount(config, volumes{}, NewNamespace("foo")) - assert.NoError(t, err) - assert.Equal(t, expected, mount) + assert.NilError(t, err) + assert.Check(t, is.DeepEqual(expected, mount)) } func TestConvertTmpfsToMountVolumeWithSource(t *testing.T) { @@ -340,5 +341,5 @@ func TestConvertTmpfsToMountVolumeWithSource(t *testing.T) { } _, err := convertVolumeToMount(config, volumes{}, NewNamespace("foo")) - assert.EqualError(t, err, "invalid tmpfs source, source must be empty") + assert.Error(t, err, "invalid tmpfs source, source must be empty") } diff --git a/components/cli/cli/compose/interpolation/interpolation_test.go b/components/cli/cli/compose/interpolation/interpolation_test.go index 8f5d50db65..42ea55ff1f 100644 --- a/components/cli/cli/compose/interpolation/interpolation_test.go +++ b/components/cli/cli/compose/interpolation/interpolation_test.go @@ -5,8 +5,9 @@ import ( "strconv" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" "github.com/gotestyourself/gotestyourself/env" - "github.com/stretchr/testify/assert" ) var defaults = map[string]string{ @@ -46,8 +47,8 @@ func TestInterpolate(t *testing.T) { }, } result, err := Interpolate(services, Options{LookupValue: defaultMapping}) - assert.NoError(t, err) - assert.Equal(t, expected, result) + assert.NilError(t, err) + assert.Check(t, is.DeepEqual(expected, result)) } func TestInvalidInterpolation(t *testing.T) { @@ -57,7 +58,7 @@ func TestInvalidInterpolation(t *testing.T) { }, } _, err := Interpolate(services, Options{LookupValue: defaultMapping}) - assert.EqualError(t, err, `invalid interpolation format for servicea.image: "${". You may need to escape any $ with another $.`) + assert.Error(t, err, `invalid interpolation format for servicea.image: "${". You may need to escape any $ with another $.`) } func TestInterpolateWithDefaults(t *testing.T) { @@ -74,8 +75,8 @@ func TestInterpolateWithDefaults(t *testing.T) { }, } result, err := Interpolate(config, Options{}) - assert.NoError(t, err) - assert.Equal(t, expected, result) + assert.NilError(t, err) + assert.Check(t, is.DeepEqual(expected, result)) } func TestInterpolateWithCast(t *testing.T) { @@ -91,13 +92,13 @@ func TestInterpolateWithCast(t *testing.T) { LookupValue: defaultMapping, TypeCastMapping: map[Path]Cast{NewPath(PathMatchAll, "replicas"): toInt}, }) - assert.NoError(t, err) + assert.NilError(t, err) expected := map[string]interface{}{ "foo": map[string]interface{}{ "replicas": 5, }, } - assert.Equal(t, expected, result) + assert.Check(t, is.DeepEqual(expected, result)) } func TestPathMatches(t *testing.T) { @@ -141,6 +142,6 @@ func TestPathMatches(t *testing.T) { }, } for _, testcase := range testcases { - assert.Equal(t, testcase.expected, testcase.path.matches(testcase.pattern)) + assert.Check(t, is.Equal(testcase.expected, testcase.path.matches(testcase.pattern))) } } diff --git a/components/cli/cli/compose/loader/full-struct_test.go b/components/cli/cli/compose/loader/full-struct_test.go index 3f2a587bce..b9afa7ef65 100644 --- a/components/cli/cli/compose/loader/full-struct_test.go +++ b/components/cli/cli/compose/loader/full-struct_test.go @@ -1,6 +1,8 @@ package loader import ( + "fmt" + "path/filepath" "time" "github.com/docker/cli/cli/compose/types" @@ -313,10 +315,10 @@ func services(workingDir, homeDir string) []types.ServiceConfig { {Target: "/var/lib/mysql", Type: "volume"}, {Source: "/opt/data", Target: "/var/lib/mysql", Type: "bind"}, {Source: workingDir, Target: "/code", Type: "bind"}, - {Source: workingDir + "/static", Target: "/var/www/html", Type: "bind"}, + {Source: filepath.Join(workingDir, "static"), Target: "/var/www/html", Type: "bind"}, {Source: homeDir + "/configs", Target: "/etc/configs/", Type: "bind", ReadOnly: true}, {Source: "datavolume", Target: "/var/lib/mysql", Type: "volume"}, - {Source: workingDir + "/opt", Target: "/opt", Consistency: "cached", Type: "bind"}, + {Source: filepath.Join(workingDir, "opt"), Target: "/opt", Consistency: "cached", Type: "bind"}, {Target: "/opt", Type: "tmpfs", Tmpfs: &types.ServiceVolumeTmpfs{ Size: int64(10000), }}, @@ -389,3 +391,313 @@ func volumes() map[string]types.VolumeConfig { }, } } + +func fullExampleYAML(workingDir string) string { + return fmt.Sprintf(`version: "3.6" +services: + foo: + build: + context: ./dir + dockerfile: Dockerfile + args: + foo: bar + labels: + FOO: BAR + cache_from: + - foo + - bar + network: foo + target: foo + cap_add: + - ALL + cap_drop: + - NET_ADMIN + - SYS_ADMIN + cgroup_parent: m-executor-abcd + command: + - bundle + - exec + - thin + - -p + - "3000" + container_name: my-web-container + depends_on: + - db + - redis + deploy: + mode: replicated + replicas: 6 + labels: + FOO: BAR + update_config: + parallelism: 3 + delay: 10s + failure_action: continue + monitor: 1m0s + max_failure_ratio: 0.3 + order: start-first + resources: + limits: + cpus: "0.001" + memory: "52428800" + reservations: + cpus: "0.0001" + memory: "20971520" + generic_resources: + - discrete_resource_spec: + kind: gpu + value: 2 + - discrete_resource_spec: + kind: ssd + value: 1 + restart_policy: + condition: on-failure + delay: 5s + max_attempts: 3 + window: 2m0s + placement: + constraints: + - node=foo + preferences: + - spread: node.labels.az + endpoint_mode: dnsrr + devices: + - /dev/ttyUSB0:/dev/ttyUSB0 + dns: + - 8.8.8.8 + - 9.9.9.9 + dns_search: + - dc1.example.com + - dc2.example.com + domainname: foo.com + entrypoint: + - /code/entrypoint.sh + - -p + - "3000" + environment: + BAR: bar_from_env_file_2 + BAZ: baz_from_service_def + FOO: foo_from_env_file + QUX: qux_from_environment + env_file: + - ./example1.env + - ./example2.env + expose: + - "3000" + - "8000" + external_links: + - redis_1 + - project_db_1:mysql + - project_db_1:postgresql + extra_hosts: + - somehost:162.242.195.82 + - otherhost:50.31.209.229 + hostname: foo + healthcheck: + test: + - CMD-SHELL + - echo "hello world" + timeout: 1s + interval: 10s + retries: 5 + start_period: 15s + image: redis + ipc: host + labels: + com.example.description: Accounting webapp + com.example.empty-label: "" + com.example.number: "42" + links: + - db + - db:database + - redis + logging: + driver: syslog + options: + syslog-address: tcp://192.168.0.42:123 + mac_address: 02:42:ac:11:65:43 + network_mode: container:0cfeab0f748b9a743dc3da582046357c6ef497631c1a016d28d2bf9b4f899f7b + networks: + other-network: + ipv4_address: 172.16.238.10 + ipv6_address: 2001:3984:3989::10 + other-other-network: null + some-network: + aliases: + - alias1 + - alias3 + pid: host + ports: + - mode: ingress + target: 3000 + protocol: tcp + - mode: ingress + target: 3001 + protocol: tcp + - mode: ingress + target: 3002 + protocol: tcp + - mode: ingress + target: 3003 + protocol: tcp + - mode: ingress + target: 3004 + protocol: tcp + - mode: ingress + target: 3005 + protocol: tcp + - mode: ingress + target: 8000 + published: 8000 + protocol: tcp + - mode: ingress + target: 8080 + published: 9090 + protocol: tcp + - mode: ingress + target: 8081 + published: 9091 + protocol: tcp + - mode: ingress + target: 22 + published: 49100 + protocol: tcp + - mode: ingress + target: 8001 + published: 8001 + protocol: tcp + - mode: ingress + target: 5000 + published: 5000 + protocol: tcp + - mode: ingress + target: 5001 + published: 5001 + protocol: tcp + - mode: ingress + target: 5002 + published: 5002 + protocol: tcp + - mode: ingress + target: 5003 + published: 5003 + protocol: tcp + - mode: ingress + target: 5004 + published: 5004 + protocol: tcp + - mode: ingress + target: 5005 + published: 5005 + protocol: tcp + - mode: ingress + target: 5006 + published: 5006 + protocol: tcp + - mode: ingress + target: 5007 + published: 5007 + protocol: tcp + - mode: ingress + target: 5008 + published: 5008 + protocol: tcp + - mode: ingress + target: 5009 + published: 5009 + protocol: tcp + - mode: ingress + target: 5010 + published: 5010 + protocol: tcp + privileged: true + read_only: true + restart: always + security_opt: + - label=level:s0:c100,c200 + - label=type:svirt_apache_t + stdin_open: true + stop_grace_period: 20s + stop_signal: SIGUSR1 + tmpfs: + - /run + - /tmp + tty: true + ulimits: + nofile: + soft: 20000 + hard: 40000 + nproc: 65535 + user: someone + volumes: + - type: volume + target: /var/lib/mysql + - type: bind + source: /opt/data + target: /var/lib/mysql + - type: bind + source: /foo + target: /code + - type: bind + source: %s + target: /var/www/html + - type: bind + source: /bar/configs + target: /etc/configs/ + read_only: true + - type: volume + source: datavolume + target: /var/lib/mysql + - type: bind + source: %s + target: /opt + consistency: cached + - type: tmpfs + target: /opt + tmpfs: + size: 10000 + working_dir: /code +networks: + external-network: + name: external-network + external: true + other-external-network: + name: my-cool-network + external: true + other-network: + driver: overlay + driver_opts: + baz: "1" + foo: bar + ipam: + driver: overlay + config: + - subnet: 172.16.238.0/24 + - subnet: 2001:3984:3989::/64 + some-network: {} +volumes: + another-volume: + name: user_specified_name + driver: vsphere + driver_opts: + baz: "1" + foo: bar + external-volume: + name: external-volume + external: true + external-volume3: + name: this-is-volume3 + external: true + other-external-volume: + name: my-cool-volume + external: true + other-volume: + driver: flocker + driver_opts: + baz: "1" + foo: bar + some-volume: {} +secrets: {} +configs: {} +`, filepath.Join(workingDir, "static"), filepath.Join(workingDir, "opt")) +} diff --git a/components/cli/cli/compose/loader/loader_test.go b/components/cli/cli/compose/loader/loader_test.go index 09d3744e84..4219593725 100644 --- a/components/cli/cli/compose/loader/loader_test.go +++ b/components/cli/cli/compose/loader/loader_test.go @@ -2,17 +2,17 @@ package loader import ( "bytes" - "fmt" "io/ioutil" "os" + "reflect" "sort" "testing" "time" "github.com/docker/cli/cli/compose/types" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" "github.com/sirupsen/logrus" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" ) func buildConfigDetails(source map[string]interface{}, env map[string]string) types.ConfigDetails { @@ -168,17 +168,17 @@ var sampleConfig = types.Config{ func TestParseYAML(t *testing.T) { dict, err := ParseYAML([]byte(sampleYAML)) - require.NoError(t, err) - assert.Equal(t, sampleDict, dict) + assert.NilError(t, err) + assert.Check(t, is.DeepEqual(sampleDict, dict)) } func TestLoad(t *testing.T) { actual, err := Load(buildConfigDetails(sampleDict, nil)) - require.NoError(t, err) - assert.Equal(t, sampleConfig.Version, actual.Version) - assert.Equal(t, serviceSort(sampleConfig.Services), serviceSort(actual.Services)) - assert.Equal(t, sampleConfig.Networks, actual.Networks) - assert.Equal(t, sampleConfig.Volumes, actual.Volumes) + assert.NilError(t, err) + assert.Check(t, is.Equal(sampleConfig.Version, actual.Version)) + assert.Check(t, is.DeepEqual(serviceSort(sampleConfig.Services), serviceSort(actual.Services))) + assert.Check(t, is.DeepEqual(sampleConfig.Networks, actual.Networks)) + assert.Check(t, is.DeepEqual(sampleConfig.Volumes, actual.Volumes)) } func TestLoadV31(t *testing.T) { @@ -192,9 +192,9 @@ secrets: super: external: true `) - require.NoError(t, err) - assert.Len(t, actual.Services, 1) - assert.Len(t, actual.Secrets, 1) + assert.NilError(t, err) + assert.Check(t, is.Len(actual.Services, 1)) + assert.Check(t, is.Len(actual.Secrets, 1)) } func TestLoadV33(t *testing.T) { @@ -210,32 +210,29 @@ configs: super: external: true `) - require.NoError(t, err) - require.Len(t, actual.Services, 1) - assert.Equal(t, actual.Services[0].CredentialSpec.File, "/foo") - require.Len(t, actual.Configs, 1) + assert.NilError(t, err) + assert.Assert(t, is.Len(actual.Services, 1)) + assert.Check(t, is.Equal(actual.Services[0].CredentialSpec.File, "/foo")) + assert.Assert(t, is.Len(actual.Configs, 1)) } func TestParseAndLoad(t *testing.T) { actual, err := loadYAML(sampleYAML) - require.NoError(t, err) - assert.Equal(t, serviceSort(sampleConfig.Services), serviceSort(actual.Services)) - assert.Equal(t, sampleConfig.Networks, actual.Networks) - assert.Equal(t, sampleConfig.Volumes, actual.Volumes) + assert.NilError(t, err) + assert.Check(t, is.DeepEqual(serviceSort(sampleConfig.Services), serviceSort(actual.Services))) + assert.Check(t, is.DeepEqual(sampleConfig.Networks, actual.Networks)) + assert.Check(t, is.DeepEqual(sampleConfig.Volumes, actual.Volumes)) } func TestInvalidTopLevelObjectType(t *testing.T) { _, err := loadYAML("1") - require.Error(t, err) - assert.Contains(t, err.Error(), "Top-level object must be a mapping") + assert.ErrorContains(t, err, "Top-level object must be a mapping") _, err = loadYAML("\"hello\"") - require.Error(t, err) - assert.Contains(t, err.Error(), "Top-level object must be a mapping") + assert.ErrorContains(t, err, "Top-level object must be a mapping") _, err = loadYAML("[\"hello\"]") - require.Error(t, err) - assert.Contains(t, err.Error(), "Top-level object must be a mapping") + assert.ErrorContains(t, err, "Top-level object must be a mapping") } func TestNonStringKeys(t *testing.T) { @@ -245,8 +242,7 @@ version: "3" foo: image: busybox `) - require.Error(t, err) - assert.Contains(t, err.Error(), "Non-string key at top level: 123") + assert.ErrorContains(t, err, "Non-string key at top level: 123") _, err = loadYAML(` version: "3" @@ -256,8 +252,7 @@ services: 123: image: busybox `) - require.Error(t, err) - assert.Contains(t, err.Error(), "Non-string key in services: 123") + assert.ErrorContains(t, err, "Non-string key in services: 123") _, err = loadYAML(` version: "3" @@ -270,8 +265,7 @@ networks: config: - 123: oh dear `) - require.Error(t, err) - assert.Contains(t, err.Error(), "Non-string key in networks.default.ipam.config[0]: 123") + assert.ErrorContains(t, err, "Non-string key in networks.default.ipam.config[0]: 123") _, err = loadYAML(` version: "3" @@ -281,8 +275,7 @@ services: environment: 1: FOO `) - require.Error(t, err) - assert.Contains(t, err.Error(), "Non-string key in services.dict-env.environment: 1") + assert.ErrorContains(t, err, "Non-string key in services.dict-env.environment: 1") } func TestSupportedVersion(t *testing.T) { @@ -292,7 +285,7 @@ services: foo: image: busybox `) - require.NoError(t, err) + assert.NilError(t, err) _, err = loadYAML(` version: "3.0" @@ -300,7 +293,7 @@ services: foo: image: busybox `) - require.NoError(t, err) + assert.NilError(t, err) } func TestUnsupportedVersion(t *testing.T) { @@ -310,8 +303,7 @@ services: foo: image: busybox `) - require.Error(t, err) - assert.Contains(t, err.Error(), "version") + assert.ErrorContains(t, err, "version") _, err = loadYAML(` version: "2.0" @@ -319,8 +311,7 @@ services: foo: image: busybox `) - require.Error(t, err) - assert.Contains(t, err.Error(), "version") + assert.ErrorContains(t, err, "version") } func TestInvalidVersion(t *testing.T) { @@ -330,8 +321,7 @@ services: foo: image: busybox `) - require.Error(t, err) - assert.Contains(t, err.Error(), "version must be a string") + assert.ErrorContains(t, err, "version must be a string") } func TestV1Unsupported(t *testing.T) { @@ -339,7 +329,7 @@ func TestV1Unsupported(t *testing.T) { foo: image: busybox `) - assert.Error(t, err) + assert.ErrorContains(t, err, "unsupported Compose file version: 1.0") } func TestNonMappingObject(t *testing.T) { @@ -349,16 +339,14 @@ services: - foo: image: busybox `) - require.Error(t, err) - assert.Contains(t, err.Error(), "services must be a mapping") + assert.ErrorContains(t, err, "services must be a mapping") _, err = loadYAML(` version: "3" services: foo: busybox `) - require.Error(t, err) - assert.Contains(t, err.Error(), "services.foo must be a mapping") + assert.ErrorContains(t, err, "services.foo must be a mapping") _, err = loadYAML(` version: "3" @@ -366,16 +354,14 @@ networks: - default: driver: bridge `) - require.Error(t, err) - assert.Contains(t, err.Error(), "networks must be a mapping") + assert.ErrorContains(t, err, "networks must be a mapping") _, err = loadYAML(` version: "3" networks: default: bridge `) - require.Error(t, err) - assert.Contains(t, err.Error(), "networks.default must be a mapping") + assert.ErrorContains(t, err, "networks.default must be a mapping") _, err = loadYAML(` version: "3" @@ -383,16 +369,14 @@ volumes: - data: driver: local `) - require.Error(t, err) - assert.Contains(t, err.Error(), "volumes must be a mapping") + assert.ErrorContains(t, err, "volumes must be a mapping") _, err = loadYAML(` version: "3" volumes: data: local `) - require.Error(t, err) - assert.Contains(t, err.Error(), "volumes.data must be a mapping") + assert.ErrorContains(t, err, "volumes.data must be a mapping") } func TestNonStringImage(t *testing.T) { @@ -402,8 +386,7 @@ services: foo: image: ["busybox", "latest"] `) - require.Error(t, err) - assert.Contains(t, err.Error(), "services.foo.image must be a string") + assert.ErrorContains(t, err, "services.foo.image must be a string") } func TestLoadWithEnvironment(t *testing.T) { @@ -427,7 +410,7 @@ services: - QUX= - QUUX `, map[string]string{"QUX": "qux"}) - assert.NoError(t, err) + assert.NilError(t, err) expected := types.MappingWithEquals{ "FOO": strPtr("1"), @@ -437,10 +420,10 @@ services: "QUUX": nil, } - assert.Equal(t, 2, len(config.Services)) + assert.Check(t, is.Equal(2, len(config.Services))) for _, service := range config.Services { - assert.Equal(t, expected, service.Environment) + assert.Check(t, is.DeepEqual(expected, service.Environment)) } } @@ -453,8 +436,7 @@ services: environment: FOO: ["1"] `) - require.Error(t, err) - assert.Contains(t, err.Error(), "services.dict-env.environment.FOO must be a string, number or null") + assert.ErrorContains(t, err, "services.dict-env.environment.FOO must be a string, number or null") } func TestInvalidEnvironmentObject(t *testing.T) { @@ -465,8 +447,7 @@ services: image: busybox environment: "FOO=1" `) - require.Error(t, err) - assert.Contains(t, err.Error(), "services.dict-env.environment must be a mapping") + assert.ErrorContains(t, err, "services.dict-env.environment must be a mapping") } func TestLoadWithEnvironmentInterpolation(t *testing.T) { @@ -492,7 +473,7 @@ volumes: "FOO": "foo", }) - require.NoError(t, err) + assert.NilError(t, err) expectedLabels := types.Labels{ "home1": home, @@ -501,9 +482,9 @@ volumes: "default": "default", } - assert.Equal(t, expectedLabels, config.Services[0].Labels) - assert.Equal(t, home, config.Networks["test"].Driver) - assert.Equal(t, home, config.Volumes["test"].Driver) + assert.Check(t, is.DeepEqual(expectedLabels, config.Services[0].Labels)) + assert.Check(t, is.Equal(home, config.Networks["test"].Driver)) + assert.Check(t, is.Equal(home, config.Volumes["test"].Driver)) } func TestLoadWithInterpolationCastFull(t *testing.T) { @@ -564,7 +545,7 @@ networks: attachable: $thebool `)) - require.NoError(t, err) + assert.NilError(t, err) env := map[string]string{ "theint": "555", "thefloat": "3.14", @@ -572,7 +553,7 @@ networks: } config, err := Load(buildConfigDetails(dict, env)) - require.NoError(t, err) + assert.NilError(t, err) expected := &types.Config{ Filename: "filename.yml", Version: "3.4", @@ -648,7 +629,7 @@ networks: }, } - assert.Equal(t, expected, config) + assert.Check(t, is.DeepEqual(expected, config)) } func TestUnsupportedProperties(t *testing.T) { @@ -667,15 +648,15 @@ services: build: context: ./db `)) - require.NoError(t, err) + assert.NilError(t, err) configDetails := buildConfigDetails(dict, nil) _, err = Load(configDetails) - require.NoError(t, err) + assert.NilError(t, err) unsupported := GetUnsupportedProperties(dict) - assert.Equal(t, []string{"build", "links", "pid"}, unsupported) + assert.Check(t, is.DeepEqual([]string{"build", "links", "pid"}, unsupported)) } func TestBuildProperties(t *testing.T) { @@ -692,10 +673,10 @@ services: build: context: ./db `)) - require.NoError(t, err) + assert.NilError(t, err) configDetails := buildConfigDetails(dict, nil) _, err = Load(configDetails) - require.NoError(t, err) + assert.NilError(t, err) } func TestDeprecatedProperties(t *testing.T) { @@ -710,17 +691,17 @@ services: container_name: db expose: ["5434"] `)) - require.NoError(t, err) + assert.NilError(t, err) configDetails := buildConfigDetails(dict, nil) _, err = Load(configDetails) - require.NoError(t, err) + assert.NilError(t, err) deprecated := GetDeprecatedProperties(dict) - assert.Len(t, deprecated, 2) - assert.Contains(t, deprecated, "container_name") - assert.Contains(t, deprecated, "expose") + assert.Check(t, is.Len(deprecated, 2)) + assert.Check(t, is.Contains(deprecated, "container_name")) + assert.Check(t, is.Contains(deprecated, "expose")) } func TestForbiddenProperties(t *testing.T) { @@ -737,14 +718,12 @@ services: service: foo `) - require.Error(t, err) - assert.IsType(t, &ForbiddenPropertiesError{}, err) - fmt.Println(err) - forbidden := err.(*ForbiddenPropertiesError).Properties + assert.ErrorType(t, err, reflect.TypeOf(&ForbiddenPropertiesError{})) - assert.Len(t, forbidden, 2) - assert.Contains(t, forbidden, "volume_driver") - assert.Contains(t, forbidden, "extends") + props := err.(*ForbiddenPropertiesError).Properties + assert.Check(t, is.Len(props, 2)) + assert.Check(t, is.Contains(props, "volume_driver")) + assert.Check(t, is.Contains(props, "extends")) } func TestInvalidResource(t *testing.T) { @@ -758,8 +737,7 @@ func TestInvalidResource(t *testing.T) { impossible: x: 1 `) - require.Error(t, err) - assert.Contains(t, err.Error(), "Additional property impossible is not allowed") + assert.ErrorContains(t, err, "Additional property impossible is not allowed") } func TestInvalidExternalAndDriverCombination(t *testing.T) { @@ -771,9 +749,8 @@ volumes: driver: foobar `) - require.Error(t, err) - assert.Contains(t, err.Error(), "conflicting parameters \"external\" and \"driver\" specified for volume") - assert.Contains(t, err.Error(), "external_volume") + assert.ErrorContains(t, err, "conflicting parameters \"external\" and \"driver\" specified for volume") + assert.ErrorContains(t, err, "external_volume") } func TestInvalidExternalAndDirverOptsCombination(t *testing.T) { @@ -786,9 +763,8 @@ volumes: beep: boop `) - require.Error(t, err) - assert.Contains(t, err.Error(), "conflicting parameters \"external\" and \"driver_opts\" specified for volume") - assert.Contains(t, err.Error(), "external_volume") + assert.ErrorContains(t, err, "conflicting parameters \"external\" and \"driver_opts\" specified for volume") + assert.ErrorContains(t, err, "external_volume") } func TestInvalidExternalAndLabelsCombination(t *testing.T) { @@ -801,9 +777,8 @@ volumes: - beep=boop `) - require.Error(t, err) - assert.Contains(t, err.Error(), "conflicting parameters \"external\" and \"labels\" specified for volume") - assert.Contains(t, err.Error(), "external_volume") + assert.ErrorContains(t, err, "conflicting parameters \"external\" and \"labels\" specified for volume") + assert.ErrorContains(t, err, "external_volume") } func TestLoadVolumeInvalidExternalNameAndNameCombination(t *testing.T) { @@ -816,9 +791,8 @@ volumes: name: external_name `) - require.Error(t, err) - assert.Contains(t, err.Error(), "volume.external.name and volume.name conflict; only use volume.name") - assert.Contains(t, err.Error(), "external_volume") + assert.ErrorContains(t, err, "volume.external.name and volume.name conflict; only use volume.name") + assert.ErrorContains(t, err, "external_volume") } func durationPtr(value time.Duration) *time.Duration { @@ -835,21 +809,21 @@ func uint32Ptr(value uint32) *uint32 { func TestFullExample(t *testing.T) { bytes, err := ioutil.ReadFile("full-example.yml") - require.NoError(t, err) + assert.NilError(t, err) homeDir := "/home/foo" env := map[string]string{"HOME": homeDir, "QUX": "qux_from_environment"} config, err := loadYAMLWithEnv(string(bytes), env) - require.NoError(t, err) + assert.NilError(t, err) workingDir, err := os.Getwd() - require.NoError(t, err) + assert.NilError(t, err) expectedConfig := fullExampleConfig(workingDir, homeDir) - assert.Equal(t, expectedConfig.Services, config.Services) - assert.Equal(t, expectedConfig.Networks, config.Networks) - assert.Equal(t, expectedConfig.Volumes, config.Volumes) + assert.Check(t, is.DeepEqual(expectedConfig.Services, config.Services)) + assert.Check(t, is.DeepEqual(expectedConfig.Networks, config.Networks)) + assert.Check(t, is.DeepEqual(expectedConfig.Volumes, config.Volumes)) } func TestLoadTmpfsVolume(t *testing.T) { @@ -864,7 +838,7 @@ services: tmpfs: size: 10000 `) - require.NoError(t, err) + assert.NilError(t, err) expected := types.ServiceVolumeConfig{ Target: "/app", @@ -874,9 +848,9 @@ services: }, } - require.Len(t, config.Services, 1) - assert.Len(t, config.Services[0].Volumes, 1) - assert.Equal(t, expected, config.Services[0].Volumes[0]) + assert.Assert(t, is.Len(config.Services, 1)) + assert.Check(t, is.Len(config.Services[0].Volumes, 1)) + assert.Check(t, is.DeepEqual(expected, config.Services[0].Volumes[0])) } func TestLoadTmpfsVolumeAdditionalPropertyNotAllowed(t *testing.T) { @@ -891,8 +865,7 @@ services: tmpfs: size: 10000 `) - require.Error(t, err) - assert.Contains(t, err.Error(), "services.tmpfs.volumes.0 Additional property tmpfs is not allowed") + assert.ErrorContains(t, err, "services.tmpfs.volumes.0 Additional property tmpfs is not allowed") } func TestLoadBindMountSourceMustNotBeEmpty(t *testing.T) { @@ -905,7 +878,7 @@ services: - type: bind target: /app `) - require.EqualError(t, err, `invalid mount config for type "bind": field Source must not be empty`) + assert.Error(t, err, `invalid mount config for type "bind": field Source must not be empty`) } func TestLoadBindMountWithSource(t *testing.T) { @@ -919,10 +892,10 @@ services: target: /app source: "." `) - require.NoError(t, err) + assert.NilError(t, err) workingDir, err := os.Getwd() - require.NoError(t, err) + assert.NilError(t, err) expected := types.ServiceVolumeConfig{ Type: "bind", @@ -930,9 +903,9 @@ services: Target: "/app", } - require.Len(t, config.Services, 1) - assert.Len(t, config.Services[0].Volumes, 1) - assert.Equal(t, expected, config.Services[0].Volumes[0]) + assert.Assert(t, is.Len(config.Services, 1)) + assert.Check(t, is.Len(config.Services[0].Volumes, 1)) + assert.Check(t, is.DeepEqual(expected, config.Services[0].Volumes[0])) } func TestLoadTmpfsVolumeSizeCanBeZero(t *testing.T) { @@ -947,7 +920,7 @@ services: tmpfs: size: 0 `) - require.NoError(t, err) + assert.NilError(t, err) expected := types.ServiceVolumeConfig{ Target: "/app", @@ -955,9 +928,9 @@ services: Tmpfs: &types.ServiceVolumeTmpfs{}, } - require.Len(t, config.Services, 1) - assert.Len(t, config.Services[0].Volumes, 1) - assert.Equal(t, expected, config.Services[0].Volumes[0]) + assert.Assert(t, is.Len(config.Services, 1)) + assert.Check(t, is.Len(config.Services[0].Volumes, 1)) + assert.Check(t, is.DeepEqual(expected, config.Services[0].Volumes[0])) } func TestLoadTmpfsVolumeSizeMustBeGTEQZero(t *testing.T) { @@ -972,8 +945,7 @@ services: tmpfs: size: -1 `) - require.Error(t, err) - assert.Contains(t, err.Error(), "services.tmpfs.volumes.0.tmpfs.size Must be greater than or equal to 0") + assert.ErrorContains(t, err, "services.tmpfs.volumes.0.tmpfs.size Must be greater than or equal to 0") } func TestLoadTmpfsVolumeSizeMustBeInteger(t *testing.T) { @@ -988,8 +960,7 @@ services: tmpfs: size: 0.0001 `) - require.Error(t, err) - assert.Contains(t, err.Error(), "services.tmpfs.volumes.0.tmpfs.size must be a integer") + assert.ErrorContains(t, err, "services.tmpfs.volumes.0.tmpfs.size must be a integer") } func serviceSort(services []types.ServiceConfig) []types.ServiceConfig { @@ -1013,7 +984,7 @@ networks: mynet2: driver: bridge `) - require.NoError(t, err) + assert.NilError(t, err) expected := map[string]types.NetworkConfig{ "mynet1": { @@ -1026,7 +997,7 @@ networks: }, } - assert.Equal(t, expected, config.Networks) + assert.Check(t, is.DeepEqual(expected, config.Networks)) } func TestLoadExpandedPortFormat(t *testing.T) { @@ -1047,7 +1018,7 @@ services: target: 22 published: 10022 `) - require.NoError(t, err) + assert.NilError(t, err) expected := []types.ServicePortConfig{ { @@ -1110,8 +1081,8 @@ services: }, } - assert.Len(t, config.Services, 1) - assert.Equal(t, expected, config.Services[0].Ports) + assert.Check(t, is.Len(config.Services, 1)) + assert.Check(t, is.DeepEqual(expected, config.Services[0].Ports)) } func TestLoadExpandedMountFormat(t *testing.T) { @@ -1128,7 +1099,7 @@ services: volumes: foo: {} `) - require.NoError(t, err) + assert.NilError(t, err) expected := types.ServiceVolumeConfig{ Type: "volume", @@ -1137,9 +1108,9 @@ volumes: ReadOnly: true, } - require.Len(t, config.Services, 1) - assert.Len(t, config.Services[0].Volumes, 1) - assert.Equal(t, expected, config.Services[0].Volumes[0]) + assert.Assert(t, is.Len(config.Services, 1)) + assert.Check(t, is.Len(config.Services[0].Volumes, 1)) + assert.Check(t, is.DeepEqual(expected, config.Services[0].Volumes[0])) } func TestLoadExtraHostsMap(t *testing.T) { @@ -1152,15 +1123,15 @@ services: "zulu": "162.242.195.82" "alpha": "50.31.209.229" `) - require.NoError(t, err) + assert.NilError(t, err) expected := types.HostsList{ "alpha:50.31.209.229", "zulu:162.242.195.82", } - require.Len(t, config.Services, 1) - assert.Equal(t, expected, config.Services[0].ExtraHosts) + assert.Assert(t, is.Len(config.Services, 1)) + assert.Check(t, is.DeepEqual(expected, config.Services[0].ExtraHosts)) } func TestLoadExtraHostsList(t *testing.T) { @@ -1174,7 +1145,7 @@ services: - "alpha:50.31.209.229" - "zulu:ff02::1" `) - require.NoError(t, err) + assert.NilError(t, err) expected := types.HostsList{ "zulu:162.242.195.82", @@ -1182,8 +1153,8 @@ services: "zulu:ff02::1", } - require.Len(t, config.Services, 1) - assert.Equal(t, expected, config.Services[0].ExtraHosts) + assert.Assert(t, is.Len(config.Services, 1)) + assert.Check(t, is.DeepEqual(expected, config.Services[0].ExtraHosts)) } func TestLoadVolumesWarnOnDeprecatedExternalNameVersion34(t *testing.T) { @@ -1198,15 +1169,15 @@ func TestLoadVolumesWarnOnDeprecatedExternalNameVersion34(t *testing.T) { }, } volumes, err := LoadVolumes(source, "3.4") - require.NoError(t, err) + assert.NilError(t, err) expected := map[string]types.VolumeConfig{ "foo": { Name: "oops", External: types.External{External: true}, }, } - assert.Equal(t, expected, volumes) - assert.Contains(t, buf.String(), "volume.external.name is deprecated") + assert.Check(t, is.DeepEqual(expected, volumes)) + assert.Check(t, is.Contains(buf.String(), "volume.external.name is deprecated")) } @@ -1229,15 +1200,15 @@ func TestLoadVolumesWarnOnDeprecatedExternalNameVersion33(t *testing.T) { }, } volumes, err := LoadVolumes(source, "3.3") - require.NoError(t, err) + assert.NilError(t, err) expected := map[string]types.VolumeConfig{ "foo": { Name: "oops", External: types.External{External: true}, }, } - assert.Equal(t, expected, volumes) - assert.Equal(t, "", buf.String()) + assert.Check(t, is.DeepEqual(expected, volumes)) + assert.Check(t, is.Equal("", buf.String())) } func TestLoadV35(t *testing.T) { @@ -1262,11 +1233,11 @@ secrets: name: barqux file: ./full-example.yml `) - require.NoError(t, err) - assert.Len(t, actual.Services, 1) - assert.Len(t, actual.Secrets, 2) - assert.Len(t, actual.Configs, 2) - assert.Equal(t, "process", actual.Services[0].Isolation) + assert.NilError(t, err) + assert.Check(t, is.Len(actual.Services, 1)) + assert.Check(t, is.Len(actual.Secrets, 2)) + assert.Check(t, is.Len(actual.Configs, 2)) + assert.Check(t, is.Equal("process", actual.Services[0].Isolation)) } func TestLoadV35InvalidIsolation(t *testing.T) { @@ -1281,9 +1252,9 @@ configs: super: external: true `) - require.NoError(t, err) - require.Len(t, actual.Services, 1) - assert.Equal(t, "invalid", actual.Services[0].Isolation) + assert.NilError(t, err) + assert.Assert(t, is.Len(actual.Services, 1)) + assert.Check(t, is.Equal("invalid", actual.Services[0].Isolation)) } func TestLoadSecretInvalidExternalNameAndNameCombination(t *testing.T) { @@ -1296,9 +1267,8 @@ secrets: name: external_name `) - require.Error(t, err) - assert.Contains(t, err.Error(), "secret.external.name and secret.name conflict; only use secret.name") - assert.Contains(t, err.Error(), "external_secret") + assert.ErrorContains(t, err, "secret.external.name and secret.name conflict; only use secret.name") + assert.ErrorContains(t, err, "external_secret") } func TestLoadSecretsWarnOnDeprecatedExternalNameVersion35(t *testing.T) { @@ -1316,15 +1286,15 @@ func TestLoadSecretsWarnOnDeprecatedExternalNameVersion35(t *testing.T) { Version: "3.5", } secrets, err := LoadSecrets(source, details) - require.NoError(t, err) + assert.NilError(t, err) expected := map[string]types.SecretConfig{ "foo": { Name: "oops", External: types.External{External: true}, }, } - assert.Equal(t, expected, secrets) - assert.Contains(t, buf.String(), "secret.external.name is deprecated") + assert.Check(t, is.DeepEqual(expected, secrets)) + assert.Check(t, is.Contains(buf.String(), "secret.external.name is deprecated")) } func TestLoadNetworksWarnOnDeprecatedExternalNameVersion35(t *testing.T) { @@ -1339,15 +1309,15 @@ func TestLoadNetworksWarnOnDeprecatedExternalNameVersion35(t *testing.T) { }, } networks, err := LoadNetworks(source, "3.5") - require.NoError(t, err) + assert.NilError(t, err) expected := map[string]types.NetworkConfig{ "foo": { Name: "oops", External: types.External{External: true}, }, } - assert.Equal(t, expected, networks) - assert.Contains(t, buf.String(), "network.external.name is deprecated") + assert.Check(t, is.DeepEqual(expected, networks)) + assert.Check(t, is.Contains(buf.String(), "network.external.name is deprecated")) } @@ -1363,15 +1333,15 @@ func TestLoadNetworksWarnOnDeprecatedExternalNameVersion34(t *testing.T) { }, } networks, err := LoadNetworks(source, "3.4") - require.NoError(t, err) + assert.NilError(t, err) expected := map[string]types.NetworkConfig{ "foo": { Name: "oops", External: types.External{External: true}, }, } - assert.Equal(t, expected, networks) - assert.Equal(t, "", buf.String()) + assert.Check(t, is.DeepEqual(expected, networks)) + assert.Check(t, is.Equal("", buf.String())) } func TestLoadNetworkInvalidExternalNameAndNameCombination(t *testing.T) { @@ -1384,7 +1354,6 @@ networks: name: external_name `) - require.Error(t, err) - assert.Contains(t, err.Error(), "network.external.name and network.name conflict; only use network.name") - assert.Contains(t, err.Error(), "foo") + assert.ErrorContains(t, err, "network.external.name and network.name conflict; only use network.name") + assert.ErrorContains(t, err, "foo") } diff --git a/components/cli/cli/compose/loader/merge_test.go b/components/cli/cli/compose/loader/merge_test.go index d9e8599359..b039231723 100644 --- a/components/cli/cli/compose/loader/merge_test.go +++ b/components/cli/cli/compose/loader/merge_test.go @@ -4,7 +4,7 @@ import ( "testing" "github.com/docker/cli/cli/compose/types" - "github.com/stretchr/testify/require" + "github.com/gotestyourself/gotestyourself/assert" ) func TestLoadTwoDifferentVersion(t *testing.T) { @@ -19,7 +19,7 @@ func TestLoadTwoDifferentVersion(t *testing.T) { }, } _, err := Load(configDetails) - require.EqualError(t, err, "version mismatched between two composefiles : 3.1 and 3.4") + assert.Error(t, err, "version mismatched between two composefiles : 3.1 and 3.4") } func TestLoadLogging(t *testing.T) { @@ -204,8 +204,8 @@ func TestLoadLogging(t *testing.T) { }, } config, err := Load(configDetails) - require.NoError(t, err) - require.Equal(t, &types.Config{ + assert.NilError(t, err) + assert.DeepEqual(t, &types.Config{ Filename: "base.yml", Version: "3.4", Services: []types.ServiceConfig{ @@ -323,8 +323,8 @@ func TestLoadMultipleServicePorts(t *testing.T) { }, } config, err := Load(configDetails) - require.NoError(t, err) - require.Equal(t, &types.Config{ + assert.NilError(t, err) + assert.DeepEqual(t, &types.Config{ Filename: "base.yml", Version: "3.4", Services: []types.ServiceConfig{ @@ -449,8 +449,8 @@ func TestLoadMultipleSecretsConfig(t *testing.T) { }, } config, err := Load(configDetails) - require.NoError(t, err) - require.Equal(t, &types.Config{ + assert.NilError(t, err) + assert.DeepEqual(t, &types.Config{ Filename: "base.yml", Version: "3.4", Services: []types.ServiceConfig{ @@ -575,8 +575,8 @@ func TestLoadMultipleConfigobjsConfig(t *testing.T) { }, } config, err := Load(configDetails) - require.NoError(t, err) - require.Equal(t, &types.Config{ + assert.NilError(t, err) + assert.DeepEqual(t, &types.Config{ Filename: "base.yml", Version: "3.4", Services: []types.ServiceConfig{ @@ -691,8 +691,8 @@ func TestLoadMultipleUlimits(t *testing.T) { }, } config, err := Load(configDetails) - require.NoError(t, err) - require.Equal(t, &types.Config{ + assert.NilError(t, err) + assert.DeepEqual(t, &types.Config{ Filename: "base.yml", Version: "3.4", Services: []types.ServiceConfig{ @@ -810,8 +810,8 @@ func TestLoadMultipleNetworks(t *testing.T) { }, } config, err := Load(configDetails) - require.NoError(t, err) - require.Equal(t, &types.Config{ + assert.NilError(t, err) + assert.DeepEqual(t, &types.Config{ Filename: "base.yml", Version: "3.4", Services: []types.ServiceConfig{ @@ -898,8 +898,8 @@ func TestLoadMultipleConfigs(t *testing.T) { }, } config, err := Load(configDetails) - require.NoError(t, err) - require.Equal(t, &types.Config{ + assert.NilError(t, err) + assert.DeepEqual(t, &types.Config{ Filename: "base.yml", Version: "3.4", Services: []types.ServiceConfig{ diff --git a/components/cli/cli/compose/loader/types_test.go b/components/cli/cli/compose/loader/types_test.go index f27539f88f..75ea835bd7 100644 --- a/components/cli/cli/compose/loader/types_test.go +++ b/components/cli/cli/compose/loader/types_test.go @@ -3,327 +3,24 @@ package loader import ( "testing" - "github.com/stretchr/testify/assert" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" yaml "gopkg.in/yaml.v2" ) func TestMarshallConfig(t *testing.T) { - cfg := fullExampleConfig("/foo", "/bar") - expected := `version: "3.6" -services: - foo: - build: - context: ./dir - dockerfile: Dockerfile - args: - foo: bar - labels: - FOO: BAR - cache_from: - - foo - - bar - network: foo - target: foo - cap_add: - - ALL - cap_drop: - - NET_ADMIN - - SYS_ADMIN - cgroup_parent: m-executor-abcd - command: - - bundle - - exec - - thin - - -p - - "3000" - container_name: my-web-container - depends_on: - - db - - redis - deploy: - mode: replicated - replicas: 6 - labels: - FOO: BAR - update_config: - parallelism: 3 - delay: 10s - failure_action: continue - monitor: 1m0s - max_failure_ratio: 0.3 - order: start-first - resources: - limits: - cpus: "0.001" - memory: "52428800" - reservations: - cpus: "0.0001" - memory: "20971520" - generic_resources: - - discrete_resource_spec: - kind: gpu - value: 2 - - discrete_resource_spec: - kind: ssd - value: 1 - restart_policy: - condition: on-failure - delay: 5s - max_attempts: 3 - window: 2m0s - placement: - constraints: - - node=foo - preferences: - - spread: node.labels.az - endpoint_mode: dnsrr - devices: - - /dev/ttyUSB0:/dev/ttyUSB0 - dns: - - 8.8.8.8 - - 9.9.9.9 - dns_search: - - dc1.example.com - - dc2.example.com - domainname: foo.com - entrypoint: - - /code/entrypoint.sh - - -p - - "3000" - environment: - BAR: bar_from_env_file_2 - BAZ: baz_from_service_def - FOO: foo_from_env_file - QUX: qux_from_environment - env_file: - - ./example1.env - - ./example2.env - expose: - - "3000" - - "8000" - external_links: - - redis_1 - - project_db_1:mysql - - project_db_1:postgresql - extra_hosts: - - somehost:162.242.195.82 - - otherhost:50.31.209.229 - hostname: foo - healthcheck: - test: - - CMD-SHELL - - echo "hello world" - timeout: 1s - interval: 10s - retries: 5 - start_period: 15s - image: redis - ipc: host - labels: - com.example.description: Accounting webapp - com.example.empty-label: "" - com.example.number: "42" - links: - - db - - db:database - - redis - logging: - driver: syslog - options: - syslog-address: tcp://192.168.0.42:123 - mac_address: 02:42:ac:11:65:43 - network_mode: container:0cfeab0f748b9a743dc3da582046357c6ef497631c1a016d28d2bf9b4f899f7b - networks: - other-network: - ipv4_address: 172.16.238.10 - ipv6_address: 2001:3984:3989::10 - other-other-network: null - some-network: - aliases: - - alias1 - - alias3 - pid: host - ports: - - mode: ingress - target: 3000 - protocol: tcp - - mode: ingress - target: 3001 - protocol: tcp - - mode: ingress - target: 3002 - protocol: tcp - - mode: ingress - target: 3003 - protocol: tcp - - mode: ingress - target: 3004 - protocol: tcp - - mode: ingress - target: 3005 - protocol: tcp - - mode: ingress - target: 8000 - published: 8000 - protocol: tcp - - mode: ingress - target: 8080 - published: 9090 - protocol: tcp - - mode: ingress - target: 8081 - published: 9091 - protocol: tcp - - mode: ingress - target: 22 - published: 49100 - protocol: tcp - - mode: ingress - target: 8001 - published: 8001 - protocol: tcp - - mode: ingress - target: 5000 - published: 5000 - protocol: tcp - - mode: ingress - target: 5001 - published: 5001 - protocol: tcp - - mode: ingress - target: 5002 - published: 5002 - protocol: tcp - - mode: ingress - target: 5003 - published: 5003 - protocol: tcp - - mode: ingress - target: 5004 - published: 5004 - protocol: tcp - - mode: ingress - target: 5005 - published: 5005 - protocol: tcp - - mode: ingress - target: 5006 - published: 5006 - protocol: tcp - - mode: ingress - target: 5007 - published: 5007 - protocol: tcp - - mode: ingress - target: 5008 - published: 5008 - protocol: tcp - - mode: ingress - target: 5009 - published: 5009 - protocol: tcp - - mode: ingress - target: 5010 - published: 5010 - protocol: tcp - privileged: true - read_only: true - restart: always - security_opt: - - label=level:s0:c100,c200 - - label=type:svirt_apache_t - stdin_open: true - stop_grace_period: 20s - stop_signal: SIGUSR1 - tmpfs: - - /run - - /tmp - tty: true - ulimits: - nofile: - soft: 20000 - hard: 40000 - nproc: 65535 - user: someone - volumes: - - type: volume - target: /var/lib/mysql - - type: bind - source: /opt/data - target: /var/lib/mysql - - type: bind - source: /foo - target: /code - - type: bind - source: /foo/static - target: /var/www/html - - type: bind - source: /bar/configs - target: /etc/configs/ - read_only: true - - type: volume - source: datavolume - target: /var/lib/mysql - - type: bind - source: /foo/opt - target: /opt - consistency: cached - - type: tmpfs - target: /opt - tmpfs: - size: 10000 - working_dir: /code -networks: - external-network: - name: external-network - external: true - other-external-network: - name: my-cool-network - external: true - other-network: - driver: overlay - driver_opts: - baz: "1" - foo: bar - ipam: - driver: overlay - config: - - subnet: 172.16.238.0/24 - - subnet: 2001:3984:3989::/64 - some-network: {} -volumes: - another-volume: - name: user_specified_name - driver: vsphere - driver_opts: - baz: "1" - foo: bar - external-volume: - name: external-volume - external: true - external-volume3: - name: this-is-volume3 - external: true - other-external-volume: - name: my-cool-volume - external: true - other-volume: - driver: flocker - driver_opts: - baz: "1" - foo: bar - some-volume: {} -secrets: {} -configs: {} -` + workingDir := "/foo" + homeDir := "/bar" + cfg := fullExampleConfig(workingDir, homeDir) + expected := fullExampleYAML(workingDir) actual, err := yaml.Marshal(cfg) - assert.NoError(t, err) - assert.Equal(t, expected, string(actual)) + assert.NilError(t, err) + assert.Check(t, is.Equal(expected, string(actual))) // Make sure the expected still dict, err := ParseYAML([]byte("version: '3.6'\n" + expected)) - assert.NoError(t, err) + assert.NilError(t, err) _, err = Load(buildConfigDetails(dict, map[string]string{})) - assert.NoError(t, err) + assert.NilError(t, err) } diff --git a/components/cli/cli/compose/loader/volume_test.go b/components/cli/cli/compose/loader/volume_test.go index 90110133fa..5e96b0aeff 100644 --- a/components/cli/cli/compose/loader/volume_test.go +++ b/components/cli/cli/compose/loader/volume_test.go @@ -5,17 +5,16 @@ import ( "testing" "github.com/docker/cli/cli/compose/types" - "github.com/docker/cli/internal/test/testutil" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" ) func TestParseVolumeAnonymousVolume(t *testing.T) { for _, path := range []string{"/path", "/path/foo"} { volume, err := ParseVolume(path) expected := types.ServiceVolumeConfig{Type: "volume", Target: path} - assert.NoError(t, err) - assert.Equal(t, expected, volume) + assert.NilError(t, err) + assert.Check(t, is.DeepEqual(expected, volume)) } } @@ -23,29 +22,29 @@ func TestParseVolumeAnonymousVolumeWindows(t *testing.T) { for _, path := range []string{"C:\\path", "Z:\\path\\foo"} { volume, err := ParseVolume(path) expected := types.ServiceVolumeConfig{Type: "volume", Target: path} - assert.NoError(t, err) - assert.Equal(t, expected, volume) + assert.NilError(t, err) + assert.Check(t, is.DeepEqual(expected, volume)) } } func TestParseVolumeTooManyColons(t *testing.T) { _, err := ParseVolume("/foo:/foo:ro:foo") - assert.EqualError(t, err, "invalid spec: /foo:/foo:ro:foo: too many colons") + assert.Error(t, err, "invalid spec: /foo:/foo:ro:foo: too many colons") } func TestParseVolumeShortVolumes(t *testing.T) { for _, path := range []string{".", "/a"} { volume, err := ParseVolume(path) expected := types.ServiceVolumeConfig{Type: "volume", Target: path} - assert.NoError(t, err) - assert.Equal(t, expected, volume) + assert.NilError(t, err) + assert.Check(t, is.DeepEqual(expected, volume)) } } func TestParseVolumeMissingSource(t *testing.T) { for _, spec := range []string{":foo", "/foo::ro"} { _, err := ParseVolume(spec) - testutil.ErrorContains(t, err, "empty section between colons") + assert.ErrorContains(t, err, "empty section between colons") } } @@ -57,8 +56,8 @@ func TestParseVolumeBindMount(t *testing.T) { Source: path, Target: "/target", } - assert.NoError(t, err) - assert.Equal(t, expected, volume) + assert.NilError(t, err) + assert.Check(t, is.DeepEqual(expected, volume)) } } @@ -75,8 +74,8 @@ func TestParseVolumeRelativeBindMountWindows(t *testing.T) { Source: path, Target: "d:\\target", } - assert.NoError(t, err) - assert.Equal(t, expected, volume) + assert.NilError(t, err) + assert.Check(t, is.DeepEqual(expected, volume)) } } @@ -88,8 +87,8 @@ func TestParseVolumeWithBindOptions(t *testing.T) { Target: "/target", Bind: &types.ServiceVolumeBind{Propagation: "slave"}, } - assert.NoError(t, err) - assert.Equal(t, expected, volume) + assert.NilError(t, err) + assert.Check(t, is.DeepEqual(expected, volume)) } func TestParseVolumeWithBindOptionsWindows(t *testing.T) { @@ -101,13 +100,13 @@ func TestParseVolumeWithBindOptionsWindows(t *testing.T) { ReadOnly: true, Bind: &types.ServiceVolumeBind{Propagation: "rprivate"}, } - assert.NoError(t, err) - assert.Equal(t, expected, volume) + assert.NilError(t, err) + assert.Check(t, is.DeepEqual(expected, volume)) } func TestParseVolumeWithInvalidVolumeOptions(t *testing.T) { _, err := ParseVolume("name:/target:bogus") - assert.NoError(t, err) + assert.NilError(t, err) } func TestParseVolumeWithVolumeOptions(t *testing.T) { @@ -118,8 +117,8 @@ func TestParseVolumeWithVolumeOptions(t *testing.T) { Target: "/target", Volume: &types.ServiceVolumeVolume{NoCopy: true}, } - assert.NoError(t, err) - assert.Equal(t, expected, volume) + assert.NilError(t, err) + assert.Check(t, is.DeepEqual(expected, volume)) } func TestParseVolumeWithReadOnly(t *testing.T) { @@ -131,8 +130,8 @@ func TestParseVolumeWithReadOnly(t *testing.T) { Target: "/target", ReadOnly: true, } - assert.NoError(t, err) - assert.Equal(t, expected, volume) + assert.NilError(t, err) + assert.Check(t, is.DeepEqual(expected, volume)) } } @@ -145,24 +144,24 @@ func TestParseVolumeWithRW(t *testing.T) { Target: "/target", ReadOnly: false, } - assert.NoError(t, err) - assert.Equal(t, expected, volume) + assert.NilError(t, err) + assert.Check(t, is.DeepEqual(expected, volume)) } } func TestParseVolumeWindowsNamedPipe(t *testing.T) { volume, err := ParseVolume(`\\.\pipe\docker_engine:\\.\pipe\inside`) - require.NoError(t, err) + assert.NilError(t, err) expected := types.ServiceVolumeConfig{ Type: "bind", Source: `\\.\pipe\docker_engine`, Target: `\\.\pipe\inside`, } - assert.Equal(t, expected, volume) + assert.Check(t, is.DeepEqual(expected, volume)) } func TestIsFilePath(t *testing.T) { - assert.False(t, isFilePath("a界")) + assert.Check(t, !isFilePath("a界")) } // Preserve the test cases for VolumeSplitN @@ -209,16 +208,16 @@ func TestParseVolumeSplitCases(t *testing.T) { expected := len(x.expected) > 1 msg := fmt.Sprintf("Case %d: %s", casenumber, x.input) - assert.Equal(t, expected, parsed.Source != "", msg) + assert.Check(t, is.Equal(expected, parsed.Source != ""), msg) } } func TestParseVolumeInvalidEmptySpec(t *testing.T) { _, err := ParseVolume("") - testutil.ErrorContains(t, err, "invalid empty volume spec") + assert.ErrorContains(t, err, "invalid empty volume spec") } func TestParseVolumeInvalidSections(t *testing.T) { _, err := ParseVolume("/foo::rw") - testutil.ErrorContains(t, err, "invalid spec") + assert.ErrorContains(t, err, "invalid spec") } diff --git a/components/cli/cli/compose/schema/schema_test.go b/components/cli/cli/compose/schema/schema_test.go index bce6276dce..f80af40113 100644 --- a/components/cli/cli/compose/schema/schema_test.go +++ b/components/cli/cli/compose/schema/schema_test.go @@ -3,7 +3,7 @@ package schema import ( "testing" - "github.com/stretchr/testify/assert" + "github.com/gotestyourself/gotestyourself/assert" ) type dict map[string]interface{} @@ -18,7 +18,7 @@ func TestValidate(t *testing.T) { }, } - assert.NoError(t, Validate(config, "3.0")) + assert.NilError(t, Validate(config, "3.0")) } func TestValidateUndefinedTopLevelOption(t *testing.T) { @@ -32,8 +32,7 @@ func TestValidateUndefinedTopLevelOption(t *testing.T) { } err := Validate(config, "3.0") - assert.Error(t, err) - assert.Contains(t, err.Error(), "Additional property helicopters is not allowed") + assert.ErrorContains(t, err, "Additional property helicopters is not allowed") } func TestValidateAllowsXTopLevelFields(t *testing.T) { @@ -43,7 +42,7 @@ func TestValidateAllowsXTopLevelFields(t *testing.T) { } err := Validate(config, "3.4") - assert.NoError(t, err) + assert.NilError(t, err) } func TestValidateSecretConfigNames(t *testing.T) { @@ -62,7 +61,7 @@ func TestValidateSecretConfigNames(t *testing.T) { } err := Validate(config, "3.5") - assert.NoError(t, err) + assert.NilError(t, err) } func TestValidateInvalidVersion(t *testing.T) { @@ -76,8 +75,7 @@ func TestValidateInvalidVersion(t *testing.T) { } err := Validate(config, "2.1") - assert.Error(t, err) - assert.Contains(t, err.Error(), "unsupported Compose file version: 2.1") + assert.ErrorContains(t, err, "unsupported Compose file version: 2.1") } type array []interface{} @@ -101,7 +99,7 @@ func TestValidatePlacement(t *testing.T) { }, } - assert.NoError(t, Validate(config, "3.3")) + assert.NilError(t, Validate(config, "3.3")) } func TestValidateIsolation(t *testing.T) { @@ -114,5 +112,5 @@ func TestValidateIsolation(t *testing.T) { }, }, } - assert.NoError(t, Validate(config, "3.5")) + assert.NilError(t, Validate(config, "3.5")) } diff --git a/components/cli/cli/compose/template/template_test.go b/components/cli/cli/compose/template/template_test.go index 4ad3f252ac..0e74677f7d 100644 --- a/components/cli/cli/compose/template/template_test.go +++ b/components/cli/cli/compose/template/template_test.go @@ -3,7 +3,8 @@ package template import ( "testing" - "github.com/stretchr/testify/assert" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" ) var defaults = map[string]string{ @@ -18,8 +19,8 @@ func defaultMapping(name string) (string, bool) { func TestEscaped(t *testing.T) { result, err := Substitute("$${foo}", defaultMapping) - assert.Nil(t, err) - assert.Equal(t, "${foo}", result) + assert.NilError(t, err) + assert.Check(t, is.Equal("${foo}", result)) } func TestInvalid(t *testing.T) { @@ -35,49 +36,48 @@ func TestInvalid(t *testing.T) { for _, template := range invalidTemplates { _, err := Substitute(template, defaultMapping) - assert.Error(t, err) - assert.IsType(t, &InvalidTemplateError{}, err) + assert.ErrorContains(t, err, "Invalid template") } } func TestNoValueNoDefault(t *testing.T) { for _, template := range []string{"This ${missing} var", "This ${BAR} var"} { result, err := Substitute(template, defaultMapping) - assert.Nil(t, err) - assert.Equal(t, "This var", result) + assert.NilError(t, err) + assert.Check(t, is.Equal("This var", result)) } } func TestValueNoDefault(t *testing.T) { for _, template := range []string{"This $FOO var", "This ${FOO} var"} { result, err := Substitute(template, defaultMapping) - assert.Nil(t, err) - assert.Equal(t, "This first var", result) + assert.NilError(t, err) + assert.Check(t, is.Equal("This first var", result)) } } func TestNoValueWithDefault(t *testing.T) { for _, template := range []string{"ok ${missing:-def}", "ok ${missing-def}"} { result, err := Substitute(template, defaultMapping) - assert.Nil(t, err) - assert.Equal(t, "ok def", result) + assert.NilError(t, err) + assert.Check(t, is.Equal("ok def", result)) } } func TestEmptyValueWithSoftDefault(t *testing.T) { result, err := Substitute("ok ${BAR:-def}", defaultMapping) - assert.Nil(t, err) - assert.Equal(t, "ok def", result) + assert.NilError(t, err) + assert.Check(t, is.Equal("ok def", result)) } func TestEmptyValueWithHardDefault(t *testing.T) { result, err := Substitute("ok ${BAR-def}", defaultMapping) - assert.Nil(t, err) - assert.Equal(t, "ok ", result) + assert.NilError(t, err) + assert.Check(t, is.Equal("ok ", result)) } func TestNonAlphanumericDefault(t *testing.T) { result, err := Substitute("ok ${BAR:-/non:-alphanumeric}", defaultMapping) - assert.Nil(t, err) - assert.Equal(t, "ok /non:-alphanumeric", result) + assert.NilError(t, err) + assert.Check(t, is.Equal("ok /non:-alphanumeric", result)) } diff --git a/components/cli/cli/config/config.go b/components/cli/cli/config/config.go index 90529ebd4c..143436b9ec 100644 --- a/components/cli/cli/config/config.go +++ b/components/cli/cli/config/config.go @@ -75,18 +75,18 @@ func Load(configDir string) (*configfile.ConfigFile, error) { if _, err := os.Stat(filename); err == nil { file, err := os.Open(filename) if err != nil { - return configFile, errors.Errorf("%s - %v", filename, err) + return configFile, errors.Wrap(err, filename) } defer file.Close() err = configFile.LoadFromReader(file) if err != nil { - err = errors.Errorf("%s - %v", filename, err) + err = errors.Wrap(err, filename) } return configFile, err } else if !os.IsNotExist(err) { // if file is there but we can't stat it for any reason other // than it doesn't exist then stop - return configFile, errors.Errorf("%s - %v", filename, err) + return configFile, errors.Wrap(err, filename) } // Can't find latest config file so check for the old one @@ -96,12 +96,12 @@ func Load(configDir string) (*configfile.ConfigFile, error) { } file, err := os.Open(confFile) if err != nil { - return configFile, errors.Errorf("%s - %v", confFile, err) + return configFile, errors.Wrap(err, confFile) } defer file.Close() err = configFile.LegacyLoadFromReader(file) if err != nil { - return configFile, errors.Errorf("%s - %v", confFile, err) + return configFile, errors.Wrap(err, confFile) } return configFile, nil } diff --git a/components/cli/cli/config/config_test.go b/components/cli/cli/config/config_test.go index 25ed97cdf1..74c441a4b4 100644 --- a/components/cli/cli/config/config_test.go +++ b/components/cli/cli/config/config_test.go @@ -2,6 +2,7 @@ package config import ( "bytes" + "io" "io/ioutil" "os" "path/filepath" @@ -10,15 +11,15 @@ import ( "github.com/docker/cli/cli/config/configfile" "github.com/docker/cli/cli/config/credentials" - "github.com/docker/cli/internal/test/testutil" "github.com/docker/docker/pkg/homedir" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" + "github.com/pkg/errors" ) func setupConfigDir(t *testing.T) (string, func()) { tmpdir, err := ioutil.TempDir("", "config-test") - require.NoError(t, err) + assert.NilError(t, err) oldDir := Dir() SetDir(tmpdir) @@ -33,10 +34,10 @@ func TestEmptyConfigDir(t *testing.T) { defer cleanup() config, err := Load("") - require.NoError(t, err) + assert.NilError(t, err) expectedConfigFilename := filepath.Join(tmpHome, ConfigFileName) - assert.Equal(t, expectedConfigFilename, config.Filename) + assert.Check(t, is.Equal(expectedConfigFilename, config.Filename)) // Now save it and make sure it shows up in new form saveConfigAndValidateNewFormat(t, config, tmpHome) @@ -44,11 +45,11 @@ func TestEmptyConfigDir(t *testing.T) { func TestMissingFile(t *testing.T) { tmpHome, err := ioutil.TempDir("", "config-test") - require.NoError(t, err) + assert.NilError(t, err) defer os.RemoveAll(tmpHome) config, err := Load(tmpHome) - require.NoError(t, err) + assert.NilError(t, err) // Now save it and make sure it shows up in new form saveConfigAndValidateNewFormat(t, config, tmpHome) @@ -56,13 +57,13 @@ func TestMissingFile(t *testing.T) { func TestSaveFileToDirs(t *testing.T) { tmpHome, err := ioutil.TempDir("", "config-test") - require.NoError(t, err) + assert.NilError(t, err) defer os.RemoveAll(tmpHome) tmpHome += "/.docker" config, err := Load(tmpHome) - require.NoError(t, err) + assert.NilError(t, err) // Now save it and make sure it shows up in new form saveConfigAndValidateNewFormat(t, config, tmpHome) @@ -70,28 +71,29 @@ func TestSaveFileToDirs(t *testing.T) { func TestEmptyFile(t *testing.T) { tmpHome, err := ioutil.TempDir("", "config-test") - require.NoError(t, err) + assert.NilError(t, err) defer os.RemoveAll(tmpHome) fn := filepath.Join(tmpHome, ConfigFileName) err = ioutil.WriteFile(fn, []byte(""), 0600) - require.NoError(t, err) + assert.NilError(t, err) _, err = Load(tmpHome) - testutil.ErrorContains(t, err, "EOF") + assert.Equal(t, errors.Cause(err), io.EOF) + assert.ErrorContains(t, err, ConfigFileName) } func TestEmptyJSON(t *testing.T) { tmpHome, err := ioutil.TempDir("", "config-test") - require.NoError(t, err) + assert.NilError(t, err) defer os.RemoveAll(tmpHome) fn := filepath.Join(tmpHome, ConfigFileName) err = ioutil.WriteFile(fn, []byte("{}"), 0600) - require.NoError(t, err) + assert.NilError(t, err) config, err := Load(tmpHome) - require.NoError(t, err) + assert.NilError(t, err) // Now save it and make sure it shows up in new form saveConfigAndValidateNewFormat(t, config, tmpHome) @@ -107,7 +109,7 @@ email`: "Invalid auth configuration file", } tmpHome, err := ioutil.TempDir("", "config-test") - require.NoError(t, err) + assert.NilError(t, err) defer os.RemoveAll(tmpHome) homeKey := homedir.Key() @@ -119,16 +121,16 @@ email`: "Invalid auth configuration file", for content, expectedError := range invalids { fn := filepath.Join(tmpHome, oldConfigfile) err := ioutil.WriteFile(fn, []byte(content), 0600) - require.NoError(t, err) + assert.NilError(t, err) _, err = Load(tmpHome) - testutil.ErrorContains(t, err, expectedError) + assert.ErrorContains(t, err, expectedError) } } func TestOldValidAuth(t *testing.T) { tmpHome, err := ioutil.TempDir("", "config-test") - require.NoError(t, err) + assert.NilError(t, err) defer os.RemoveAll(tmpHome) homeKey := homedir.Key() @@ -141,10 +143,10 @@ func TestOldValidAuth(t *testing.T) { js := `username = am9lam9lOmhlbGxv email = user@example.com` err = ioutil.WriteFile(fn, []byte(js), 0600) - require.NoError(t, err) + assert.NilError(t, err) config, err := Load(tmpHome) - require.NoError(t, err) + assert.NilError(t, err) // defaultIndexserver is https://index.docker.io/v1/ ac := config.AuthConfigs["https://index.docker.io/v1/"] @@ -163,12 +165,12 @@ func TestOldValidAuth(t *testing.T) { } }` - assert.Equal(t, expConfStr, configStr) + assert.Check(t, is.Equal(expConfStr, configStr)) } func TestOldJSONInvalid(t *testing.T) { tmpHome, err := ioutil.TempDir("", "config-test") - require.NoError(t, err) + assert.NilError(t, err) defer os.RemoveAll(tmpHome) homeKey := homedir.Key() @@ -192,7 +194,7 @@ func TestOldJSONInvalid(t *testing.T) { func TestOldJSON(t *testing.T) { tmpHome, err := ioutil.TempDir("", "config-test") - require.NoError(t, err) + assert.NilError(t, err) defer os.RemoveAll(tmpHome) homeKey := homedir.Key() @@ -208,7 +210,7 @@ func TestOldJSON(t *testing.T) { } config, err := Load(tmpHome) - require.NoError(t, err) + assert.NilError(t, err) ac := config.AuthConfigs["https://index.docker.io/v1/"] if ac.Username != "joejoe" || ac.Password != "hello" { @@ -234,7 +236,7 @@ func TestOldJSON(t *testing.T) { func TestNewJSON(t *testing.T) { tmpHome, err := ioutil.TempDir("", "config-test") - require.NoError(t, err) + assert.NilError(t, err) defer os.RemoveAll(tmpHome) fn := filepath.Join(tmpHome, ConfigFileName) @@ -244,7 +246,7 @@ func TestNewJSON(t *testing.T) { } config, err := Load(tmpHome) - require.NoError(t, err) + assert.NilError(t, err) ac := config.AuthConfigs["https://index.docker.io/v1/"] if ac.Username != "joejoe" || ac.Password != "hello" { @@ -269,7 +271,7 @@ func TestNewJSON(t *testing.T) { func TestNewJSONNoEmail(t *testing.T) { tmpHome, err := ioutil.TempDir("", "config-test") - require.NoError(t, err) + assert.NilError(t, err) defer os.RemoveAll(tmpHome) fn := filepath.Join(tmpHome, ConfigFileName) @@ -279,7 +281,7 @@ func TestNewJSONNoEmail(t *testing.T) { } config, err := Load(tmpHome) - require.NoError(t, err) + assert.NilError(t, err) ac := config.AuthConfigs["https://index.docker.io/v1/"] if ac.Username != "joejoe" || ac.Password != "hello" { @@ -304,7 +306,7 @@ func TestNewJSONNoEmail(t *testing.T) { func TestJSONWithPsFormat(t *testing.T) { tmpHome, err := ioutil.TempDir("", "config-test") - require.NoError(t, err) + assert.NilError(t, err) defer os.RemoveAll(tmpHome) fn := filepath.Join(tmpHome, ConfigFileName) @@ -317,7 +319,7 @@ func TestJSONWithPsFormat(t *testing.T) { } config, err := Load(tmpHome) - require.NoError(t, err) + assert.NilError(t, err) if config.PsFormat != `table {{.ID}}\t{{.Label "com.docker.label.cpu"}}` { t.Fatalf("Unknown ps format: %s\n", config.PsFormat) @@ -333,7 +335,7 @@ func TestJSONWithPsFormat(t *testing.T) { func TestJSONWithCredentialStore(t *testing.T) { tmpHome, err := ioutil.TempDir("", "config-test") - require.NoError(t, err) + assert.NilError(t, err) defer os.RemoveAll(tmpHome) fn := filepath.Join(tmpHome, ConfigFileName) @@ -346,7 +348,7 @@ func TestJSONWithCredentialStore(t *testing.T) { } config, err := Load(tmpHome) - require.NoError(t, err) + assert.NilError(t, err) if config.CredentialsStore != "crazy-secure-storage" { t.Fatalf("Unknown credential store: %s\n", config.CredentialsStore) @@ -362,7 +364,7 @@ func TestJSONWithCredentialStore(t *testing.T) { func TestJSONWithCredentialHelpers(t *testing.T) { tmpHome, err := ioutil.TempDir("", "config-test") - require.NoError(t, err) + assert.NilError(t, err) defer os.RemoveAll(tmpHome) fn := filepath.Join(tmpHome, ConfigFileName) @@ -375,7 +377,7 @@ func TestJSONWithCredentialHelpers(t *testing.T) { } config, err := Load(tmpHome) - require.NoError(t, err) + assert.NilError(t, err) if config.CredentialHelpers == nil { t.Fatal("config.CredentialHelpers was nil") @@ -397,17 +399,17 @@ func TestJSONWithCredentialHelpers(t *testing.T) { // Save it and make sure it shows up in new form func saveConfigAndValidateNewFormat(t *testing.T, config *configfile.ConfigFile, configDir string) string { - require.NoError(t, config.Save()) + assert.NilError(t, config.Save()) buf, err := ioutil.ReadFile(filepath.Join(configDir, ConfigFileName)) - require.NoError(t, err) - assert.Contains(t, string(buf), `"auths":`) + assert.NilError(t, err) + assert.Check(t, is.Contains(string(buf), `"auths":`)) return string(buf) } func TestConfigDir(t *testing.T) { tmpHome, err := ioutil.TempDir("", "config-test") - require.NoError(t, err) + assert.NilError(t, err) defer os.RemoveAll(tmpHome) if Dir() == tmpHome { @@ -426,7 +428,7 @@ func TestJSONReaderNoFile(t *testing.T) { js := ` { "auths": { "https://index.docker.io/v1/": { "auth": "am9lam9lOmhlbGxv", "email": "user@example.com" } } }` config, err := LoadFromReader(strings.NewReader(js)) - require.NoError(t, err) + assert.NilError(t, err) ac := config.AuthConfigs["https://index.docker.io/v1/"] if ac.Username != "joejoe" || ac.Password != "hello" { @@ -439,7 +441,7 @@ func TestOldJSONReaderNoFile(t *testing.T) { js := `{"https://index.docker.io/v1/":{"auth":"am9lam9lOmhlbGxv","email":"user@example.com"}}` config, err := LegacyLoadFromReader(strings.NewReader(js)) - require.NoError(t, err) + assert.NilError(t, err) ac := config.AuthConfigs["https://index.docker.io/v1/"] if ac.Username != "joejoe" || ac.Password != "hello" { @@ -453,7 +455,7 @@ func TestJSONWithPsFormatNoFile(t *testing.T) { "psFormat": "table {{.ID}}\\t{{.Label \"com.docker.label.cpu\"}}" }` config, err := LoadFromReader(strings.NewReader(js)) - require.NoError(t, err) + assert.NilError(t, err) if config.PsFormat != `table {{.ID}}\t{{.Label "com.docker.label.cpu"}}` { t.Fatalf("Unknown ps format: %s\n", config.PsFormat) @@ -467,21 +469,21 @@ func TestJSONSaveWithNoFile(t *testing.T) { "psFormat": "table {{.ID}}\\t{{.Label \"com.docker.label.cpu\"}}" }` config, err := LoadFromReader(strings.NewReader(js)) - require.NoError(t, err) + assert.NilError(t, err) err = config.Save() - testutil.ErrorContains(t, err, "with empty filename") + assert.ErrorContains(t, err, "with empty filename") tmpHome, err := ioutil.TempDir("", "config-test") - require.NoError(t, err) + assert.NilError(t, err) defer os.RemoveAll(tmpHome) fn := filepath.Join(tmpHome, ConfigFileName) f, _ := os.OpenFile(fn, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) defer f.Close() - require.NoError(t, config.SaveToWriter(f)) + assert.NilError(t, config.SaveToWriter(f)) buf, err := ioutil.ReadFile(filepath.Join(tmpHome, ConfigFileName)) - require.NoError(t, err) + assert.NilError(t, err) expConfStr := `{ "auths": { "https://index.docker.io/v1/": { @@ -498,21 +500,21 @@ func TestJSONSaveWithNoFile(t *testing.T) { func TestLegacyJSONSaveWithNoFile(t *testing.T) { js := `{"https://index.docker.io/v1/":{"auth":"am9lam9lOmhlbGxv","email":"user@example.com"}}` config, err := LegacyLoadFromReader(strings.NewReader(js)) - require.NoError(t, err) + assert.NilError(t, err) err = config.Save() - testutil.ErrorContains(t, err, "with empty filename") + assert.ErrorContains(t, err, "with empty filename") tmpHome, err := ioutil.TempDir("", "config-test") - require.NoError(t, err) + assert.NilError(t, err) defer os.RemoveAll(tmpHome) fn := filepath.Join(tmpHome, ConfigFileName) f, _ := os.OpenFile(fn, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) defer f.Close() - require.NoError(t, config.SaveToWriter(f)) + assert.NilError(t, config.SaveToWriter(f)) buf, err := ioutil.ReadFile(filepath.Join(tmpHome, ConfigFileName)) - require.NoError(t, err) + assert.NilError(t, err) expConfStr := `{ "auths": { @@ -536,7 +538,7 @@ func TestLoadDefaultConfigFile(t *testing.T) { filename := filepath.Join(dir, ConfigFileName) content := []byte(`{"PsFormat": "format"}`) err := ioutil.WriteFile(filename, content, 0644) - require.NoError(t, err) + assert.NilError(t, err) configFile := LoadDefaultConfigFile(buffer) credStore := credentials.DetectDefaultStore("") @@ -544,5 +546,5 @@ func TestLoadDefaultConfigFile(t *testing.T) { expected.CredentialsStore = credStore expected.PsFormat = "format" - assert.Equal(t, expected, configFile) + assert.Check(t, is.DeepEqual(expected, configFile)) } diff --git a/components/cli/cli/config/configfile/file_test.go b/components/cli/cli/config/configfile/file_test.go index 75df18c49a..c769f53c26 100644 --- a/components/cli/cli/config/configfile/file_test.go +++ b/components/cli/cli/config/configfile/file_test.go @@ -6,8 +6,8 @@ import ( "github.com/docker/cli/cli/config/credentials" "github.com/docker/docker/api/types" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" ) func TestEncodeAuth(t *testing.T) { @@ -17,8 +17,8 @@ func TestEncodeAuth(t *testing.T) { expected := &types.AuthConfig{} var err error expected.Username, expected.Password, err = decodeAuth(authStr) - require.NoError(t, err) - assert.Equal(t, expected, newAuthConfig) + assert.NilError(t, err) + assert.Check(t, is.DeepEqual(expected, newAuthConfig)) } func TestProxyConfig(t *testing.T) { @@ -50,7 +50,7 @@ func TestProxyConfig(t *testing.T) { "NO_PROXY": &noProxy, "no_proxy": &noProxy, } - assert.Equal(t, expected, proxyConfig) + assert.Check(t, is.DeepEqual(expected, proxyConfig)) } func TestProxyConfigOverride(t *testing.T) { @@ -88,7 +88,7 @@ func TestProxyConfigOverride(t *testing.T) { "NO_PROXY": &overrideNoProxy, "no_proxy": &noProxy, } - assert.Equal(t, expected, proxyConfig) + assert.Check(t, is.DeepEqual(expected, proxyConfig)) } func TestProxyConfigPerHost(t *testing.T) { @@ -133,14 +133,14 @@ func TestProxyConfigPerHost(t *testing.T) { "NO_PROXY": &extNoProxy, "no_proxy": &extNoProxy, } - assert.Equal(t, expected, proxyConfig) + assert.Check(t, is.DeepEqual(expected, proxyConfig)) } func TestConfigFile(t *testing.T) { configFilename := "configFilename" configFile := New(configFilename) - assert.Equal(t, configFilename, configFile.Filename) + assert.Check(t, is.Equal(configFilename, configFile.Filename)) } type mockNativeStore struct { @@ -182,11 +182,11 @@ func TestGetAllCredentialsFileStoreOnly(t *testing.T) { configFile.AuthConfigs["example.com/foo"] = exampleAuth authConfigs, err := configFile.GetAllCredentials() - require.NoError(t, err) + assert.NilError(t, err) expected := make(map[string]types.AuthConfig) expected["example.com/foo"] = exampleAuth - assert.Equal(t, expected, authConfigs) + assert.Check(t, is.DeepEqual(expected, authConfigs)) } func TestGetAllCredentialsCredsStore(t *testing.T) { @@ -207,12 +207,12 @@ func TestGetAllCredentialsCredsStore(t *testing.T) { } authConfigs, err := configFile.GetAllCredentials() - require.NoError(t, err) + assert.NilError(t, err) expected := make(map[string]types.AuthConfig) expected[testRegistryHostname] = expectedAuth - assert.Equal(t, expected, authConfigs) - assert.Equal(t, 1, testCredsStore.(*mockNativeStore).GetAllCallCount) + assert.Check(t, is.DeepEqual(expected, authConfigs)) + assert.Check(t, is.Equal(1, testCredsStore.(*mockNativeStore).GetAllCallCount)) } func TestGetAllCredentialsCredHelper(t *testing.T) { @@ -246,12 +246,12 @@ func TestGetAllCredentialsCredHelper(t *testing.T) { } authConfigs, err := configFile.GetAllCredentials() - require.NoError(t, err) + assert.NilError(t, err) expected := make(map[string]types.AuthConfig) expected[testCredHelperRegistryHostname] = expectedCredHelperAuth - assert.Equal(t, expected, authConfigs) - assert.Equal(t, 0, testCredHelper.(*mockNativeStore).GetAllCallCount) + assert.Check(t, is.DeepEqual(expected, authConfigs)) + assert.Check(t, is.Equal(0, testCredHelper.(*mockNativeStore).GetAllCallCount)) } func TestGetAllCredentialsFileStoreAndCredHelper(t *testing.T) { @@ -281,13 +281,13 @@ func TestGetAllCredentialsFileStoreAndCredHelper(t *testing.T) { tmpNewNativeStore := newNativeStore defer func() { newNativeStore = tmpNewNativeStore }() authConfigs, err := configFile.GetAllCredentials() - require.NoError(t, err) + assert.NilError(t, err) expected := make(map[string]types.AuthConfig) expected[testFileStoreRegistryHostname] = expectedFileStoreAuth expected[testCredHelperRegistryHostname] = expectedCredHelperAuth - assert.Equal(t, expected, authConfigs) - assert.Equal(t, 0, testCredHelper.(*mockNativeStore).GetAllCallCount) + assert.Check(t, is.DeepEqual(expected, authConfigs)) + assert.Check(t, is.Equal(0, testCredHelper.(*mockNativeStore).GetAllCallCount)) } func TestGetAllCredentialsCredStoreAndCredHelper(t *testing.T) { @@ -322,14 +322,14 @@ func TestGetAllCredentialsCredStoreAndCredHelper(t *testing.T) { } authConfigs, err := configFile.GetAllCredentials() - require.NoError(t, err) + assert.NilError(t, err) expected := make(map[string]types.AuthConfig) expected[testCredStoreRegistryHostname] = expectedCredStoreAuth expected[testCredHelperRegistryHostname] = expectedCredHelperAuth - assert.Equal(t, expected, authConfigs) - assert.Equal(t, 1, testCredsStore.(*mockNativeStore).GetAllCallCount) - assert.Equal(t, 0, testCredHelper.(*mockNativeStore).GetAllCallCount) + assert.Check(t, is.DeepEqual(expected, authConfigs)) + assert.Check(t, is.Equal(1, testCredsStore.(*mockNativeStore).GetAllCallCount)) + assert.Check(t, is.Equal(0, testCredHelper.(*mockNativeStore).GetAllCallCount)) } func TestGetAllCredentialsCredHelperOverridesDefaultStore(t *testing.T) { @@ -363,11 +363,11 @@ func TestGetAllCredentialsCredHelperOverridesDefaultStore(t *testing.T) { } authConfigs, err := configFile.GetAllCredentials() - require.NoError(t, err) + assert.NilError(t, err) expected := make(map[string]types.AuthConfig) expected[testRegistryHostname] = expectedCredHelperAuth - assert.Equal(t, expected, authConfigs) - assert.Equal(t, 1, testCredsStore.(*mockNativeStore).GetAllCallCount) - assert.Equal(t, 0, testCredHelper.(*mockNativeStore).GetAllCallCount) + assert.Check(t, is.DeepEqual(expected, authConfigs)) + assert.Check(t, is.Equal(1, testCredsStore.(*mockNativeStore).GetAllCallCount)) + assert.Check(t, is.Equal(0, testCredHelper.(*mockNativeStore).GetAllCallCount)) } diff --git a/components/cli/cli/config/credentials/file_store_test.go b/components/cli/cli/config/credentials/file_store_test.go index 0d4fbd6bdc..b849d62456 100644 --- a/components/cli/cli/config/credentials/file_store_test.go +++ b/components/cli/cli/config/credentials/file_store_test.go @@ -4,8 +4,8 @@ import ( "testing" "github.com/docker/docker/api/types" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" ) type fakeStore struct { @@ -34,12 +34,12 @@ func TestFileStoreAddCredentials(t *testing.T) { ServerAddress: "https://example.com", } err := s.Store(auth) - require.NoError(t, err) - assert.Len(t, f.GetAuthConfigs(), 1) + assert.NilError(t, err) + assert.Check(t, is.Len(f.GetAuthConfigs(), 1)) actual, ok := f.GetAuthConfigs()["https://example.com"] - assert.True(t, ok) - assert.Equal(t, auth, actual) + assert.Check(t, ok) + assert.Check(t, is.DeepEqual(auth, actual)) } func TestFileStoreGet(t *testing.T) { diff --git a/components/cli/cli/config/credentials/native_store_test.go b/components/cli/cli/config/credentials/native_store_test.go index c85e3f0d18..57a506f62c 100644 --- a/components/cli/cli/config/credentials/native_store_test.go +++ b/components/cli/cli/config/credentials/native_store_test.go @@ -8,13 +8,12 @@ import ( "strings" "testing" - "github.com/docker/cli/internal/test/testutil" "github.com/docker/docker-credential-helpers/client" "github.com/docker/docker-credential-helpers/credentials" "github.com/docker/docker/api/types" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" "github.com/pkg/errors" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" ) const ( @@ -105,16 +104,16 @@ func TestNativeStoreAddCredentials(t *testing.T) { ServerAddress: validServerAddress, } err := s.Store(auth) - require.NoError(t, err) - assert.Len(t, f.GetAuthConfigs(), 1) + assert.NilError(t, err) + assert.Check(t, is.Len(f.GetAuthConfigs(), 1)) actual, ok := f.GetAuthConfigs()[validServerAddress] - assert.True(t, ok) + assert.Check(t, ok) expected := types.AuthConfig{ Email: auth.Email, ServerAddress: auth.ServerAddress, } - assert.Equal(t, expected, actual) + assert.Check(t, is.DeepEqual(expected, actual)) } func TestNativeStoreAddInvalidCredentials(t *testing.T) { @@ -129,8 +128,8 @@ func TestNativeStoreAddInvalidCredentials(t *testing.T) { Email: "foo@example.com", ServerAddress: invalidServerAddress, }) - testutil.ErrorContains(t, err, "program failed") - assert.Len(t, f.GetAuthConfigs(), 0) + assert.ErrorContains(t, err, "program failed") + assert.Check(t, is.Len(f.GetAuthConfigs(), 0)) } func TestNativeStoreGet(t *testing.T) { @@ -144,14 +143,14 @@ func TestNativeStoreGet(t *testing.T) { fileStore: NewFileStore(f), } actual, err := s.Get(validServerAddress) - require.NoError(t, err) + assert.NilError(t, err) expected := types.AuthConfig{ Username: "foo", Password: "bar", Email: "foo@example.com", } - assert.Equal(t, expected, actual) + assert.Check(t, is.DeepEqual(expected, actual)) } func TestNativeStoreGetIdentityToken(t *testing.T) { @@ -166,13 +165,13 @@ func TestNativeStoreGetIdentityToken(t *testing.T) { fileStore: NewFileStore(f), } actual, err := s.Get(validServerAddress2) - require.NoError(t, err) + assert.NilError(t, err) expected := types.AuthConfig{ IdentityToken: "abcd1234", Email: "foo@example2.com", } - assert.Equal(t, expected, actual) + assert.Check(t, is.DeepEqual(expected, actual)) } func TestNativeStoreGetAll(t *testing.T) { @@ -187,8 +186,8 @@ func TestNativeStoreGetAll(t *testing.T) { fileStore: NewFileStore(f), } as, err := s.GetAll() - require.NoError(t, err) - assert.Len(t, as, 2) + assert.NilError(t, err) + assert.Check(t, is.Len(as, 2)) if as[validServerAddress].Username != "foo" { t.Fatalf("expected username `foo` for %s, got %s", validServerAddress, as[validServerAddress].Username) @@ -228,7 +227,7 @@ func TestNativeStoreGetMissingCredentials(t *testing.T) { fileStore: NewFileStore(f), } _, err := s.Get(missingCredsAddress) - assert.NoError(t, err) + assert.NilError(t, err) } func TestNativeStoreGetInvalidAddress(t *testing.T) { @@ -243,7 +242,7 @@ func TestNativeStoreGetInvalidAddress(t *testing.T) { fileStore: NewFileStore(f), } _, err := s.Get(invalidServerAddress) - testutil.ErrorContains(t, err, "program failed") + assert.ErrorContains(t, err, "program failed") } func TestNativeStoreErase(t *testing.T) { @@ -258,8 +257,8 @@ func TestNativeStoreErase(t *testing.T) { fileStore: NewFileStore(f), } err := s.Erase(validServerAddress) - require.NoError(t, err) - assert.Len(t, f.GetAuthConfigs(), 0) + assert.NilError(t, err) + assert.Check(t, is.Len(f.GetAuthConfigs(), 0)) } func TestNativeStoreEraseInvalidAddress(t *testing.T) { @@ -274,5 +273,5 @@ func TestNativeStoreEraseInvalidAddress(t *testing.T) { fileStore: NewFileStore(f), } err := s.Erase(invalidServerAddress) - testutil.ErrorContains(t, err, "program failed") + assert.ErrorContains(t, err, "program failed") } diff --git a/components/cli/cli/flags/common_test.go b/components/cli/cli/flags/common_test.go index a00cd7a6ca..2f06e11e44 100644 --- a/components/cli/cli/flags/common_test.go +++ b/components/cli/cli/flags/common_test.go @@ -5,8 +5,9 @@ import ( "testing" cliconfig "github.com/docker/cli/cli/config" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" "github.com/spf13/pflag" - "github.com/stretchr/testify/assert" ) func TestCommonOptionsInstallFlags(t *testing.T) { @@ -19,10 +20,10 @@ func TestCommonOptionsInstallFlags(t *testing.T) { "--tlscert=\"/foo/cert\"", "--tlskey=\"/foo/key\"", }) - assert.NoError(t, err) - assert.Equal(t, "/foo/cafile", opts.TLSOptions.CAFile) - assert.Equal(t, "/foo/cert", opts.TLSOptions.CertFile) - assert.Equal(t, opts.TLSOptions.KeyFile, "/foo/key") + assert.NilError(t, err) + assert.Check(t, is.Equal("/foo/cafile", opts.TLSOptions.CAFile)) + assert.Check(t, is.Equal("/foo/cert", opts.TLSOptions.CertFile)) + assert.Check(t, is.Equal(opts.TLSOptions.KeyFile, "/foo/key")) } func defaultPath(filename string) string { @@ -35,8 +36,8 @@ func TestCommonOptionsInstallFlagsWithDefaults(t *testing.T) { opts.InstallFlags(flags) err := flags.Parse([]string{}) - assert.NoError(t, err) - assert.Equal(t, defaultPath("ca.pem"), opts.TLSOptions.CAFile) - assert.Equal(t, defaultPath("cert.pem"), opts.TLSOptions.CertFile) - assert.Equal(t, defaultPath("key.pem"), opts.TLSOptions.KeyFile) + assert.NilError(t, err) + assert.Check(t, is.Equal(defaultPath("ca.pem"), opts.TLSOptions.CAFile)) + assert.Check(t, is.Equal(defaultPath("cert.pem"), opts.TLSOptions.CertFile)) + assert.Check(t, is.Equal(defaultPath("key.pem"), opts.TLSOptions.KeyFile)) } diff --git a/components/cli/cli/manifest/store/store_test.go b/components/cli/cli/manifest/store/store_test.go index fdf51dd641..669b81e7c5 100644 --- a/components/cli/cli/manifest/store/store_test.go +++ b/components/cli/cli/manifest/store/store_test.go @@ -7,8 +7,9 @@ import ( "github.com/docker/cli/cli/manifest/types" "github.com/docker/distribution/reference" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" + "github.com/google/go-cmp/cmp" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" ) type fakeRef struct { @@ -29,20 +30,20 @@ func ref(name string) fakeRef { func sref(t *testing.T, name string) *types.SerializableNamed { named, err := reference.ParseNamed("example.com/" + name) - require.NoError(t, err) + assert.NilError(t, err) return &types.SerializableNamed{Named: named} } func newTestStore(t *testing.T) (Store, func()) { tmpdir, err := ioutil.TempDir("", "manifest-store-test") - require.NoError(t, err) + assert.NilError(t, err) return NewStore(tmpdir), func() { os.RemoveAll(tmpdir) } } func getFiles(t *testing.T, store Store) []os.FileInfo { infos, err := ioutil.ReadDir(store.(*fsStore).root) - require.NoError(t, err) + assert.NilError(t, err) return infos } @@ -52,11 +53,11 @@ func TestStoreRemove(t *testing.T) { listRef := ref("list") data := types.ImageManifest{Ref: sref(t, "abcdef")} - require.NoError(t, store.Save(listRef, ref("manifest"), data)) - require.Len(t, getFiles(t, store), 1) + assert.NilError(t, store.Save(listRef, ref("manifest"), data)) + assert.Assert(t, is.Len(getFiles(t, store), 1)) - assert.NoError(t, store.Remove(listRef)) - assert.Len(t, getFiles(t, store), 0) + assert.Check(t, store.Remove(listRef)) + assert.Check(t, is.Len(getFiles(t, store), 0)) } func TestStoreSaveAndGet(t *testing.T) { @@ -66,7 +67,7 @@ func TestStoreSaveAndGet(t *testing.T) { listRef := ref("list") data := types.ImageManifest{Ref: sref(t, "abcdef")} err := store.Save(listRef, ref("exists"), data) - require.NoError(t, err) + assert.NilError(t, err) var testcases = []struct { listRef reference.Reference @@ -92,32 +93,36 @@ func TestStoreSaveAndGet(t *testing.T) { } for _, testcase := range testcases { - actual, err := store.Get(testcase.listRef, testcase.manifestRef) - if testcase.expectedErr != "" { - assert.EqualError(t, err, testcase.expectedErr) - assert.True(t, IsNotFound(err)) - continue - } - if !assert.NoError(t, err, testcase.manifestRef.String()) { - continue - } - assert.Equal(t, testcase.expected, actual, testcase.manifestRef.String()) + t.Run(testcase.manifestRef.String(), func(t *testing.T) { + actual, err := store.Get(testcase.listRef, testcase.manifestRef) + if testcase.expectedErr != "" { + assert.Error(t, err, testcase.expectedErr) + assert.Check(t, IsNotFound(err)) + return + } + assert.NilError(t, err) + assert.DeepEqual(t, testcase.expected, actual, cmpReferenceNamed) + }) } } +var cmpReferenceNamed = cmp.Transformer("namedref", func(r reference.Named) string { + return r.String() +}) + func TestStoreGetList(t *testing.T) { store, cleanup := newTestStore(t) defer cleanup() listRef := ref("list") first := types.ImageManifest{Ref: sref(t, "first")} - require.NoError(t, store.Save(listRef, ref("first"), first)) + assert.NilError(t, store.Save(listRef, ref("first"), first)) second := types.ImageManifest{Ref: sref(t, "second")} - require.NoError(t, store.Save(listRef, ref("exists"), second)) + assert.NilError(t, store.Save(listRef, ref("exists"), second)) list, err := store.GetList(listRef) - require.NoError(t, err) - assert.Len(t, list, 2) + assert.NilError(t, err) + assert.Check(t, is.Len(list, 2)) } func TestStoreGetListDoesNotExist(t *testing.T) { @@ -126,6 +131,6 @@ func TestStoreGetListDoesNotExist(t *testing.T) { listRef := ref("list") _, err := store.GetList(listRef) - assert.EqualError(t, err, "No such manifest: list") - assert.True(t, IsNotFound(err)) + assert.Error(t, err, "No such manifest: list") + assert.Check(t, IsNotFound(err)) } diff --git a/components/cli/cli/registry/client/fetcher.go b/components/cli/cli/registry/client/fetcher.go index 1e748f255d..796bc01eb1 100644 --- a/components/cli/cli/registry/client/fetcher.go +++ b/components/cli/cli/registry/client/fetcher.go @@ -155,7 +155,7 @@ func pullManifestList(ctx context.Context, ref reference.Named, repo distributio } v, ok := manifest.(*schema2.DeserializedManifest) if !ok { - return nil, fmt.Errorf("unsupported manifest format: %s", v) + return nil, fmt.Errorf("unsupported manifest format: %v", v) } manifestRef, err := reference.WithDigest(ref, manifestDescriptor.Digest) diff --git a/components/cli/cli/required_test.go b/components/cli/cli/required_test.go index b70bafb57b..c82e3b965f 100644 --- a/components/cli/cli/required_test.go +++ b/components/cli/cli/required_test.go @@ -5,9 +5,8 @@ import ( "io/ioutil" "testing" + "github.com/gotestyourself/gotestyourself/assert" "github.com/spf13/cobra" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" ) func TestRequiresNoArgs(t *testing.T) { @@ -123,9 +122,7 @@ func runTestCases(t *testing.T, testCases []testCase) { cmd.SetOutput(ioutil.Discard) err := cmd.Execute() - - require.Error(t, err, "Expected an error: %s", tc.expectedError) - assert.Contains(t, err.Error(), tc.expectedError) + assert.ErrorContains(t, err, tc.expectedError) } } diff --git a/components/cli/cli/trust/trust.go b/components/cli/cli/trust/trust.go index 07fb0d09d6..23c23715f7 100644 --- a/components/cli/cli/trust/trust.go +++ b/components/cli/cli/trust/trust.go @@ -300,14 +300,23 @@ type ImageRefAndAuth struct { // GetImageReferencesAndAuth retrieves the necessary reference and auth information for an image name // as an ImageRefAndAuth struct -func GetImageReferencesAndAuth(ctx context.Context, authResolver func(ctx context.Context, index *registrytypes.IndexInfo) types.AuthConfig, imgName string) (ImageRefAndAuth, error) { +func GetImageReferencesAndAuth(ctx context.Context, rs registry.Service, + authResolver func(ctx context.Context, index *registrytypes.IndexInfo) types.AuthConfig, + imgName string, +) (ImageRefAndAuth, error) { ref, err := reference.ParseNormalizedNamed(imgName) if err != nil { return ImageRefAndAuth{}, err } // Resolve the Repository name from fqn to RepositoryInfo - repoInfo, err := registry.ParseRepositoryInfo(ref) + var repoInfo *registry.RepositoryInfo + if rs != nil { + repoInfo, err = rs.ResolveRepository(ref) + } else { + repoInfo, err = registry.ParseRepositoryInfo(ref) + } + if err != nil { return ImageRefAndAuth{}, err } diff --git a/components/cli/cli/trust/trust_test.go b/components/cli/cli/trust/trust_test.go index ea5971482e..67e181ba20 100644 --- a/components/cli/cli/trust/trust_test.go +++ b/components/cli/cli/trust/trust_test.go @@ -6,9 +6,9 @@ import ( "testing" "github.com/docker/distribution/reference" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" digest "github.com/opencontainers/go-digest" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" "github.com/theupdateframework/notary/client" "github.com/theupdateframework/notary/passphrase" "github.com/theupdateframework/notary/trustpinning" @@ -16,46 +16,46 @@ import ( func TestGetTag(t *testing.T) { ref, err := reference.ParseNormalizedNamed("ubuntu@sha256:45b23dee08af5e43a7fea6c4cf9c25ccf269ee113168c19722f87876677c5cb2") - assert.NoError(t, err) + assert.NilError(t, err) tag := getTag(ref) - assert.Equal(t, "", tag) + assert.Check(t, is.Equal("", tag)) ref, err = reference.ParseNormalizedNamed("alpine:latest") - assert.NoError(t, err) + assert.NilError(t, err) tag = getTag(ref) - assert.Equal(t, tag, "latest") + assert.Check(t, is.Equal(tag, "latest")) ref, err = reference.ParseNormalizedNamed("alpine") - assert.NoError(t, err) + assert.NilError(t, err) tag = getTag(ref) - assert.Equal(t, tag, "") + assert.Check(t, is.Equal(tag, "")) } func TestGetDigest(t *testing.T) { ref, err := reference.ParseNormalizedNamed("ubuntu@sha256:45b23dee08af5e43a7fea6c4cf9c25ccf269ee113168c19722f87876677c5cb2") - assert.NoError(t, err) + assert.NilError(t, err) d := getDigest(ref) - assert.Equal(t, digest.Digest("sha256:45b23dee08af5e43a7fea6c4cf9c25ccf269ee113168c19722f87876677c5cb2"), d) + assert.Check(t, is.Equal(digest.Digest("sha256:45b23dee08af5e43a7fea6c4cf9c25ccf269ee113168c19722f87876677c5cb2"), d)) ref, err = reference.ParseNormalizedNamed("alpine:latest") - assert.NoError(t, err) + assert.NilError(t, err) d = getDigest(ref) - assert.Equal(t, digest.Digest(""), d) + assert.Check(t, is.Equal(digest.Digest(""), d)) ref, err = reference.ParseNormalizedNamed("alpine") - assert.NoError(t, err) + assert.NilError(t, err) d = getDigest(ref) - assert.Equal(t, digest.Digest(""), d) + assert.Check(t, is.Equal(digest.Digest(""), d)) } func TestGetSignableRolesError(t *testing.T) { tmpDir, err := ioutil.TempDir("", "notary-test-") - assert.NoError(t, err) + assert.NilError(t, err) defer os.RemoveAll(tmpDir) notaryRepo, err := client.NewFileCachedRepository(tmpDir, "gun", "https://localhost", nil, passphrase.ConstantRetriever("password"), trustpinning.TrustPinConfig{}) - require.NoError(t, err) + assert.NilError(t, err) target := client.Target{} _, err = GetSignableRoles(notaryRepo, &target) - assert.EqualError(t, err, "client is offline") + assert.Error(t, err, "client is offline") } diff --git a/components/cli/cmd/docker/docker.go b/components/cli/cmd/docker/docker.go index 18d7698650..b96c1e2a98 100644 --- a/components/cli/cmd/docker/docker.go +++ b/components/cli/cmd/docker/docker.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" "os" + "strconv" "strings" "github.com/docker/cli/cli" @@ -155,7 +156,7 @@ func main() { stdin, stdout, stderr := term.StdStreams() logrus.SetOutput(stderr) - dockerCli := command.NewDockerCli(stdin, stdout, stderr) + dockerCli := command.NewDockerCli(stdin, stdout, stderr, contentTrustEnabled()) cmd := newDockerCommand(dockerCli) if err := cmd.Execute(); err != nil { @@ -175,6 +176,16 @@ func main() { } } +func contentTrustEnabled() bool { + if e := os.Getenv("DOCKER_CONTENT_TRUST"); e != "" { + if t, err := strconv.ParseBool(e); t || err != nil { + // treat any other value as true + return true + } + } + return false +} + func showVersion() { fmt.Printf("Docker version %s, build %s\n", cli.Version, cli.GitCommit) } diff --git a/components/cli/cmd/docker/docker_test.go b/components/cli/cmd/docker/docker_test.go index cec08495a4..1bf30bf7cb 100644 --- a/components/cli/cmd/docker/docker_test.go +++ b/components/cli/cmd/docker/docker_test.go @@ -7,8 +7,9 @@ import ( "github.com/docker/cli/cli/command" "github.com/docker/cli/cli/debug" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" "github.com/sirupsen/logrus" - "github.com/stretchr/testify/assert" ) func TestClientDebugEnabled(t *testing.T) { @@ -18,15 +19,15 @@ func TestClientDebugEnabled(t *testing.T) { cmd.Flags().Set("debug", "true") err := cmd.PersistentPreRunE(cmd, []string{}) - assert.NoError(t, err) - assert.Equal(t, "1", os.Getenv("DEBUG")) - assert.Equal(t, logrus.DebugLevel, logrus.GetLevel()) + assert.NilError(t, err) + assert.Check(t, is.Equal("1", os.Getenv("DEBUG"))) + assert.Check(t, is.Equal(logrus.DebugLevel, logrus.GetLevel())) } func TestExitStatusForInvalidSubcommandWithHelpFlag(t *testing.T) { discard := ioutil.Discard - cmd := newDockerCommand(command.NewDockerCli(os.Stdin, discard, discard)) + cmd := newDockerCommand(command.NewDockerCli(os.Stdin, discard, discard, false)) cmd.SetArgs([]string{"help", "invalid"}) err := cmd.Execute() - assert.EqualError(t, err, "unknown help topic: invalid") + assert.Error(t, err, "unknown help topic: invalid") } diff --git a/components/cli/dockerfiles/Dockerfile.dev b/components/cli/dockerfiles/Dockerfile.dev index 5734b0cf3a..2cb9862fcd 100644 --- a/components/cli/dockerfiles/Dockerfile.dev +++ b/components/cli/dockerfiles/Dockerfile.dev @@ -1,7 +1,7 @@ FROM golang:1.9.4-alpine3.6 -RUN apk add -U git make bash coreutils ca-certificates +RUN apk add -U git make bash coreutils ca-certificates curl ARG VNDR_SHA=a6e196d8b4b0cbbdc29aebdb20c59ac6926bb384 RUN go get -d github.com/LK4D4/vndr && \ @@ -17,12 +17,11 @@ RUN go get -d github.com/mjibson/esc && \ go build -v -o /usr/bin/esc . && \ rm -rf /go/src/* /go/pkg/* /go/bin/* -ARG FILEWATCHER_SHA=2e12ea42f6c8c089b19e992145bb94e8adaecedb -RUN go get -d github.com/dnephin/filewatcher && \ - cd /go/src/github.com/dnephin/filewatcher && \ - git checkout -q "$FILEWATCHER_SHA" && \ - go build -v -o /usr/bin/filewatcher . && \ - rm -rf /go/src/* /go/pkg/* /go/bin/* +# FIXME(vdemeester) only used for e2e, could be in e2e special image in the future +ARG NOTARY_VERSION=v0.6.0 +RUN export URL=https://github.com/theupdateframework/notary/releases/download; \ + curl -Ls $URL/${NOTARY_VERSION}/notary-Linux-amd64 -o /usr/local/bin/notary && \ + chmod +x /usr/local/bin/notary ENV CGO_ENABLED=0 \ PATH=$PATH:/go/src/github.com/docker/cli/build \ diff --git a/components/cli/docs/yaml/generate.go b/components/cli/docs/yaml/generate.go index fd227eb6e9..3603c97c4a 100644 --- a/components/cli/docs/yaml/generate.go +++ b/components/cli/docs/yaml/generate.go @@ -19,7 +19,7 @@ const descriptionSourcePath = "docs/reference/commandline/" func generateCliYaml(opts *options) error { stdin, stdout, stderr := term.StdStreams() - dockerCli := command.NewDockerCli(stdin, stdout, stderr) + dockerCli := command.NewDockerCli(stdin, stdout, stderr, false) cmd := &cobra.Command{Use: "docker"} commands.AddCommands(cmd, dockerCli) source := filepath.Join(opts.source, descriptionSourcePath) diff --git a/components/cli/e2e/container/create_test.go b/components/cli/e2e/container/create_test.go new file mode 100644 index 0000000000..7aef9136b6 --- /dev/null +++ b/components/cli/e2e/container/create_test.go @@ -0,0 +1,31 @@ +package container + +import ( + "fmt" + "testing" + + "github.com/docker/cli/e2e/internal/fixtures" + "github.com/gotestyourself/gotestyourself/icmd" +) + +func TestCreateWithContentTrust(t *testing.T) { + dir := fixtures.SetupConfigFile(t) + defer dir.Remove() + image := fixtures.CreateMaskedTrustedRemoteImage(t, registryPrefix, "trust-create", "latest") + + defer func() { + icmd.RunCommand("docker", "image", "rm", image).Assert(t, icmd.Success) + }() + + result := icmd.RunCmd( + icmd.Command("docker", "create", image), + fixtures.WithConfig(dir.Path()), + fixtures.WithTrust, + fixtures.WithNotary, + ) + result.Assert(t, icmd.Expected{ + Err: fmt.Sprintf("Tagging %s@sha", image[:len(image)-7]), + }) +} + +// FIXME(vdemeester) TestTrustedCreateFromBadTrustServer needs to be backport too (see https://github.com/moby/moby/pull/36515/files#diff-4b1e56bb77ac16f2ccf956fc24cf0a82L331) diff --git a/components/cli/e2e/container/run_test.go b/components/cli/e2e/container/run_test.go index d0902b152a..3c954ae488 100644 --- a/components/cli/e2e/container/run_test.go +++ b/components/cli/e2e/container/run_test.go @@ -6,12 +6,14 @@ import ( "github.com/docker/cli/e2e/internal/fixtures" shlex "github.com/flynn-archive/go-shlex" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" "github.com/gotestyourself/gotestyourself/golden" "github.com/gotestyourself/gotestyourself/icmd" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" ) +const registryPrefix = "registry:5000" + func TestRunAttachedFromRemoteImageAndRemove(t *testing.T) { image := createRemoteImage(t) @@ -19,10 +21,30 @@ func TestRunAttachedFromRemoteImageAndRemove(t *testing.T) { "docker run --rm %s echo this is output", image)) result.Assert(t, icmd.Success) - assert.Equal(t, "this is output\n", result.Stdout()) + assert.Check(t, is.Equal("this is output\n", result.Stdout())) golden.Assert(t, result.Stderr(), "run-attached-from-remote-and-remove.golden") } +func TestRunWithContentTrust(t *testing.T) { + dir := fixtures.SetupConfigFile(t) + defer dir.Remove() + image := fixtures.CreateMaskedTrustedRemoteImage(t, registryPrefix, "trust-run", "latest") + + defer func() { + icmd.RunCommand("docker", "image", "rm", image).Assert(t, icmd.Success) + }() + + result := icmd.RunCmd( + icmd.Command("docker", "run", image), + fixtures.WithConfig(dir.Path()), + fixtures.WithTrust, + fixtures.WithNotary, + ) + result.Assert(t, icmd.Expected{ + Err: fmt.Sprintf("Tagging %s@sha", image[:len(image)-7]), + }) +} + // TODO: create this with registry API instead of engine API func createRemoteImage(t *testing.T) string { image := "registry:5000/alpine:test-run-pulls" @@ -36,6 +58,6 @@ func createRemoteImage(t *testing.T) string { // TODO: move to gotestyourself func shell(t *testing.T, format string, args ...interface{}) icmd.Cmd { cmd, err := shlex.Split(fmt.Sprintf(format, args...)) - require.NoError(t, err) + assert.NilError(t, err) return icmd.Cmd{Command: cmd} } diff --git a/components/cli/e2e/image/build_test.go b/components/cli/e2e/image/build_test.go index b94566d321..7a1255c493 100644 --- a/components/cli/e2e/image/build_test.go +++ b/components/cli/e2e/image/build_test.go @@ -2,13 +2,12 @@ package image import ( "fmt" - "strings" "testing" "github.com/docker/cli/e2e/internal/fixtures" + "github.com/docker/cli/internal/test/output" "github.com/gotestyourself/gotestyourself/fs" "github.com/gotestyourself/gotestyourself/icmd" - "github.com/pkg/errors" ) func TestBuildFromContextDirectoryWithTag(t *testing.T) { @@ -28,16 +27,72 @@ func TestBuildFromContextDirectoryWithTag(t *testing.T) { withWorkingDir(dir)) result.Assert(t, icmd.Expected{Err: icmd.None}) - assertBuildOutput(t, result.Stdout(), map[int]lineCompare{ - 0: prefix("Sending build context to Docker daemon"), - 1: equals("Step 1/4 : FROM\tregistry:5000/alpine:3.6"), - 3: equals("Step 2/4 : COPY\trun /usr/bin/run"), - 5: equals("Step 3/4 : RUN\t\trun"), - 7: equals("running"), - 8: prefix("Removing intermediate container "), - 10: equals("Step 4/4 : COPY\tdata /data"), - 12: prefix("Successfully built "), - 13: equals("Successfully tagged myimage:latest"), + output.Assert(t, result.Stdout(), map[int]func(string) error{ + 0: output.Prefix("Sending build context to Docker daemon"), + 1: output.Equals("Step 1/4 : FROM\tregistry:5000/alpine:3.6"), + 3: output.Equals("Step 2/4 : COPY\trun /usr/bin/run"), + 5: output.Equals("Step 3/4 : RUN\t\trun"), + 7: output.Equals("running"), + 8: output.Prefix("Removing intermediate container "), + 10: output.Equals("Step 4/4 : COPY\tdata /data"), + 12: output.Prefix("Successfully built "), + 13: output.Equals("Successfully tagged myimage:latest"), + }) +} + +func TestTrustedBuild(t *testing.T) { + dir := fixtures.SetupConfigFile(t) + defer dir.Remove() + image1 := fixtures.CreateMaskedTrustedRemoteImage(t, registryPrefix, "trust-build1", "latest") + image2 := fixtures.CreateMaskedTrustedRemoteImage(t, registryPrefix, "trust-build2", "latest") + + buildDir := fs.NewDir(t, "test-trusted-build-context-dir", + fs.WithFile("Dockerfile", fmt.Sprintf(` + FROM %s as build-base + RUN echo ok > /foo + FROM %s + COPY --from=build-base foo bar + `, image1, image2))) + defer buildDir.Remove() + + result := icmd.RunCmd( + icmd.Command("docker", "build", "-t", "myimage", "."), + withWorkingDir(buildDir), + fixtures.WithConfig(dir.Path()), + fixtures.WithTrust, + fixtures.WithNotary, + ) + + result.Assert(t, icmd.Expected{ + Out: fmt.Sprintf("FROM %s@sha", image1[:len(image1)-7]), + Err: fmt.Sprintf("Tagging %s@sha", image1[:len(image1)-7]), + }) + result.Assert(t, icmd.Expected{ + Out: fmt.Sprintf("FROM %s@sha", image2[:len(image2)-7]), + }) +} + +func TestTrustedBuildUntrustedImage(t *testing.T) { + dir := fixtures.SetupConfigFile(t) + defer dir.Remove() + buildDir := fs.NewDir(t, "test-trusted-build-context-dir", + fs.WithFile("Dockerfile", fmt.Sprintf(` + FROM %s + RUN [] + `, fixtures.AlpineImage))) + defer buildDir.Remove() + + result := icmd.RunCmd( + icmd.Command("docker", "build", "-t", "myimage", "."), + withWorkingDir(buildDir), + fixtures.WithConfig(dir.Path()), + fixtures.WithTrust, + fixtures.WithNotary, + ) + + result.Assert(t, icmd.Expected{ + ExitCode: 1, + Err: "does not have trust data for", }) } @@ -46,38 +101,3 @@ func withWorkingDir(dir *fs.Dir) func(*icmd.Cmd) { cmd.Dir = dir.Path() } } - -func assertBuildOutput(t *testing.T, actual string, expectedLines map[int]lineCompare) { - for i, line := range strings.Split(actual, "\n") { - cmp, ok := expectedLines[i] - if !ok { - continue - } - if err := cmp(line); err != nil { - t.Errorf("line %d: %s", i, err) - } - } - if t.Failed() { - t.Log(actual) - } -} - -type lineCompare func(string) error - -func prefix(expected string) func(string) error { - return func(actual string) error { - if strings.HasPrefix(actual, expected) { - return nil - } - return errors.Errorf("expected %s to start with %s", actual, expected) - } -} - -func equals(expected string) func(string) error { - return func(actual string) error { - if expected == actual { - return nil - } - return errors.Errorf("got %s, expected %s", actual, expected) - } -} diff --git a/components/cli/e2e/image/pull_test.go b/components/cli/e2e/image/pull_test.go index 6316a939e5..d8758d8e86 100644 --- a/components/cli/e2e/image/pull_test.go +++ b/components/cli/e2e/image/pull_test.go @@ -1,7 +1,6 @@ package image import ( - "fmt" "testing" "github.com/docker/cli/e2e/internal/fixtures" @@ -12,37 +11,55 @@ import ( const registryPrefix = "registry:5000" func TestPullWithContentTrust(t *testing.T) { - image := createMaskedTrustedRemoteImage(t, "trust", "latest") + dir := fixtures.SetupConfigFile(t) + defer dir.Remove() + image := fixtures.CreateMaskedTrustedRemoteImage(t, registryPrefix, "trust-pull", "latest") + defer func() { + icmd.RunCommand("docker", "image", "rm", image).Assert(t, icmd.Success) + }() - result := icmd.RunCmd(icmd.Command("docker", "pull", image), fixtures.WithTrust, fixtures.WithNotary) + result := icmd.RunCmd(icmd.Command("docker", "pull", image), + fixtures.WithConfig(dir.Path()), + fixtures.WithTrust, + fixtures.WithNotary, + ) result.Assert(t, icmd.Success) golden.Assert(t, result.Stderr(), "pull-with-content-trust-err.golden") golden.Assert(t, result.Stdout(), "pull-with-content-trust.golden") } -// createMaskedTrustedRemoteImage creates a remote image that is signed with -// content trust, then pushes a different untrusted image at the same tag. -func createMaskedTrustedRemoteImage(t *testing.T, repo, tag string) string { - image := createTrustedRemoteImage(t, repo, tag) - createNamedUnsignedImageFromBusyBox(t, image) - return image -} +func TestPullWithContentTrustUsesCacheWhenNotaryUnavailable(t *testing.T) { + dir := fixtures.SetupConfigFile(t) + defer dir.Remove() + image := fixtures.CreateMaskedTrustedRemoteImage(t, registryPrefix, "trust-pull-unreachable", "latest") + defer func() { + icmd.RunCommand("docker", "image", "rm", image).Assert(t, icmd.Success) + }() + result := icmd.RunCmd(icmd.Command("docker", "pull", image), + fixtures.WithConfig(dir.Path()), + fixtures.WithTrust, + fixtures.WithNotaryServer("https://invalidnotaryserver"), + ) + result.Assert(t, icmd.Expected{ + ExitCode: 1, + Err: "error contacting notary server", + }) -func createTrustedRemoteImage(t *testing.T, repo, tag string) string { - image := fmt.Sprintf("%s/%s:%s", registryPrefix, repo, tag) - icmd.RunCommand("docker", "pull", fixtures.AlpineImage).Assert(t, icmd.Success) - icmd.RunCommand("docker", "tag", fixtures.AlpineImage, image).Assert(t, icmd.Success) - result := icmd.RunCmd( - icmd.Command("docker", "push", image), - fixtures.WithPassphrase("root_password", "repo_password"), fixtures.WithTrust, fixtures.WithNotary) + // Do valid trusted pull to warm cache + result = icmd.RunCmd(icmd.Command("docker", "pull", image), + fixtures.WithConfig(dir.Path()), + fixtures.WithTrust, + fixtures.WithNotary, + ) + result.Assert(t, icmd.Success) + result = icmd.RunCommand("docker", "rmi", image) result.Assert(t, icmd.Success) - icmd.RunCommand("docker", "rmi", image).Assert(t, icmd.Success) - return image -} -func createNamedUnsignedImageFromBusyBox(t *testing.T, image string) { - icmd.RunCommand("docker", "pull", fixtures.BusyboxImage).Assert(t, icmd.Success) - icmd.RunCommand("docker", "tag", fixtures.BusyboxImage, image).Assert(t, icmd.Success) - icmd.RunCommand("docker", "push", image).Assert(t, icmd.Success) - icmd.RunCommand("docker", "rmi", image).Assert(t, icmd.Success) + // Try pull again with invalid notary server, should use cache + result = icmd.RunCmd(icmd.Command("docker", "pull", image), + fixtures.WithConfig(dir.Path()), + fixtures.WithTrust, + fixtures.WithNotaryServer("https://invalidnotaryserver"), + ) + result.Assert(t, icmd.Success) } diff --git a/components/cli/e2e/image/push_test.go b/components/cli/e2e/image/push_test.go new file mode 100644 index 0000000000..9adfa5817f --- /dev/null +++ b/components/cli/e2e/image/push_test.go @@ -0,0 +1,378 @@ +package image + +import ( + "fmt" + "os" + "path/filepath" + "strings" + "testing" + + "github.com/docker/cli/e2e/internal/fixtures" + "github.com/docker/cli/internal/test/output" + "github.com/gotestyourself/gotestyourself/assert" + "github.com/gotestyourself/gotestyourself/fs" + "github.com/gotestyourself/gotestyourself/golden" + "github.com/gotestyourself/gotestyourself/icmd" +) + +const ( + notary = "/usr/local/bin/notary" + + pubkey1 = "./testdata/notary/delgkey1.crt" + privkey1 = "./testdata/notary/delgkey1.key" + pubkey2 = "./testdata/notary/delgkey2.crt" + privkey2 = "./testdata/notary/delgkey2.key" + pubkey3 = "./testdata/notary/delgkey3.crt" + privkey3 = "./testdata/notary/delgkey3.key" + pubkey4 = "./testdata/notary/delgkey4.crt" + privkey4 = "./testdata/notary/delgkey4.key" +) + +func TestPushWithContentTrust(t *testing.T) { + dir := fixtures.SetupConfigFile(t) + defer dir.Remove() + image := createImage(t, registryPrefix, "trust-push", "latest") + + result := icmd.RunCmd(icmd.Command("docker", "push", image), + fixtures.WithConfig(dir.Path()), + fixtures.WithTrust, + fixtures.WithNotary, + fixtures.WithPassphrase("foo", "bar"), + ) + result.Assert(t, icmd.Success) + golden.Assert(t, result.Stderr(), "push-with-content-trust-err.golden") + output.Assert(t, result.Stdout(), map[int]func(string) error{ + 0: output.Equals("The push refers to repository [registry:5000/trust-push]"), + 1: output.Equals("5bef08742407: Preparing"), + 3: output.Equals("latest: digest: sha256:641b95ddb2ea9dc2af1a0113b6b348ebc20872ba615204fbe12148e98fd6f23d size: 528"), + 4: output.Equals("Signing and pushing trust metadata"), + 5: output.Equals(`Finished initializing "registry:5000/trust-push"`), + 6: output.Equals("Successfully signed registry:5000/trust-push:latest"), + }) +} + +func TestPushWithContentTrustUnreachableServer(t *testing.T) { + dir := fixtures.SetupConfigFile(t) + defer dir.Remove() + image := createImage(t, registryPrefix, "trust-push-unreachable", "latest") + + result := icmd.RunCmd(icmd.Command("docker", "push", image), + fixtures.WithConfig(dir.Path()), + fixtures.WithTrust, + fixtures.WithNotaryServer("https://invalidnotaryserver"), + ) + result.Assert(t, icmd.Expected{ + ExitCode: 1, + Err: "error contacting notary server", + }) +} + +func TestPushWithContentTrustExistingTag(t *testing.T) { + dir := fixtures.SetupConfigFile(t) + defer dir.Remove() + image := createImage(t, registryPrefix, "trust-push-existing", "latest") + + result := icmd.RunCmd(icmd.Command("docker", "push", image)) + result.Assert(t, icmd.Success) + + result = icmd.RunCmd(icmd.Command("docker", "push", image), + fixtures.WithConfig(dir.Path()), + fixtures.WithTrust, + fixtures.WithNotary, + fixtures.WithPassphrase("foo", "bar"), + ) + result.Assert(t, icmd.Expected{ + Out: "Signing and pushing trust metadata", + }) + + // Re-push + result = icmd.RunCmd(icmd.Command("docker", "push", image), + fixtures.WithConfig(dir.Path()), + fixtures.WithTrust, + fixtures.WithNotary, + fixtures.WithPassphrase("foo", "bar"), + ) + result.Assert(t, icmd.Expected{ + Out: "Signing and pushing trust metadata", + }) +} + +func TestPushWithContentTrustReleasesDelegationOnly(t *testing.T) { + role := "targets/releases" + + dir := fixtures.SetupConfigFile(t) + defer dir.Remove() + copyPrivateKey(t, dir.Join("trust", "private"), privkey1) + notaryDir := setupNotaryConfig(t, dir) + defer notaryDir.Remove() + homeDir := fs.NewDir(t, "push_test_home") + defer notaryDir.Remove() + + baseRef := fmt.Sprintf("%s/%s", registryPrefix, "trust-push-releases-delegation") + targetRef := fmt.Sprintf("%s:%s", baseRef, "latest") + + // Init repository + notaryInit(t, notaryDir, homeDir, baseRef) + // Add delegation key (public key) + notaryAddDelegation(t, notaryDir, homeDir, baseRef, role, pubkey1) + // Publish it + notaryPublish(t, notaryDir, homeDir, baseRef) + // Import private key + notaryImportPrivateKey(t, notaryDir, homeDir, baseRef, role, privkey1) + + // Tag & push with content trust + icmd.RunCommand("docker", "pull", fixtures.AlpineImage).Assert(t, icmd.Success) + icmd.RunCommand("docker", "tag", fixtures.AlpineImage, targetRef).Assert(t, icmd.Success) + result := icmd.RunCmd(icmd.Command("docker", "push", targetRef), + fixtures.WithConfig(dir.Path()), + fixtures.WithTrust, + fixtures.WithNotary, + fixtures.WithPassphrase("foo", "foo"), + ) + result.Assert(t, icmd.Expected{ + Out: "Signing and pushing trust metadata", + }) + + targetsInRole := notaryListTargetsInRole(t, notaryDir, homeDir, baseRef, role) + assert.Assert(t, targetsInRole["latest"] == role, "%v", targetsInRole) + targetsInRole = notaryListTargetsInRole(t, notaryDir, homeDir, baseRef, "targets") + assert.Assert(t, targetsInRole["latest"] != "targets", "%v", targetsInRole) + + result = icmd.RunCmd(icmd.Command("docker", "pull", targetRef), + fixtures.WithConfig(dir.Path()), + fixtures.WithTrust, + fixtures.WithNotary, + ) + result.Assert(t, icmd.Success) +} + +func TestPushWithContentTrustSignsAllFirstLevelRolesWeHaveKeysFor(t *testing.T) { + dir := fixtures.SetupConfigFile(t) + defer dir.Remove() + copyPrivateKey(t, dir.Join("trust", "private"), privkey1) + copyPrivateKey(t, dir.Join("trust", "private"), privkey2) + copyPrivateKey(t, dir.Join("trust", "private"), privkey3) + notaryDir := setupNotaryConfig(t, dir) + defer notaryDir.Remove() + homeDir := fs.NewDir(t, "push_test_home") + defer notaryDir.Remove() + + baseRef := fmt.Sprintf("%s/%s", registryPrefix, "trust-push-releases-first-roles") + targetRef := fmt.Sprintf("%s:%s", baseRef, "latest") + + // Init repository + notaryInit(t, notaryDir, homeDir, baseRef) + // Add delegation key (public key) + notaryAddDelegation(t, notaryDir, homeDir, baseRef, "targets/role1", pubkey1) + notaryAddDelegation(t, notaryDir, homeDir, baseRef, "targets/role2", pubkey2) + notaryAddDelegation(t, notaryDir, homeDir, baseRef, "targets/role3", pubkey3) + notaryAddDelegation(t, notaryDir, homeDir, baseRef, "targets/role1/subrole", pubkey3) + // Import private key + notaryImportPrivateKey(t, notaryDir, homeDir, baseRef, "targets/role1", privkey1) + notaryImportPrivateKey(t, notaryDir, homeDir, baseRef, "targets/role2", privkey2) + notaryImportPrivateKey(t, notaryDir, homeDir, baseRef, "targets/role1/subrole", privkey3) + // Publish it + notaryPublish(t, notaryDir, homeDir, baseRef) + + // Tag & push with content trust + icmd.RunCommand("docker", "pull", fixtures.AlpineImage).Assert(t, icmd.Success) + icmd.RunCommand("docker", "tag", fixtures.AlpineImage, targetRef).Assert(t, icmd.Success) + result := icmd.RunCmd(icmd.Command("docker", "push", targetRef), + fixtures.WithConfig(dir.Path()), + fixtures.WithTrust, + fixtures.WithNotary, + fixtures.WithPassphrase("foo", "foo"), + ) + result.Assert(t, icmd.Expected{ + Out: "Signing and pushing trust metadata", + }) + + // check to make sure that the target has been added to targets/role1 and targets/role2, and + // not targets (because there are delegations) or targets/role3 (due to missing key) or + // targets/role1/subrole (due to it being a second level delegation) + targetsInRole := notaryListTargetsInRole(t, notaryDir, homeDir, baseRef, "targets/role1") + assert.Assert(t, targetsInRole["latest"] == "targets/role1", "%v", targetsInRole) + targetsInRole = notaryListTargetsInRole(t, notaryDir, homeDir, baseRef, "targets/role2") + assert.Assert(t, targetsInRole["latest"] == "targets/role2", "%v", targetsInRole) + targetsInRole = notaryListTargetsInRole(t, notaryDir, homeDir, baseRef, "targets") + assert.Assert(t, targetsInRole["latest"] != "targets", "%v", targetsInRole) + + assert.NilError(t, os.RemoveAll(filepath.Join(dir.Join("trust")))) + // Try to pull, should fail because non of these are the release role + // FIXME(vdemeester) should be unit test + result = icmd.RunCmd(icmd.Command("docker", "pull", targetRef), + fixtures.WithConfig(dir.Path()), + fixtures.WithTrust, + fixtures.WithNotary, + ) + result.Assert(t, icmd.Expected{ + ExitCode: 1, + }) +} + +func TestPushWithContentTrustSignsForRolesWithKeysAndValidPaths(t *testing.T) { + dir := fixtures.SetupConfigFile(t) + defer dir.Remove() + copyPrivateKey(t, dir.Join("trust", "private"), privkey1) + copyPrivateKey(t, dir.Join("trust", "private"), privkey2) + copyPrivateKey(t, dir.Join("trust", "private"), privkey3) + copyPrivateKey(t, dir.Join("trust", "private"), privkey4) + notaryDir := setupNotaryConfig(t, dir) + defer notaryDir.Remove() + homeDir := fs.NewDir(t, "push_test_home") + defer notaryDir.Remove() + + baseRef := fmt.Sprintf("%s/%s", registryPrefix, "trust-push-releases-keys-valid-paths") + targetRef := fmt.Sprintf("%s:%s", baseRef, "latest") + + // Init repository + notaryInit(t, notaryDir, homeDir, baseRef) + // Add delegation key (public key) + notaryAddDelegation(t, notaryDir, homeDir, baseRef, "targets/role1", pubkey1, "l", "z") + notaryAddDelegation(t, notaryDir, homeDir, baseRef, "targets/role2", pubkey2, "x", "y") + notaryAddDelegation(t, notaryDir, homeDir, baseRef, "targets/role3", pubkey3, "latest") + notaryAddDelegation(t, notaryDir, homeDir, baseRef, "targets/role4", pubkey4, "latest") + // Import private keys (except 3rd key) + notaryImportPrivateKey(t, notaryDir, homeDir, baseRef, "targets/role1", privkey1) + notaryImportPrivateKey(t, notaryDir, homeDir, baseRef, "targets/role2", privkey2) + notaryImportPrivateKey(t, notaryDir, homeDir, baseRef, "targets/role4", privkey4) + // Publish it + notaryPublish(t, notaryDir, homeDir, baseRef) + + // Tag & push with content trust + icmd.RunCommand("docker", "pull", fixtures.AlpineImage).Assert(t, icmd.Success) + icmd.RunCommand("docker", "tag", fixtures.AlpineImage, targetRef).Assert(t, icmd.Success) + result := icmd.RunCmd(icmd.Command("docker", "push", targetRef), + fixtures.WithConfig(dir.Path()), + fixtures.WithTrust, + fixtures.WithNotary, + fixtures.WithPassphrase("foo", "foo"), + ) + result.Assert(t, icmd.Expected{ + Out: "Signing and pushing trust metadata", + }) + + // check to make sure that the target has been added to targets/role1 and targets/role4, and + // not targets (because there are delegations) or targets/role2 (due to path restrictions) or + // targets/role3 (due to missing key) + targetsInRole := notaryListTargetsInRole(t, notaryDir, homeDir, baseRef, "targets/role1") + assert.Assert(t, targetsInRole["latest"] == "targets/role1", "%v", targetsInRole) + targetsInRole = notaryListTargetsInRole(t, notaryDir, homeDir, baseRef, "targets/role4") + assert.Assert(t, targetsInRole["latest"] == "targets/role4", "%v", targetsInRole) + targetsInRole = notaryListTargetsInRole(t, notaryDir, homeDir, baseRef, "targets") + assert.Assert(t, targetsInRole["latest"] != "targets", "%v", targetsInRole) + + assert.NilError(t, os.RemoveAll(filepath.Join(dir.Join("trust")))) + // Try to pull, should fail because non of these are the release role + // FIXME(vdemeester) should be unit test + result = icmd.RunCmd(icmd.Command("docker", "pull", targetRef), + fixtures.WithConfig(dir.Path()), + fixtures.WithTrust, + fixtures.WithNotary, + ) + result.Assert(t, icmd.Expected{ + ExitCode: 1, + }) +} + +func createImage(t *testing.T, registryPrefix, repo, tag string) string { + image := fmt.Sprintf("%s/%s:%s", registryPrefix, repo, tag) + icmd.RunCommand("docker", "pull", fixtures.AlpineImage).Assert(t, icmd.Success) + icmd.RunCommand("docker", "tag", fixtures.AlpineImage, image).Assert(t, icmd.Success) + return image +} + +func withNotaryPassphrase(pwd string) func(*icmd.Cmd) { + return func(c *icmd.Cmd) { + c.Env = append(c.Env, []string{ + fmt.Sprintf("NOTARY_ROOT_PASSPHRASE=%s", pwd), + fmt.Sprintf("NOTARY_TARGETS_PASSPHRASE=%s", pwd), + fmt.Sprintf("NOTARY_SNAPSHOT_PASSPHRASE=%s", pwd), + fmt.Sprintf("NOTARY_DELEGATION_PASSPHRASE=%s", pwd), + }...) + } +} + +func notaryImportPrivateKey(t *testing.T, notaryDir, homeDir *fs.Dir, baseRef, role, privkey string) { + icmd.RunCmd( + icmd.Command(notary, "-c", notaryDir.Join("client-config.json"), "key", "import", privkey, "-g", baseRef, "-r", role), + withNotaryPassphrase("foo"), + fixtures.WithHome(homeDir.Path()), + ).Assert(t, icmd.Success) +} + +func notaryPublish(t *testing.T, notaryDir, homeDir *fs.Dir, baseRef string) { + icmd.RunCmd( + icmd.Command(notary, "-c", notaryDir.Join("client-config.json"), "publish", baseRef), + withNotaryPassphrase("foo"), + fixtures.WithHome(homeDir.Path()), + ).Assert(t, icmd.Success) +} + +func notaryAddDelegation(t *testing.T, notaryDir, homeDir *fs.Dir, baseRef, role, pubkey string, paths ...string) { + pathsArg := "--all-paths" + if len(paths) > 0 { + pathsArg = "--paths=" + strings.Join(paths, ",") + } + icmd.RunCmd( + icmd.Command(notary, "-c", notaryDir.Join("client-config.json"), "delegation", "add", baseRef, role, pubkey, pathsArg), + withNotaryPassphrase("foo"), + fixtures.WithHome(homeDir.Path()), + ).Assert(t, icmd.Success) +} + +func notaryInit(t *testing.T, notaryDir, homeDir *fs.Dir, baseRef string) { + icmd.RunCmd( + icmd.Command(notary, "-c", notaryDir.Join("client-config.json"), "init", baseRef), + withNotaryPassphrase("foo"), + fixtures.WithHome(homeDir.Path()), + ).Assert(t, icmd.Success) +} + +func notaryListTargetsInRole(t *testing.T, notaryDir, homeDir *fs.Dir, baseRef, role string) map[string]string { + result := icmd.RunCmd( + icmd.Command(notary, "-c", notaryDir.Join("client-config.json"), "list", baseRef, "-r", role), + fixtures.WithHome(homeDir.Path()), + ) + out := result.Combined() + + // should look something like: + // NAME DIGEST SIZE (BYTES) ROLE + // ------------------------------------------------------------------------------------------------------ + // latest 24a36bbc059b1345b7e8be0df20f1b23caa3602e85d42fff7ecd9d0bd255de56 1377 targets + + targets := make(map[string]string) + + // no target + lines := strings.Split(strings.TrimSpace(out), "\n") + if len(lines) == 1 && strings.Contains(out, "No targets present in this repository.") { + return targets + } + + // otherwise, there is at least one target + assert.Assert(t, len(lines) >= 3, "output is %s", out) + + for _, line := range lines[2:] { + tokens := strings.Fields(line) + assert.Assert(t, len(tokens) == 4) + targets[tokens[0]] = tokens[3] + } + + return targets +} + +func setupNotaryConfig(t *testing.T, dockerConfigDir fs.Dir) *fs.Dir { + return fs.NewDir(t, "notary_test", fs.WithMode(0700), + fs.WithFile("client-config.json", fmt.Sprintf(` +{ + "trust_dir": "%s", + "remote_server": { + "url": "%s" + } +}`, dockerConfigDir.Join("trust"), fixtures.NotaryURL)), + ) +} + +func copyPrivateKey(t *testing.T, dir, source string) { + icmd.RunCommand("/bin/cp", source, dir).Assert(t, icmd.Success) +} diff --git a/components/cli/e2e/image/testdata/notary/delgkey1.crt b/components/cli/e2e/image/testdata/notary/delgkey1.crt new file mode 100644 index 0000000000..2218f23c89 --- /dev/null +++ b/components/cli/e2e/image/testdata/notary/delgkey1.crt @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDhTCCAm2gAwIBAgIJAP2EcMN2UXPcMA0GCSqGSIb3DQEBCwUAMFcxCzAJBgNV +BAYTAlVTMQswCQYDVQQIEwJDQTEVMBMGA1UEBxMMU2FuRnJhbmNpc2NvMQ8wDQYD +VQQKEwZEb2NrZXIxEzARBgNVBAMTCmRlbGVnYXRpb24wHhcNMTYwOTI4MTc0ODQ4 +WhcNMjYwNjI4MTc0ODQ4WjBXMQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExFTAT +BgNVBAcTDFNhbkZyYW5jaXNjbzEPMA0GA1UEChMGRG9ja2VyMRMwEQYDVQQDEwpk +ZWxlZ2F0aW9uMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvgewhaYs +Ke5s2AM7xxKrT4A6n7hW17qSnBjonCcPcwTFmYqIOdxWjYITgJuHrTwB4ZhBqWS7 +tTsUUu6hWLMeB7Uo5/GEQAAZspKkT9G/rNKF9lbWK9PPhGGkeR01c/Q932m92Hsn +fCQ0Pp/OzD3nVTh0v9HKk+PObNMOCcqG87eYs4ylPRxs0RrE/rP+bEGssKQSbeCZ +wazDnO+kiatVgKQZ2CK23iFdRE1z2rzqVDeaFWdvBqrRdWnkOZClhlLgEQ5nK2yV +B6tSqOiI3MmHyHzIkGOQJp2/s7Pe0ckEkzsjTsJW8oKHlBBl6pRxHIKzNN4VFbeB +vvYvrogrDrC/owIDAQABo1QwUjAMBgNVHRMBAf8EAjAAMA4GA1UdDwEB/wQEAwIF +oDATBgNVHSUEDDAKBggrBgEFBQcDAzAdBgNVHQ4EFgQUFoHfukRa6qGk1ncON64Z +ASKlZdkwDQYJKoZIhvcNAQELBQADggEBAEq9Adpd03CPmpbRtTAJGAkjjLFr60sV +2r+/l/m9R31ZCN9ymM9nxToQ8zfMdeAh/nnPcErziil2gDVqXueCNDkRj09tmDIE +Q1Oc92uyNZNgcECow77cKZCTZSTku+qsJrYaykH5vSnia8ltcKj8inJedIcpBR+p +608HEQvF0Eg5eaLPJwH48BCb0Gqdri1dJgrNnqptz7MDr8M+u7tHVulbAd3YxLlq +JH1W2bkVUx6esbn/MUE5HL5iTuOYREEINvBSmLdmmFkampmCnCB/bDEyJeL9bAkt +ZPIi0UNSnqFKLSP1Vf8AGLXt6iO7+1OGvtsDXEEYdXVOMsSXZtUuT7A= +-----END CERTIFICATE----- diff --git a/components/cli/e2e/image/testdata/notary/delgkey1.key b/components/cli/e2e/image/testdata/notary/delgkey1.key new file mode 100644 index 0000000000..cb37efc94a --- /dev/null +++ b/components/cli/e2e/image/testdata/notary/delgkey1.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEAvgewhaYsKe5s2AM7xxKrT4A6n7hW17qSnBjonCcPcwTFmYqI +OdxWjYITgJuHrTwB4ZhBqWS7tTsUUu6hWLMeB7Uo5/GEQAAZspKkT9G/rNKF9lbW +K9PPhGGkeR01c/Q932m92HsnfCQ0Pp/OzD3nVTh0v9HKk+PObNMOCcqG87eYs4yl +PRxs0RrE/rP+bEGssKQSbeCZwazDnO+kiatVgKQZ2CK23iFdRE1z2rzqVDeaFWdv +BqrRdWnkOZClhlLgEQ5nK2yVB6tSqOiI3MmHyHzIkGOQJp2/s7Pe0ckEkzsjTsJW +8oKHlBBl6pRxHIKzNN4VFbeBvvYvrogrDrC/owIDAQABAoIBAB/o8KZwsgfUhqh7 +WoViSCwQb0e0z7hoFwhpUl4uXPTGf1v6HEgDDPG0PwwgkdbwNaypQZVtWevj4NTQ +R326jjdjH1xbfQa2PZpz722L3jDqJR6plEtFxRoIv3KrCffPsrgabIu2mnnJJpDB +ixtW5cq0sT4ov2i4H0i85CWWwbSY/G/MHsvCuK9PhoCj9uToVqrf1KrAESE5q4fh +mPSYUL99KVnj7SZkUz+79rc8sLLPVks3szZACMlm1n05ZTj/d6Nd2ZZUO45DllIj +1XJghfWmnChrB/P/KYXgQ3Y9BofIAw1ra2y3wOZeqRFNsbmojcGldfdtN/iQzhEj +uk4ThokCgYEA9FTmv36N8qSPWuqX/KzkixDQ8WrDGohcB54kK98Wx4ijXx3i38SY +tFjO8YUS9GVo1+UgmRjZbzVX7xeum6+TdBBwOjNOxEQ4tzwiQBWDdGpli8BccdJ2 +OOIVxSslWhiUWfpYloXVetrR88iHbT882g795pbonDaJdXSLnij4UW8CgYEAxxrr +QFpsmOEZvI/yPSOGdG7A1RIsCeH+cEOf4cKghs7+aCtAHlIweztNOrqirl3oKI1r +I0zQl46WsaW8S/y99v9lmmnZbWwqLa4vIu0NWs0zaZdzKZw3xljMhgp4Ge69hHa2 +utCtAxcX+7q/yLlHoTiYwKdxX54iLkheCB8csw0CgYEAleEG820kkjXUIodJ2JwO +Tihwo8dEC6CeI6YktizRgnEVFqH0rCOjMO5Rc+KX8AfNOrK5PnD54LguSuKSH7qi +j04OKgWTSd43lF90+y63RtCFnibQDpp2HwrBJAQFk7EEP/XMJfnPLN/SbuMSADgM +kg8kPTFRW5Iw3DYz9z9WpE0CgYAkn6/8Q2XMbUOFqti9JEa8Lg8sYk5VdwuNbPMA +3QMYKQUk9ieyLB4c3Nik3+XCuyVUKEc31A5egmz3umu7cn8i6vGuiJ/k/8t2YZ7s +Bry5Ihu95Yzab5DW3Eiqs0xKQN79ebS9AluAwQO5Wy2h52rknfuDHIm/M+BHsSoS +xl5KFQKBgQCokCsYuX1z2GojHw369/R2aX3ovCGuHqy4k7fWxUrpHTHvth2+qNPr +84qLJ9rLWoZE5sUiZ5YdwCgW877EdfkT+v4aaBX79ixso5VdqgJ/PdnoNntah/Vq +njQiW1skn6/P5V/eyimN2n0VsyBr/zMDEtYTRP/Tb1zi/njFLQkZEA== +-----END RSA PRIVATE KEY----- diff --git a/components/cli/e2e/image/testdata/notary/delgkey2.crt b/components/cli/e2e/image/testdata/notary/delgkey2.crt new file mode 100644 index 0000000000..bec084790a --- /dev/null +++ b/components/cli/e2e/image/testdata/notary/delgkey2.crt @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDhTCCAm2gAwIBAgIJAIq8naKlYAQfMA0GCSqGSIb3DQEBCwUAMFcxCzAJBgNV +BAYTAlVTMQswCQYDVQQIEwJDQTEVMBMGA1UEBxMMU2FuRnJhbmNpc2NvMQ8wDQYD +VQQKEwZEb2NrZXIxEzARBgNVBAMTCmRlbGVnYXRpb24wHhcNMTYwOTI4MTc0ODQ4 +WhcNMjYwNjI4MTc0ODQ4WjBXMQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExFTAT +BgNVBAcTDFNhbkZyYW5jaXNjbzEPMA0GA1UEChMGRG9ja2VyMRMwEQYDVQQDEwpk +ZWxlZ2F0aW9uMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyY2EWYTW +5VHipw08t675upmD6a+akiuZ1z+XpuOxZCgjZ0aHfoOe8wGKg3Ohz7UCBdD5Mob/ +L/qvRlsCaqPHGZKIyyX1HDO4mpuQQFBhYxt+ZAO3AaawEUOw2rwwMDEjLnDDTSZM +z8jxCMvsJjBDqgb8g3z+AmjducQ/OH6llldgHIBY8ioRbROCL2PGgqywWq2fThav +c70YMxtKviBGDNCouYeQ8JMK/PuLwPNDXNQAagFHVARXiUv/ILHk7ImYnSGJUcuk +JTUGN2MBnpY0eakg7i+4za8sjjqOdn+2I6aVzlGJDSiRP72nkg/cE4BqMl9FrMwK +9iS8xa9yMDLUvwIDAQABo1QwUjAMBgNVHRMBAf8EAjAAMA4GA1UdDwEB/wQEAwIF +oDATBgNVHSUEDDAKBggrBgEFBQcDAzAdBgNVHQ4EFgQUvQzzFmh3Sv3HcdExY3wx +/1u6JLAwDQYJKoZIhvcNAQELBQADggEBAJcmDme2Xj/HPUPwaN/EyCmjhY73EiHO +x6Pm16tscg5JGn5A+u3CZ1DmxUYl8Hp6MaW/sWzdtL0oKJg76pynadCWh5EacFR8 +u+2GV/IcN9mSX6JQzvrqbjSqo5/FehqBD+W5h3euwwApWA3STAadYeyEfmdOA3SQ +W1vzrA1y7i8qgTqeJ7UX1sEAXlIhBK2zPYaMB+en+ZOiPyNxJYj6IDdGdD2paC9L +6H9wKC+GAUTSdCWp89HP7ETSXEGr94AXkrwU+qNsiN+OyK8ke0EMngEPh5IQoplw +/7zEZCth3oKxvR1/4S5LmTVaHI2ZlbU4q9bnY72G4tw8YQr2gcBGo4w= +-----END CERTIFICATE----- diff --git a/components/cli/e2e/image/testdata/notary/delgkey2.key b/components/cli/e2e/image/testdata/notary/delgkey2.key new file mode 100644 index 0000000000..5ccabe908f --- /dev/null +++ b/components/cli/e2e/image/testdata/notary/delgkey2.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEogIBAAKCAQEAyY2EWYTW5VHipw08t675upmD6a+akiuZ1z+XpuOxZCgjZ0aH +foOe8wGKg3Ohz7UCBdD5Mob/L/qvRlsCaqPHGZKIyyX1HDO4mpuQQFBhYxt+ZAO3 +AaawEUOw2rwwMDEjLnDDTSZMz8jxCMvsJjBDqgb8g3z+AmjducQ/OH6llldgHIBY +8ioRbROCL2PGgqywWq2fThavc70YMxtKviBGDNCouYeQ8JMK/PuLwPNDXNQAagFH +VARXiUv/ILHk7ImYnSGJUcukJTUGN2MBnpY0eakg7i+4za8sjjqOdn+2I6aVzlGJ +DSiRP72nkg/cE4BqMl9FrMwK9iS8xa9yMDLUvwIDAQABAoIBAHmffvzx7ydESWwa +zcfdu26BkptiTvjjfJrqEd4wSewxWGPKqJqMXE8xX99A2KTZClZuKuH1mmnecQQY +iRXGrK9ewFMuHYGeKEiLlPlqR8ohXhyGLVm+t0JDwaXMp5t9G0i73O5iLTm5fNGd +FGxa9YnVW20Q8MqNczbVGH1D1zInhxzzOyFzBd4bBBJ8PdrUdyLpd7+RxY2ghnbT +p9ZANR2vk5zmDLJgZx72n/u+miJWuhY6p0v3Vq4z/HHgdhf+K6vpDdzTcYlA0rO4 +c/c+RKED3ZadGUD5QoLsmEN0e3FVSMPN1kt4ZRTqWfH8f2X4mLz33aBryTjktP6+ +1rX6ThECgYEA74wc1Tq23B5R0/GaMm1AK3Ko2zzTD8wK7NSCElh2dls02B+GzrEB +aE3A2GMQSuzb+EA0zkipwANBaqs3ZemH5G1pu4hstQsXCMd4jAJn0TmTXlplXBCf +PSc8ZUU6XcJENRr9Q7O9/TGlgahX+z0ndxYx/CMCsSu7XsMg4IZsbAcCgYEA12Vb +wKOVG15GGp7pMshr+2rQfVimARUP4gf3JnQmenktI4PfdnMW3a4L3DEHfLhIerwT +6lRp/NpxSADmuT4h1UO1l2lc+gmTVPw0Vbl6VwHpgS5Kfu4ZyM6n3S66f/dE4nu7 +hQF9yZz7vn5Agghak4p6a1wC1gdMzR1tvxFzk4kCgYByBMTskWfcWeok8Yitm+bB +R3Ar+kWT7VD97SCETusD5uG+RTNLSmEbHnc+B9kHcLo67YS0800pAeOvPBPARGnU +RmffRU5I1iB+o0MzkSmNItSMQoagTaEd4IEUyuC/I+qHRHNsOC+kRm86ycAm67LP +MhdUpe1wGxqyPjp15EXTHQKBgDKzFu+3EWfJvvKRKQ7dAh3BvKVkcl6a2Iw5l8Ej +YdM+JpPPfI/i8yTmzL/dgoem0Nii4IUtrWzo9fUe0TAVId2S/HFRSaNJEbbVTnRH +HjbQqmfPv5U08jjD+9siHp/0UfCFc1QRT8xe+RqTmReCY9+KntoaZEiAm2FEZgqt +TukRAoGAf7QqbTP5/UH1KSkX89F5qy/6GS3pw6TLj9Ufm/l/NO8Um8gag6YhEKWR +7HpkpCqjfWj8Av8ESR9cqddPGrbdqXFm9z7dCjlAd5T3Q3h/h+v+JzLQWbsI6WOb +SsOSWNyE006ZZdIiFwO6GfxpLI24sVtYKgyob6Q71oxSqfnrnT0= +-----END RSA PRIVATE KEY----- diff --git a/components/cli/e2e/image/testdata/notary/delgkey3.crt b/components/cli/e2e/image/testdata/notary/delgkey3.crt new file mode 100644 index 0000000000..f434b45fc8 --- /dev/null +++ b/components/cli/e2e/image/testdata/notary/delgkey3.crt @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDhTCCAm2gAwIBAgIJAKHt/jxiWqMtMA0GCSqGSIb3DQEBCwUAMFcxCzAJBgNV +BAYTAlVTMQswCQYDVQQIEwJDQTEVMBMGA1UEBxMMU2FuRnJhbmNpc2NvMQ8wDQYD +VQQKEwZEb2NrZXIxEzARBgNVBAMTCmRlbGVnYXRpb24wHhcNMTYwOTI4MTc0ODQ5 +WhcNMjYwNjI4MTc0ODQ5WjBXMQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExFTAT +BgNVBAcTDFNhbkZyYW5jaXNjbzEPMA0GA1UEChMGRG9ja2VyMRMwEQYDVQQDEwpk +ZWxlZ2F0aW9uMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqfbJk2Dk +C9FJVjV2+Q2CQrJphG3vFc1Qlu9jgVA5RhGmF9jJzetsclsV/95nBhinIGcSmPQA +l318G7Bz/cG/6O2n5+hj+S1+YOvQweReZj3d4kCeS86SOyLNTpMD9gsF0S8nR1RN +h0jD4t1vxAVeGD1o61U8/k0O5eDoeOfOSWZagKk5PhyrMZgNip4IrG46umCkFlrw +zMMcgQdwTQXywPqkr/LmYpqT1WpMlzHYTQEY8rKorIJQbPtHVYdr4UxYnNmk6fbU +biEP1DQlwjBWcFTsDLqXKP/K+e3O0/e/hMB0y7Tj9fZ7Viw0t5IKXZPsxMhwknUT +9vmPzIJO6NiniwIDAQABo1QwUjAMBgNVHRMBAf8EAjAAMA4GA1UdDwEB/wQEAwIF +oDATBgNVHSUEDDAKBggrBgEFBQcDAzAdBgNVHQ4EFgQUdTXRP1EzxQ+UDZSoheVo +Mobud1cwDQYJKoZIhvcNAQELBQADggEBADV9asTWWdbmpkeRuKyi0xGho39ONK88 +xxkFlco766BVgemo/rGQj3oPuw6M6SzHFoJ6JUPjmLiAQDIGEU/2/b6LcOuLjP+4 +YejCcDTY3lSW/HMNoAmzr2foo/LngNGfe/qhVFUqV7GjFT9+XzFFBfIZ1cQiL2ed +kc8rgQxFPwWXFCSwaENWeFnMDugkd+7xanoAHq8GsJpg5fTruDTmJkUqC2RNiMLn +WM7QaqW7+lmUnMnc1IBoz0hFhgoiadWM/1RQxx51zTVw6Au1koIm4ZXu5a+/WyC8 +K1+HyUbc0AVaDaRBpRSOR9aHRwLGh6WQ4aUZQNyJroc999qfYrDEEV8= +-----END CERTIFICATE----- diff --git a/components/cli/e2e/image/testdata/notary/delgkey3.key b/components/cli/e2e/image/testdata/notary/delgkey3.key new file mode 100644 index 0000000000..a61d18cc3d --- /dev/null +++ b/components/cli/e2e/image/testdata/notary/delgkey3.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpQIBAAKCAQEAqfbJk2DkC9FJVjV2+Q2CQrJphG3vFc1Qlu9jgVA5RhGmF9jJ +zetsclsV/95nBhinIGcSmPQAl318G7Bz/cG/6O2n5+hj+S1+YOvQweReZj3d4kCe +S86SOyLNTpMD9gsF0S8nR1RNh0jD4t1vxAVeGD1o61U8/k0O5eDoeOfOSWZagKk5 +PhyrMZgNip4IrG46umCkFlrwzMMcgQdwTQXywPqkr/LmYpqT1WpMlzHYTQEY8rKo +rIJQbPtHVYdr4UxYnNmk6fbUbiEP1DQlwjBWcFTsDLqXKP/K+e3O0/e/hMB0y7Tj +9fZ7Viw0t5IKXZPsxMhwknUT9vmPzIJO6NiniwIDAQABAoIBAQCAr/ed3A2umO7T +FDYZik3nXBiiiW4t7r+nGGgZ3/kNgY1lnuHlROxehXLZwbX1mrLnyML/BjhwezV9 +7ZNVPd6laVPpNj6DyxtWHRZ5yARlm1Al39E7CpQTrF0QsiWcpGnqIa62xjDRTpnq +askV/Q5qggyvqmE9FnFCQpEiAjlhvp7F0kVHVJm9s3MK3zSyR0UTZ3cpYus2Jr2z +OotHgAMHq5Hgb3dvxOeE2xRMeYAVDujbkNzXm2SddAtiRdLhWDh7JIr3zXhp0HyN +4rLOyhlgz00oIGeDt/C0q3fRmghr3iZOG+7m2sUx0FD1Ru1dI9v2A+jYmIVNW6+x +YJk5PzxJAoGBANDj7AGdcHSci/LDBPoTTUiz3uucAd27/IJma/iy8mdbVfOAb0Fy +PRSPvoozlpZyOxg2J4eH/o4QxQR4lVKtnLKZLNHK2tg3LarwyBX1LiI3vVlB+DT1 +AmV8i5bJAckDhqFeEH5qdWZFi03oZsSXWEqX5iMYCrdK5lTZggcrFZeHAoGBANBL +fkk3knAdcVfTYpmHx18GBi2AsCWTd20KD49YBdbVy0Y2Jaa1EJAmGWpTUKdYx40R +H5CuGgcAviXQz3bugdTU1I3tAclBtpJNU7JkhuE+Epz0CM/6WERJrE0YxcGQA5ui +6fOguFyiXD1/85jrDBOKy74aoS7lYz9r/a6eqmjdAoGBAJpm/nmrIAZx+Ff2ouUe +A1Ar9Ch/Zjm5zEmu3zwzOU4AiyWz14iuoktifNq2iyalRNz+mnVpplToPFizsNwu +C9dPtXtU0DJlhtIFrD/evLz6KnGhe4/ZUm4lgyBvb2xfuNHqL5Lhqelwmil6EQxb +Oh3Y7XkfOjyFln89TwlxZUJdAoGAJRMa4kta7EvBTeGZLjyltvsqhFTghX+vBSCC +ToBbYbbiHJgssXSPAylU4sD7nR3HPwuqM6VZip+OOMrm8oNXZpuPTce+xqTEq1vK +JvmPrG3RAFDLdMFZjqYSXhKnuGE60yv3Ol8EEbDwfB3XLQPBPYU56Jdy0xcPSE2f +dMJXEJ0CgYEAisZw0nXw6lFeYecu642EGuU0wv1O9i21p7eho9QwOcsoTl4Q9l+M +M8iBv+qTHO+D19l4JbkGvy2H2diKoYduUFACcuiFYs8fjrT+4Z6DyOQAQGAf6Ylw +BFbU15k6KbA9v4mZDfd1tY9x62L/XO55ZxYG+J+q0e26tEThgD8cEog= +-----END RSA PRIVATE KEY----- diff --git a/components/cli/e2e/image/testdata/notary/delgkey4.crt b/components/cli/e2e/image/testdata/notary/delgkey4.crt new file mode 100644 index 0000000000..c8cbe46bdf --- /dev/null +++ b/components/cli/e2e/image/testdata/notary/delgkey4.crt @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDhTCCAm2gAwIBAgIJANae++ZkUEWMMA0GCSqGSIb3DQEBCwUAMFcxCzAJBgNV +BAYTAlVTMQswCQYDVQQIEwJDQTEVMBMGA1UEBxMMU2FuRnJhbmNpc2NvMQ8wDQYD +VQQKEwZEb2NrZXIxEzARBgNVBAMTCmRlbGVnYXRpb24wHhcNMTYwOTI4MTc0ODQ5 +WhcNMjYwNjI4MTc0ODQ5WjBXMQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExFTAT +BgNVBAcTDFNhbkZyYW5jaXNjbzEPMA0GA1UEChMGRG9ja2VyMRMwEQYDVQQDEwpk +ZWxlZ2F0aW9uMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqULAjgba +Y2I10WfqdmYnPfEqEe6iMDbzcgECb2xKafXcI4ltkQj1iO4zBTs0Ft9EzXFc5ZBh +pTjZrL6vrIa0y/CH2BiIHBJ0wRHx/40HXp4DSj3HZpVOlEMI3npRfBGNIBllUaRN +PWG7zL7DcKMIepBfPXyjBsxzH3yNiISq0W5hSiy+ImhSo3aipJUHHcp9Z9NgvpNC +3QvnxsGKRnECmDRDlxkq+FQu9Iqs/HWFYWgyfcsw+YTrWZq3qVnnqUouHO//c9PG +Ry3sZSDU97MwvkjvWys1e01Xvd3AbHx08YAsxih58i/OBKe81eD9NuZDP2KrjTxI +5xkXKhj6DV2NnQIDAQABo1QwUjAMBgNVHRMBAf8EAjAAMA4GA1UdDwEB/wQEAwIF +oDATBgNVHSUEDDAKBggrBgEFBQcDAzAdBgNVHQ4EFgQUDt95hiqbQvi0KcvZGAUu +VisnztQwDQYJKoZIhvcNAQELBQADggEBAGi7qHai7MWbfeu6SlXhzIP3AIMa8TMi +lp/+mvPUFPswIVqYJ71MAN8uA7CTH3z50a2vYupGeOEtZqVJeRf+xgOEpwycncxp +Qz6wc6TWPVIoT5q1Hqxw1RD2MyKL+Y+QBDYwFxFkthpDMlX48I9frcqoJUWFxBF2 +lnRr/cE7BbPE3sMbXV3wGPlH7+eUf+CgzXJo2HB6THzagyEgNrDiz/0rCQa1ipFd +mNU3D/U6BFGmJNxhvSOtXX9escg8yjr05YwwzokHS2K4jE0ZuJPBd50C/Rvo3Mf4 +0h7/2Q95e7d42zPe9WYPu2F8KTWsf4r+6ddhKrKhYzXIcTAfHIOiO+U= +-----END CERTIFICATE----- diff --git a/components/cli/e2e/image/testdata/notary/delgkey4.key b/components/cli/e2e/image/testdata/notary/delgkey4.key new file mode 100644 index 0000000000..f473cc495a --- /dev/null +++ b/components/cli/e2e/image/testdata/notary/delgkey4.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEAqULAjgbaY2I10WfqdmYnPfEqEe6iMDbzcgECb2xKafXcI4lt +kQj1iO4zBTs0Ft9EzXFc5ZBhpTjZrL6vrIa0y/CH2BiIHBJ0wRHx/40HXp4DSj3H +ZpVOlEMI3npRfBGNIBllUaRNPWG7zL7DcKMIepBfPXyjBsxzH3yNiISq0W5hSiy+ +ImhSo3aipJUHHcp9Z9NgvpNC3QvnxsGKRnECmDRDlxkq+FQu9Iqs/HWFYWgyfcsw ++YTrWZq3qVnnqUouHO//c9PGRy3sZSDU97MwvkjvWys1e01Xvd3AbHx08YAsxih5 +8i/OBKe81eD9NuZDP2KrjTxI5xkXKhj6DV2NnQIDAQABAoIBAGK0ZKnuYSiXux60 +5MvK4pOCsa/nY3mOcgVHhW4IzpRgJdIrcFOlz9ncXrBsSAIWjX7o3u2Ydvjs4DOW +t8d6frB3QiDInYcRVDjLCD6otWV97Bk9Ua0G4N4hAWkMF7ysV4oihS1JDSoAdo39 +qOdki6s9yeyHZGKwk2oHLlowU5TxQMBA8DHmxqBII1HTm+8xRz45bcEqRXydYSUn +P1JuSU9jFqdylxU+Nrq6ehslMQ3y7qNWQyiLGxu6EmR+vgrzSU0s3iAOqCHthaOS +VBBXPL3DNEYUS+0QGnGrACuJhanOMBfdiO6Orelx6ZzWZm38PNGv0yBt0WCM+8/A +TtQNGkECgYEA1LqR6AH9XikUQ0+rM4526BgVuYqtjw21h4Lj9alaA+YTQntBBJOv +iAcUpnJiV4T8jzAMLeqpK8R/rbxRnK5S9jOV2gr+puk4L6tH46cgahBUESDigDp8 +6vK8ur6ubBcXNPh3AT6rsPj+Ph2EU3raqiYdouvCdga/OCYZb+jr6UkCgYEAy7Cr +l8WssI/8/ORcQ4MFJFNyfz/Y2beNXyLd1PX0H+wRSiGcKzeUuTHNtzFFpMbrK/nx +ZOPCT2ROdHsBHzp1L+WquCb0fyMVSiYiXBU+VCFDbUU5tBr3ycTc7VwuFPENOiha +IdlWgew/aW110FQHIaqe9g+htRe+mXe++faZtbUCgYB/MSJmNzJX53XvHSZ/CBJ+ +iVAMBSfq3caJRLCqRNzGcf1YBbwFUYxlZ95n+wJj0+byckcF+UW3HqE8rtmZNf3y +qTtTCLnj8JQgpGeybU4LPMIXD7N9+fqQvBwuCC7gABpnGJyHCQK9KNNTLnDdPRqb +G3ki3ZYC3dvdZaJV8E2FyQKBgQCMa5Mf4kqWvezueo+QizZ0QILibqWUEhIH0AWV +1qkhiKCytlDvCjYhJdBnxjP40Jk3i+t6XfmKud/MNTAk0ywOhQoYQeKz8v+uSnPN +f2ekn/nXzq1lGGJSWsDjcXTjQvqXaVIZm7cjgjaE+80IfaUc9H75qvUT3vaq3f5u +XC7DMQKBgQDMAzCCpWlEPbZoFMl6F49+7jG0/TiqM/WRUSQnNtufPMbrR9Je4QM1 +L1UCANCPaHFOncKYer15NfIV1ctt5MZKImevDsUaQO8CUlO+dzd5H8KvHw9E29gA +B22v8k3jIjsYeRL+UJ/sBnWHgxdAe/NEM+TdlP2oP9D1gTifutPqAg== +-----END RSA PRIVATE KEY----- diff --git a/components/cli/e2e/image/testdata/notary/gen.sh b/components/cli/e2e/image/testdata/notary/gen.sh new file mode 100755 index 0000000000..8d6381cec4 --- /dev/null +++ b/components/cli/e2e/image/testdata/notary/gen.sh @@ -0,0 +1,18 @@ +for selfsigned in delgkey1 delgkey2 delgkey3 delgkey4; do + subj='/C=US/ST=CA/L=SanFrancisco/O=Docker/CN=delegation' + + openssl genrsa -out "${selfsigned}.key" 2048 + openssl req -new -key "${selfsigned}.key" -out "${selfsigned}.csr" -sha256 -subj "${subj}" + cat > "${selfsigned}.cnf" < + + +param( + [Parameter(Mandatory=$False)][switch]$Binary, + [Parameter(Mandatory=$False)][switch]$Race, + [Parameter(Mandatory=$False)][switch]$Noisy, + [Parameter(Mandatory=$False)][switch]$ForceBuildAll, + [Parameter(Mandatory=$False)][switch]$NoOpt, + [Parameter(Mandatory=$False)][switch]$TestUnit, + [Parameter(Mandatory=$False)][switch]$All +) + +$ErrorActionPreference = "Stop" +$ProgressPreference = "SilentlyContinue" +$pushed=$False # To restore the directory if we have temporarily pushed to one. + +# Build a binary (client or daemon) +Function Execute-Build($additionalBuildTags, $directory) { + # Generate the build flags + $buildTags = "autogen" + if ($Noisy) { $verboseParm=" -v" } + if ($Race) { Write-Warning "Using race detector"; $raceParm=" -race"} + if ($ForceBuildAll) { $allParm=" -a" } + if ($NoOpt) { $optParm=" -gcflags "+""""+"-N -l"+"""" } + if ($additionalBuildTags -ne "") { $buildTags += $(" " + $additionalBuildTags) } + + # Do the go build in the appropriate directory + # Note -linkmode=internal is required to be able to debug on Windows. + # https://github.com/golang/go/issues/14319#issuecomment-189576638 + Write-Host "INFO: Building..." + Push-Location $root\cmd\$directory; $global:pushed=$True + $buildCommand = "go build" + ` + $raceParm + ` + $verboseParm + ` + $allParm + ` + $optParm + ` + " -tags """ + $buildTags + """" + ` + " -ldflags """ + "-linkmode=internal" + """" + ` + " -o $root\build\"+$directory+".exe" + Invoke-Expression $buildCommand + if ($LASTEXITCODE -ne 0) { Throw "Failed to compile" } + Pop-Location; $global:pushed=$False +} + +# Run the unit tests +Function Run-UnitTests() { + Write-Host "INFO: Running unit tests..." + $testPath="./..." + $goListCommand = "go list -e -f '{{if ne .Name """ + '\"github.com/docker/cli\"' + """}}{{.ImportPath}}{{end}}' $testPath" + $pkgList = $(Invoke-Expression $goListCommand) + if ($LASTEXITCODE -ne 0) { Throw "go list for unit tests failed" } + $pkgList = $pkgList | Select-String -Pattern "github.com/docker/cli" + $pkgList = $pkgList | Select-String -NotMatch "github.com/docker/cli/vendor" + $pkgList = $pkgList | Select-String -NotMatch "github.com/docker/cli/man" + $pkgList = $pkgList | Select-String -NotMatch "github.com/docker/cli/e2e" + $pkgList = $pkgList -replace "`r`n", " " + $goTestCommand = "go test" + $raceParm + " -cover -ldflags -w -tags """ + "autogen" + """ -a """ + "-test.timeout=10m" + """ $pkgList" + Invoke-Expression $goTestCommand + if ($LASTEXITCODE -ne 0) { Throw "Unit tests failed" } +} + +# Start of main code. +Try { + Write-Host -ForegroundColor Cyan "INFO: make.ps1 starting at $(Get-Date)" + + # Get to the root of the repo + $root = $(Split-Path $MyInvocation.MyCommand.Definition -Parent | Split-Path -Parent) + Push-Location $root + + # Handle the "-All" shortcut to turn on all things we can handle. + # Note we expressly only include the items which can run in a container - the validations tests cannot + # as they require the .git directory which is excluded from the image by .dockerignore + if ($All) { $Client=$True; $TestUnit=$True } + + # Handle the "-Binary" shortcut to build both client and daemon. + if ($Binary) { $Client = $True; } + + # Verify git is installed + if ($(Get-Command git -ErrorAction SilentlyContinue) -eq $nil) { Throw "Git does not appear to be installed" } + + # Verify go is installed + if ($(Get-Command go -ErrorAction SilentlyContinue) -eq $nil) { Throw "GoLang does not appear to be installed" } + + # Build the binaries + if ($Client) { + # Create the build directory if it doesn't exist + if (-not (Test-Path ".\build")) { New-Item ".\build" -ItemType Directory | Out-Null } + + # Perform the actual build + Execute-Build "" "docker" + } + + # Run unit tests + if ($TestUnit) { Run-UnitTests } + + # Gratuitous ASCII art. + if ($Client) { + Write-Host + Write-Host -ForegroundColor Green " ________ ____ __." + Write-Host -ForegroundColor Green " \_____ \ `| `|/ _`|" + Write-Host -ForegroundColor Green " / `| \`| `<" + Write-Host -ForegroundColor Green " / `| \ `| \" + Write-Host -ForegroundColor Green " \_______ /____`|__ \" + Write-Host -ForegroundColor Green " \/ \/" + Write-Host + } +} +Catch [Exception] { + Write-Host -ForegroundColor Red ("`nERROR: make.ps1 failed:`n$_") + + # More gratuitous ASCII art. + Write-Host + Write-Host -ForegroundColor Red "___________ .__.__ .___" + Write-Host -ForegroundColor Red "\_ _____/____ `|__`| `| ____ __`| _/" + Write-Host -ForegroundColor Red " `| __) \__ \ `| `| `| _/ __ \ / __ `| " + Write-Host -ForegroundColor Red " `| \ / __ \`| `| `|_\ ___// /_/ `| " + Write-Host -ForegroundColor Red " \___ / (____ /__`|____/\___ `>____ `| " + Write-Host -ForegroundColor Red " \/ \/ \/ \/ " + Write-Host + + Throw $_ +} +Finally { + Pop-Location # As we pushed to the root of the repo as the very first thing + if ($global:pushed) { Pop-Location } + Write-Host -ForegroundColor Cyan "INFO: make.ps1 ended at $(Get-Date)" +} diff --git a/components/cli/scripts/test/watch b/components/cli/scripts/test/watch deleted file mode 100755 index 264eb7c249..0000000000 --- a/components/cli/scripts/test/watch +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh -# shellcheck disable=SC2016 -exec filewatcher -L 6 -x build -x script go test -timeout 30s -v './${dir}' diff --git a/components/cli/scripts/validate/shellcheck b/components/cli/scripts/validate/shellcheck index f7ce7024d3..63cedf137b 100755 --- a/components/cli/scripts/validate/shellcheck +++ b/components/cli/scripts/validate/shellcheck @@ -2,4 +2,4 @@ set -eo pipefail shellcheck contrib/completion/bash/docker -find scripts/ -type f | grep -v scripts/winresources | xargs shellcheck +find scripts/ -type f | grep -v scripts/winresources | grep -v '.*.ps1' | xargs shellcheck diff --git a/components/cli/service/logs/parse_logs_test.go b/components/cli/service/logs/parse_logs_test.go index 223323ea02..a8f612ce4a 100644 --- a/components/cli/service/logs/parse_logs_test.go +++ b/components/cli/service/logs/parse_logs_test.go @@ -3,8 +3,9 @@ package logs import ( "testing" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" "github.com/pkg/errors" - "github.com/stretchr/testify/assert" ) func TestParseLogDetails(t *testing.T) { @@ -24,10 +25,10 @@ func TestParseLogDetails(t *testing.T) { t.Run(testcase.line, func(t *testing.T) { actual, err := ParseLogDetails(testcase.line) if testcase.err != nil { - assert.EqualError(t, err, testcase.err.Error()) + assert.Error(t, err, testcase.err.Error()) return } - assert.Equal(t, testcase.expected, actual) + assert.Check(t, is.DeepEqual(testcase.expected, actual)) }) } } diff --git a/components/cli/templates/templates_test.go b/components/cli/templates/templates_test.go index 8355804d31..fa4d773395 100644 --- a/components/cli/templates/templates_test.go +++ b/components/cli/templates/templates_test.go @@ -4,38 +4,39 @@ import ( "bytes" "testing" - "github.com/stretchr/testify/assert" + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" ) // GitHub #32120 func TestParseJSONFunctions(t *testing.T) { tm, err := Parse(`{{json .Ports}}`) - assert.NoError(t, err) + assert.NilError(t, err) var b bytes.Buffer - assert.NoError(t, tm.Execute(&b, map[string]string{"Ports": "0.0.0.0:2->8/udp"})) + assert.NilError(t, tm.Execute(&b, map[string]string{"Ports": "0.0.0.0:2->8/udp"})) want := "\"0.0.0.0:2->8/udp\"" - assert.Equal(t, want, b.String()) + assert.Check(t, is.Equal(want, b.String())) } func TestParseStringFunctions(t *testing.T) { tm, err := Parse(`{{join (split . ":") "/"}}`) - assert.NoError(t, err) + assert.NilError(t, err) var b bytes.Buffer - assert.NoError(t, tm.Execute(&b, "text:with:colon")) + assert.NilError(t, tm.Execute(&b, "text:with:colon")) want := "text/with/colon" - assert.Equal(t, want, b.String()) + assert.Check(t, is.Equal(want, b.String())) } func TestNewParse(t *testing.T) { tm, err := NewParse("foo", "this is a {{ . }}") - assert.NoError(t, err) + assert.NilError(t, err) var b bytes.Buffer - assert.NoError(t, tm.Execute(&b, "string")) + assert.NilError(t, tm.Execute(&b, "string")) want := "this is a string" - assert.Equal(t, want, b.String()) + assert.Check(t, is.Equal(want, b.String())) } func TestParseTruncateFunction(t *testing.T) { @@ -65,24 +66,24 @@ func TestParseTruncateFunction(t *testing.T) { for _, testCase := range testCases { tm, err := Parse(testCase.template) - assert.NoError(t, err) + assert.NilError(t, err) t.Run("Non Empty Source Test with template: "+testCase.template, func(t *testing.T) { var b bytes.Buffer - assert.NoError(t, tm.Execute(&b, source)) - assert.Equal(t, testCase.expected, b.String()) + assert.NilError(t, tm.Execute(&b, source)) + assert.Check(t, is.Equal(testCase.expected, b.String())) }) t.Run("Empty Source Test with template: "+testCase.template, func(t *testing.T) { var c bytes.Buffer - assert.NoError(t, tm.Execute(&c, "")) - assert.Equal(t, "", c.String()) + assert.NilError(t, tm.Execute(&c, "")) + assert.Check(t, is.Equal("", c.String())) }) t.Run("Nil Source Test with template: "+testCase.template, func(t *testing.T) { var c bytes.Buffer - assert.Error(t, tm.Execute(&c, nil)) - assert.Equal(t, "", c.String()) + assert.Check(t, tm.Execute(&c, nil) != nil) + assert.Check(t, is.Equal("", c.String())) }) } } diff --git a/components/cli/vendor.conf b/components/cli/vendor.conf index cd9c2b49fd..1f7a7f2e03 100755 --- a/components/cli/vendor.conf +++ b/components/cli/vendor.conf @@ -20,13 +20,14 @@ github.com/flynn-archive/go-shlex 3f9db97f856818214da2e1057f8ad84803971cff github.com/ghodss/yaml 0ca9ea5df5451ffdf184b4428c902747c2c11cd7 github.com/gogo/protobuf v0.4 github.com/golang/glog 44145f04b68cf362d9c4df2182967c2275eaefed +github.com/google/go-cmp v0.2.0 github.com/golang/protobuf 7a211bcf3bce0e3f1d74f9894916e6f116ae83b4 github.com/google/btree 316fb6d3f031ae8f4d457c6c5186b9e3ded70435 github.com/google/gofuzz 44d81051d367757e1c7c6a5a86423ece9afcf63c github.com/googleapis/gnostic e4f56557df6250e1945ee6854f181ce4e1c2c646 github.com/gorilla/context v1.1 github.com/gorilla/mux v1.1 -github.com/gotestyourself/gotestyourself v1.2.0 +github.com/gotestyourself/gotestyourself cf3a5ab914a2efa8bc838d09f5918c1d44d02909 # FIXME(vdemeester) try to deduplicate this with gojsonpointer github.com/go-openapi/jsonpointer 46af16f9f7b149af66e5d1bd010e3574dc06de98 # FIXME(vdemeester) try to deduplicate this with gojsonreference diff --git a/components/cli/vendor/github.com/google/go-cmp/LICENSE b/components/cli/vendor/github.com/google/go-cmp/LICENSE new file mode 100644 index 0000000000..32017f8fa1 --- /dev/null +++ b/components/cli/vendor/github.com/google/go-cmp/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2017 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/components/cli/vendor/github.com/google/go-cmp/README.md b/components/cli/vendor/github.com/google/go-cmp/README.md new file mode 100644 index 0000000000..61c9c4cd98 --- /dev/null +++ b/components/cli/vendor/github.com/google/go-cmp/README.md @@ -0,0 +1,44 @@ +# Package for equality of Go values + +[![GoDoc](https://godoc.org/github.com/google/go-cmp/cmp?status.svg)][godoc] +[![Build Status](https://travis-ci.org/google/go-cmp.svg?branch=master)][travis] + +This package is intended to be a more powerful and safer alternative to +`reflect.DeepEqual` for comparing whether two values are semantically equal. + +The primary features of `cmp` are: + +* When the default behavior of equality does not suit the needs of the test, + custom equality functions can override the equality operation. + For example, an equality function may report floats as equal so long as they + are within some tolerance of each other. + +* Types that have an `Equal` method may use that method to determine equality. + This allows package authors to determine the equality operation for the types + that they define. + +* If no custom equality functions are used and no `Equal` method is defined, + equality is determined by recursively comparing the primitive kinds on both + values, much like `reflect.DeepEqual`. Unlike `reflect.DeepEqual`, unexported + fields are not compared by default; they result in panics unless suppressed + by using an `Ignore` option (see `cmpopts.IgnoreUnexported`) or explicitly + compared using the `AllowUnexported` option. + +See the [GoDoc documentation][godoc] for more information. + +This is not an official Google product. + +[godoc]: https://godoc.org/github.com/google/go-cmp/cmp +[travis]: https://travis-ci.org/google/go-cmp + +## Install + +``` +go get -u github.com/google/go-cmp/cmp +``` + +## License + +BSD - See [LICENSE][license] file + +[license]: https://github.com/google/go-cmp/blob/master/LICENSE diff --git a/components/cli/vendor/github.com/google/go-cmp/cmp/compare.go b/components/cli/vendor/github.com/google/go-cmp/cmp/compare.go new file mode 100644 index 0000000000..7e215f2202 --- /dev/null +++ b/components/cli/vendor/github.com/google/go-cmp/cmp/compare.go @@ -0,0 +1,553 @@ +// Copyright 2017, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE.md file. + +// Package cmp determines equality of values. +// +// This package is intended to be a more powerful and safer alternative to +// reflect.DeepEqual for comparing whether two values are semantically equal. +// +// The primary features of cmp are: +// +// • When the default behavior of equality does not suit the needs of the test, +// custom equality functions can override the equality operation. +// For example, an equality function may report floats as equal so long as they +// are within some tolerance of each other. +// +// • Types that have an Equal method may use that method to determine equality. +// This allows package authors to determine the equality operation for the types +// that they define. +// +// • If no custom equality functions are used and no Equal method is defined, +// equality is determined by recursively comparing the primitive kinds on both +// values, much like reflect.DeepEqual. Unlike reflect.DeepEqual, unexported +// fields are not compared by default; they result in panics unless suppressed +// by using an Ignore option (see cmpopts.IgnoreUnexported) or explicitly compared +// using the AllowUnexported option. +package cmp + +import ( + "fmt" + "reflect" + + "github.com/google/go-cmp/cmp/internal/diff" + "github.com/google/go-cmp/cmp/internal/function" + "github.com/google/go-cmp/cmp/internal/value" +) + +// BUG(dsnet): Maps with keys containing NaN values cannot be properly compared due to +// the reflection package's inability to retrieve such entries. Equal will panic +// anytime it comes across a NaN key, but this behavior may change. +// +// See https://golang.org/issue/11104 for more details. + +var nothing = reflect.Value{} + +// Equal reports whether x and y are equal by recursively applying the +// following rules in the given order to x and y and all of their sub-values: +// +// • If two values are not of the same type, then they are never equal +// and the overall result is false. +// +// • Let S be the set of all Ignore, Transformer, and Comparer options that +// remain after applying all path filters, value filters, and type filters. +// If at least one Ignore exists in S, then the comparison is ignored. +// If the number of Transformer and Comparer options in S is greater than one, +// then Equal panics because it is ambiguous which option to use. +// If S contains a single Transformer, then use that to transform the current +// values and recursively call Equal on the output values. +// If S contains a single Comparer, then use that to compare the current values. +// Otherwise, evaluation proceeds to the next rule. +// +// • If the values have an Equal method of the form "(T) Equal(T) bool" or +// "(T) Equal(I) bool" where T is assignable to I, then use the result of +// x.Equal(y) even if x or y is nil. +// Otherwise, no such method exists and evaluation proceeds to the next rule. +// +// • Lastly, try to compare x and y based on their basic kinds. +// Simple kinds like booleans, integers, floats, complex numbers, strings, and +// channels are compared using the equivalent of the == operator in Go. +// Functions are only equal if they are both nil, otherwise they are unequal. +// Pointers are equal if the underlying values they point to are also equal. +// Interfaces are equal if their underlying concrete values are also equal. +// +// Structs are equal if all of their fields are equal. If a struct contains +// unexported fields, Equal panics unless the AllowUnexported option is used or +// an Ignore option (e.g., cmpopts.IgnoreUnexported) ignores that field. +// +// Arrays, slices, and maps are equal if they are both nil or both non-nil +// with the same length and the elements at each index or key are equal. +// Note that a non-nil empty slice and a nil slice are not equal. +// To equate empty slices and maps, consider using cmpopts.EquateEmpty. +// Map keys are equal according to the == operator. +// To use custom comparisons for map keys, consider using cmpopts.SortMaps. +func Equal(x, y interface{}, opts ...Option) bool { + s := newState(opts) + s.compareAny(reflect.ValueOf(x), reflect.ValueOf(y)) + return s.result.Equal() +} + +// Diff returns a human-readable report of the differences between two values. +// It returns an empty string if and only if Equal returns true for the same +// input values and options. The output string will use the "-" symbol to +// indicate elements removed from x, and the "+" symbol to indicate elements +// added to y. +// +// Do not depend on this output being stable. +func Diff(x, y interface{}, opts ...Option) string { + r := new(defaultReporter) + opts = Options{Options(opts), r} + eq := Equal(x, y, opts...) + d := r.String() + if (d == "") != eq { + panic("inconsistent difference and equality results") + } + return d +} + +type state struct { + // These fields represent the "comparison state". + // Calling statelessCompare must not result in observable changes to these. + result diff.Result // The current result of comparison + curPath Path // The current path in the value tree + reporter reporter // Optional reporter used for difference formatting + + // dynChecker triggers pseudo-random checks for option correctness. + // It is safe for statelessCompare to mutate this value. + dynChecker dynChecker + + // These fields, once set by processOption, will not change. + exporters map[reflect.Type]bool // Set of structs with unexported field visibility + opts Options // List of all fundamental and filter options +} + +func newState(opts []Option) *state { + s := new(state) + for _, opt := range opts { + s.processOption(opt) + } + return s +} + +func (s *state) processOption(opt Option) { + switch opt := opt.(type) { + case nil: + case Options: + for _, o := range opt { + s.processOption(o) + } + case coreOption: + type filtered interface { + isFiltered() bool + } + if fopt, ok := opt.(filtered); ok && !fopt.isFiltered() { + panic(fmt.Sprintf("cannot use an unfiltered option: %v", opt)) + } + s.opts = append(s.opts, opt) + case visibleStructs: + if s.exporters == nil { + s.exporters = make(map[reflect.Type]bool) + } + for t := range opt { + s.exporters[t] = true + } + case reporter: + if s.reporter != nil { + panic("difference reporter already registered") + } + s.reporter = opt + default: + panic(fmt.Sprintf("unknown option %T", opt)) + } +} + +// statelessCompare compares two values and returns the result. +// This function is stateless in that it does not alter the current result, +// or output to any registered reporters. +func (s *state) statelessCompare(vx, vy reflect.Value) diff.Result { + // We do not save and restore the curPath because all of the compareX + // methods should properly push and pop from the path. + // It is an implementation bug if the contents of curPath differs from + // when calling this function to when returning from it. + + oldResult, oldReporter := s.result, s.reporter + s.result = diff.Result{} // Reset result + s.reporter = nil // Remove reporter to avoid spurious printouts + s.compareAny(vx, vy) + res := s.result + s.result, s.reporter = oldResult, oldReporter + return res +} + +func (s *state) compareAny(vx, vy reflect.Value) { + // TODO: Support cyclic data structures. + + // Rule 0: Differing types are never equal. + if !vx.IsValid() || !vy.IsValid() { + s.report(vx.IsValid() == vy.IsValid(), vx, vy) + return + } + if vx.Type() != vy.Type() { + s.report(false, vx, vy) // Possible for path to be empty + return + } + t := vx.Type() + if len(s.curPath) == 0 { + s.curPath.push(&pathStep{typ: t}) + defer s.curPath.pop() + } + vx, vy = s.tryExporting(vx, vy) + + // Rule 1: Check whether an option applies on this node in the value tree. + if s.tryOptions(vx, vy, t) { + return + } + + // Rule 2: Check whether the type has a valid Equal method. + if s.tryMethod(vx, vy, t) { + return + } + + // Rule 3: Recursively descend into each value's underlying kind. + switch t.Kind() { + case reflect.Bool: + s.report(vx.Bool() == vy.Bool(), vx, vy) + return + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + s.report(vx.Int() == vy.Int(), vx, vy) + return + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + s.report(vx.Uint() == vy.Uint(), vx, vy) + return + case reflect.Float32, reflect.Float64: + s.report(vx.Float() == vy.Float(), vx, vy) + return + case reflect.Complex64, reflect.Complex128: + s.report(vx.Complex() == vy.Complex(), vx, vy) + return + case reflect.String: + s.report(vx.String() == vy.String(), vx, vy) + return + case reflect.Chan, reflect.UnsafePointer: + s.report(vx.Pointer() == vy.Pointer(), vx, vy) + return + case reflect.Func: + s.report(vx.IsNil() && vy.IsNil(), vx, vy) + return + case reflect.Ptr: + if vx.IsNil() || vy.IsNil() { + s.report(vx.IsNil() && vy.IsNil(), vx, vy) + return + } + s.curPath.push(&indirect{pathStep{t.Elem()}}) + defer s.curPath.pop() + s.compareAny(vx.Elem(), vy.Elem()) + return + case reflect.Interface: + if vx.IsNil() || vy.IsNil() { + s.report(vx.IsNil() && vy.IsNil(), vx, vy) + return + } + if vx.Elem().Type() != vy.Elem().Type() { + s.report(false, vx.Elem(), vy.Elem()) + return + } + s.curPath.push(&typeAssertion{pathStep{vx.Elem().Type()}}) + defer s.curPath.pop() + s.compareAny(vx.Elem(), vy.Elem()) + return + case reflect.Slice: + if vx.IsNil() || vy.IsNil() { + s.report(vx.IsNil() && vy.IsNil(), vx, vy) + return + } + fallthrough + case reflect.Array: + s.compareArray(vx, vy, t) + return + case reflect.Map: + s.compareMap(vx, vy, t) + return + case reflect.Struct: + s.compareStruct(vx, vy, t) + return + default: + panic(fmt.Sprintf("%v kind not handled", t.Kind())) + } +} + +func (s *state) tryExporting(vx, vy reflect.Value) (reflect.Value, reflect.Value) { + if sf, ok := s.curPath[len(s.curPath)-1].(*structField); ok && sf.unexported { + if sf.force { + // Use unsafe pointer arithmetic to get read-write access to an + // unexported field in the struct. + vx = unsafeRetrieveField(sf.pvx, sf.field) + vy = unsafeRetrieveField(sf.pvy, sf.field) + } else { + // We are not allowed to export the value, so invalidate them + // so that tryOptions can panic later if not explicitly ignored. + vx = nothing + vy = nothing + } + } + return vx, vy +} + +func (s *state) tryOptions(vx, vy reflect.Value, t reflect.Type) bool { + // If there were no FilterValues, we will not detect invalid inputs, + // so manually check for them and append invalid if necessary. + // We still evaluate the options since an ignore can override invalid. + opts := s.opts + if !vx.IsValid() || !vy.IsValid() { + opts = Options{opts, invalid{}} + } + + // Evaluate all filters and apply the remaining options. + if opt := opts.filter(s, vx, vy, t); opt != nil { + opt.apply(s, vx, vy) + return true + } + return false +} + +func (s *state) tryMethod(vx, vy reflect.Value, t reflect.Type) bool { + // Check if this type even has an Equal method. + m, ok := t.MethodByName("Equal") + if !ok || !function.IsType(m.Type, function.EqualAssignable) { + return false + } + + eq := s.callTTBFunc(m.Func, vx, vy) + s.report(eq, vx, vy) + return true +} + +func (s *state) callTRFunc(f, v reflect.Value) reflect.Value { + v = sanitizeValue(v, f.Type().In(0)) + if !s.dynChecker.Next() { + return f.Call([]reflect.Value{v})[0] + } + + // Run the function twice and ensure that we get the same results back. + // We run in goroutines so that the race detector (if enabled) can detect + // unsafe mutations to the input. + c := make(chan reflect.Value) + go detectRaces(c, f, v) + want := f.Call([]reflect.Value{v})[0] + if got := <-c; !s.statelessCompare(got, want).Equal() { + // To avoid false-positives with non-reflexive equality operations, + // we sanity check whether a value is equal to itself. + if !s.statelessCompare(want, want).Equal() { + return want + } + fn := getFuncName(f.Pointer()) + panic(fmt.Sprintf("non-deterministic function detected: %s", fn)) + } + return want +} + +func (s *state) callTTBFunc(f, x, y reflect.Value) bool { + x = sanitizeValue(x, f.Type().In(0)) + y = sanitizeValue(y, f.Type().In(1)) + if !s.dynChecker.Next() { + return f.Call([]reflect.Value{x, y})[0].Bool() + } + + // Swapping the input arguments is sufficient to check that + // f is symmetric and deterministic. + // We run in goroutines so that the race detector (if enabled) can detect + // unsafe mutations to the input. + c := make(chan reflect.Value) + go detectRaces(c, f, y, x) + want := f.Call([]reflect.Value{x, y})[0].Bool() + if got := <-c; !got.IsValid() || got.Bool() != want { + fn := getFuncName(f.Pointer()) + panic(fmt.Sprintf("non-deterministic or non-symmetric function detected: %s", fn)) + } + return want +} + +func detectRaces(c chan<- reflect.Value, f reflect.Value, vs ...reflect.Value) { + var ret reflect.Value + defer func() { + recover() // Ignore panics, let the other call to f panic instead + c <- ret + }() + ret = f.Call(vs)[0] +} + +// sanitizeValue converts nil interfaces of type T to those of type R, +// assuming that T is assignable to R. +// Otherwise, it returns the input value as is. +func sanitizeValue(v reflect.Value, t reflect.Type) reflect.Value { + // TODO(dsnet): Remove this hacky workaround. + // See https://golang.org/issue/22143 + if v.Kind() == reflect.Interface && v.IsNil() && v.Type() != t { + return reflect.New(t).Elem() + } + return v +} + +func (s *state) compareArray(vx, vy reflect.Value, t reflect.Type) { + step := &sliceIndex{pathStep{t.Elem()}, 0, 0} + s.curPath.push(step) + + // Compute an edit-script for slices vx and vy. + es := diff.Difference(vx.Len(), vy.Len(), func(ix, iy int) diff.Result { + step.xkey, step.ykey = ix, iy + return s.statelessCompare(vx.Index(ix), vy.Index(iy)) + }) + + // Report the entire slice as is if the arrays are of primitive kind, + // and the arrays are different enough. + isPrimitive := false + switch t.Elem().Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, + reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr, + reflect.Bool, reflect.Float32, reflect.Float64, reflect.Complex64, reflect.Complex128: + isPrimitive = true + } + if isPrimitive && es.Dist() > (vx.Len()+vy.Len())/4 { + s.curPath.pop() // Pop first since we are reporting the whole slice + s.report(false, vx, vy) + return + } + + // Replay the edit-script. + var ix, iy int + for _, e := range es { + switch e { + case diff.UniqueX: + step.xkey, step.ykey = ix, -1 + s.report(false, vx.Index(ix), nothing) + ix++ + case diff.UniqueY: + step.xkey, step.ykey = -1, iy + s.report(false, nothing, vy.Index(iy)) + iy++ + default: + step.xkey, step.ykey = ix, iy + if e == diff.Identity { + s.report(true, vx.Index(ix), vy.Index(iy)) + } else { + s.compareAny(vx.Index(ix), vy.Index(iy)) + } + ix++ + iy++ + } + } + s.curPath.pop() + return +} + +func (s *state) compareMap(vx, vy reflect.Value, t reflect.Type) { + if vx.IsNil() || vy.IsNil() { + s.report(vx.IsNil() && vy.IsNil(), vx, vy) + return + } + + // We combine and sort the two map keys so that we can perform the + // comparisons in a deterministic order. + step := &mapIndex{pathStep: pathStep{t.Elem()}} + s.curPath.push(step) + defer s.curPath.pop() + for _, k := range value.SortKeys(append(vx.MapKeys(), vy.MapKeys()...)) { + step.key = k + vvx := vx.MapIndex(k) + vvy := vy.MapIndex(k) + switch { + case vvx.IsValid() && vvy.IsValid(): + s.compareAny(vvx, vvy) + case vvx.IsValid() && !vvy.IsValid(): + s.report(false, vvx, nothing) + case !vvx.IsValid() && vvy.IsValid(): + s.report(false, nothing, vvy) + default: + // It is possible for both vvx and vvy to be invalid if the + // key contained a NaN value in it. There is no way in + // reflection to be able to retrieve these values. + // See https://golang.org/issue/11104 + panic(fmt.Sprintf("%#v has map key with NaNs", s.curPath)) + } + } +} + +func (s *state) compareStruct(vx, vy reflect.Value, t reflect.Type) { + var vax, vay reflect.Value // Addressable versions of vx and vy + + step := &structField{} + s.curPath.push(step) + defer s.curPath.pop() + for i := 0; i < t.NumField(); i++ { + vvx := vx.Field(i) + vvy := vy.Field(i) + step.typ = t.Field(i).Type + step.name = t.Field(i).Name + step.idx = i + step.unexported = !isExported(step.name) + if step.unexported { + // Defer checking of unexported fields until later to give an + // Ignore a chance to ignore the field. + if !vax.IsValid() || !vay.IsValid() { + // For unsafeRetrieveField to work, the parent struct must + // be addressable. Create a new copy of the values if + // necessary to make them addressable. + vax = makeAddressable(vx) + vay = makeAddressable(vy) + } + step.force = s.exporters[t] + step.pvx = vax + step.pvy = vay + step.field = t.Field(i) + } + s.compareAny(vvx, vvy) + } +} + +// report records the result of a single comparison. +// It also calls Report if any reporter is registered. +func (s *state) report(eq bool, vx, vy reflect.Value) { + if eq { + s.result.NSame++ + } else { + s.result.NDiff++ + } + if s.reporter != nil { + s.reporter.Report(vx, vy, eq, s.curPath) + } +} + +// dynChecker tracks the state needed to periodically perform checks that +// user provided functions are symmetric and deterministic. +// The zero value is safe for immediate use. +type dynChecker struct{ curr, next int } + +// Next increments the state and reports whether a check should be performed. +// +// Checks occur every Nth function call, where N is a triangular number: +// 0 1 3 6 10 15 21 28 36 45 55 66 78 91 105 120 136 153 171 190 ... +// See https://en.wikipedia.org/wiki/Triangular_number +// +// This sequence ensures that the cost of checks drops significantly as +// the number of functions calls grows larger. +func (dc *dynChecker) Next() bool { + ok := dc.curr == dc.next + if ok { + dc.curr = 0 + dc.next++ + } + dc.curr++ + return ok +} + +// makeAddressable returns a value that is always addressable. +// It returns the input verbatim if it is already addressable, +// otherwise it creates a new value and returns an addressable copy. +func makeAddressable(v reflect.Value) reflect.Value { + if v.CanAddr() { + return v + } + vc := reflect.New(v.Type()).Elem() + vc.Set(v) + return vc +} diff --git a/components/cli/vendor/github.com/google/go-cmp/cmp/internal/diff/debug_disable.go b/components/cli/vendor/github.com/google/go-cmp/cmp/internal/diff/debug_disable.go new file mode 100644 index 0000000000..42afa4960e --- /dev/null +++ b/components/cli/vendor/github.com/google/go-cmp/cmp/internal/diff/debug_disable.go @@ -0,0 +1,17 @@ +// Copyright 2017, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE.md file. + +// +build !debug + +package diff + +var debug debugger + +type debugger struct{} + +func (debugger) Begin(_, _ int, f EqualFunc, _, _ *EditScript) EqualFunc { + return f +} +func (debugger) Update() {} +func (debugger) Finish() {} diff --git a/components/cli/vendor/github.com/google/go-cmp/cmp/internal/diff/debug_enable.go b/components/cli/vendor/github.com/google/go-cmp/cmp/internal/diff/debug_enable.go new file mode 100644 index 0000000000..fd9f7f1773 --- /dev/null +++ b/components/cli/vendor/github.com/google/go-cmp/cmp/internal/diff/debug_enable.go @@ -0,0 +1,122 @@ +// Copyright 2017, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE.md file. + +// +build debug + +package diff + +import ( + "fmt" + "strings" + "sync" + "time" +) + +// The algorithm can be seen running in real-time by enabling debugging: +// go test -tags=debug -v +// +// Example output: +// === RUN TestDifference/#34 +// ┌───────────────────────────────┐ +// │ \ · · · · · · · · · · · · · · │ +// │ · # · · · · · · · · · · · · · │ +// │ · \ · · · · · · · · · · · · · │ +// │ · · \ · · · · · · · · · · · · │ +// │ · · · X # · · · · · · · · · · │ +// │ · · · # \ · · · · · · · · · · │ +// │ · · · · · # # · · · · · · · · │ +// │ · · · · · # \ · · · · · · · · │ +// │ · · · · · · · \ · · · · · · · │ +// │ · · · · · · · · \ · · · · · · │ +// │ · · · · · · · · · \ · · · · · │ +// │ · · · · · · · · · · \ · · # · │ +// │ · · · · · · · · · · · \ # # · │ +// │ · · · · · · · · · · · # # # · │ +// │ · · · · · · · · · · # # # # · │ +// │ · · · · · · · · · # # # # # · │ +// │ · · · · · · · · · · · · · · \ │ +// └───────────────────────────────┘ +// [.Y..M.XY......YXYXY.|] +// +// The grid represents the edit-graph where the horizontal axis represents +// list X and the vertical axis represents list Y. The start of the two lists +// is the top-left, while the ends are the bottom-right. The '·' represents +// an unexplored node in the graph. The '\' indicates that the two symbols +// from list X and Y are equal. The 'X' indicates that two symbols are similar +// (but not exactly equal) to each other. The '#' indicates that the two symbols +// are different (and not similar). The algorithm traverses this graph trying to +// make the paths starting in the top-left and the bottom-right connect. +// +// The series of '.', 'X', 'Y', and 'M' characters at the bottom represents +// the currently established path from the forward and reverse searches, +// separated by a '|' character. + +const ( + updateDelay = 100 * time.Millisecond + finishDelay = 500 * time.Millisecond + ansiTerminal = true // ANSI escape codes used to move terminal cursor +) + +var debug debugger + +type debugger struct { + sync.Mutex + p1, p2 EditScript + fwdPath, revPath *EditScript + grid []byte + lines int +} + +func (dbg *debugger) Begin(nx, ny int, f EqualFunc, p1, p2 *EditScript) EqualFunc { + dbg.Lock() + dbg.fwdPath, dbg.revPath = p1, p2 + top := "┌─" + strings.Repeat("──", nx) + "┐\n" + row := "│ " + strings.Repeat("· ", nx) + "│\n" + btm := "└─" + strings.Repeat("──", nx) + "┘\n" + dbg.grid = []byte(top + strings.Repeat(row, ny) + btm) + dbg.lines = strings.Count(dbg.String(), "\n") + fmt.Print(dbg) + + // Wrap the EqualFunc so that we can intercept each result. + return func(ix, iy int) (r Result) { + cell := dbg.grid[len(top)+iy*len(row):][len("│ ")+len("· ")*ix:][:len("·")] + for i := range cell { + cell[i] = 0 // Zero out the multiple bytes of UTF-8 middle-dot + } + switch r = f(ix, iy); { + case r.Equal(): + cell[0] = '\\' + case r.Similar(): + cell[0] = 'X' + default: + cell[0] = '#' + } + return + } +} + +func (dbg *debugger) Update() { + dbg.print(updateDelay) +} + +func (dbg *debugger) Finish() { + dbg.print(finishDelay) + dbg.Unlock() +} + +func (dbg *debugger) String() string { + dbg.p1, dbg.p2 = *dbg.fwdPath, dbg.p2[:0] + for i := len(*dbg.revPath) - 1; i >= 0; i-- { + dbg.p2 = append(dbg.p2, (*dbg.revPath)[i]) + } + return fmt.Sprintf("%s[%v|%v]\n\n", dbg.grid, dbg.p1, dbg.p2) +} + +func (dbg *debugger) print(d time.Duration) { + if ansiTerminal { + fmt.Printf("\x1b[%dA", dbg.lines) // Reset terminal cursor + } + fmt.Print(dbg) + time.Sleep(d) +} diff --git a/components/cli/vendor/github.com/google/go-cmp/cmp/internal/diff/diff.go b/components/cli/vendor/github.com/google/go-cmp/cmp/internal/diff/diff.go new file mode 100644 index 0000000000..260befea2f --- /dev/null +++ b/components/cli/vendor/github.com/google/go-cmp/cmp/internal/diff/diff.go @@ -0,0 +1,363 @@ +// Copyright 2017, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE.md file. + +// Package diff implements an algorithm for producing edit-scripts. +// The edit-script is a sequence of operations needed to transform one list +// of symbols into another (or vice-versa). The edits allowed are insertions, +// deletions, and modifications. The summation of all edits is called the +// Levenshtein distance as this problem is well-known in computer science. +// +// This package prioritizes performance over accuracy. That is, the run time +// is more important than obtaining a minimal Levenshtein distance. +package diff + +// EditType represents a single operation within an edit-script. +type EditType uint8 + +const ( + // Identity indicates that a symbol pair is identical in both list X and Y. + Identity EditType = iota + // UniqueX indicates that a symbol only exists in X and not Y. + UniqueX + // UniqueY indicates that a symbol only exists in Y and not X. + UniqueY + // Modified indicates that a symbol pair is a modification of each other. + Modified +) + +// EditScript represents the series of differences between two lists. +type EditScript []EditType + +// String returns a human-readable string representing the edit-script where +// Identity, UniqueX, UniqueY, and Modified are represented by the +// '.', 'X', 'Y', and 'M' characters, respectively. +func (es EditScript) String() string { + b := make([]byte, len(es)) + for i, e := range es { + switch e { + case Identity: + b[i] = '.' + case UniqueX: + b[i] = 'X' + case UniqueY: + b[i] = 'Y' + case Modified: + b[i] = 'M' + default: + panic("invalid edit-type") + } + } + return string(b) +} + +// stats returns a histogram of the number of each type of edit operation. +func (es EditScript) stats() (s struct{ NI, NX, NY, NM int }) { + for _, e := range es { + switch e { + case Identity: + s.NI++ + case UniqueX: + s.NX++ + case UniqueY: + s.NY++ + case Modified: + s.NM++ + default: + panic("invalid edit-type") + } + } + return +} + +// Dist is the Levenshtein distance and is guaranteed to be 0 if and only if +// lists X and Y are equal. +func (es EditScript) Dist() int { return len(es) - es.stats().NI } + +// LenX is the length of the X list. +func (es EditScript) LenX() int { return len(es) - es.stats().NY } + +// LenY is the length of the Y list. +func (es EditScript) LenY() int { return len(es) - es.stats().NX } + +// EqualFunc reports whether the symbols at indexes ix and iy are equal. +// When called by Difference, the index is guaranteed to be within nx and ny. +type EqualFunc func(ix int, iy int) Result + +// Result is the result of comparison. +// NSame is the number of sub-elements that are equal. +// NDiff is the number of sub-elements that are not equal. +type Result struct{ NSame, NDiff int } + +// Equal indicates whether the symbols are equal. Two symbols are equal +// if and only if NDiff == 0. If Equal, then they are also Similar. +func (r Result) Equal() bool { return r.NDiff == 0 } + +// Similar indicates whether two symbols are similar and may be represented +// by using the Modified type. As a special case, we consider binary comparisons +// (i.e., those that return Result{1, 0} or Result{0, 1}) to be similar. +// +// The exact ratio of NSame to NDiff to determine similarity may change. +func (r Result) Similar() bool { + // Use NSame+1 to offset NSame so that binary comparisons are similar. + return r.NSame+1 >= r.NDiff +} + +// Difference reports whether two lists of lengths nx and ny are equal +// given the definition of equality provided as f. +// +// This function returns an edit-script, which is a sequence of operations +// needed to convert one list into the other. The following invariants for +// the edit-script are maintained: +// • eq == (es.Dist()==0) +// • nx == es.LenX() +// • ny == es.LenY() +// +// This algorithm is not guaranteed to be an optimal solution (i.e., one that +// produces an edit-script with a minimal Levenshtein distance). This algorithm +// favors performance over optimality. The exact output is not guaranteed to +// be stable and may change over time. +func Difference(nx, ny int, f EqualFunc) (es EditScript) { + // This algorithm is based on traversing what is known as an "edit-graph". + // See Figure 1 from "An O(ND) Difference Algorithm and Its Variations" + // by Eugene W. Myers. Since D can be as large as N itself, this is + // effectively O(N^2). Unlike the algorithm from that paper, we are not + // interested in the optimal path, but at least some "decent" path. + // + // For example, let X and Y be lists of symbols: + // X = [A B C A B B A] + // Y = [C B A B A C] + // + // The edit-graph can be drawn as the following: + // A B C A B B A + // ┌─────────────┐ + // C │_|_|\|_|_|_|_│ 0 + // B │_|\|_|_|\|\|_│ 1 + // A │\|_|_|\|_|_|\│ 2 + // B │_|\|_|_|\|\|_│ 3 + // A │\|_|_|\|_|_|\│ 4 + // C │ | |\| | | | │ 5 + // └─────────────┘ 6 + // 0 1 2 3 4 5 6 7 + // + // List X is written along the horizontal axis, while list Y is written + // along the vertical axis. At any point on this grid, if the symbol in + // list X matches the corresponding symbol in list Y, then a '\' is drawn. + // The goal of any minimal edit-script algorithm is to find a path from the + // top-left corner to the bottom-right corner, while traveling through the + // fewest horizontal or vertical edges. + // A horizontal edge is equivalent to inserting a symbol from list X. + // A vertical edge is equivalent to inserting a symbol from list Y. + // A diagonal edge is equivalent to a matching symbol between both X and Y. + + // Invariants: + // • 0 ≤ fwdPath.X ≤ (fwdFrontier.X, revFrontier.X) ≤ revPath.X ≤ nx + // • 0 ≤ fwdPath.Y ≤ (fwdFrontier.Y, revFrontier.Y) ≤ revPath.Y ≤ ny + // + // In general: + // • fwdFrontier.X < revFrontier.X + // • fwdFrontier.Y < revFrontier.Y + // Unless, it is time for the algorithm to terminate. + fwdPath := path{+1, point{0, 0}, make(EditScript, 0, (nx+ny)/2)} + revPath := path{-1, point{nx, ny}, make(EditScript, 0)} + fwdFrontier := fwdPath.point // Forward search frontier + revFrontier := revPath.point // Reverse search frontier + + // Search budget bounds the cost of searching for better paths. + // The longest sequence of non-matching symbols that can be tolerated is + // approximately the square-root of the search budget. + searchBudget := 4 * (nx + ny) // O(n) + + // The algorithm below is a greedy, meet-in-the-middle algorithm for + // computing sub-optimal edit-scripts between two lists. + // + // The algorithm is approximately as follows: + // • Searching for differences switches back-and-forth between + // a search that starts at the beginning (the top-left corner), and + // a search that starts at the end (the bottom-right corner). The goal of + // the search is connect with the search from the opposite corner. + // • As we search, we build a path in a greedy manner, where the first + // match seen is added to the path (this is sub-optimal, but provides a + // decent result in practice). When matches are found, we try the next pair + // of symbols in the lists and follow all matches as far as possible. + // • When searching for matches, we search along a diagonal going through + // through the "frontier" point. If no matches are found, we advance the + // frontier towards the opposite corner. + // • This algorithm terminates when either the X coordinates or the + // Y coordinates of the forward and reverse frontier points ever intersect. + // + // This algorithm is correct even if searching only in the forward direction + // or in the reverse direction. We do both because it is commonly observed + // that two lists commonly differ because elements were added to the front + // or end of the other list. + // + // Running the tests with the "debug" build tag prints a visualization of + // the algorithm running in real-time. This is educational for understanding + // how the algorithm works. See debug_enable.go. + f = debug.Begin(nx, ny, f, &fwdPath.es, &revPath.es) + for { + // Forward search from the beginning. + if fwdFrontier.X >= revFrontier.X || fwdFrontier.Y >= revFrontier.Y || searchBudget == 0 { + break + } + for stop1, stop2, i := false, false, 0; !(stop1 && stop2) && searchBudget > 0; i++ { + // Search in a diagonal pattern for a match. + z := zigzag(i) + p := point{fwdFrontier.X + z, fwdFrontier.Y - z} + switch { + case p.X >= revPath.X || p.Y < fwdPath.Y: + stop1 = true // Hit top-right corner + case p.Y >= revPath.Y || p.X < fwdPath.X: + stop2 = true // Hit bottom-left corner + case f(p.X, p.Y).Equal(): + // Match found, so connect the path to this point. + fwdPath.connect(p, f) + fwdPath.append(Identity) + // Follow sequence of matches as far as possible. + for fwdPath.X < revPath.X && fwdPath.Y < revPath.Y { + if !f(fwdPath.X, fwdPath.Y).Equal() { + break + } + fwdPath.append(Identity) + } + fwdFrontier = fwdPath.point + stop1, stop2 = true, true + default: + searchBudget-- // Match not found + } + debug.Update() + } + // Advance the frontier towards reverse point. + if revPath.X-fwdFrontier.X >= revPath.Y-fwdFrontier.Y { + fwdFrontier.X++ + } else { + fwdFrontier.Y++ + } + + // Reverse search from the end. + if fwdFrontier.X >= revFrontier.X || fwdFrontier.Y >= revFrontier.Y || searchBudget == 0 { + break + } + for stop1, stop2, i := false, false, 0; !(stop1 && stop2) && searchBudget > 0; i++ { + // Search in a diagonal pattern for a match. + z := zigzag(i) + p := point{revFrontier.X - z, revFrontier.Y + z} + switch { + case fwdPath.X >= p.X || revPath.Y < p.Y: + stop1 = true // Hit bottom-left corner + case fwdPath.Y >= p.Y || revPath.X < p.X: + stop2 = true // Hit top-right corner + case f(p.X-1, p.Y-1).Equal(): + // Match found, so connect the path to this point. + revPath.connect(p, f) + revPath.append(Identity) + // Follow sequence of matches as far as possible. + for fwdPath.X < revPath.X && fwdPath.Y < revPath.Y { + if !f(revPath.X-1, revPath.Y-1).Equal() { + break + } + revPath.append(Identity) + } + revFrontier = revPath.point + stop1, stop2 = true, true + default: + searchBudget-- // Match not found + } + debug.Update() + } + // Advance the frontier towards forward point. + if revFrontier.X-fwdPath.X >= revFrontier.Y-fwdPath.Y { + revFrontier.X-- + } else { + revFrontier.Y-- + } + } + + // Join the forward and reverse paths and then append the reverse path. + fwdPath.connect(revPath.point, f) + for i := len(revPath.es) - 1; i >= 0; i-- { + t := revPath.es[i] + revPath.es = revPath.es[:i] + fwdPath.append(t) + } + debug.Finish() + return fwdPath.es +} + +type path struct { + dir int // +1 if forward, -1 if reverse + point // Leading point of the EditScript path + es EditScript +} + +// connect appends any necessary Identity, Modified, UniqueX, or UniqueY types +// to the edit-script to connect p.point to dst. +func (p *path) connect(dst point, f EqualFunc) { + if p.dir > 0 { + // Connect in forward direction. + for dst.X > p.X && dst.Y > p.Y { + switch r := f(p.X, p.Y); { + case r.Equal(): + p.append(Identity) + case r.Similar(): + p.append(Modified) + case dst.X-p.X >= dst.Y-p.Y: + p.append(UniqueX) + default: + p.append(UniqueY) + } + } + for dst.X > p.X { + p.append(UniqueX) + } + for dst.Y > p.Y { + p.append(UniqueY) + } + } else { + // Connect in reverse direction. + for p.X > dst.X && p.Y > dst.Y { + switch r := f(p.X-1, p.Y-1); { + case r.Equal(): + p.append(Identity) + case r.Similar(): + p.append(Modified) + case p.Y-dst.Y >= p.X-dst.X: + p.append(UniqueY) + default: + p.append(UniqueX) + } + } + for p.X > dst.X { + p.append(UniqueX) + } + for p.Y > dst.Y { + p.append(UniqueY) + } + } +} + +func (p *path) append(t EditType) { + p.es = append(p.es, t) + switch t { + case Identity, Modified: + p.add(p.dir, p.dir) + case UniqueX: + p.add(p.dir, 0) + case UniqueY: + p.add(0, p.dir) + } + debug.Update() +} + +type point struct{ X, Y int } + +func (p *point) add(dx, dy int) { p.X += dx; p.Y += dy } + +// zigzag maps a consecutive sequence of integers to a zig-zag sequence. +// [0 1 2 3 4 5 ...] => [0 -1 +1 -2 +2 ...] +func zigzag(x int) int { + if x&1 != 0 { + x = ^x + } + return x >> 1 +} diff --git a/components/cli/vendor/github.com/google/go-cmp/cmp/internal/function/func.go b/components/cli/vendor/github.com/google/go-cmp/cmp/internal/function/func.go new file mode 100644 index 0000000000..4c35ff11ee --- /dev/null +++ b/components/cli/vendor/github.com/google/go-cmp/cmp/internal/function/func.go @@ -0,0 +1,49 @@ +// Copyright 2017, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE.md file. + +// Package function identifies function types. +package function + +import "reflect" + +type funcType int + +const ( + _ funcType = iota + + ttbFunc // func(T, T) bool + tibFunc // func(T, I) bool + trFunc // func(T) R + + Equal = ttbFunc // func(T, T) bool + EqualAssignable = tibFunc // func(T, I) bool; encapsulates func(T, T) bool + Transformer = trFunc // func(T) R + ValueFilter = ttbFunc // func(T, T) bool + Less = ttbFunc // func(T, T) bool +) + +var boolType = reflect.TypeOf(true) + +// IsType reports whether the reflect.Type is of the specified function type. +func IsType(t reflect.Type, ft funcType) bool { + if t == nil || t.Kind() != reflect.Func || t.IsVariadic() { + return false + } + ni, no := t.NumIn(), t.NumOut() + switch ft { + case ttbFunc: // func(T, T) bool + if ni == 2 && no == 1 && t.In(0) == t.In(1) && t.Out(0) == boolType { + return true + } + case tibFunc: // func(T, I) bool + if ni == 2 && no == 1 && t.In(0).AssignableTo(t.In(1)) && t.Out(0) == boolType { + return true + } + case trFunc: // func(T) R + if ni == 1 && no == 1 { + return true + } + } + return false +} diff --git a/components/cli/vendor/github.com/google/go-cmp/cmp/internal/value/format.go b/components/cli/vendor/github.com/google/go-cmp/cmp/internal/value/format.go new file mode 100644 index 0000000000..657e508779 --- /dev/null +++ b/components/cli/vendor/github.com/google/go-cmp/cmp/internal/value/format.go @@ -0,0 +1,277 @@ +// Copyright 2017, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE.md file. + +// Package value provides functionality for reflect.Value types. +package value + +import ( + "fmt" + "reflect" + "strconv" + "strings" + "unicode" +) + +var stringerIface = reflect.TypeOf((*fmt.Stringer)(nil)).Elem() + +// Format formats the value v as a string. +// +// This is similar to fmt.Sprintf("%+v", v) except this: +// * Prints the type unless it can be elided +// * Avoids printing struct fields that are zero +// * Prints a nil-slice as being nil, not empty +// * Prints map entries in deterministic order +func Format(v reflect.Value, conf FormatConfig) string { + conf.printType = true + conf.followPointers = true + conf.realPointers = true + return formatAny(v, conf, nil) +} + +type FormatConfig struct { + UseStringer bool // Should the String method be used if available? + printType bool // Should we print the type before the value? + PrintPrimitiveType bool // Should we print the type of primitives? + followPointers bool // Should we recursively follow pointers? + realPointers bool // Should we print the real address of pointers? +} + +func formatAny(v reflect.Value, conf FormatConfig, visited map[uintptr]bool) string { + // TODO: Should this be a multi-line printout in certain situations? + + if !v.IsValid() { + return "" + } + if conf.UseStringer && v.Type().Implements(stringerIface) && v.CanInterface() { + if (v.Kind() == reflect.Ptr || v.Kind() == reflect.Interface) && v.IsNil() { + return "" + } + + const stringerPrefix = "s" // Indicates that the String method was used + s := v.Interface().(fmt.Stringer).String() + return stringerPrefix + formatString(s) + } + + switch v.Kind() { + case reflect.Bool: + return formatPrimitive(v.Type(), v.Bool(), conf) + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return formatPrimitive(v.Type(), v.Int(), conf) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + if v.Type().PkgPath() == "" || v.Kind() == reflect.Uintptr { + // Unnamed uints are usually bytes or words, so use hexadecimal. + return formatPrimitive(v.Type(), formatHex(v.Uint()), conf) + } + return formatPrimitive(v.Type(), v.Uint(), conf) + case reflect.Float32, reflect.Float64: + return formatPrimitive(v.Type(), v.Float(), conf) + case reflect.Complex64, reflect.Complex128: + return formatPrimitive(v.Type(), v.Complex(), conf) + case reflect.String: + return formatPrimitive(v.Type(), formatString(v.String()), conf) + case reflect.UnsafePointer, reflect.Chan, reflect.Func: + return formatPointer(v, conf) + case reflect.Ptr: + if v.IsNil() { + if conf.printType { + return fmt.Sprintf("(%v)(nil)", v.Type()) + } + return "" + } + if visited[v.Pointer()] || !conf.followPointers { + return formatPointer(v, conf) + } + visited = insertPointer(visited, v.Pointer()) + return "&" + formatAny(v.Elem(), conf, visited) + case reflect.Interface: + if v.IsNil() { + if conf.printType { + return fmt.Sprintf("%v(nil)", v.Type()) + } + return "" + } + return formatAny(v.Elem(), conf, visited) + case reflect.Slice: + if v.IsNil() { + if conf.printType { + return fmt.Sprintf("%v(nil)", v.Type()) + } + return "" + } + if visited[v.Pointer()] { + return formatPointer(v, conf) + } + visited = insertPointer(visited, v.Pointer()) + fallthrough + case reflect.Array: + var ss []string + subConf := conf + subConf.printType = v.Type().Elem().Kind() == reflect.Interface + for i := 0; i < v.Len(); i++ { + s := formatAny(v.Index(i), subConf, visited) + ss = append(ss, s) + } + s := fmt.Sprintf("{%s}", strings.Join(ss, ", ")) + if conf.printType { + return v.Type().String() + s + } + return s + case reflect.Map: + if v.IsNil() { + if conf.printType { + return fmt.Sprintf("%v(nil)", v.Type()) + } + return "" + } + if visited[v.Pointer()] { + return formatPointer(v, conf) + } + visited = insertPointer(visited, v.Pointer()) + + var ss []string + keyConf, valConf := conf, conf + keyConf.printType = v.Type().Key().Kind() == reflect.Interface + keyConf.followPointers = false + valConf.printType = v.Type().Elem().Kind() == reflect.Interface + for _, k := range SortKeys(v.MapKeys()) { + sk := formatAny(k, keyConf, visited) + sv := formatAny(v.MapIndex(k), valConf, visited) + ss = append(ss, fmt.Sprintf("%s: %s", sk, sv)) + } + s := fmt.Sprintf("{%s}", strings.Join(ss, ", ")) + if conf.printType { + return v.Type().String() + s + } + return s + case reflect.Struct: + var ss []string + subConf := conf + subConf.printType = true + for i := 0; i < v.NumField(); i++ { + vv := v.Field(i) + if isZero(vv) { + continue // Elide zero value fields + } + name := v.Type().Field(i).Name + subConf.UseStringer = conf.UseStringer + s := formatAny(vv, subConf, visited) + ss = append(ss, fmt.Sprintf("%s: %s", name, s)) + } + s := fmt.Sprintf("{%s}", strings.Join(ss, ", ")) + if conf.printType { + return v.Type().String() + s + } + return s + default: + panic(fmt.Sprintf("%v kind not handled", v.Kind())) + } +} + +func formatString(s string) string { + // Use quoted string if it the same length as a raw string literal. + // Otherwise, attempt to use the raw string form. + qs := strconv.Quote(s) + if len(qs) == 1+len(s)+1 { + return qs + } + + // Disallow newlines to ensure output is a single line. + // Only allow printable runes for readability purposes. + rawInvalid := func(r rune) bool { + return r == '`' || r == '\n' || !unicode.IsPrint(r) + } + if strings.IndexFunc(s, rawInvalid) < 0 { + return "`" + s + "`" + } + return qs +} + +func formatPrimitive(t reflect.Type, v interface{}, conf FormatConfig) string { + if conf.printType && (conf.PrintPrimitiveType || t.PkgPath() != "") { + return fmt.Sprintf("%v(%v)", t, v) + } + return fmt.Sprintf("%v", v) +} + +func formatPointer(v reflect.Value, conf FormatConfig) string { + p := v.Pointer() + if !conf.realPointers { + p = 0 // For deterministic printing purposes + } + s := formatHex(uint64(p)) + if conf.printType { + return fmt.Sprintf("(%v)(%s)", v.Type(), s) + } + return s +} + +func formatHex(u uint64) string { + var f string + switch { + case u <= 0xff: + f = "0x%02x" + case u <= 0xffff: + f = "0x%04x" + case u <= 0xffffff: + f = "0x%06x" + case u <= 0xffffffff: + f = "0x%08x" + case u <= 0xffffffffff: + f = "0x%010x" + case u <= 0xffffffffffff: + f = "0x%012x" + case u <= 0xffffffffffffff: + f = "0x%014x" + case u <= 0xffffffffffffffff: + f = "0x%016x" + } + return fmt.Sprintf(f, u) +} + +// insertPointer insert p into m, allocating m if necessary. +func insertPointer(m map[uintptr]bool, p uintptr) map[uintptr]bool { + if m == nil { + m = make(map[uintptr]bool) + } + m[p] = true + return m +} + +// isZero reports whether v is the zero value. +// This does not rely on Interface and so can be used on unexported fields. +func isZero(v reflect.Value) bool { + switch v.Kind() { + case reflect.Bool: + return v.Bool() == false + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return v.Int() == 0 + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return v.Uint() == 0 + case reflect.Float32, reflect.Float64: + return v.Float() == 0 + case reflect.Complex64, reflect.Complex128: + return v.Complex() == 0 + case reflect.String: + return v.String() == "" + case reflect.UnsafePointer: + return v.Pointer() == 0 + case reflect.Chan, reflect.Func, reflect.Interface, reflect.Ptr, reflect.Map, reflect.Slice: + return v.IsNil() + case reflect.Array: + for i := 0; i < v.Len(); i++ { + if !isZero(v.Index(i)) { + return false + } + } + return true + case reflect.Struct: + for i := 0; i < v.NumField(); i++ { + if !isZero(v.Field(i)) { + return false + } + } + return true + } + return false +} diff --git a/components/cli/vendor/github.com/google/go-cmp/cmp/internal/value/sort.go b/components/cli/vendor/github.com/google/go-cmp/cmp/internal/value/sort.go new file mode 100644 index 0000000000..fe8aa27a07 --- /dev/null +++ b/components/cli/vendor/github.com/google/go-cmp/cmp/internal/value/sort.go @@ -0,0 +1,111 @@ +// Copyright 2017, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE.md file. + +package value + +import ( + "fmt" + "math" + "reflect" + "sort" +) + +// SortKeys sorts a list of map keys, deduplicating keys if necessary. +// The type of each value must be comparable. +func SortKeys(vs []reflect.Value) []reflect.Value { + if len(vs) == 0 { + return vs + } + + // Sort the map keys. + sort.Sort(valueSorter(vs)) + + // Deduplicate keys (fails for NaNs). + vs2 := vs[:1] + for _, v := range vs[1:] { + if isLess(vs2[len(vs2)-1], v) { + vs2 = append(vs2, v) + } + } + return vs2 +} + +// TODO: Use sort.Slice once Google AppEngine is on Go1.8 or above. +type valueSorter []reflect.Value + +func (vs valueSorter) Len() int { return len(vs) } +func (vs valueSorter) Less(i, j int) bool { return isLess(vs[i], vs[j]) } +func (vs valueSorter) Swap(i, j int) { vs[i], vs[j] = vs[j], vs[i] } + +// isLess is a generic function for sorting arbitrary map keys. +// The inputs must be of the same type and must be comparable. +func isLess(x, y reflect.Value) bool { + switch x.Type().Kind() { + case reflect.Bool: + return !x.Bool() && y.Bool() + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return x.Int() < y.Int() + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return x.Uint() < y.Uint() + case reflect.Float32, reflect.Float64: + fx, fy := x.Float(), y.Float() + return fx < fy || math.IsNaN(fx) && !math.IsNaN(fy) + case reflect.Complex64, reflect.Complex128: + cx, cy := x.Complex(), y.Complex() + rx, ix, ry, iy := real(cx), imag(cx), real(cy), imag(cy) + if rx == ry || (math.IsNaN(rx) && math.IsNaN(ry)) { + return ix < iy || math.IsNaN(ix) && !math.IsNaN(iy) + } + return rx < ry || math.IsNaN(rx) && !math.IsNaN(ry) + case reflect.Ptr, reflect.UnsafePointer, reflect.Chan: + return x.Pointer() < y.Pointer() + case reflect.String: + return x.String() < y.String() + case reflect.Array: + for i := 0; i < x.Len(); i++ { + if isLess(x.Index(i), y.Index(i)) { + return true + } + if isLess(y.Index(i), x.Index(i)) { + return false + } + } + return false + case reflect.Struct: + for i := 0; i < x.NumField(); i++ { + if isLess(x.Field(i), y.Field(i)) { + return true + } + if isLess(y.Field(i), x.Field(i)) { + return false + } + } + return false + case reflect.Interface: + vx, vy := x.Elem(), y.Elem() + if !vx.IsValid() || !vy.IsValid() { + return !vx.IsValid() && vy.IsValid() + } + tx, ty := vx.Type(), vy.Type() + if tx == ty { + return isLess(x.Elem(), y.Elem()) + } + if tx.Kind() != ty.Kind() { + return vx.Kind() < vy.Kind() + } + if tx.String() != ty.String() { + return tx.String() < ty.String() + } + if tx.PkgPath() != ty.PkgPath() { + return tx.PkgPath() < ty.PkgPath() + } + // This can happen in rare situations, so we fallback to just comparing + // the unique pointer for a reflect.Type. This guarantees deterministic + // ordering within a program, but it is obviously not stable. + return reflect.ValueOf(vx.Type()).Pointer() < reflect.ValueOf(vy.Type()).Pointer() + default: + // Must be Func, Map, or Slice; which are not comparable. + panic(fmt.Sprintf("%T is not comparable", x.Type())) + } +} diff --git a/components/cli/vendor/github.com/google/go-cmp/cmp/options.go b/components/cli/vendor/github.com/google/go-cmp/cmp/options.go new file mode 100644 index 0000000000..91d4b066e0 --- /dev/null +++ b/components/cli/vendor/github.com/google/go-cmp/cmp/options.go @@ -0,0 +1,453 @@ +// Copyright 2017, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE.md file. + +package cmp + +import ( + "fmt" + "reflect" + "runtime" + "strings" + + "github.com/google/go-cmp/cmp/internal/function" +) + +// Option configures for specific behavior of Equal and Diff. In particular, +// the fundamental Option functions (Ignore, Transformer, and Comparer), +// configure how equality is determined. +// +// The fundamental options may be composed with filters (FilterPath and +// FilterValues) to control the scope over which they are applied. +// +// The cmp/cmpopts package provides helper functions for creating options that +// may be used with Equal and Diff. +type Option interface { + // filter applies all filters and returns the option that remains. + // Each option may only read s.curPath and call s.callTTBFunc. + // + // An Options is returned only if multiple comparers or transformers + // can apply simultaneously and will only contain values of those types + // or sub-Options containing values of those types. + filter(s *state, vx, vy reflect.Value, t reflect.Type) applicableOption +} + +// applicableOption represents the following types: +// Fundamental: ignore | invalid | *comparer | *transformer +// Grouping: Options +type applicableOption interface { + Option + + // apply executes the option, which may mutate s or panic. + apply(s *state, vx, vy reflect.Value) +} + +// coreOption represents the following types: +// Fundamental: ignore | invalid | *comparer | *transformer +// Filters: *pathFilter | *valuesFilter +type coreOption interface { + Option + isCore() +} + +type core struct{} + +func (core) isCore() {} + +// Options is a list of Option values that also satisfies the Option interface. +// Helper comparison packages may return an Options value when packing multiple +// Option values into a single Option. When this package processes an Options, +// it will be implicitly expanded into a flat list. +// +// Applying a filter on an Options is equivalent to applying that same filter +// on all individual options held within. +type Options []Option + +func (opts Options) filter(s *state, vx, vy reflect.Value, t reflect.Type) (out applicableOption) { + for _, opt := range opts { + switch opt := opt.filter(s, vx, vy, t); opt.(type) { + case ignore: + return ignore{} // Only ignore can short-circuit evaluation + case invalid: + out = invalid{} // Takes precedence over comparer or transformer + case *comparer, *transformer, Options: + switch out.(type) { + case nil: + out = opt + case invalid: + // Keep invalid + case *comparer, *transformer, Options: + out = Options{out, opt} // Conflicting comparers or transformers + } + } + } + return out +} + +func (opts Options) apply(s *state, _, _ reflect.Value) { + const warning = "ambiguous set of applicable options" + const help = "consider using filters to ensure at most one Comparer or Transformer may apply" + var ss []string + for _, opt := range flattenOptions(nil, opts) { + ss = append(ss, fmt.Sprint(opt)) + } + set := strings.Join(ss, "\n\t") + panic(fmt.Sprintf("%s at %#v:\n\t%s\n%s", warning, s.curPath, set, help)) +} + +func (opts Options) String() string { + var ss []string + for _, opt := range opts { + ss = append(ss, fmt.Sprint(opt)) + } + return fmt.Sprintf("Options{%s}", strings.Join(ss, ", ")) +} + +// FilterPath returns a new Option where opt is only evaluated if filter f +// returns true for the current Path in the value tree. +// +// The option passed in may be an Ignore, Transformer, Comparer, Options, or +// a previously filtered Option. +func FilterPath(f func(Path) bool, opt Option) Option { + if f == nil { + panic("invalid path filter function") + } + if opt := normalizeOption(opt); opt != nil { + return &pathFilter{fnc: f, opt: opt} + } + return nil +} + +type pathFilter struct { + core + fnc func(Path) bool + opt Option +} + +func (f pathFilter) filter(s *state, vx, vy reflect.Value, t reflect.Type) applicableOption { + if f.fnc(s.curPath) { + return f.opt.filter(s, vx, vy, t) + } + return nil +} + +func (f pathFilter) String() string { + fn := getFuncName(reflect.ValueOf(f.fnc).Pointer()) + return fmt.Sprintf("FilterPath(%s, %v)", fn, f.opt) +} + +// FilterValues returns a new Option where opt is only evaluated if filter f, +// which is a function of the form "func(T, T) bool", returns true for the +// current pair of values being compared. If the type of the values is not +// assignable to T, then this filter implicitly returns false. +// +// The filter function must be +// symmetric (i.e., agnostic to the order of the inputs) and +// deterministic (i.e., produces the same result when given the same inputs). +// If T is an interface, it is possible that f is called with two values with +// different concrete types that both implement T. +// +// The option passed in may be an Ignore, Transformer, Comparer, Options, or +// a previously filtered Option. +func FilterValues(f interface{}, opt Option) Option { + v := reflect.ValueOf(f) + if !function.IsType(v.Type(), function.ValueFilter) || v.IsNil() { + panic(fmt.Sprintf("invalid values filter function: %T", f)) + } + if opt := normalizeOption(opt); opt != nil { + vf := &valuesFilter{fnc: v, opt: opt} + if ti := v.Type().In(0); ti.Kind() != reflect.Interface || ti.NumMethod() > 0 { + vf.typ = ti + } + return vf + } + return nil +} + +type valuesFilter struct { + core + typ reflect.Type // T + fnc reflect.Value // func(T, T) bool + opt Option +} + +func (f valuesFilter) filter(s *state, vx, vy reflect.Value, t reflect.Type) applicableOption { + if !vx.IsValid() || !vy.IsValid() { + return invalid{} + } + if (f.typ == nil || t.AssignableTo(f.typ)) && s.callTTBFunc(f.fnc, vx, vy) { + return f.opt.filter(s, vx, vy, t) + } + return nil +} + +func (f valuesFilter) String() string { + fn := getFuncName(f.fnc.Pointer()) + return fmt.Sprintf("FilterValues(%s, %v)", fn, f.opt) +} + +// Ignore is an Option that causes all comparisons to be ignored. +// This value is intended to be combined with FilterPath or FilterValues. +// It is an error to pass an unfiltered Ignore option to Equal. +func Ignore() Option { return ignore{} } + +type ignore struct{ core } + +func (ignore) isFiltered() bool { return false } +func (ignore) filter(_ *state, _, _ reflect.Value, _ reflect.Type) applicableOption { return ignore{} } +func (ignore) apply(_ *state, _, _ reflect.Value) { return } +func (ignore) String() string { return "Ignore()" } + +// invalid is a sentinel Option type to indicate that some options could not +// be evaluated due to unexported fields. +type invalid struct{ core } + +func (invalid) filter(_ *state, _, _ reflect.Value, _ reflect.Type) applicableOption { return invalid{} } +func (invalid) apply(s *state, _, _ reflect.Value) { + const help = "consider using AllowUnexported or cmpopts.IgnoreUnexported" + panic(fmt.Sprintf("cannot handle unexported field: %#v\n%s", s.curPath, help)) +} + +// Transformer returns an Option that applies a transformation function that +// converts values of a certain type into that of another. +// +// The transformer f must be a function "func(T) R" that converts values of +// type T to those of type R and is implicitly filtered to input values +// assignable to T. The transformer must not mutate T in any way. +// +// To help prevent some cases of infinite recursive cycles applying the +// same transform to the output of itself (e.g., in the case where the +// input and output types are the same), an implicit filter is added such that +// a transformer is applicable only if that exact transformer is not already +// in the tail of the Path since the last non-Transform step. +// +// The name is a user provided label that is used as the Transform.Name in the +// transformation PathStep. If empty, an arbitrary name is used. +func Transformer(name string, f interface{}) Option { + v := reflect.ValueOf(f) + if !function.IsType(v.Type(), function.Transformer) || v.IsNil() { + panic(fmt.Sprintf("invalid transformer function: %T", f)) + } + if name == "" { + name = "λ" // Lambda-symbol as place-holder for anonymous transformer + } + if !isValid(name) { + panic(fmt.Sprintf("invalid name: %q", name)) + } + tr := &transformer{name: name, fnc: reflect.ValueOf(f)} + if ti := v.Type().In(0); ti.Kind() != reflect.Interface || ti.NumMethod() > 0 { + tr.typ = ti + } + return tr +} + +type transformer struct { + core + name string + typ reflect.Type // T + fnc reflect.Value // func(T) R +} + +func (tr *transformer) isFiltered() bool { return tr.typ != nil } + +func (tr *transformer) filter(s *state, _, _ reflect.Value, t reflect.Type) applicableOption { + for i := len(s.curPath) - 1; i >= 0; i-- { + if t, ok := s.curPath[i].(*transform); !ok { + break // Hit most recent non-Transform step + } else if tr == t.trans { + return nil // Cannot directly use same Transform + } + } + if tr.typ == nil || t.AssignableTo(tr.typ) { + return tr + } + return nil +} + +func (tr *transformer) apply(s *state, vx, vy reflect.Value) { + // Update path before calling the Transformer so that dynamic checks + // will use the updated path. + s.curPath.push(&transform{pathStep{tr.fnc.Type().Out(0)}, tr}) + defer s.curPath.pop() + + vx = s.callTRFunc(tr.fnc, vx) + vy = s.callTRFunc(tr.fnc, vy) + s.compareAny(vx, vy) +} + +func (tr transformer) String() string { + return fmt.Sprintf("Transformer(%s, %s)", tr.name, getFuncName(tr.fnc.Pointer())) +} + +// Comparer returns an Option that determines whether two values are equal +// to each other. +// +// The comparer f must be a function "func(T, T) bool" and is implicitly +// filtered to input values assignable to T. If T is an interface, it is +// possible that f is called with two values of different concrete types that +// both implement T. +// +// The equality function must be: +// • Symmetric: equal(x, y) == equal(y, x) +// • Deterministic: equal(x, y) == equal(x, y) +// • Pure: equal(x, y) does not modify x or y +func Comparer(f interface{}) Option { + v := reflect.ValueOf(f) + if !function.IsType(v.Type(), function.Equal) || v.IsNil() { + panic(fmt.Sprintf("invalid comparer function: %T", f)) + } + cm := &comparer{fnc: v} + if ti := v.Type().In(0); ti.Kind() != reflect.Interface || ti.NumMethod() > 0 { + cm.typ = ti + } + return cm +} + +type comparer struct { + core + typ reflect.Type // T + fnc reflect.Value // func(T, T) bool +} + +func (cm *comparer) isFiltered() bool { return cm.typ != nil } + +func (cm *comparer) filter(_ *state, _, _ reflect.Value, t reflect.Type) applicableOption { + if cm.typ == nil || t.AssignableTo(cm.typ) { + return cm + } + return nil +} + +func (cm *comparer) apply(s *state, vx, vy reflect.Value) { + eq := s.callTTBFunc(cm.fnc, vx, vy) + s.report(eq, vx, vy) +} + +func (cm comparer) String() string { + return fmt.Sprintf("Comparer(%s)", getFuncName(cm.fnc.Pointer())) +} + +// AllowUnexported returns an Option that forcibly allows operations on +// unexported fields in certain structs, which are specified by passing in a +// value of each struct type. +// +// Users of this option must understand that comparing on unexported fields +// from external packages is not safe since changes in the internal +// implementation of some external package may cause the result of Equal +// to unexpectedly change. However, it may be valid to use this option on types +// defined in an internal package where the semantic meaning of an unexported +// field is in the control of the user. +// +// For some cases, a custom Comparer should be used instead that defines +// equality as a function of the public API of a type rather than the underlying +// unexported implementation. +// +// For example, the reflect.Type documentation defines equality to be determined +// by the == operator on the interface (essentially performing a shallow pointer +// comparison) and most attempts to compare *regexp.Regexp types are interested +// in only checking that the regular expression strings are equal. +// Both of these are accomplished using Comparers: +// +// Comparer(func(x, y reflect.Type) bool { return x == y }) +// Comparer(func(x, y *regexp.Regexp) bool { return x.String() == y.String() }) +// +// In other cases, the cmpopts.IgnoreUnexported option can be used to ignore +// all unexported fields on specified struct types. +func AllowUnexported(types ...interface{}) Option { + if !supportAllowUnexported { + panic("AllowUnexported is not supported on purego builds, Google App Engine Standard, or GopherJS") + } + m := make(map[reflect.Type]bool) + for _, typ := range types { + t := reflect.TypeOf(typ) + if t.Kind() != reflect.Struct { + panic(fmt.Sprintf("invalid struct type: %T", typ)) + } + m[t] = true + } + return visibleStructs(m) +} + +type visibleStructs map[reflect.Type]bool + +func (visibleStructs) filter(_ *state, _, _ reflect.Value, _ reflect.Type) applicableOption { + panic("not implemented") +} + +// reporter is an Option that configures how differences are reported. +type reporter interface { + // TODO: Not exported yet. + // + // Perhaps add PushStep and PopStep and change Report to only accept + // a PathStep instead of the full-path? Adding a PushStep and PopStep makes + // it clear that we are traversing the value tree in a depth-first-search + // manner, which has an effect on how values are printed. + + Option + + // Report is called for every comparison made and will be provided with + // the two values being compared, the equality result, and the + // current path in the value tree. It is possible for x or y to be an + // invalid reflect.Value if one of the values is non-existent; + // which is possible with maps and slices. + Report(x, y reflect.Value, eq bool, p Path) +} + +// normalizeOption normalizes the input options such that all Options groups +// are flattened and groups with a single element are reduced to that element. +// Only coreOptions and Options containing coreOptions are allowed. +func normalizeOption(src Option) Option { + switch opts := flattenOptions(nil, Options{src}); len(opts) { + case 0: + return nil + case 1: + return opts[0] + default: + return opts + } +} + +// flattenOptions copies all options in src to dst as a flat list. +// Only coreOptions and Options containing coreOptions are allowed. +func flattenOptions(dst, src Options) Options { + for _, opt := range src { + switch opt := opt.(type) { + case nil: + continue + case Options: + dst = flattenOptions(dst, opt) + case coreOption: + dst = append(dst, opt) + default: + panic(fmt.Sprintf("invalid option type: %T", opt)) + } + } + return dst +} + +// getFuncName returns a short function name from the pointer. +// The string parsing logic works up until Go1.9. +func getFuncName(p uintptr) string { + fnc := runtime.FuncForPC(p) + if fnc == nil { + return "" + } + name := fnc.Name() // E.g., "long/path/name/mypkg.(mytype).(long/path/name/mypkg.myfunc)-fm" + if strings.HasSuffix(name, ")-fm") || strings.HasSuffix(name, ")·fm") { + // Strip the package name from method name. + name = strings.TrimSuffix(name, ")-fm") + name = strings.TrimSuffix(name, ")·fm") + if i := strings.LastIndexByte(name, '('); i >= 0 { + methodName := name[i+1:] // E.g., "long/path/name/mypkg.myfunc" + if j := strings.LastIndexByte(methodName, '.'); j >= 0 { + methodName = methodName[j+1:] // E.g., "myfunc" + } + name = name[:i] + methodName // E.g., "long/path/name/mypkg.(mytype)." + "myfunc" + } + } + if i := strings.LastIndexByte(name, '/'); i >= 0 { + // Strip the package name. + name = name[i+1:] // E.g., "mypkg.(mytype).myfunc" + } + return name +} diff --git a/components/cli/vendor/github.com/google/go-cmp/cmp/path.go b/components/cli/vendor/github.com/google/go-cmp/cmp/path.go new file mode 100644 index 0000000000..c08a3cf80d --- /dev/null +++ b/components/cli/vendor/github.com/google/go-cmp/cmp/path.go @@ -0,0 +1,309 @@ +// Copyright 2017, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE.md file. + +package cmp + +import ( + "fmt" + "reflect" + "strings" + "unicode" + "unicode/utf8" +) + +type ( + // Path is a list of PathSteps describing the sequence of operations to get + // from some root type to the current position in the value tree. + // The first Path element is always an operation-less PathStep that exists + // simply to identify the initial type. + // + // When traversing structs with embedded structs, the embedded struct will + // always be accessed as a field before traversing the fields of the + // embedded struct themselves. That is, an exported field from the + // embedded struct will never be accessed directly from the parent struct. + Path []PathStep + + // PathStep is a union-type for specific operations to traverse + // a value's tree structure. Users of this package never need to implement + // these types as values of this type will be returned by this package. + PathStep interface { + String() string + Type() reflect.Type // Resulting type after performing the path step + isPathStep() + } + + // SliceIndex is an index operation on a slice or array at some index Key. + SliceIndex interface { + PathStep + Key() int // May return -1 if in a split state + + // SplitKeys returns the indexes for indexing into slices in the + // x and y values, respectively. These indexes may differ due to the + // insertion or removal of an element in one of the slices, causing + // all of the indexes to be shifted. If an index is -1, then that + // indicates that the element does not exist in the associated slice. + // + // Key is guaranteed to return -1 if and only if the indexes returned + // by SplitKeys are not the same. SplitKeys will never return -1 for + // both indexes. + SplitKeys() (x int, y int) + + isSliceIndex() + } + // MapIndex is an index operation on a map at some index Key. + MapIndex interface { + PathStep + Key() reflect.Value + isMapIndex() + } + // TypeAssertion represents a type assertion on an interface. + TypeAssertion interface { + PathStep + isTypeAssertion() + } + // StructField represents a struct field access on a field called Name. + StructField interface { + PathStep + Name() string + Index() int + isStructField() + } + // Indirect represents pointer indirection on the parent type. + Indirect interface { + PathStep + isIndirect() + } + // Transform is a transformation from the parent type to the current type. + Transform interface { + PathStep + Name() string + Func() reflect.Value + + // Option returns the originally constructed Transformer option. + // The == operator can be used to detect the exact option used. + Option() Option + + isTransform() + } +) + +func (pa *Path) push(s PathStep) { + *pa = append(*pa, s) +} + +func (pa *Path) pop() { + *pa = (*pa)[:len(*pa)-1] +} + +// Last returns the last PathStep in the Path. +// If the path is empty, this returns a non-nil PathStep that reports a nil Type. +func (pa Path) Last() PathStep { + return pa.Index(-1) +} + +// Index returns the ith step in the Path and supports negative indexing. +// A negative index starts counting from the tail of the Path such that -1 +// refers to the last step, -2 refers to the second-to-last step, and so on. +// If index is invalid, this returns a non-nil PathStep that reports a nil Type. +func (pa Path) Index(i int) PathStep { + if i < 0 { + i = len(pa) + i + } + if i < 0 || i >= len(pa) { + return pathStep{} + } + return pa[i] +} + +// String returns the simplified path to a node. +// The simplified path only contains struct field accesses. +// +// For example: +// MyMap.MySlices.MyField +func (pa Path) String() string { + var ss []string + for _, s := range pa { + if _, ok := s.(*structField); ok { + ss = append(ss, s.String()) + } + } + return strings.TrimPrefix(strings.Join(ss, ""), ".") +} + +// GoString returns the path to a specific node using Go syntax. +// +// For example: +// (*root.MyMap["key"].(*mypkg.MyStruct).MySlices)[2][3].MyField +func (pa Path) GoString() string { + var ssPre, ssPost []string + var numIndirect int + for i, s := range pa { + var nextStep PathStep + if i+1 < len(pa) { + nextStep = pa[i+1] + } + switch s := s.(type) { + case *indirect: + numIndirect++ + pPre, pPost := "(", ")" + switch nextStep.(type) { + case *indirect: + continue // Next step is indirection, so let them batch up + case *structField: + numIndirect-- // Automatic indirection on struct fields + case nil: + pPre, pPost = "", "" // Last step; no need for parenthesis + } + if numIndirect > 0 { + ssPre = append(ssPre, pPre+strings.Repeat("*", numIndirect)) + ssPost = append(ssPost, pPost) + } + numIndirect = 0 + continue + case *transform: + ssPre = append(ssPre, s.trans.name+"(") + ssPost = append(ssPost, ")") + continue + case *typeAssertion: + // As a special-case, elide type assertions on anonymous types + // since they are typically generated dynamically and can be very + // verbose. For example, some transforms return interface{} because + // of Go's lack of generics, but typically take in and return the + // exact same concrete type. + if s.Type().PkgPath() == "" { + continue + } + } + ssPost = append(ssPost, s.String()) + } + for i, j := 0, len(ssPre)-1; i < j; i, j = i+1, j-1 { + ssPre[i], ssPre[j] = ssPre[j], ssPre[i] + } + return strings.Join(ssPre, "") + strings.Join(ssPost, "") +} + +type ( + pathStep struct { + typ reflect.Type + } + + sliceIndex struct { + pathStep + xkey, ykey int + } + mapIndex struct { + pathStep + key reflect.Value + } + typeAssertion struct { + pathStep + } + structField struct { + pathStep + name string + idx int + + // These fields are used for forcibly accessing an unexported field. + // pvx, pvy, and field are only valid if unexported is true. + unexported bool + force bool // Forcibly allow visibility + pvx, pvy reflect.Value // Parent values + field reflect.StructField // Field information + } + indirect struct { + pathStep + } + transform struct { + pathStep + trans *transformer + } +) + +func (ps pathStep) Type() reflect.Type { return ps.typ } +func (ps pathStep) String() string { + if ps.typ == nil { + return "" + } + s := ps.typ.String() + if s == "" || strings.ContainsAny(s, "{}\n") { + return "root" // Type too simple or complex to print + } + return fmt.Sprintf("{%s}", s) +} + +func (si sliceIndex) String() string { + switch { + case si.xkey == si.ykey: + return fmt.Sprintf("[%d]", si.xkey) + case si.ykey == -1: + // [5->?] means "I don't know where X[5] went" + return fmt.Sprintf("[%d->?]", si.xkey) + case si.xkey == -1: + // [?->3] means "I don't know where Y[3] came from" + return fmt.Sprintf("[?->%d]", si.ykey) + default: + // [5->3] means "X[5] moved to Y[3]" + return fmt.Sprintf("[%d->%d]", si.xkey, si.ykey) + } +} +func (mi mapIndex) String() string { return fmt.Sprintf("[%#v]", mi.key) } +func (ta typeAssertion) String() string { return fmt.Sprintf(".(%v)", ta.typ) } +func (sf structField) String() string { return fmt.Sprintf(".%s", sf.name) } +func (in indirect) String() string { return "*" } +func (tf transform) String() string { return fmt.Sprintf("%s()", tf.trans.name) } + +func (si sliceIndex) Key() int { + if si.xkey != si.ykey { + return -1 + } + return si.xkey +} +func (si sliceIndex) SplitKeys() (x, y int) { return si.xkey, si.ykey } +func (mi mapIndex) Key() reflect.Value { return mi.key } +func (sf structField) Name() string { return sf.name } +func (sf structField) Index() int { return sf.idx } +func (tf transform) Name() string { return tf.trans.name } +func (tf transform) Func() reflect.Value { return tf.trans.fnc } +func (tf transform) Option() Option { return tf.trans } + +func (pathStep) isPathStep() {} +func (sliceIndex) isSliceIndex() {} +func (mapIndex) isMapIndex() {} +func (typeAssertion) isTypeAssertion() {} +func (structField) isStructField() {} +func (indirect) isIndirect() {} +func (transform) isTransform() {} + +var ( + _ SliceIndex = sliceIndex{} + _ MapIndex = mapIndex{} + _ TypeAssertion = typeAssertion{} + _ StructField = structField{} + _ Indirect = indirect{} + _ Transform = transform{} + + _ PathStep = sliceIndex{} + _ PathStep = mapIndex{} + _ PathStep = typeAssertion{} + _ PathStep = structField{} + _ PathStep = indirect{} + _ PathStep = transform{} +) + +// isExported reports whether the identifier is exported. +func isExported(id string) bool { + r, _ := utf8.DecodeRuneInString(id) + return unicode.IsUpper(r) +} + +// isValid reports whether the identifier is valid. +// Empty and underscore-only strings are not valid. +func isValid(id string) bool { + ok := id != "" && id != "_" + for j, c := range id { + ok = ok && (j > 0 || !unicode.IsDigit(c)) + ok = ok && (c == '_' || unicode.IsLetter(c) || unicode.IsDigit(c)) + } + return ok +} diff --git a/components/cli/vendor/github.com/google/go-cmp/cmp/reporter.go b/components/cli/vendor/github.com/google/go-cmp/cmp/reporter.go new file mode 100644 index 0000000000..20e9f18e0d --- /dev/null +++ b/components/cli/vendor/github.com/google/go-cmp/cmp/reporter.go @@ -0,0 +1,53 @@ +// Copyright 2017, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE.md file. + +package cmp + +import ( + "fmt" + "reflect" + "strings" + + "github.com/google/go-cmp/cmp/internal/value" +) + +type defaultReporter struct { + Option + diffs []string // List of differences, possibly truncated + ndiffs int // Total number of differences + nbytes int // Number of bytes in diffs + nlines int // Number of lines in diffs +} + +var _ reporter = (*defaultReporter)(nil) + +func (r *defaultReporter) Report(x, y reflect.Value, eq bool, p Path) { + if eq { + return // Ignore equal results + } + const maxBytes = 4096 + const maxLines = 256 + r.ndiffs++ + if r.nbytes < maxBytes && r.nlines < maxLines { + sx := value.Format(x, value.FormatConfig{UseStringer: true}) + sy := value.Format(y, value.FormatConfig{UseStringer: true}) + if sx == sy { + // Unhelpful output, so use more exact formatting. + sx = value.Format(x, value.FormatConfig{PrintPrimitiveType: true}) + sy = value.Format(y, value.FormatConfig{PrintPrimitiveType: true}) + } + s := fmt.Sprintf("%#v:\n\t-: %s\n\t+: %s\n", p, sx, sy) + r.diffs = append(r.diffs, s) + r.nbytes += len(s) + r.nlines += strings.Count(s, "\n") + } +} + +func (r *defaultReporter) String() string { + s := strings.Join(r.diffs, "") + if r.ndiffs == len(r.diffs) { + return s + } + return fmt.Sprintf("%s... %d more differences ...", s, r.ndiffs-len(r.diffs)) +} diff --git a/components/cli/vendor/github.com/google/go-cmp/cmp/unsafe_panic.go b/components/cli/vendor/github.com/google/go-cmp/cmp/unsafe_panic.go new file mode 100644 index 0000000000..d1518eb3a8 --- /dev/null +++ b/components/cli/vendor/github.com/google/go-cmp/cmp/unsafe_panic.go @@ -0,0 +1,15 @@ +// Copyright 2017, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE.md file. + +// +build purego appengine js + +package cmp + +import "reflect" + +const supportAllowUnexported = false + +func unsafeRetrieveField(reflect.Value, reflect.StructField) reflect.Value { + panic("unsafeRetrieveField is not implemented") +} diff --git a/components/cli/vendor/github.com/google/go-cmp/cmp/unsafe_reflect.go b/components/cli/vendor/github.com/google/go-cmp/cmp/unsafe_reflect.go new file mode 100644 index 0000000000..579b65507f --- /dev/null +++ b/components/cli/vendor/github.com/google/go-cmp/cmp/unsafe_reflect.go @@ -0,0 +1,23 @@ +// Copyright 2017, The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE.md file. + +// +build !purego,!appengine,!js + +package cmp + +import ( + "reflect" + "unsafe" +) + +const supportAllowUnexported = true + +// unsafeRetrieveField uses unsafe to forcibly retrieve any field from a struct +// such that the value has read-write permissions. +// +// The parent struct, v, must be addressable, while f must be a StructField +// describing the field to retrieve. +func unsafeRetrieveField(v reflect.Value, f reflect.StructField) reflect.Value { + return reflect.NewAt(f.Type, unsafe.Pointer(v.UnsafeAddr()+f.Offset)).Elem() +} diff --git a/components/cli/vendor/github.com/gotestyourself/gotestyourself/README.md b/components/cli/vendor/github.com/gotestyourself/gotestyourself/README.md index 037fba6c77..aad893ae03 100644 --- a/components/cli/vendor/github.com/gotestyourself/gotestyourself/README.md +++ b/components/cli/vendor/github.com/gotestyourself/gotestyourself/README.md @@ -10,24 +10,24 @@ patterns. ## Packages +* [assert](http://godoc.org/github.com/gotestyourself/gotestyourself/assert) - + compare values and fail the test when the comparison fails +* [env](http://godoc.org/github.com/gotestyourself/gotestyourself/env) - + test code that uses environment variables * [fs](http://godoc.org/github.com/gotestyourself/gotestyourself/fs) - create test files and directories * [golden](http://godoc.org/github.com/gotestyourself/gotestyourself/golden) - compare large multi-line strings -* [testsum](http://godoc.org/github.com/gotestyourself/gotestyourself/testsum) - - a program to summarize `go test` output and test failures * [icmd](http://godoc.org/github.com/gotestyourself/gotestyourself/icmd) - execute binaries and test the output * [poll](http://godoc.org/github.com/gotestyourself/gotestyourself/poll) - test asynchronous code by polling until a desired state is reached * [skip](http://godoc.org/github.com/gotestyourself/gotestyourself/skip) - skip tests based on conditions +* [testsum](http://godoc.org/github.com/gotestyourself/gotestyourself/testsum) - + a program to summarize `go test` output and test failures ## Related -* [testify/assert](https://godoc.org/github.com/stretchr/testify/assert) and - [testify/require](https://godoc.org/github.com/stretchr/testify/require) - - assertion libraries with common assertions -* [golang/mock](https://github.com/golang/mock) - generate mocks for interfaces -* [testify/suite](https://godoc.org/github.com/stretchr/testify/suite) - - group test into suites to share common setup/teardown logic +* [maxbrunsfeld/counterfeiter](https://github.com/maxbrunsfeld/counterfeiter) - generate fakes for interfaces +* [jonboulle/clockwork](https://github.com/jonboulle/clockwork) - a fake clock for testing code that uses `time` diff --git a/components/cli/vendor/github.com/gotestyourself/gotestyourself/assert/assert.go b/components/cli/vendor/github.com/gotestyourself/gotestyourself/assert/assert.go new file mode 100644 index 0000000000..d2b05f4165 --- /dev/null +++ b/components/cli/vendor/github.com/gotestyourself/gotestyourself/assert/assert.go @@ -0,0 +1,289 @@ +/*Package assert provides assertions for comparing expected values to actual +values. When an assertion fails a helpful error message is printed. + +Assert and Check + +Assert() and Check() both accept a Comparison, and fail the test when the +comparison fails. The one difference is that Assert() will end the test execution +immediately (using t.FailNow()) whereas Check() will fail the test (using t.Fail()), +return the value of the comparison, then proceed with the rest of the test case. + +Example Usage + +The example below shows assert used with some common types. + + + import ( + "testing" + + "github.com/gotestyourself/gotestyourself/assert" + is "github.com/gotestyourself/gotestyourself/assert/cmp" + ) + + func TestEverything(t *testing.T) { + // booleans + assert.Assert(t, ok) + assert.Assert(t, !missing) + + // primitives + assert.Equal(t, count, 1) + assert.Equal(t, msg, "the message") + assert.Assert(t, total != 10) // NotEqual + + // errors + assert.NilError(t, closer.Close()) + assert.Error(t, err, "the exact error message") + assert.ErrorContains(t, err, "includes this") + assert.ErrorType(t, err, os.IsNotExist) + + // complex types + assert.DeepEqual(t, result, myStruct{Name: "title"}) + assert.Assert(t, is.Len(items, 3)) + assert.Assert(t, len(sequence) != 0) // NotEmpty + assert.Assert(t, is.Contains(mapping, "key")) + + // pointers and interface + assert.Assert(t, is.Nil(ref)) + assert.Assert(t, ref != nil) // NotNil + } + +Comparisons + +https://godoc.org/github.com/gotestyourself/gotestyourself/assert/cmp provides +many common comparisons. Additional comparisons can be written to compare +values in other ways. See the example Assert (CustomComparison). + +*/ +package assert + +import ( + "fmt" + "go/ast" + "go/token" + + gocmp "github.com/google/go-cmp/cmp" + "github.com/gotestyourself/gotestyourself/assert/cmp" + "github.com/gotestyourself/gotestyourself/internal/format" + "github.com/gotestyourself/gotestyourself/internal/source" +) + +// BoolOrComparison can be a bool, or cmp.Comparison. See Assert() for usage. +type BoolOrComparison interface{} + +// TestingT is the subset of testing.T used by the assert package. +type TestingT interface { + FailNow() + Fail() + Log(args ...interface{}) +} + +type helperT interface { + Helper() +} + +const failureMessage = "assertion failed: " + +// nolint: gocyclo +func assert( + t TestingT, + failer func(), + argSelector argSelector, + comparison BoolOrComparison, + msgAndArgs ...interface{}, +) bool { + if ht, ok := t.(helperT); ok { + ht.Helper() + } + var success bool + switch check := comparison.(type) { + case bool: + if check { + return true + } + logFailureFromBool(t, msgAndArgs...) + + // Undocumented legacy comparison without Result type + case func() (success bool, message string): + success = runCompareFunc(t, check, msgAndArgs...) + + case nil: + return true + + case error: + msg := "error is not nil: " + t.Log(format.WithCustomMessage(failureMessage+msg+check.Error(), msgAndArgs...)) + + case cmp.Comparison: + success = runComparison(t, argSelector, check, msgAndArgs...) + + case func() cmp.Result: + success = runComparison(t, argSelector, check, msgAndArgs...) + + default: + t.Log(fmt.Sprintf("invalid Comparison: %v (%T)", check, check)) + } + + if success { + return true + } + failer() + return false +} + +func runCompareFunc( + t TestingT, + f func() (success bool, message string), + msgAndArgs ...interface{}, +) bool { + if ht, ok := t.(helperT); ok { + ht.Helper() + } + if success, message := f(); !success { + t.Log(format.WithCustomMessage(failureMessage+message, msgAndArgs...)) + return false + } + return true +} + +func logFailureFromBool(t TestingT, msgAndArgs ...interface{}) { + if ht, ok := t.(helperT); ok { + ht.Helper() + } + const stackIndex = 3 // Assert()/Check(), assert(), formatFailureFromBool() + const comparisonArgPos = 1 + args, err := source.CallExprArgs(stackIndex) + if err != nil { + t.Log(err.Error()) + return + } + + msg, err := boolFailureMessage(args[comparisonArgPos]) + if err != nil { + t.Log(err.Error()) + msg = "expression is false" + } + + t.Log(format.WithCustomMessage(failureMessage+msg, msgAndArgs...)) +} + +func boolFailureMessage(expr ast.Expr) (string, error) { + if binaryExpr, ok := expr.(*ast.BinaryExpr); ok && binaryExpr.Op == token.NEQ { + x, err := source.FormatNode(binaryExpr.X) + if err != nil { + return "", err + } + y, err := source.FormatNode(binaryExpr.Y) + if err != nil { + return "", err + } + return x + " is " + y, nil + } + + if unaryExpr, ok := expr.(*ast.UnaryExpr); ok && unaryExpr.Op == token.NOT { + x, err := source.FormatNode(unaryExpr.X) + if err != nil { + return "", err + } + return x + " is true", nil + } + + formatted, err := source.FormatNode(expr) + if err != nil { + return "", err + } + return "expression is false: " + formatted, nil +} + +// Assert performs a comparison. If the comparison fails the test is marked as +// failed, a failure message is logged, and execution is stopped immediately. +// +// The comparison argument may be one of three types: bool, cmp.Comparison or +// error. +// When called with a bool the failure message will contain the literal source +// code of the expression. +// When called with a cmp.Comparison the comparison is responsible for producing +// a helpful failure message. +// When called with an error a nil value is considered success. A non-nil error +// is a failure, and Error() is used as the failure message. +func Assert(t TestingT, comparison BoolOrComparison, msgAndArgs ...interface{}) { + if ht, ok := t.(helperT); ok { + ht.Helper() + } + assert(t, t.FailNow, argsFromComparisonCall, comparison, msgAndArgs...) +} + +// Check performs a comparison. If the comparison fails the test is marked as +// failed, a failure message is logged, and Check returns false. Otherwise returns +// true. +// +// See Assert for details about the comparison arg and failure messages. +func Check(t TestingT, comparison BoolOrComparison, msgAndArgs ...interface{}) bool { + if ht, ok := t.(helperT); ok { + ht.Helper() + } + return assert(t, t.Fail, argsFromComparisonCall, comparison, msgAndArgs...) +} + +// NilError fails the test immediately if err is not nil. +// This is equivalent to Assert(t, err) +func NilError(t TestingT, err error, msgAndArgs ...interface{}) { + if ht, ok := t.(helperT); ok { + ht.Helper() + } + assert(t, t.FailNow, argsAfterT, err, msgAndArgs...) +} + +// Equal uses the == operator to assert two values are equal and fails the test +// if they are not equal. This is equivalent to Assert(t, cmp.Equal(x, y)). +func Equal(t TestingT, x, y interface{}, msgAndArgs ...interface{}) { + if ht, ok := t.(helperT); ok { + ht.Helper() + } + assert(t, t.FailNow, argsAfterT, cmp.Equal(x, y), msgAndArgs...) +} + +// DeepEqual uses https://github.com/google/go-cmp/cmp to assert two values +// are equal and fails the test if they are not equal. +// This is equivalent to Assert(t, cmp.DeepEqual(x, y)). +func DeepEqual(t TestingT, x, y interface{}, opts ...gocmp.Option) { + if ht, ok := t.(helperT); ok { + ht.Helper() + } + assert(t, t.FailNow, argsAfterT, cmp.DeepEqual(x, y, opts...)) +} + +// Error fails the test if err is nil, or the error message is not the expected +// message. +// Equivalent to Assert(t, cmp.Error(err, message)). +func Error(t TestingT, err error, message string, msgAndArgs ...interface{}) { + if ht, ok := t.(helperT); ok { + ht.Helper() + } + assert(t, t.FailNow, argsAfterT, cmp.Error(err, message), msgAndArgs...) +} + +// ErrorContains fails the test if err is nil, or the error message does not +// contain the expected substring. +// Equivalent to Assert(t, cmp.ErrorContains(err, substring)). +func ErrorContains(t TestingT, err error, substring string, msgAndArgs ...interface{}) { + if ht, ok := t.(helperT); ok { + ht.Helper() + } + assert(t, t.FailNow, argsAfterT, cmp.ErrorContains(err, substring), msgAndArgs...) +} + +// ErrorType fails the test if err is nil, or err is not the expected type. +// +// Expected can be one of: +// a func(error) bool which returns true if the error is the expected type, +// an instance of a struct of the expected type, +// a pointer to an interface the error is expected to implement, +// a reflect.Type of the expected struct or interface. +// +// Equivalent to Assert(t, cmp.ErrorType(err, expected)). +func ErrorType(t TestingT, err error, expected interface{}, msgAndArgs ...interface{}) { + if ht, ok := t.(helperT); ok { + ht.Helper() + } + assert(t, t.FailNow, argsAfterT, cmp.ErrorType(err, expected), msgAndArgs...) +} diff --git a/components/cli/vendor/github.com/gotestyourself/gotestyourself/assert/cmp/compare.go b/components/cli/vendor/github.com/gotestyourself/gotestyourself/assert/cmp/compare.go new file mode 100644 index 0000000000..64a25f00b3 --- /dev/null +++ b/components/cli/vendor/github.com/gotestyourself/gotestyourself/assert/cmp/compare.go @@ -0,0 +1,310 @@ +/*Package cmp provides Comparisons for Assert and Check*/ +package cmp + +import ( + "fmt" + "reflect" + "strings" + + "github.com/google/go-cmp/cmp" + "github.com/pmezard/go-difflib/difflib" +) + +// Comparison is a function which compares values and returns ResultSuccess if +// the actual value matches the expected value. If the values do not match the +// Result will contain a message about why it failed. +type Comparison func() Result + +// DeepEqual compares two values using https://godoc.org/github.com/google/go-cmp/cmp +// and succeeds if the values are equal. +// +// The comparison can be customized using comparison Options. +func DeepEqual(x, y interface{}, opts ...cmp.Option) Comparison { + return func() (result Result) { + defer func() { + if panicmsg, handled := handleCmpPanic(recover()); handled { + result = ResultFailure(panicmsg) + } + }() + diff := cmp.Diff(x, y, opts...) + return toResult(diff == "", "\n"+diff) + } +} + +func handleCmpPanic(r interface{}) (string, bool) { + if r == nil { + return "", false + } + panicmsg, ok := r.(string) + if !ok { + panic(r) + } + switch { + case strings.HasPrefix(panicmsg, "cannot handle unexported field"): + return panicmsg, true + } + panic(r) +} + +func toResult(success bool, msg string) Result { + if success { + return ResultSuccess + } + return ResultFailure(msg) +} + +// Equal succeeds if x == y. +func Equal(x, y interface{}) Comparison { + return func() Result { + switch { + case x == y: + return ResultSuccess + case isMultiLineStringCompare(x, y): + return multiLineStringDiffResult(x.(string), y.(string)) + } + return ResultFailureTemplate(` + {{- .Data.x}} ( + {{- with callArg 0 }}{{ formatNode . }} {{end -}} + {{- printf "%T" .Data.x -}} + ) != {{ .Data.y}} ( + {{- with callArg 1 }}{{ formatNode . }} {{end -}} + {{- printf "%T" .Data.y -}} + )`, + map[string]interface{}{"x": x, "y": y}) + } +} + +func isMultiLineStringCompare(x, y interface{}) bool { + strX, ok := x.(string) + if !ok { + return false + } + strY, ok := y.(string) + if !ok { + return false + } + return strings.Contains(strX, "\n") || strings.Contains(strY, "\n") +} + +func multiLineStringDiffResult(x, y string) Result { + diff, err := difflib.GetUnifiedDiffString(difflib.UnifiedDiff{ + A: difflib.SplitLines(x), + B: difflib.SplitLines(y), + Context: 3, + }) + if err != nil { + return ResultFailure(fmt.Sprintf("failed to diff: %s", err)) + } + return ResultFailureTemplate(` +--- {{ with callArg 0 }}{{ formatNode . }}{{else}}←{{end}} ++++ {{ with callArg 1 }}{{ formatNode . }}{{else}}→{{end}} +{{ .Data.diff }}`, + map[string]interface{}{"diff": diff}) +} + +// Len succeeds if the sequence has the expected length. +func Len(seq interface{}, expected int) Comparison { + return func() (result Result) { + defer func() { + if e := recover(); e != nil { + result = ResultFailure(fmt.Sprintf("type %T does not have a length", seq)) + } + }() + value := reflect.ValueOf(seq) + length := value.Len() + if length == expected { + return ResultSuccess + } + msg := fmt.Sprintf("expected %s (length %d) to have length %d", seq, length, expected) + return ResultFailure(msg) + } +} + +// Contains succeeds if item is in collection. Collection may be a string, map, +// slice, or array. +// +// If collection is a string, item must also be a string, and is compared using +// strings.Contains(). +// If collection is a Map, contains will succeed if item is a key in the map. +// If collection is a slice or array, item is compared to each item in the +// sequence using reflect.DeepEqual(). +func Contains(collection interface{}, item interface{}) Comparison { + return func() Result { + colValue := reflect.ValueOf(collection) + if !colValue.IsValid() { + return ResultFailure(fmt.Sprintf("nil does not contain items")) + } + msg := fmt.Sprintf("%v does not contain %v", collection, item) + + itemValue := reflect.ValueOf(item) + switch colValue.Type().Kind() { + case reflect.String: + if itemValue.Type().Kind() != reflect.String { + return ResultFailure("string may only contain strings") + } + return toResult( + strings.Contains(colValue.String(), itemValue.String()), + fmt.Sprintf("string %q does not contain %q", collection, item)) + + case reflect.Map: + if itemValue.Type() != colValue.Type().Key() { + return ResultFailure(fmt.Sprintf( + "%v can not contain a %v key", colValue.Type(), itemValue.Type())) + } + return toResult(colValue.MapIndex(itemValue).IsValid(), msg) + + case reflect.Slice, reflect.Array: + for i := 0; i < colValue.Len(); i++ { + if reflect.DeepEqual(colValue.Index(i).Interface(), item) { + return ResultSuccess + } + } + return ResultFailure(msg) + default: + return ResultFailure(fmt.Sprintf("type %T does not contain items", collection)) + } + } +} + +// Panics succeeds if f() panics. +func Panics(f func()) Comparison { + return func() (result Result) { + defer func() { + if err := recover(); err != nil { + result = ResultSuccess + } + }() + f() + return ResultFailure("did not panic") + } +} + +// Error succeeds if err is a non-nil error, and the error message equals the +// expected message. +func Error(err error, message string) Comparison { + return func() Result { + switch { + case err == nil: + return ResultFailure("expected an error, got nil") + case err.Error() != message: + return ResultFailure(fmt.Sprintf( + "expected error %q, got %+v", message, err)) + } + return ResultSuccess + } +} + +// ErrorContains succeeds if err is a non-nil error, and the error message contains +// the expected substring. +func ErrorContains(err error, substring string) Comparison { + return func() Result { + switch { + case err == nil: + return ResultFailure("expected an error, got nil") + case !strings.Contains(err.Error(), substring): + return ResultFailure(fmt.Sprintf( + "expected error to contain %q, got %+v", substring, err)) + } + return ResultSuccess + } +} + +// Nil succeeds if obj is a nil interface, pointer, or function. +// +// Use NilError() for comparing errors. Use Len(obj, 0) for comparing slices, +// maps, and channels. +func Nil(obj interface{}) Comparison { + msgFunc := func(value reflect.Value) string { + return fmt.Sprintf("%v (type %s) is not nil", reflect.Indirect(value), value.Type()) + } + return isNil(obj, msgFunc) +} + +func isNil(obj interface{}, msgFunc func(reflect.Value) string) Comparison { + return func() Result { + if obj == nil { + return ResultSuccess + } + value := reflect.ValueOf(obj) + kind := value.Type().Kind() + if kind >= reflect.Chan && kind <= reflect.Slice { + if value.IsNil() { + return ResultSuccess + } + return ResultFailure(msgFunc(value)) + } + + return ResultFailure(fmt.Sprintf("%v (type %s) can not be nil", value, value.Type())) + } +} + +// ErrorType succeeds if err is not nil and is of the expected type. +// +// Expected can be one of: +// a func(error) bool which returns true if the error is the expected type, +// an instance of a struct of the expected type, +// a pointer to an interface the error is expected to implement, +// a reflect.Type of the expected struct or interface. +func ErrorType(err error, expected interface{}) Comparison { + return func() Result { + switch expectedType := expected.(type) { + case func(error) bool: + return cmpErrorTypeFunc(err, expectedType) + case reflect.Type: + if expectedType.Kind() == reflect.Interface { + return cmpErrorTypeImplementsType(err, expectedType) + } + return cmpErrorTypeEqualType(err, expectedType) + case nil: + return ResultFailure(fmt.Sprintf("invalid type for expected: nil")) + } + + expectedType := reflect.TypeOf(expected) + switch { + case expectedType.Kind() == reflect.Struct: + return cmpErrorTypeEqualType(err, expectedType) + case isPtrToInterface(expectedType): + return cmpErrorTypeImplementsType(err, expectedType.Elem()) + } + return ResultFailure(fmt.Sprintf("invalid type for expected: %T", expected)) + } +} + +func cmpErrorTypeFunc(err error, f func(error) bool) Result { + if f(err) { + return ResultSuccess + } + actual := "nil" + if err != nil { + actual = fmt.Sprintf("%s (%T)", err, err) + } + return ResultFailureTemplate(`error is {{ .Data.actual }} + {{- with callArg 1 }}, not {{ formatNode . }}{{end -}}`, + map[string]interface{}{"actual": actual}) +} + +func cmpErrorTypeEqualType(err error, expectedType reflect.Type) Result { + if err == nil { + return ResultFailure(fmt.Sprintf("error is nil, not %s", expectedType)) + } + errValue := reflect.ValueOf(err) + if errValue.Type() == expectedType { + return ResultSuccess + } + return ResultFailure(fmt.Sprintf("error is %s (%T), not %s", err, err, expectedType)) +} + +func cmpErrorTypeImplementsType(err error, expectedType reflect.Type) Result { + if err == nil { + return ResultFailure(fmt.Sprintf("error is nil, not %s", expectedType)) + } + errValue := reflect.ValueOf(err) + if errValue.Type().Implements(expectedType) { + return ResultSuccess + } + return ResultFailure(fmt.Sprintf("error is %s (%T), not %s", err, err, expectedType)) +} + +func isPtrToInterface(typ reflect.Type) bool { + return typ.Kind() == reflect.Ptr && typ.Elem().Kind() == reflect.Interface +} diff --git a/components/cli/vendor/github.com/gotestyourself/gotestyourself/assert/cmp/result.go b/components/cli/vendor/github.com/gotestyourself/gotestyourself/assert/cmp/result.go new file mode 100644 index 0000000000..99f39eeb27 --- /dev/null +++ b/components/cli/vendor/github.com/gotestyourself/gotestyourself/assert/cmp/result.go @@ -0,0 +1,94 @@ +package cmp + +import ( + "bytes" + "fmt" + "go/ast" + "text/template" + + "github.com/gotestyourself/gotestyourself/internal/source" +) + +// Result of a Comparison. +type Result interface { + Success() bool +} + +type result struct { + success bool + message string +} + +func (r result) Success() bool { + return r.success +} + +func (r result) FailureMessage() string { + return r.message +} + +// ResultSuccess is a constant which is returned by a ComparisonWithResult to +// indicate success. +var ResultSuccess = result{success: true} + +// ResultFailure returns a failed Result with a failure message. +func ResultFailure(message string) Result { + return result{message: message} +} + +// ResultFromError returns ResultSuccess if err is nil. Otherwise ResultFailure +// is returned with the error message as the failure message. +func ResultFromError(err error) Result { + if err == nil { + return ResultSuccess + } + return ResultFailure(err.Error()) +} + +type templatedResult struct { + success bool + template string + data map[string]interface{} +} + +func (r templatedResult) Success() bool { + return r.success +} + +func (r templatedResult) FailureMessage(args []ast.Expr) string { + msg, err := renderMessage(r, args) + if err != nil { + return fmt.Sprintf("failed to render failure message: %s", err) + } + return msg +} + +// ResultFailureTemplate returns a Result with a template string and data which +// can be used to format a failure message. The template may access data from .Data, +// the comparison args with the callArg function, and the formatNode function may +// be used to format the call args. +func ResultFailureTemplate(template string, data map[string]interface{}) Result { + return templatedResult{template: template, data: data} +} + +func renderMessage(result templatedResult, args []ast.Expr) (string, error) { + tmpl := template.New("failure").Funcs(template.FuncMap{ + "formatNode": source.FormatNode, + "callArg": func(index int) ast.Expr { + if index >= len(args) { + return nil + } + return args[index] + }, + }) + var err error + tmpl, err = tmpl.Parse(result.template) + if err != nil { + return "", err + } + buf := new(bytes.Buffer) + err = tmpl.Execute(buf, map[string]interface{}{ + "Data": result.data, + }) + return buf.String(), err +} diff --git a/components/cli/vendor/github.com/gotestyourself/gotestyourself/assert/result.go b/components/cli/vendor/github.com/gotestyourself/gotestyourself/assert/result.go new file mode 100644 index 0000000000..b685b7f3e5 --- /dev/null +++ b/components/cli/vendor/github.com/gotestyourself/gotestyourself/assert/result.go @@ -0,0 +1,107 @@ +package assert + +import ( + "fmt" + "go/ast" + + "github.com/gotestyourself/gotestyourself/assert/cmp" + "github.com/gotestyourself/gotestyourself/internal/format" + "github.com/gotestyourself/gotestyourself/internal/source" +) + +func runComparison( + t TestingT, + argSelector argSelector, + f cmp.Comparison, + msgAndArgs ...interface{}, +) bool { + if ht, ok := t.(helperT); ok { + ht.Helper() + } + result := f() + if result.Success() { + return true + } + + var message string + switch typed := result.(type) { + case resultWithComparisonArgs: + const stackIndex = 3 // Assert/Check, assert, runComparison + args, err := source.CallExprArgs(stackIndex) + if err != nil { + t.Log(err.Error()) + } + message = typed.FailureMessage(filterPrintableExpr(argSelector(args))) + case resultBasic: + message = typed.FailureMessage() + default: + message = fmt.Sprintf("comparison returned invalid Result type: %T", result) + } + + t.Log(format.WithCustomMessage(failureMessage+message, msgAndArgs...)) + return false +} + +type resultWithComparisonArgs interface { + FailureMessage(args []ast.Expr) string +} + +type resultBasic interface { + FailureMessage() string +} + +// filterPrintableExpr filters the ast.Expr slice to only include Expr that are +// easy to read when printed and contain relevant information to an assertion. +// +// Ident and SelectorExpr are included because they print nicely and the variable +// names may provide additional context to their values. +// BasicLit and CompositeLit are excluded because their source is equivalent to +// their value, which is already available. +// Other types are ignored for now, but could be added if they are relevant. +func filterPrintableExpr(args []ast.Expr) []ast.Expr { + result := make([]ast.Expr, len(args)) + for i, arg := range args { + if isShortPrintableExpr(arg) { + result[i] = arg + continue + } + + if starExpr, ok := arg.(*ast.StarExpr); ok { + result[i] = starExpr.X + continue + } + result[i] = nil + } + return result +} + +func isShortPrintableExpr(expr ast.Expr) bool { + switch expr.(type) { + case *ast.Ident, *ast.SelectorExpr, *ast.IndexExpr, *ast.SliceExpr: + return true + case *ast.BinaryExpr, *ast.UnaryExpr: + return true + default: + // CallExpr, ParenExpr, TypeAssertExpr, KeyValueExpr, StarExpr + return false + } +} + +type argSelector func([]ast.Expr) []ast.Expr + +func argsAfterT(args []ast.Expr) []ast.Expr { + if len(args) < 1 { + return nil + } + return args[1:] +} + +func argsFromComparisonCall(args []ast.Expr) []ast.Expr { + if len(args) < 1 { + return nil + } + if callExpr, ok := args[1].(*ast.CallExpr); ok { + return callExpr.Args + } + return nil +} diff --git a/components/cli/vendor/github.com/gotestyourself/gotestyourself/env/env.go b/components/cli/vendor/github.com/gotestyourself/gotestyourself/env/env.go index 61a45a438d..700430f1dd 100644 --- a/components/cli/vendor/github.com/gotestyourself/gotestyourself/env/env.go +++ b/components/cli/vendor/github.com/gotestyourself/gotestyourself/env/env.go @@ -1,42 +1,59 @@ /*Package env provides functions to test code that read environment variables - */ +or the current working directory. +*/ package env import ( "os" "strings" - "github.com/stretchr/testify/require" + "github.com/gotestyourself/gotestyourself/assert" ) +type helperT interface { + Helper() +} + // Patch changes the value of an environment variable, and returns a // function which will reset the the value of that variable back to the // previous state. -func Patch(t require.TestingT, key, value string) func() { +func Patch(t assert.TestingT, key, value string) func() { + if ht, ok := t.(helperT); ok { + ht.Helper() + } oldValue, ok := os.LookupEnv(key) - require.NoError(t, os.Setenv(key, value)) + assert.NilError(t, os.Setenv(key, value)) return func() { + if ht, ok := t.(helperT); ok { + ht.Helper() + } if !ok { - require.NoError(t, os.Unsetenv(key)) + assert.NilError(t, os.Unsetenv(key)) return } - require.NoError(t, os.Setenv(key, oldValue)) + assert.NilError(t, os.Setenv(key, oldValue)) } } // PatchAll sets the environment to env, and returns a function which will // reset the environment back to the previous state. -func PatchAll(t require.TestingT, env map[string]string) func() { +func PatchAll(t assert.TestingT, env map[string]string) func() { + if ht, ok := t.(helperT); ok { + ht.Helper() + } oldEnv := os.Environ() os.Clearenv() for key, value := range env { - require.NoError(t, os.Setenv(key, value)) + assert.NilError(t, os.Setenv(key, value), "setenv %s=%s", key, value) } return func() { + if ht, ok := t.(helperT); ok { + ht.Helper() + } os.Clearenv() for key, oldVal := range ToMap(oldEnv) { - require.NoError(t, os.Setenv(key, oldVal)) + assert.NilError(t, os.Setenv(key, oldVal), "setenv %s=%s", key, oldVal) } } } @@ -46,13 +63,36 @@ func PatchAll(t require.TestingT, env map[string]string) func() { func ToMap(env []string) map[string]string { result := map[string]string{} for _, raw := range env { - parts := strings.SplitN(raw, "=", 2) - switch len(parts) { - case 1: - result[raw] = "" - case 2: - result[parts[0]] = parts[1] - } + key, value := getParts(raw) + result[key] = value } return result } + +func getParts(raw string) (string, string) { + // Environment variables on windows can begin with = + // http://blogs.msdn.com/b/oldnewthing/archive/2010/05/06/10008132.aspx + parts := strings.SplitN(raw[1:], "=", 2) + key := raw[:1] + parts[0] + if len(parts) == 1 { + return key, "" + } + return key, parts[1] +} + +// ChangeWorkingDir to the directory, and return a function which restores the +// previous working directory. +func ChangeWorkingDir(t assert.TestingT, dir string) func() { + if ht, ok := t.(helperT); ok { + ht.Helper() + } + cwd, err := os.Getwd() + assert.NilError(t, err) + assert.NilError(t, os.Chdir(dir)) + return func() { + if ht, ok := t.(helperT); ok { + ht.Helper() + } + assert.NilError(t, os.Chdir(cwd)) + } +} diff --git a/components/cli/vendor/github.com/gotestyourself/gotestyourself/fs/file.go b/components/cli/vendor/github.com/gotestyourself/gotestyourself/fs/file.go index dcda10a02a..a2f731093b 100644 --- a/components/cli/vendor/github.com/gotestyourself/gotestyourself/fs/file.go +++ b/components/cli/vendor/github.com/gotestyourself/gotestyourself/fs/file.go @@ -8,29 +8,42 @@ import ( "os" "path/filepath" - "github.com/stretchr/testify/require" + "github.com/gotestyourself/gotestyourself/assert" ) // Path objects return their filesystem path. Both File and Dir implement Path. type Path interface { Path() string + Remove() } +var ( + _ Path = &Dir{} + _ Path = &File{} +) + // File is a temporary file on the filesystem type File struct { path string } +type helperT interface { + Helper() +} + // NewFile creates a new file in a temporary directory using prefix as part of // the filename. The PathOps are applied to the before returning the File. -func NewFile(t require.TestingT, prefix string, ops ...PathOp) *File { +func NewFile(t assert.TestingT, prefix string, ops ...PathOp) *File { + if ht, ok := t.(helperT); ok { + ht.Helper() + } tempfile, err := ioutil.TempFile("", prefix+"-") - require.NoError(t, err) + assert.NilError(t, err) file := &File{path: tempfile.Name()} - require.NoError(t, tempfile.Close()) + assert.NilError(t, tempfile.Close()) for _, op := range ops { - require.NoError(t, op(file)) + assert.NilError(t, op(file)) } return file } @@ -53,13 +66,16 @@ type Dir struct { // NewDir returns a new temporary directory using prefix as part of the directory // name. The PathOps are applied before returning the Dir. -func NewDir(t require.TestingT, prefix string, ops ...PathOp) *Dir { +func NewDir(t assert.TestingT, prefix string, ops ...PathOp) *Dir { + if ht, ok := t.(helperT); ok { + ht.Helper() + } path, err := ioutil.TempDir("", prefix+"-") - require.NoError(t, err) + assert.NilError(t, err) dir := &Dir{path: path} for _, op := range ops { - require.NoError(t, op(dir)) + assert.NilError(t, op(dir)) } return dir } diff --git a/components/cli/vendor/github.com/gotestyourself/gotestyourself/fs/ops.go b/components/cli/vendor/github.com/gotestyourself/gotestyourself/fs/ops.go index 7cc63994c8..bf9d2150b3 100644 --- a/components/cli/vendor/github.com/gotestyourself/gotestyourself/fs/ops.go +++ b/components/cli/vendor/github.com/gotestyourself/gotestyourself/fs/ops.go @@ -4,6 +4,7 @@ import ( "io/ioutil" "os" "path/filepath" + "time" ) // PathOp is a function which accepts a Path to perform some operation @@ -125,3 +126,33 @@ func copyFile(source, dest string) error { } return ioutil.WriteFile(dest, content, 0644) } + +// WithSymlink creates a symlink in the directory which links to target. +// Target must be a path relative to the directory. +// +// Note: the argument order is the inverse of os.Symlink to be consistent with +// the other functions in this package. +func WithSymlink(path, target string) PathOp { + return func(root Path) error { + return os.Symlink(filepath.Join(root.Path(), target), filepath.Join(root.Path(), path)) + } +} + +// WithHardlink creates a link in the directory which links to target. +// Target must be a path relative to the directory. +// +// Note: the argument order is the inverse of os.Link to be consistent with +// the other functions in this package. +func WithHardlink(path, target string) PathOp { + return func(root Path) error { + return os.Link(filepath.Join(root.Path(), target), filepath.Join(root.Path(), path)) + } +} + +// WithTimestamps sets the access and modification times of the file system object +// at path. +func WithTimestamps(atime, mtime time.Time) PathOp { + return func(root Path) error { + return os.Chtimes(root.Path(), atime, mtime) + } +} diff --git a/components/cli/vendor/github.com/gotestyourself/gotestyourself/golden/golden.go b/components/cli/vendor/github.com/gotestyourself/gotestyourself/golden/golden.go index 9b1c8ba4d0..357edcfd44 100644 --- a/components/cli/vendor/github.com/gotestyourself/gotestyourself/golden/golden.go +++ b/components/cli/vendor/github.com/gotestyourself/gotestyourself/golden/golden.go @@ -5,67 +5,152 @@ Golden files are files in the ./testdata/ subdirectory of the package under test package golden import ( + "bytes" "flag" "fmt" "io/ioutil" "path/filepath" + "github.com/gotestyourself/gotestyourself/assert" + "github.com/gotestyourself/gotestyourself/assert/cmp" "github.com/pmezard/go-difflib/difflib" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" ) var flagUpdate = flag.Bool("test.update-golden", false, "update golden file") -// Get returns the golden file content -func Get(t require.TestingT, filename string) []byte { +type helperT interface { + Helper() +} + +// Get returns the contents of the file in ./testdata +func Get(t assert.TestingT, filename string) []byte { + if ht, ok := t.(helperT); ok { + ht.Helper() + } expected, err := ioutil.ReadFile(Path(filename)) - require.NoError(t, err) + assert.NilError(t, err) return expected } -// Path returns the full path to a golden file +// Path returns the full path to a file in ./testdata func Path(filename string) string { + if filepath.IsAbs(filename) { + return filename + } return filepath.Join("testdata", filename) } -func update(t require.TestingT, filename string, actual []byte) { +func update(filename string, actual []byte, normalize normalize) error { if *flagUpdate { - err := ioutil.WriteFile(Path(filename), actual, 0644) - require.NoError(t, err) + return ioutil.WriteFile(Path(filename), normalize(actual), 0644) } + return nil +} + +type normalize func([]byte) []byte + +func removeCarriageReturn(in []byte) []byte { + return bytes.Replace(in, []byte("\r\n"), []byte("\n"), -1) +} + +func exactBytes(in []byte) []byte { + return in } // Assert compares the actual content to the expected content in the golden file. // If the `-test.update-golden` flag is set then the actual content is written // to the golden file. -// Returns whether the assertion was successful (true) or not (false) -func Assert(t require.TestingT, actual string, filename string, msgAndArgs ...interface{}) bool { - expected := Get(t, filename) - update(t, filename, []byte(actual)) - - if assert.ObjectsAreEqual(expected, []byte(actual)) { - return true +// Returns whether the assertion was successful (true) or not (false). +// This is equivalent to assert.Check(t, String(actual, filename)) +// +// Deprecated: In a future version this function will change to use assert.Assert +// instead of assert.Check to be consistent with other assert functions. +// Use assert.Check(t, String(actual, filename) if you want to preserve the +// current behaviour. +func Assert(t assert.TestingT, actual string, filename string, msgAndArgs ...interface{}) bool { + if ht, ok := t.(helperT); ok { + ht.Helper() } + return assert.Check(t, String(actual, filename), msgAndArgs...) +} - diff, err := difflib.GetUnifiedDiffString(difflib.UnifiedDiff{ - A: difflib.SplitLines(string(expected)), - B: difflib.SplitLines(actual), - FromFile: "Expected", - ToFile: "Actual", - Context: 3, - }) - require.NoError(t, err, msgAndArgs...) - return assert.Fail(t, fmt.Sprintf("Not Equal: \n%s", diff), msgAndArgs...) +// String compares actual to the contents of filename and returns success +// if the strings are equal. +// If the `-test.update-golden` flag is set then the actual content is written +// to the golden file. +// +// Any \r\n substrings in actual are converted to a single \n character +// before comparing it to the expected string. When updating the golden file the +// normalized version will be written to the file. This allows Windows to use +// the same golden files as other operating systems. +func String(actual string, filename string) cmp.Comparison { + return func() cmp.Result { + actualBytes := removeCarriageReturn([]byte(actual)) + result, expected := compare(actualBytes, filename, removeCarriageReturn) + if result != nil { + return result + } + diff, err := difflib.GetUnifiedDiffString(difflib.UnifiedDiff{ + A: difflib.SplitLines(string(expected)), + B: difflib.SplitLines(string(actualBytes)), + FromFile: "expected", + ToFile: "actual", + Context: 3, + }) + if err != nil { + return cmp.ResultFromError(err) + } + return cmp.ResultFailure("\n" + diff) + } } // AssertBytes compares the actual result to the expected result in the golden // file. If the `-test.update-golden` flag is set then the actual content is // written to the golden file. // Returns whether the assertion was successful (true) or not (false) -// nolint: lll -func AssertBytes(t require.TestingT, actual []byte, filename string, msgAndArgs ...interface{}) bool { - expected := Get(t, filename) - update(t, filename, actual) - return assert.Equal(t, expected, actual, msgAndArgs...) +// This is equivalent to assert.Check(t, Bytes(actual, filename)) +// +// Deprecated: In a future version this function will change to use assert.Assert +// instead of assert.Check to be consistent with other assert functions. +// Use assert.Check(t, Bytes(actual, filename) if you want to preserve the +// current behaviour. +func AssertBytes( + t assert.TestingT, + actual []byte, + filename string, + msgAndArgs ...interface{}, +) bool { + if ht, ok := t.(helperT); ok { + ht.Helper() + } + return assert.Check(t, Bytes(actual, filename), msgAndArgs...) +} + +// Bytes compares actual to the contents of filename and returns success +// if the bytes are equal. +// If the `-test.update-golden` flag is set then the actual content is written +// to the golden file. +func Bytes(actual []byte, filename string) cmp.Comparison { + return func() cmp.Result { + result, expected := compare(actual, filename, exactBytes) + if result != nil { + return result + } + msg := fmt.Sprintf("%v (actual) != %v (expected)", actual, expected) + return cmp.ResultFailure(msg) + } +} + +func compare(actual []byte, filename string, normalize normalize) (cmp.Result, []byte) { + if err := update(filename, actual, normalize); err != nil { + return cmp.ResultFromError(err), nil + } + expected, err := ioutil.ReadFile(Path(filename)) + if err != nil { + return cmp.ResultFromError(err), nil + } + if bytes.Equal(expected, actual) { + return cmp.ResultSuccess, nil + } + return nil, expected } diff --git a/components/cli/vendor/github.com/gotestyourself/gotestyourself/icmd/command.go b/components/cli/vendor/github.com/gotestyourself/gotestyourself/icmd/command.go index 8729457b4f..0066c51514 100644 --- a/components/cli/vendor/github.com/gotestyourself/gotestyourself/icmd/command.go +++ b/components/cli/vendor/github.com/gotestyourself/gotestyourself/icmd/command.go @@ -7,19 +7,20 @@ import ( "fmt" "io" "os/exec" - "path/filepath" - "runtime" "strings" "sync" "time" + + "github.com/gotestyourself/gotestyourself/assert" + "github.com/gotestyourself/gotestyourself/assert/cmp" ) -type testingT interface { - Fatalf(string, ...interface{}) +type helperT interface { + Helper() } // None is a token to inform Result.Assert that the output should be empty -const None string = "[NOTHING]" +const None = "[NOTHING]" type lockedBuffer struct { m sync.RWMutex @@ -51,24 +52,32 @@ type Result struct { // Assert compares the Result against the Expected struct, and fails the test if // any of the expectations are not met. -func (r *Result) Assert(t testingT, exp Expected) *Result { - err := r.Compare(exp) - if err == nil { - return r +// +// This function is equivalent to assert.Assert(t, result.Equal(exp)). +func (r *Result) Assert(t assert.TestingT, exp Expected) *Result { + if ht, ok := t.(helperT); ok { + ht.Helper() } - _, file, line, ok := runtime.Caller(1) - if ok { - t.Fatalf("at %s:%d - %s\n", filepath.Base(file), line, err.Error()) - } else { - t.Fatalf("(no file/line info) - %s", err.Error()) - } - return nil + assert.Assert(t, r.Equal(exp)) + return r } -// Compare returns a formatted error with the command, stdout, stderr, exit -// code, and any failed expectations -// nolint: gocyclo +// Equal compares the result to Expected. If the result doesn't match expected +// returns a formatted failure message with the command, stdout, stderr, exit code, +// and any failed expectations. +func (r *Result) Equal(exp Expected) cmp.Comparison { + return func() cmp.Result { + return cmp.ResultFromError(r.match(exp)) + } +} + +// Compare the result to Expected and return an error if they do not match. func (r *Result) Compare(exp Expected) error { + return r.match(exp) +} + +// nolint: gocyclo +func (r *Result) match(exp Expected) error { errors := []string{} add := func(format string, args ...interface{}) { errors = append(errors, fmt.Sprintf(format, args...)) diff --git a/components/cli/vendor/github.com/gotestyourself/gotestyourself/internal/format/format.go b/components/cli/vendor/github.com/gotestyourself/gotestyourself/internal/format/format.go new file mode 100644 index 0000000000..6961ffe6da --- /dev/null +++ b/components/cli/vendor/github.com/gotestyourself/gotestyourself/internal/format/format.go @@ -0,0 +1,27 @@ +package format + +import "fmt" + +// Message accepts a msgAndArgs varargs and formats it using fmt.Sprintf +func Message(msgAndArgs ...interface{}) string { + switch len(msgAndArgs) { + case 0: + return "" + case 1: + return fmt.Sprintf("%v", msgAndArgs[0]) + default: + return fmt.Sprintf(msgAndArgs[0].(string), msgAndArgs[1:]...) + } +} + +// WithCustomMessage accepts one or two messages and formats them appropriately +func WithCustomMessage(source string, msgAndArgs ...interface{}) string { + custom := Message(msgAndArgs...) + switch { + case custom == "": + return source + case source == "": + return custom + } + return fmt.Sprintf("%s: %s", source, custom) +} diff --git a/components/cli/vendor/github.com/gotestyourself/gotestyourself/internal/source/source.go b/components/cli/vendor/github.com/gotestyourself/gotestyourself/internal/source/source.go new file mode 100644 index 0000000000..f71c5129b8 --- /dev/null +++ b/components/cli/vendor/github.com/gotestyourself/gotestyourself/internal/source/source.go @@ -0,0 +1,163 @@ +package source + +import ( + "bytes" + "fmt" + "go/ast" + "go/format" + "go/parser" + "go/token" + "os" + "runtime" + "strconv" + "strings" + + "github.com/pkg/errors" +) + +const baseStackIndex = 1 + +// FormattedCallExprArg returns the argument from an ast.CallExpr at the +// index in the call stack. The argument is formatted using FormatNode. +func FormattedCallExprArg(stackIndex int, argPos int) (string, error) { + args, err := CallExprArgs(stackIndex + 1) + if err != nil { + return "", err + } + return FormatNode(args[argPos]) +} + +func getNodeAtLine(filename string, lineNum int) (ast.Node, error) { + fileset := token.NewFileSet() + astFile, err := parser.ParseFile(fileset, filename, nil, parser.AllErrors) + if err != nil { + return nil, errors.Wrapf(err, "failed to parse source file: %s", filename) + } + + node := scanToLine(fileset, astFile, lineNum) + if node == nil { + return nil, errors.Errorf( + "failed to find an expression on line %d in %s", lineNum, filename) + } + return node, nil +} + +func scanToLine(fileset *token.FileSet, node ast.Node, lineNum int) ast.Node { + v := &scanToLineVisitor{lineNum: lineNum, fileset: fileset} + ast.Walk(v, node) + return v.matchedNode +} + +type scanToLineVisitor struct { + lineNum int + matchedNode ast.Node + fileset *token.FileSet +} + +func (v *scanToLineVisitor) Visit(node ast.Node) ast.Visitor { + if node == nil || v.matchedNode != nil { + return nil + } + if v.nodePosition(node).Line == v.lineNum { + v.matchedNode = node + return nil + } + return v +} + +// In golang 1.9 the line number changed from being the line where the statement +// ended to the line where the statement began. +func (v *scanToLineVisitor) nodePosition(node ast.Node) token.Position { + if goVersionBefore19 { + return v.fileset.Position(node.End()) + } + return v.fileset.Position(node.Pos()) +} + +var goVersionBefore19 = isGOVersionBefore19() + +func isGOVersionBefore19() bool { + version := runtime.Version() + // not a release version + if !strings.HasPrefix(version, "go") { + return false + } + version = strings.TrimPrefix(version, "go") + parts := strings.Split(version, ".") + if len(parts) < 2 { + return false + } + minor, err := strconv.ParseInt(parts[1], 10, 32) + return err == nil && parts[0] == "1" && minor < 9 +} + +func getCallExprArgs(node ast.Node) ([]ast.Expr, error) { + visitor := &callExprVisitor{} + ast.Walk(visitor, node) + if visitor.expr == nil { + return nil, errors.New("failed to find call expression") + } + return visitor.expr.Args, nil +} + +type callExprVisitor struct { + expr *ast.CallExpr +} + +func (v *callExprVisitor) Visit(node ast.Node) ast.Visitor { + if v.expr != nil || node == nil { + return nil + } + debug("visit (%T): %s", node, debugFormatNode{node}) + + if callExpr, ok := node.(*ast.CallExpr); ok { + v.expr = callExpr + return nil + } + return v +} + +// FormatNode using go/format.Node and return the result as a string +func FormatNode(node ast.Node) (string, error) { + buf := new(bytes.Buffer) + err := format.Node(buf, token.NewFileSet(), node) + return buf.String(), err +} + +// CallExprArgs returns the ast.Expr slice for the args of an ast.CallExpr at +// the index in the call stack. +func CallExprArgs(stackIndex int) ([]ast.Expr, error) { + _, filename, lineNum, ok := runtime.Caller(baseStackIndex + stackIndex) + if !ok { + return nil, errors.New("failed to get call stack") + } + debug("call stack position: %s:%d", filename, lineNum) + + node, err := getNodeAtLine(filename, lineNum) + if err != nil { + return nil, err + } + debug("found node (%T): %s", node, debugFormatNode{node}) + + return getCallExprArgs(node) +} + +var debugEnabled = os.Getenv("GOTESTYOURSELF_DEBUG") != "" + +func debug(format string, args ...interface{}) { + if debugEnabled { + fmt.Fprintf(os.Stderr, "DEBUG: "+format+"\n", args...) + } +} + +type debugFormatNode struct { + ast.Node +} + +func (n debugFormatNode) String() string { + out, err := FormatNode(n.Node) + if err != nil { + return fmt.Sprintf("failed to format %s: %s", n.Node, err) + } + return out +} diff --git a/components/cli/vendor/github.com/gotestyourself/gotestyourself/poll/poll.go b/components/cli/vendor/github.com/gotestyourself/gotestyourself/poll/poll.go index 3838d38e13..42a42cc149 100644 --- a/components/cli/vendor/github.com/gotestyourself/gotestyourself/poll/poll.go +++ b/components/cli/vendor/github.com/gotestyourself/gotestyourself/poll/poll.go @@ -19,6 +19,10 @@ type LogT interface { Logf(format string, args ...interface{}) } +type helperT interface { + Helper() +} + // Settings are used to configure the behaviour of WaitOn type Settings struct { // Timeout is the maximum time to wait for the condition. Defaults to 10s @@ -101,6 +105,9 @@ func Error(err error) Result { // check returns a done Result. To fail a test and exit polling with an error // return a error result. func WaitOn(t TestingT, check func(t LogT) Result, pollOps ...SettingOp) { + if ht, ok := t.(helperT); ok { + ht.Helper() + } config := defaultConfig() for _, pollOp := range pollOps { pollOp(config) diff --git a/components/cli/vendor/github.com/gotestyourself/gotestyourself/skip/skip.go b/components/cli/vendor/github.com/gotestyourself/gotestyourself/skip/skip.go index 1a6446593f..46ab288819 100644 --- a/components/cli/vendor/github.com/gotestyourself/gotestyourself/skip/skip.go +++ b/components/cli/vendor/github.com/gotestyourself/gotestyourself/skip/skip.go @@ -3,19 +3,14 @@ package skip import ( - "bytes" "fmt" - "go/ast" - "go/format" - "go/parser" - "go/token" - "io/ioutil" "path" "reflect" "runtime" "strings" - "github.com/pkg/errors" + "github.com/gotestyourself/gotestyourself/internal/format" + "github.com/gotestyourself/gotestyourself/internal/source" ) type skipT interface { @@ -23,14 +18,29 @@ type skipT interface { Log(args ...interface{}) } +type helperT interface { + Helper() +} + +// BoolOrCheckFunc can be a bool or func() bool, other types will panic +type BoolOrCheckFunc interface{} + // If skips the test if the check function returns true. The skip message will // contain the name of the check function. Extra message text can be passed as a // format string with args -func If(t skipT, check func() bool, msgAndArgs ...interface{}) { - if check() { - t.Skip(formatWithCustomMessage( - getFunctionName(check), - formatMessage(msgAndArgs...))) +func If(t skipT, condition BoolOrCheckFunc, msgAndArgs ...interface{}) { + if ht, ok := t.(helperT); ok { + ht.Helper() + } + switch check := condition.(type) { + case bool: + ifCondition(t, check, msgAndArgs...) + case func() bool: + if check() { + t.Skip(format.WithCustomMessage(getFunctionName(check), msgAndArgs...)) + } + default: + panic(fmt.Sprintf("invalid type for condition arg: %T", check)) } } @@ -42,91 +52,30 @@ func getFunctionName(function func() bool) string { // IfCondition skips the test if the condition is true. The skip message will // contain the source of the expression passed as the condition. Extra message // text can be passed as a format string with args. +// +// Deprecated: Use If() which now accepts bool arguments func IfCondition(t skipT, condition bool, msgAndArgs ...interface{}) { + if ht, ok := t.(helperT); ok { + ht.Helper() + } + ifCondition(t, condition, msgAndArgs...) +} + +func ifCondition(t skipT, condition bool, msgAndArgs ...interface{}) { + if ht, ok := t.(helperT); ok { + ht.Helper() + } if !condition { return } - source, err := getConditionSource() + const ( + stackIndex = 2 + argPos = 1 + ) + source, err := source.FormattedCallExprArg(stackIndex, argPos) if err != nil { t.Log(err.Error()) - t.Skip(formatMessage(msgAndArgs...)) + t.Skip(format.Message(msgAndArgs...)) } - t.Skip(formatWithCustomMessage(source, formatMessage(msgAndArgs...))) -} - -// getConditionSource returns the condition string by reading it from the file -// identified in the callstack. In golang 1.9 the line number changed from -// being the line where the statement ended to the line where the statement began. -func getConditionSource() (string, error) { - lines, err := getSourceLine() - if err != nil { - return "", err - } - - for i := range lines { - node, err := parser.ParseExpr(getSource(lines, i)) - if err == nil { - return getConditionArgFromAST(node) - } - } - return "", errors.Wrapf(err, "failed to parse source") -} - -// maxContextLines is the maximum number of lines to scan for a complete -// skip.If() statement -const maxContextLines = 10 - -// getSourceLines returns the source line which called skip.If() along with a -// few preceding lines. To properly parse the AST a complete statement is -// required, and that statement may be split across multiple lines, so include -// up to maxContextLines. -func getSourceLine() ([]string, error) { - const stackIndex = 3 - _, filename, line, ok := runtime.Caller(stackIndex) - if !ok { - return nil, errors.New("failed to get caller info") - } - - raw, err := ioutil.ReadFile(filename) - if err != nil { - return nil, errors.Wrapf(err, "failed to read source file: %s", filename) - } - - lines := strings.Split(string(raw), "\n") - if len(lines) < line { - return nil, errors.Errorf("file %s does not have line %d", filename, line) - } - firstLine, lastLine := getSourceLinesRange(line, len(lines)) - return lines[firstLine:lastLine], nil -} - -func getConditionArgFromAST(node ast.Expr) (string, error) { - switch expr := node.(type) { - case *ast.CallExpr: - buf := new(bytes.Buffer) - err := format.Node(buf, token.NewFileSet(), expr.Args[1]) - return buf.String(), err - } - return "", errors.New("unexpected ast") -} - -func formatMessage(msgAndArgs ...interface{}) string { - switch len(msgAndArgs) { - case 0: - return "" - case 1: - return msgAndArgs[0].(string) - default: - return fmt.Sprintf(msgAndArgs[0].(string), msgAndArgs[1:]...) - } -} - -func formatWithCustomMessage(source, custom string) string { - switch { - case custom == "": - return source - case source == "": - return custom - } - return fmt.Sprintf("%s: %s", source, custom) + t.Skip(format.WithCustomMessage(source, msgAndArgs...)) } diff --git a/components/cli/vendor/github.com/gotestyourself/gotestyourself/skip/skip_go18.go b/components/cli/vendor/github.com/gotestyourself/gotestyourself/skip/skip_go18.go deleted file mode 100644 index 586e58a735..0000000000 --- a/components/cli/vendor/github.com/gotestyourself/gotestyourself/skip/skip_go18.go +++ /dev/null @@ -1,17 +0,0 @@ -// +build !go1.9,!go.10,!go.11,!go1.12 - -package skip - -import "strings" - -func getSourceLinesRange(line int, _ int) (int, int) { - firstLine := line - maxContextLines - if firstLine < 0 { - firstLine = 0 - } - return firstLine, line -} - -func getSource(lines []string, i int) string { - return strings.Join(lines[len(lines)-i-1:], "\n") -} diff --git a/components/cli/vendor/github.com/gotestyourself/gotestyourself/skip/skip_go19.go b/components/cli/vendor/github.com/gotestyourself/gotestyourself/skip/skip_go19.go deleted file mode 100644 index 8aedc4b2c1..0000000000 --- a/components/cli/vendor/github.com/gotestyourself/gotestyourself/skip/skip_go19.go +++ /dev/null @@ -1,17 +0,0 @@ -// +build go1.9 - -package skip - -import "strings" - -func getSourceLinesRange(line int, lines int) (int, int) { - lastLine := line + maxContextLines - if lastLine > lines { - lastLine = lines - } - return line - 1, lastLine -} - -func getSource(lines []string, i int) string { - return strings.Join(lines[:i], "\n") -} diff --git a/components/cli/vendor/github.com/stretchr/testify/LICENSE b/components/cli/vendor/github.com/stretchr/testify/LICENSE deleted file mode 100644 index 473b670a7c..0000000000 --- a/components/cli/vendor/github.com/stretchr/testify/LICENSE +++ /dev/null @@ -1,22 +0,0 @@ -Copyright (c) 2012 - 2013 Mat Ryer and Tyler Bunnell - -Please consider promoting this project if you find it useful. - -Permission is hereby granted, free of charge, to any person -obtaining a copy of this software and associated documentation -files (the "Software"), to deal in the Software without restriction, -including without limitation the rights to use, copy, modify, merge, -publish, distribute, sublicense, and/or sell copies of the Software, -and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included -in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT -OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE -OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/components/cli/vendor/github.com/stretchr/testify/README.md b/components/cli/vendor/github.com/stretchr/testify/README.md deleted file mode 100644 index e57b1811f0..0000000000 --- a/components/cli/vendor/github.com/stretchr/testify/README.md +++ /dev/null @@ -1,332 +0,0 @@ -Testify - Thou Shalt Write Tests -================================ - -[![Build Status](https://travis-ci.org/stretchr/testify.svg)](https://travis-ci.org/stretchr/testify) [![Go Report Card](https://goreportcard.com/badge/github.com/stretchr/testify)](https://goreportcard.com/report/github.com/stretchr/testify) [![GoDoc](https://godoc.org/github.com/stretchr/testify?status.svg)](https://godoc.org/github.com/stretchr/testify) - -Go code (golang) set of packages that provide many tools for testifying that your code will behave as you intend. - -Features include: - - * [Easy assertions](#assert-package) - * [Mocking](#mock-package) - * [HTTP response trapping](#http-package) - * [Testing suite interfaces and functions](#suite-package) - -Get started: - - * Install testify with [one line of code](#installation), or [update it with another](#staying-up-to-date) - * For an introduction to writing test code in Go, see http://golang.org/doc/code.html#Testing - * Check out the API Documentation http://godoc.org/github.com/stretchr/testify - * To make your testing life easier, check out our other project, [gorc](http://github.com/stretchr/gorc) - * A little about [Test-Driven Development (TDD)](http://en.wikipedia.org/wiki/Test-driven_development) - - - -[`assert`](http://godoc.org/github.com/stretchr/testify/assert "API documentation") package -------------------------------------------------------------------------------------------- - -The `assert` package provides some helpful methods that allow you to write better test code in Go. - - * Prints friendly, easy to read failure descriptions - * Allows for very readable code - * Optionally annotate each assertion with a message - -See it in action: - -```go -package yours - -import ( - "testing" - "github.com/stretchr/testify/assert" -) - -func TestSomething(t *testing.T) { - - // assert equality - assert.Equal(t, 123, 123, "they should be equal") - - // assert inequality - assert.NotEqual(t, 123, 456, "they should not be equal") - - // assert for nil (good for errors) - assert.Nil(t, object) - - // assert for not nil (good when you expect something) - if assert.NotNil(t, object) { - - // now we know that object isn't nil, we are safe to make - // further assertions without causing any errors - assert.Equal(t, "Something", object.Value) - - } - -} -``` - - * Every assert func takes the `testing.T` object as the first argument. This is how it writes the errors out through the normal `go test` capabilities. - * Every assert func returns a bool indicating whether the assertion was successful or not, this is useful for if you want to go on making further assertions under certain conditions. - -if you assert many times, use the below: - -```go -package yours - -import ( - "testing" - "github.com/stretchr/testify/assert" -) - -func TestSomething(t *testing.T) { - assert := assert.New(t) - - // assert equality - assert.Equal(123, 123, "they should be equal") - - // assert inequality - assert.NotEqual(123, 456, "they should not be equal") - - // assert for nil (good for errors) - assert.Nil(object) - - // assert for not nil (good when you expect something) - if assert.NotNil(object) { - - // now we know that object isn't nil, we are safe to make - // further assertions without causing any errors - assert.Equal("Something", object.Value) - } -} -``` - -[`require`](http://godoc.org/github.com/stretchr/testify/require "API documentation") package ---------------------------------------------------------------------------------------------- - -The `require` package provides same global functions as the `assert` package, but instead of returning a boolean result they terminate current test. - -See [t.FailNow](http://golang.org/pkg/testing/#T.FailNow) for details. - - -[`http`](http://godoc.org/github.com/stretchr/testify/http "API documentation") package ---------------------------------------------------------------------------------------- - -The `http` package contains test objects useful for testing code that relies on the `net/http` package. Check out the [(deprecated) API documentation for the `http` package](http://godoc.org/github.com/stretchr/testify/http). - -We recommend you use [httptest](http://golang.org/pkg/net/http/httptest) instead. - -[`mock`](http://godoc.org/github.com/stretchr/testify/mock "API documentation") package ----------------------------------------------------------------------------------------- - -The `mock` package provides a mechanism for easily writing mock objects that can be used in place of real objects when writing test code. - -An example test function that tests a piece of code that relies on an external object `testObj`, can setup expectations (testify) and assert that they indeed happened: - -```go -package yours - -import ( - "testing" - "github.com/stretchr/testify/mock" -) - -/* - Test objects -*/ - -// MyMockedObject is a mocked object that implements an interface -// that describes an object that the code I am testing relies on. -type MyMockedObject struct{ - mock.Mock -} - -// DoSomething is a method on MyMockedObject that implements some interface -// and just records the activity, and returns what the Mock object tells it to. -// -// In the real object, this method would do something useful, but since this -// is a mocked object - we're just going to stub it out. -// -// NOTE: This method is not being tested here, code that uses this object is. -func (m *MyMockedObject) DoSomething(number int) (bool, error) { - - args := m.Called(number) - return args.Bool(0), args.Error(1) - -} - -/* - Actual test functions -*/ - -// TestSomething is an example of how to use our test object to -// make assertions about some target code we are testing. -func TestSomething(t *testing.T) { - - // create an instance of our test object - testObj := new(MyMockedObject) - - // setup expectations - testObj.On("DoSomething", 123).Return(true, nil) - - // call the code we are testing - targetFuncThatDoesSomethingWithObj(testObj) - - // assert that the expectations were met - testObj.AssertExpectations(t) - -} -``` - -For more information on how to write mock code, check out the [API documentation for the `mock` package](http://godoc.org/github.com/stretchr/testify/mock). - -You can use the [mockery tool](http://github.com/vektra/mockery) to autogenerate the mock code against an interface as well, making using mocks much quicker. - -[`suite`](http://godoc.org/github.com/stretchr/testify/suite "API documentation") package ------------------------------------------------------------------------------------------ - -The `suite` package provides functionality that you might be used to from more common object oriented languages. With it, you can build a testing suite as a struct, build setup/teardown methods and testing methods on your struct, and run them with 'go test' as per normal. - -An example suite is shown below: - -```go -// Basic imports -import ( - "testing" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/suite" -) - -// Define the suite, and absorb the built-in basic suite -// functionality from testify - including a T() method which -// returns the current testing context -type ExampleTestSuite struct { - suite.Suite - VariableThatShouldStartAtFive int -} - -// Make sure that VariableThatShouldStartAtFive is set to five -// before each test -func (suite *ExampleTestSuite) SetupTest() { - suite.VariableThatShouldStartAtFive = 5 -} - -// All methods that begin with "Test" are run as tests within a -// suite. -func (suite *ExampleTestSuite) TestExample() { - assert.Equal(suite.T(), 5, suite.VariableThatShouldStartAtFive) -} - -// In order for 'go test' to run this suite, we need to create -// a normal test function and pass our suite to suite.Run -func TestExampleTestSuite(t *testing.T) { - suite.Run(t, new(ExampleTestSuite)) -} -``` - -For a more complete example, using all of the functionality provided by the suite package, look at our [example testing suite](https://github.com/stretchr/testify/blob/master/suite/suite_test.go) - -For more information on writing suites, check out the [API documentation for the `suite` package](http://godoc.org/github.com/stretchr/testify/suite). - -`Suite` object has assertion methods: - -```go -// Basic imports -import ( - "testing" - "github.com/stretchr/testify/suite" -) - -// Define the suite, and absorb the built-in basic suite -// functionality from testify - including assertion methods. -type ExampleTestSuite struct { - suite.Suite - VariableThatShouldStartAtFive int -} - -// Make sure that VariableThatShouldStartAtFive is set to five -// before each test -func (suite *ExampleTestSuite) SetupTest() { - suite.VariableThatShouldStartAtFive = 5 -} - -// All methods that begin with "Test" are run as tests within a -// suite. -func (suite *ExampleTestSuite) TestExample() { - suite.Equal(suite.VariableThatShouldStartAtFive, 5) -} - -// In order for 'go test' to run this suite, we need to create -// a normal test function and pass our suite to suite.Run -func TestExampleTestSuite(t *testing.T) { - suite.Run(t, new(ExampleTestSuite)) -} -``` - ------- - -Installation -============ - -To install Testify, use `go get`: - - * Latest version: go get github.com/stretchr/testify - * Specific version: go get gopkg.in/stretchr/testify.v1 - -This will then make the following packages available to you: - - github.com/stretchr/testify/assert - github.com/stretchr/testify/mock - github.com/stretchr/testify/http - -Import the `testify/assert` package into your code using this template: - -```go -package yours - -import ( - "testing" - "github.com/stretchr/testify/assert" -) - -func TestSomething(t *testing.T) { - - assert.True(t, true, "True is true!") - -} -``` - ------- - -Staying up to date -================== - -To update Testify to the latest version, use `go get -u github.com/stretchr/testify`. - ------- - -Version History -=============== - - * 1.0 - New package versioning strategy adopted. - ------- - -Contributing -============ - -Please feel free to submit issues, fork the repository and send pull requests! - -When submitting an issue, we ask that you please include a complete test function that demonstrates the issue. Extra credit for those using Testify to write the test code that demonstrates it. - ------- - -Licence -======= -Copyright (c) 2012 - 2013 Mat Ryer and Tyler Bunnell - -Please consider promoting this project if you find it useful. - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/components/cli/vendor/github.com/stretchr/testify/assert/assertion_forward.go b/components/cli/vendor/github.com/stretchr/testify/assert/assertion_forward.go deleted file mode 100644 index aa4311ff84..0000000000 --- a/components/cli/vendor/github.com/stretchr/testify/assert/assertion_forward.go +++ /dev/null @@ -1,352 +0,0 @@ -/* -* CODE GENERATED AUTOMATICALLY WITH github.com/stretchr/testify/_codegen -* THIS FILE MUST NOT BE EDITED BY HAND - */ - -package assert - -import ( - http "net/http" - url "net/url" - time "time" -) - -// Condition uses a Comparison to assert a complex condition. -func (a *Assertions) Condition(comp Comparison, msgAndArgs ...interface{}) bool { - return Condition(a.t, comp, msgAndArgs...) -} - -// Contains asserts that the specified string, list(array, slice...) or map contains the -// specified substring or element. -// -// a.Contains("Hello World", "World", "But 'Hello World' does contain 'World'") -// a.Contains(["Hello", "World"], "World", "But ["Hello", "World"] does contain 'World'") -// a.Contains({"Hello": "World"}, "Hello", "But {'Hello': 'World'} does contain 'Hello'") -// -// Returns whether the assertion was successful (true) or not (false). -func (a *Assertions) Contains(s interface{}, contains interface{}, msgAndArgs ...interface{}) bool { - return Contains(a.t, s, contains, msgAndArgs...) -} - -// Empty asserts that the specified object is empty. I.e. nil, "", false, 0 or either -// a slice or a channel with len == 0. -// -// a.Empty(obj) -// -// Returns whether the assertion was successful (true) or not (false). -func (a *Assertions) Empty(object interface{}, msgAndArgs ...interface{}) bool { - return Empty(a.t, object, msgAndArgs...) -} - -// Equal asserts that two objects are equal. -// -// a.Equal(123, 123, "123 and 123 should be equal") -// -// Returns whether the assertion was successful (true) or not (false). -// -// Pointer variable equality is determined based on the equality of the -// referenced values (as opposed to the memory addresses). -func (a *Assertions) Equal(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool { - return Equal(a.t, expected, actual, msgAndArgs...) -} - -// EqualError asserts that a function returned an error (i.e. not `nil`) -// and that it is equal to the provided error. -// -// actualObj, err := SomeFunction() -// a.EqualError(err, expectedErrorString, "An error was expected") -// -// Returns whether the assertion was successful (true) or not (false). -func (a *Assertions) EqualError(theError error, errString string, msgAndArgs ...interface{}) bool { - return EqualError(a.t, theError, errString, msgAndArgs...) -} - -// EqualValues asserts that two objects are equal or convertable to the same types -// and equal. -// -// a.EqualValues(uint32(123), int32(123), "123 and 123 should be equal") -// -// Returns whether the assertion was successful (true) or not (false). -func (a *Assertions) EqualValues(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool { - return EqualValues(a.t, expected, actual, msgAndArgs...) -} - -// Error asserts that a function returned an error (i.e. not `nil`). -// -// actualObj, err := SomeFunction() -// if a.Error(err, "An error was expected") { -// assert.Equal(t, err, expectedError) -// } -// -// Returns whether the assertion was successful (true) or not (false). -func (a *Assertions) Error(err error, msgAndArgs ...interface{}) bool { - return Error(a.t, err, msgAndArgs...) -} - -// Exactly asserts that two objects are equal is value and type. -// -// a.Exactly(int32(123), int64(123), "123 and 123 should NOT be equal") -// -// Returns whether the assertion was successful (true) or not (false). -func (a *Assertions) Exactly(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool { - return Exactly(a.t, expected, actual, msgAndArgs...) -} - -// Fail reports a failure through -func (a *Assertions) Fail(failureMessage string, msgAndArgs ...interface{}) bool { - return Fail(a.t, failureMessage, msgAndArgs...) -} - -// FailNow fails test -func (a *Assertions) FailNow(failureMessage string, msgAndArgs ...interface{}) bool { - return FailNow(a.t, failureMessage, msgAndArgs...) -} - -// False asserts that the specified value is false. -// -// a.False(myBool, "myBool should be false") -// -// Returns whether the assertion was successful (true) or not (false). -func (a *Assertions) False(value bool, msgAndArgs ...interface{}) bool { - return False(a.t, value, msgAndArgs...) -} - -// HTTPBodyContains asserts that a specified handler returns a -// body that contains a string. -// -// a.HTTPBodyContains(myHandler, "www.google.com", nil, "I'm Feeling Lucky") -// -// Returns whether the assertion was successful (true) or not (false). -func (a *Assertions) HTTPBodyContains(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}) bool { - return HTTPBodyContains(a.t, handler, method, url, values, str) -} - -// HTTPBodyNotContains asserts that a specified handler returns a -// body that does not contain a string. -// -// a.HTTPBodyNotContains(myHandler, "www.google.com", nil, "I'm Feeling Lucky") -// -// Returns whether the assertion was successful (true) or not (false). -func (a *Assertions) HTTPBodyNotContains(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}) bool { - return HTTPBodyNotContains(a.t, handler, method, url, values, str) -} - -// HTTPError asserts that a specified handler returns an error status code. -// -// a.HTTPError(myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} -// -// Returns whether the assertion was successful (true) or not (false). -func (a *Assertions) HTTPError(handler http.HandlerFunc, method string, url string, values url.Values) bool { - return HTTPError(a.t, handler, method, url, values) -} - -// HTTPRedirect asserts that a specified handler returns a redirect status code. -// -// a.HTTPRedirect(myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} -// -// Returns whether the assertion was successful (true) or not (false). -func (a *Assertions) HTTPRedirect(handler http.HandlerFunc, method string, url string, values url.Values) bool { - return HTTPRedirect(a.t, handler, method, url, values) -} - -// HTTPSuccess asserts that a specified handler returns a success status code. -// -// a.HTTPSuccess(myHandler, "POST", "http://www.google.com", nil) -// -// Returns whether the assertion was successful (true) or not (false). -func (a *Assertions) HTTPSuccess(handler http.HandlerFunc, method string, url string, values url.Values) bool { - return HTTPSuccess(a.t, handler, method, url, values) -} - -// Implements asserts that an object is implemented by the specified interface. -// -// a.Implements((*MyInterface)(nil), new(MyObject), "MyObject") -func (a *Assertions) Implements(interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) bool { - return Implements(a.t, interfaceObject, object, msgAndArgs...) -} - -// InDelta asserts that the two numerals are within delta of each other. -// -// a.InDelta(math.Pi, (22 / 7.0), 0.01) -// -// Returns whether the assertion was successful (true) or not (false). -func (a *Assertions) InDelta(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) bool { - return InDelta(a.t, expected, actual, delta, msgAndArgs...) -} - -// InDeltaSlice is the same as InDelta, except it compares two slices. -func (a *Assertions) InDeltaSlice(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) bool { - return InDeltaSlice(a.t, expected, actual, delta, msgAndArgs...) -} - -// InEpsilon asserts that expected and actual have a relative error less than epsilon -// -// Returns whether the assertion was successful (true) or not (false). -func (a *Assertions) InEpsilon(expected interface{}, actual interface{}, epsilon float64, msgAndArgs ...interface{}) bool { - return InEpsilon(a.t, expected, actual, epsilon, msgAndArgs...) -} - -// InEpsilonSlice is the same as InEpsilon, except it compares each value from two slices. -func (a *Assertions) InEpsilonSlice(expected interface{}, actual interface{}, epsilon float64, msgAndArgs ...interface{}) bool { - return InEpsilonSlice(a.t, expected, actual, epsilon, msgAndArgs...) -} - -// IsType asserts that the specified objects are of the same type. -func (a *Assertions) IsType(expectedType interface{}, object interface{}, msgAndArgs ...interface{}) bool { - return IsType(a.t, expectedType, object, msgAndArgs...) -} - -// JSONEq asserts that two JSON strings are equivalent. -// -// a.JSONEq(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) -// -// Returns whether the assertion was successful (true) or not (false). -func (a *Assertions) JSONEq(expected string, actual string, msgAndArgs ...interface{}) bool { - return JSONEq(a.t, expected, actual, msgAndArgs...) -} - -// Len asserts that the specified object has specific length. -// Len also fails if the object has a type that len() not accept. -// -// a.Len(mySlice, 3, "The size of slice is not 3") -// -// Returns whether the assertion was successful (true) or not (false). -func (a *Assertions) Len(object interface{}, length int, msgAndArgs ...interface{}) bool { - return Len(a.t, object, length, msgAndArgs...) -} - -// Nil asserts that the specified object is nil. -// -// a.Nil(err, "err should be nothing") -// -// Returns whether the assertion was successful (true) or not (false). -func (a *Assertions) Nil(object interface{}, msgAndArgs ...interface{}) bool { - return Nil(a.t, object, msgAndArgs...) -} - -// NoError asserts that a function returned no error (i.e. `nil`). -// -// actualObj, err := SomeFunction() -// if a.NoError(err) { -// assert.Equal(t, actualObj, expectedObj) -// } -// -// Returns whether the assertion was successful (true) or not (false). -func (a *Assertions) NoError(err error, msgAndArgs ...interface{}) bool { - return NoError(a.t, err, msgAndArgs...) -} - -// NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the -// specified substring or element. -// -// a.NotContains("Hello World", "Earth", "But 'Hello World' does NOT contain 'Earth'") -// a.NotContains(["Hello", "World"], "Earth", "But ['Hello', 'World'] does NOT contain 'Earth'") -// a.NotContains({"Hello": "World"}, "Earth", "But {'Hello': 'World'} does NOT contain 'Earth'") -// -// Returns whether the assertion was successful (true) or not (false). -func (a *Assertions) NotContains(s interface{}, contains interface{}, msgAndArgs ...interface{}) bool { - return NotContains(a.t, s, contains, msgAndArgs...) -} - -// NotEmpty asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either -// a slice or a channel with len == 0. -// -// if a.NotEmpty(obj) { -// assert.Equal(t, "two", obj[1]) -// } -// -// Returns whether the assertion was successful (true) or not (false). -func (a *Assertions) NotEmpty(object interface{}, msgAndArgs ...interface{}) bool { - return NotEmpty(a.t, object, msgAndArgs...) -} - -// NotEqual asserts that the specified values are NOT equal. -// -// a.NotEqual(obj1, obj2, "two objects shouldn't be equal") -// -// Returns whether the assertion was successful (true) or not (false). -// -// Pointer variable equality is determined based on the equality of the -// referenced values (as opposed to the memory addresses). -func (a *Assertions) NotEqual(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool { - return NotEqual(a.t, expected, actual, msgAndArgs...) -} - -// NotNil asserts that the specified object is not nil. -// -// a.NotNil(err, "err should be something") -// -// Returns whether the assertion was successful (true) or not (false). -func (a *Assertions) NotNil(object interface{}, msgAndArgs ...interface{}) bool { - return NotNil(a.t, object, msgAndArgs...) -} - -// NotPanics asserts that the code inside the specified PanicTestFunc does NOT panic. -// -// a.NotPanics(func(){ -// RemainCalm() -// }, "Calling RemainCalm() should NOT panic") -// -// Returns whether the assertion was successful (true) or not (false). -func (a *Assertions) NotPanics(f PanicTestFunc, msgAndArgs ...interface{}) bool { - return NotPanics(a.t, f, msgAndArgs...) -} - -// NotRegexp asserts that a specified regexp does not match a string. -// -// a.NotRegexp(regexp.MustCompile("starts"), "it's starting") -// a.NotRegexp("^start", "it's not starting") -// -// Returns whether the assertion was successful (true) or not (false). -func (a *Assertions) NotRegexp(rx interface{}, str interface{}, msgAndArgs ...interface{}) bool { - return NotRegexp(a.t, rx, str, msgAndArgs...) -} - -// NotZero asserts that i is not the zero value for its type and returns the truth. -func (a *Assertions) NotZero(i interface{}, msgAndArgs ...interface{}) bool { - return NotZero(a.t, i, msgAndArgs...) -} - -// Panics asserts that the code inside the specified PanicTestFunc panics. -// -// a.Panics(func(){ -// GoCrazy() -// }, "Calling GoCrazy() should panic") -// -// Returns whether the assertion was successful (true) or not (false). -func (a *Assertions) Panics(f PanicTestFunc, msgAndArgs ...interface{}) bool { - return Panics(a.t, f, msgAndArgs...) -} - -// Regexp asserts that a specified regexp matches a string. -// -// a.Regexp(regexp.MustCompile("start"), "it's starting") -// a.Regexp("start...$", "it's not starting") -// -// Returns whether the assertion was successful (true) or not (false). -func (a *Assertions) Regexp(rx interface{}, str interface{}, msgAndArgs ...interface{}) bool { - return Regexp(a.t, rx, str, msgAndArgs...) -} - -// True asserts that the specified value is true. -// -// a.True(myBool, "myBool should be true") -// -// Returns whether the assertion was successful (true) or not (false). -func (a *Assertions) True(value bool, msgAndArgs ...interface{}) bool { - return True(a.t, value, msgAndArgs...) -} - -// WithinDuration asserts that the two times are within duration delta of each other. -// -// a.WithinDuration(time.Now(), time.Now(), 10*time.Second, "The difference should not be more than 10s") -// -// Returns whether the assertion was successful (true) or not (false). -func (a *Assertions) WithinDuration(expected time.Time, actual time.Time, delta time.Duration, msgAndArgs ...interface{}) bool { - return WithinDuration(a.t, expected, actual, delta, msgAndArgs...) -} - -// Zero asserts that i is the zero value for its type and returns the truth. -func (a *Assertions) Zero(i interface{}, msgAndArgs ...interface{}) bool { - return Zero(a.t, i, msgAndArgs...) -} diff --git a/components/cli/vendor/github.com/stretchr/testify/assert/assertions.go b/components/cli/vendor/github.com/stretchr/testify/assert/assertions.go deleted file mode 100644 index d1552e5e3f..0000000000 --- a/components/cli/vendor/github.com/stretchr/testify/assert/assertions.go +++ /dev/null @@ -1,1069 +0,0 @@ -package assert - -import ( - "bufio" - "bytes" - "encoding/json" - "fmt" - "math" - "reflect" - "regexp" - "runtime" - "strings" - "time" - "unicode" - "unicode/utf8" - - "github.com/davecgh/go-spew/spew" - "github.com/pmezard/go-difflib/difflib" -) - -// TestingT is an interface wrapper around *testing.T -type TestingT interface { - Errorf(format string, args ...interface{}) -} - -// Comparison a custom function that returns true on success and false on failure -type Comparison func() (success bool) - -/* - Helper functions -*/ - -// ObjectsAreEqual determines if two objects are considered equal. -// -// This function does no assertion of any kind. -func ObjectsAreEqual(expected, actual interface{}) bool { - - if expected == nil || actual == nil { - return expected == actual - } - - return reflect.DeepEqual(expected, actual) - -} - -// ObjectsAreEqualValues gets whether two objects are equal, or if their -// values are equal. -func ObjectsAreEqualValues(expected, actual interface{}) bool { - if ObjectsAreEqual(expected, actual) { - return true - } - - actualType := reflect.TypeOf(actual) - if actualType == nil { - return false - } - expectedValue := reflect.ValueOf(expected) - if expectedValue.IsValid() && expectedValue.Type().ConvertibleTo(actualType) { - // Attempt comparison after type conversion - return reflect.DeepEqual(expectedValue.Convert(actualType).Interface(), actual) - } - - return false -} - -/* CallerInfo is necessary because the assert functions use the testing object -internally, causing it to print the file:line of the assert method, rather than where -the problem actually occurred in calling code.*/ - -// CallerInfo returns an array of strings containing the file and line number -// of each stack frame leading from the current test to the assert call that -// failed. -func CallerInfo() []string { - - pc := uintptr(0) - file := "" - line := 0 - ok := false - name := "" - - callers := []string{} - for i := 0; ; i++ { - pc, file, line, ok = runtime.Caller(i) - if !ok { - // The breaks below failed to terminate the loop, and we ran off the - // end of the call stack. - break - } - - // This is a huge edge case, but it will panic if this is the case, see #180 - if file == "" { - break - } - - f := runtime.FuncForPC(pc) - if f == nil { - break - } - name = f.Name() - - // testing.tRunner is the standard library function that calls - // tests. Subtests are called directly by tRunner, without going through - // the Test/Benchmark/Example function that contains the t.Run calls, so - // with subtests we should break when we hit tRunner, without adding it - // to the list of callers. - if name == "testing.tRunner" { - break - } - - parts := strings.Split(file, "/") - dir := parts[len(parts)-2] - file = parts[len(parts)-1] - if (dir != "assert" && dir != "mock" && dir != "require") || file == "mock_test.go" { - callers = append(callers, fmt.Sprintf("%s:%d", file, line)) - } - - // Drop the package - segments := strings.Split(name, ".") - name = segments[len(segments)-1] - if isTest(name, "Test") || - isTest(name, "Benchmark") || - isTest(name, "Example") { - break - } - } - - return callers -} - -// Stolen from the `go test` tool. -// isTest tells whether name looks like a test (or benchmark, according to prefix). -// It is a Test (say) if there is a character after Test that is not a lower-case letter. -// We don't want TesticularCancer. -func isTest(name, prefix string) bool { - if !strings.HasPrefix(name, prefix) { - return false - } - if len(name) == len(prefix) { // "Test" is ok - return true - } - rune, _ := utf8.DecodeRuneInString(name[len(prefix):]) - return !unicode.IsLower(rune) -} - -// getWhitespaceString returns a string that is long enough to overwrite the default -// output from the go testing framework. -func getWhitespaceString() string { - - _, file, line, ok := runtime.Caller(1) - if !ok { - return "" - } - parts := strings.Split(file, "/") - file = parts[len(parts)-1] - - return strings.Repeat(" ", len(fmt.Sprintf("%s:%d: ", file, line))) - -} - -func messageFromMsgAndArgs(msgAndArgs ...interface{}) string { - if len(msgAndArgs) == 0 || msgAndArgs == nil { - return "" - } - if len(msgAndArgs) == 1 { - return msgAndArgs[0].(string) - } - if len(msgAndArgs) > 1 { - return fmt.Sprintf(msgAndArgs[0].(string), msgAndArgs[1:]...) - } - return "" -} - -// Aligns the provided message so that all lines after the first line start at the same location as the first line. -// Assumes that the first line starts at the correct location (after carriage return, tab, label, spacer and tab). -// The longestLabelLen parameter specifies the length of the longest label in the output (required becaues this is the -// basis on which the alignment occurs). -func indentMessageLines(message string, longestLabelLen int) string { - outBuf := new(bytes.Buffer) - - for i, scanner := 0, bufio.NewScanner(strings.NewReader(message)); scanner.Scan(); i++ { - // no need to align first line because it starts at the correct location (after the label) - if i != 0 { - // append alignLen+1 spaces to align with "{{longestLabel}}:" before adding tab - outBuf.WriteString("\n\r\t" + strings.Repeat(" ", longestLabelLen +1) + "\t") - } - outBuf.WriteString(scanner.Text()) - } - - return outBuf.String() -} - -type failNower interface { - FailNow() -} - -// FailNow fails test -func FailNow(t TestingT, failureMessage string, msgAndArgs ...interface{}) bool { - Fail(t, failureMessage, msgAndArgs...) - - // We cannot extend TestingT with FailNow() and - // maintain backwards compatibility, so we fallback - // to panicking when FailNow is not available in - // TestingT. - // See issue #263 - - if t, ok := t.(failNower); ok { - t.FailNow() - } else { - panic("test failed and t is missing `FailNow()`") - } - return false -} - -// Fail reports a failure through -func Fail(t TestingT, failureMessage string, msgAndArgs ...interface{}) bool { - content := []labeledContent{ - {"Error Trace", strings.Join(CallerInfo(), "\n\r\t\t\t")}, - {"Error", failureMessage}, - } - - message := messageFromMsgAndArgs(msgAndArgs...) - if len(message) > 0 { - content = append(content, labeledContent{"Messages", message}) - } - - t.Errorf("\r" + getWhitespaceString() + labeledOutput(content...)) - - return false -} - -type labeledContent struct { - label string - content string -} - -// labeledOutput returns a string consisting of the provided labeledContent. Each labeled output is appended in the following manner: -// -// \r\t{{label}}:{{align_spaces}}\t{{content}}\n -// -// The initial carriage return is required to undo/erase any padding added by testing.T.Errorf. The "\t{{label}}:" is for the label. -// If a label is shorter than the longest label provided, padding spaces are added to make all the labels match in length. Once this -// alignment is achieved, "\t{{content}}\n" is added for the output. -// -// If the content of the labeledOutput contains line breaks, the subsequent lines are aligned so that they start at the same location as the first line. -func labeledOutput(content ...labeledContent) string { - longestLabel := 0 - for _, v := range content { - if len(v.label) > longestLabel { - longestLabel = len(v.label) - } - } - var output string - for _, v := range content { - output += "\r\t" + v.label + ":" + strings.Repeat(" ", longestLabel-len(v.label)) + "\t" + indentMessageLines(v.content, longestLabel) + "\n" - } - return output -} - -// Implements asserts that an object is implemented by the specified interface. -// -// assert.Implements(t, (*MyInterface)(nil), new(MyObject), "MyObject") -func Implements(t TestingT, interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) bool { - - interfaceType := reflect.TypeOf(interfaceObject).Elem() - - if !reflect.TypeOf(object).Implements(interfaceType) { - return Fail(t, fmt.Sprintf("%T must implement %v", object, interfaceType), msgAndArgs...) - } - - return true - -} - -// IsType asserts that the specified objects are of the same type. -func IsType(t TestingT, expectedType interface{}, object interface{}, msgAndArgs ...interface{}) bool { - - if !ObjectsAreEqual(reflect.TypeOf(object), reflect.TypeOf(expectedType)) { - return Fail(t, fmt.Sprintf("Object expected to be of type %v, but was %v", reflect.TypeOf(expectedType), reflect.TypeOf(object)), msgAndArgs...) - } - - return true -} - -// Equal asserts that two objects are equal. -// -// assert.Equal(t, 123, 123, "123 and 123 should be equal") -// -// Returns whether the assertion was successful (true) or not (false). -// -// Pointer variable equality is determined based on the equality of the -// referenced values (as opposed to the memory addresses). -func Equal(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool { - - if !ObjectsAreEqual(expected, actual) { - diff := diff(expected, actual) - expected, actual = formatUnequalValues(expected, actual) - return Fail(t, fmt.Sprintf("Not equal: \n"+ - "expected: %s\n"+ - "received: %s%s", expected, actual, diff), msgAndArgs...) - } - - return true - -} - -// formatUnequalValues takes two values of arbitrary types and returns string -// representations appropriate to be presented to the user. -// -// If the values are not of like type, the returned strings will be prefixed -// with the type name, and the value will be enclosed in parenthesis similar -// to a type conversion in the Go grammar. -func formatUnequalValues(expected, actual interface{}) (e string, a string) { - if reflect.TypeOf(expected) != reflect.TypeOf(actual) { - return fmt.Sprintf("%T(%#v)", expected, expected), - fmt.Sprintf("%T(%#v)", actual, actual) - } - - return fmt.Sprintf("%#v", expected), - fmt.Sprintf("%#v", actual) -} - -// EqualValues asserts that two objects are equal or convertable to the same types -// and equal. -// -// assert.EqualValues(t, uint32(123), int32(123), "123 and 123 should be equal") -// -// Returns whether the assertion was successful (true) or not (false). -func EqualValues(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool { - - if !ObjectsAreEqualValues(expected, actual) { - diff := diff(expected, actual) - expected, actual = formatUnequalValues(expected, actual) - return Fail(t, fmt.Sprintf("Not equal: \n"+ - "expected: %s\n"+ - "received: %s%s", expected, actual, diff), msgAndArgs...) - } - - return true - -} - -// Exactly asserts that two objects are equal is value and type. -// -// assert.Exactly(t, int32(123), int64(123), "123 and 123 should NOT be equal") -// -// Returns whether the assertion was successful (true) or not (false). -func Exactly(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool { - - aType := reflect.TypeOf(expected) - bType := reflect.TypeOf(actual) - - if aType != bType { - return Fail(t, fmt.Sprintf("Types expected to match exactly\n\r\t%v != %v", aType, bType), msgAndArgs...) - } - - return Equal(t, expected, actual, msgAndArgs...) - -} - -// NotNil asserts that the specified object is not nil. -// -// assert.NotNil(t, err, "err should be something") -// -// Returns whether the assertion was successful (true) or not (false). -func NotNil(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { - if !isNil(object) { - return true - } - return Fail(t, "Expected value not to be nil.", msgAndArgs...) -} - -// isNil checks if a specified object is nil or not, without Failing. -func isNil(object interface{}) bool { - if object == nil { - return true - } - - value := reflect.ValueOf(object) - kind := value.Kind() - if kind >= reflect.Chan && kind <= reflect.Slice && value.IsNil() { - return true - } - - return false -} - -// Nil asserts that the specified object is nil. -// -// assert.Nil(t, err, "err should be nothing") -// -// Returns whether the assertion was successful (true) or not (false). -func Nil(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { - if isNil(object) { - return true - } - return Fail(t, fmt.Sprintf("Expected nil, but got: %#v", object), msgAndArgs...) -} - -var numericZeros = []interface{}{ - int(0), - int8(0), - int16(0), - int32(0), - int64(0), - uint(0), - uint8(0), - uint16(0), - uint32(0), - uint64(0), - float32(0), - float64(0), -} - -// isEmpty gets whether the specified object is considered empty or not. -func isEmpty(object interface{}) bool { - - if object == nil { - return true - } else if object == "" { - return true - } else if object == false { - return true - } - - for _, v := range numericZeros { - if object == v { - return true - } - } - - objValue := reflect.ValueOf(object) - - switch objValue.Kind() { - case reflect.Map: - fallthrough - case reflect.Slice, reflect.Chan: - { - return (objValue.Len() == 0) - } - case reflect.Struct: - switch object.(type) { - case time.Time: - return object.(time.Time).IsZero() - } - case reflect.Ptr: - { - if objValue.IsNil() { - return true - } - switch object.(type) { - case *time.Time: - return object.(*time.Time).IsZero() - default: - return false - } - } - } - return false -} - -// Empty asserts that the specified object is empty. I.e. nil, "", false, 0 or either -// a slice or a channel with len == 0. -// -// assert.Empty(t, obj) -// -// Returns whether the assertion was successful (true) or not (false). -func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { - - pass := isEmpty(object) - if !pass { - Fail(t, fmt.Sprintf("Should be empty, but was %v", object), msgAndArgs...) - } - - return pass - -} - -// NotEmpty asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either -// a slice or a channel with len == 0. -// -// if assert.NotEmpty(t, obj) { -// assert.Equal(t, "two", obj[1]) -// } -// -// Returns whether the assertion was successful (true) or not (false). -func NotEmpty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { - - pass := !isEmpty(object) - if !pass { - Fail(t, fmt.Sprintf("Should NOT be empty, but was %v", object), msgAndArgs...) - } - - return pass - -} - -// getLen try to get length of object. -// return (false, 0) if impossible. -func getLen(x interface{}) (ok bool, length int) { - v := reflect.ValueOf(x) - defer func() { - if e := recover(); e != nil { - ok = false - } - }() - return true, v.Len() -} - -// Len asserts that the specified object has specific length. -// Len also fails if the object has a type that len() not accept. -// -// assert.Len(t, mySlice, 3, "The size of slice is not 3") -// -// Returns whether the assertion was successful (true) or not (false). -func Len(t TestingT, object interface{}, length int, msgAndArgs ...interface{}) bool { - ok, l := getLen(object) - if !ok { - return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", object), msgAndArgs...) - } - - if l != length { - return Fail(t, fmt.Sprintf("\"%s\" should have %d item(s), but has %d", object, length, l), msgAndArgs...) - } - return true -} - -// True asserts that the specified value is true. -// -// assert.True(t, myBool, "myBool should be true") -// -// Returns whether the assertion was successful (true) or not (false). -func True(t TestingT, value bool, msgAndArgs ...interface{}) bool { - - if value != true { - return Fail(t, "Should be true", msgAndArgs...) - } - - return true - -} - -// False asserts that the specified value is false. -// -// assert.False(t, myBool, "myBool should be false") -// -// Returns whether the assertion was successful (true) or not (false). -func False(t TestingT, value bool, msgAndArgs ...interface{}) bool { - - if value != false { - return Fail(t, "Should be false", msgAndArgs...) - } - - return true - -} - -// NotEqual asserts that the specified values are NOT equal. -// -// assert.NotEqual(t, obj1, obj2, "two objects shouldn't be equal") -// -// Returns whether the assertion was successful (true) or not (false). -// -// Pointer variable equality is determined based on the equality of the -// referenced values (as opposed to the memory addresses). -func NotEqual(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool { - - if ObjectsAreEqual(expected, actual) { - return Fail(t, fmt.Sprintf("Should not be: %#v\n", actual), msgAndArgs...) - } - - return true - -} - -// containsElement try loop over the list check if the list includes the element. -// return (false, false) if impossible. -// return (true, false) if element was not found. -// return (true, true) if element was found. -func includeElement(list interface{}, element interface{}) (ok, found bool) { - - listValue := reflect.ValueOf(list) - elementValue := reflect.ValueOf(element) - defer func() { - if e := recover(); e != nil { - ok = false - found = false - } - }() - - if reflect.TypeOf(list).Kind() == reflect.String { - return true, strings.Contains(listValue.String(), elementValue.String()) - } - - if reflect.TypeOf(list).Kind() == reflect.Map { - mapKeys := listValue.MapKeys() - for i := 0; i < len(mapKeys); i++ { - if ObjectsAreEqual(mapKeys[i].Interface(), element) { - return true, true - } - } - return true, false - } - - for i := 0; i < listValue.Len(); i++ { - if ObjectsAreEqual(listValue.Index(i).Interface(), element) { - return true, true - } - } - return true, false - -} - -// Contains asserts that the specified string, list(array, slice...) or map contains the -// specified substring or element. -// -// assert.Contains(t, "Hello World", "World", "But 'Hello World' does contain 'World'") -// assert.Contains(t, ["Hello", "World"], "World", "But ["Hello", "World"] does contain 'World'") -// assert.Contains(t, {"Hello": "World"}, "Hello", "But {'Hello': 'World'} does contain 'Hello'") -// -// Returns whether the assertion was successful (true) or not (false). -func Contains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) bool { - - ok, found := includeElement(s, contains) - if !ok { - return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", s), msgAndArgs...) - } - if !found { - return Fail(t, fmt.Sprintf("\"%s\" does not contain \"%s\"", s, contains), msgAndArgs...) - } - - return true - -} - -// NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the -// specified substring or element. -// -// assert.NotContains(t, "Hello World", "Earth", "But 'Hello World' does NOT contain 'Earth'") -// assert.NotContains(t, ["Hello", "World"], "Earth", "But ['Hello', 'World'] does NOT contain 'Earth'") -// assert.NotContains(t, {"Hello": "World"}, "Earth", "But {'Hello': 'World'} does NOT contain 'Earth'") -// -// Returns whether the assertion was successful (true) or not (false). -func NotContains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) bool { - - ok, found := includeElement(s, contains) - if !ok { - return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", s), msgAndArgs...) - } - if found { - return Fail(t, fmt.Sprintf("\"%s\" should not contain \"%s\"", s, contains), msgAndArgs...) - } - - return true - -} - -// Condition uses a Comparison to assert a complex condition. -func Condition(t TestingT, comp Comparison, msgAndArgs ...interface{}) bool { - result := comp() - if !result { - Fail(t, "Condition failed!", msgAndArgs...) - } - return result -} - -// PanicTestFunc defines a func that should be passed to the assert.Panics and assert.NotPanics -// methods, and represents a simple func that takes no arguments, and returns nothing. -type PanicTestFunc func() - -// didPanic returns true if the function passed to it panics. Otherwise, it returns false. -func didPanic(f PanicTestFunc) (bool, interface{}) { - - didPanic := false - var message interface{} - func() { - - defer func() { - if message = recover(); message != nil { - didPanic = true - } - }() - - // call the target function - f() - - }() - - return didPanic, message - -} - -// Panics asserts that the code inside the specified PanicTestFunc panics. -// -// assert.Panics(t, func(){ -// GoCrazy() -// }, "Calling GoCrazy() should panic") -// -// Returns whether the assertion was successful (true) or not (false). -func Panics(t TestingT, f PanicTestFunc, msgAndArgs ...interface{}) bool { - - if funcDidPanic, panicValue := didPanic(f); !funcDidPanic { - return Fail(t, fmt.Sprintf("func %#v should panic\n\r\tPanic value:\t%v", f, panicValue), msgAndArgs...) - } - - return true -} - -// NotPanics asserts that the code inside the specified PanicTestFunc does NOT panic. -// -// assert.NotPanics(t, func(){ -// RemainCalm() -// }, "Calling RemainCalm() should NOT panic") -// -// Returns whether the assertion was successful (true) or not (false). -func NotPanics(t TestingT, f PanicTestFunc, msgAndArgs ...interface{}) bool { - - if funcDidPanic, panicValue := didPanic(f); funcDidPanic { - return Fail(t, fmt.Sprintf("func %#v should not panic\n\r\tPanic value:\t%v", f, panicValue), msgAndArgs...) - } - - return true -} - -// WithinDuration asserts that the two times are within duration delta of each other. -// -// assert.WithinDuration(t, time.Now(), time.Now(), 10*time.Second, "The difference should not be more than 10s") -// -// Returns whether the assertion was successful (true) or not (false). -func WithinDuration(t TestingT, expected, actual time.Time, delta time.Duration, msgAndArgs ...interface{}) bool { - - dt := expected.Sub(actual) - if dt < -delta || dt > delta { - return Fail(t, fmt.Sprintf("Max difference between %v and %v allowed is %v, but difference was %v", expected, actual, delta, dt), msgAndArgs...) - } - - return true -} - -func toFloat(x interface{}) (float64, bool) { - var xf float64 - xok := true - - switch xn := x.(type) { - case uint8: - xf = float64(xn) - case uint16: - xf = float64(xn) - case uint32: - xf = float64(xn) - case uint64: - xf = float64(xn) - case int: - xf = float64(xn) - case int8: - xf = float64(xn) - case int16: - xf = float64(xn) - case int32: - xf = float64(xn) - case int64: - xf = float64(xn) - case float32: - xf = float64(xn) - case float64: - xf = float64(xn) - default: - xok = false - } - - return xf, xok -} - -// InDelta asserts that the two numerals are within delta of each other. -// -// assert.InDelta(t, math.Pi, (22 / 7.0), 0.01) -// -// Returns whether the assertion was successful (true) or not (false). -func InDelta(t TestingT, expected, actual interface{}, delta float64, msgAndArgs ...interface{}) bool { - - af, aok := toFloat(expected) - bf, bok := toFloat(actual) - - if !aok || !bok { - return Fail(t, fmt.Sprintf("Parameters must be numerical"), msgAndArgs...) - } - - if math.IsNaN(af) { - return Fail(t, fmt.Sprintf("Actual must not be NaN"), msgAndArgs...) - } - - if math.IsNaN(bf) { - return Fail(t, fmt.Sprintf("Expected %v with delta %v, but was NaN", expected, delta), msgAndArgs...) - } - - dt := af - bf - if dt < -delta || dt > delta { - return Fail(t, fmt.Sprintf("Max difference between %v and %v allowed is %v, but difference was %v", expected, actual, delta, dt), msgAndArgs...) - } - - return true -} - -// InDeltaSlice is the same as InDelta, except it compares two slices. -func InDeltaSlice(t TestingT, expected, actual interface{}, delta float64, msgAndArgs ...interface{}) bool { - if expected == nil || actual == nil || - reflect.TypeOf(actual).Kind() != reflect.Slice || - reflect.TypeOf(expected).Kind() != reflect.Slice { - return Fail(t, fmt.Sprintf("Parameters must be slice"), msgAndArgs...) - } - - actualSlice := reflect.ValueOf(actual) - expectedSlice := reflect.ValueOf(expected) - - for i := 0; i < actualSlice.Len(); i++ { - result := InDelta(t, actualSlice.Index(i).Interface(), expectedSlice.Index(i).Interface(), delta) - if !result { - return result - } - } - - return true -} - -func calcRelativeError(expected, actual interface{}) (float64, error) { - af, aok := toFloat(expected) - if !aok { - return 0, fmt.Errorf("expected value %q cannot be converted to float", expected) - } - if af == 0 { - return 0, fmt.Errorf("expected value must have a value other than zero to calculate the relative error") - } - bf, bok := toFloat(actual) - if !bok { - return 0, fmt.Errorf("expected value %q cannot be converted to float", actual) - } - - return math.Abs(af-bf) / math.Abs(af), nil -} - -// InEpsilon asserts that expected and actual have a relative error less than epsilon -// -// Returns whether the assertion was successful (true) or not (false). -func InEpsilon(t TestingT, expected, actual interface{}, epsilon float64, msgAndArgs ...interface{}) bool { - actualEpsilon, err := calcRelativeError(expected, actual) - if err != nil { - return Fail(t, err.Error(), msgAndArgs...) - } - if actualEpsilon > epsilon { - return Fail(t, fmt.Sprintf("Relative error is too high: %#v (expected)\n"+ - " < %#v (actual)", actualEpsilon, epsilon), msgAndArgs...) - } - - return true -} - -// InEpsilonSlice is the same as InEpsilon, except it compares each value from two slices. -func InEpsilonSlice(t TestingT, expected, actual interface{}, epsilon float64, msgAndArgs ...interface{}) bool { - if expected == nil || actual == nil || - reflect.TypeOf(actual).Kind() != reflect.Slice || - reflect.TypeOf(expected).Kind() != reflect.Slice { - return Fail(t, fmt.Sprintf("Parameters must be slice"), msgAndArgs...) - } - - actualSlice := reflect.ValueOf(actual) - expectedSlice := reflect.ValueOf(expected) - - for i := 0; i < actualSlice.Len(); i++ { - result := InEpsilon(t, actualSlice.Index(i).Interface(), expectedSlice.Index(i).Interface(), epsilon) - if !result { - return result - } - } - - return true -} - -/* - Errors -*/ - -// NoError asserts that a function returned no error (i.e. `nil`). -// -// actualObj, err := SomeFunction() -// if assert.NoError(t, err) { -// assert.Equal(t, actualObj, expectedObj) -// } -// -// Returns whether the assertion was successful (true) or not (false). -func NoError(t TestingT, err error, msgAndArgs ...interface{}) bool { - if err != nil { - return Fail(t, fmt.Sprintf("Received unexpected error:\n%+v", err), msgAndArgs...) - } - - return true -} - -// Error asserts that a function returned an error (i.e. not `nil`). -// -// actualObj, err := SomeFunction() -// if assert.Error(t, err, "An error was expected") { -// assert.Equal(t, err, expectedError) -// } -// -// Returns whether the assertion was successful (true) or not (false). -func Error(t TestingT, err error, msgAndArgs ...interface{}) bool { - - if err == nil { - return Fail(t, "An error is expected but got nil.", msgAndArgs...) - } - - return true -} - -// EqualError asserts that a function returned an error (i.e. not `nil`) -// and that it is equal to the provided error. -// -// actualObj, err := SomeFunction() -// assert.EqualError(t, err, expectedErrorString, "An error was expected") -// -// Returns whether the assertion was successful (true) or not (false). -func EqualError(t TestingT, theError error, errString string, msgAndArgs ...interface{}) bool { - if !Error(t, theError, msgAndArgs...) { - return false - } - expected := errString - actual := theError.Error() - // don't need to use deep equals here, we know they are both strings - if expected != actual { - return Fail(t, fmt.Sprintf("Error message not equal:\n"+ - "expected: %q\n"+ - "received: %q", expected, actual), msgAndArgs...) - } - return true -} - -// matchRegexp return true if a specified regexp matches a string. -func matchRegexp(rx interface{}, str interface{}) bool { - - var r *regexp.Regexp - if rr, ok := rx.(*regexp.Regexp); ok { - r = rr - } else { - r = regexp.MustCompile(fmt.Sprint(rx)) - } - - return (r.FindStringIndex(fmt.Sprint(str)) != nil) - -} - -// Regexp asserts that a specified regexp matches a string. -// -// assert.Regexp(t, regexp.MustCompile("start"), "it's starting") -// assert.Regexp(t, "start...$", "it's not starting") -// -// Returns whether the assertion was successful (true) or not (false). -func Regexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface{}) bool { - - match := matchRegexp(rx, str) - - if !match { - Fail(t, fmt.Sprintf("Expect \"%v\" to match \"%v\"", str, rx), msgAndArgs...) - } - - return match -} - -// NotRegexp asserts that a specified regexp does not match a string. -// -// assert.NotRegexp(t, regexp.MustCompile("starts"), "it's starting") -// assert.NotRegexp(t, "^start", "it's not starting") -// -// Returns whether the assertion was successful (true) or not (false). -func NotRegexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface{}) bool { - match := matchRegexp(rx, str) - - if match { - Fail(t, fmt.Sprintf("Expect \"%v\" to NOT match \"%v\"", str, rx), msgAndArgs...) - } - - return !match - -} - -// Zero asserts that i is the zero value for its type and returns the truth. -func Zero(t TestingT, i interface{}, msgAndArgs ...interface{}) bool { - if i != nil && !reflect.DeepEqual(i, reflect.Zero(reflect.TypeOf(i)).Interface()) { - return Fail(t, fmt.Sprintf("Should be zero, but was %v", i), msgAndArgs...) - } - return true -} - -// NotZero asserts that i is not the zero value for its type and returns the truth. -func NotZero(t TestingT, i interface{}, msgAndArgs ...interface{}) bool { - if i == nil || reflect.DeepEqual(i, reflect.Zero(reflect.TypeOf(i)).Interface()) { - return Fail(t, fmt.Sprintf("Should not be zero, but was %v", i), msgAndArgs...) - } - return true -} - -// JSONEq asserts that two JSON strings are equivalent. -// -// assert.JSONEq(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) -// -// Returns whether the assertion was successful (true) or not (false). -func JSONEq(t TestingT, expected string, actual string, msgAndArgs ...interface{}) bool { - var expectedJSONAsInterface, actualJSONAsInterface interface{} - - if err := json.Unmarshal([]byte(expected), &expectedJSONAsInterface); err != nil { - return Fail(t, fmt.Sprintf("Expected value ('%s') is not valid json.\nJSON parsing error: '%s'", expected, err.Error()), msgAndArgs...) - } - - if err := json.Unmarshal([]byte(actual), &actualJSONAsInterface); err != nil { - return Fail(t, fmt.Sprintf("Input ('%s') needs to be valid json.\nJSON parsing error: '%s'", actual, err.Error()), msgAndArgs...) - } - - return Equal(t, expectedJSONAsInterface, actualJSONAsInterface, msgAndArgs...) -} - -func typeAndKind(v interface{}) (reflect.Type, reflect.Kind) { - t := reflect.TypeOf(v) - k := t.Kind() - - if k == reflect.Ptr { - t = t.Elem() - k = t.Kind() - } - return t, k -} - -// diff returns a diff of both values as long as both are of the same type and -// are a struct, map, slice or array. Otherwise it returns an empty string. -func diff(expected interface{}, actual interface{}) string { - if expected == nil || actual == nil { - return "" - } - - et, ek := typeAndKind(expected) - at, _ := typeAndKind(actual) - - if et != at { - return "" - } - - if ek != reflect.Struct && ek != reflect.Map && ek != reflect.Slice && ek != reflect.Array { - return "" - } - - e := spewConfig.Sdump(expected) - a := spewConfig.Sdump(actual) - - diff, _ := difflib.GetUnifiedDiffString(difflib.UnifiedDiff{ - A: difflib.SplitLines(e), - B: difflib.SplitLines(a), - FromFile: "Expected", - FromDate: "", - ToFile: "Actual", - ToDate: "", - Context: 1, - }) - - return "\n\nDiff:\n" + diff -} - -var spewConfig = spew.ConfigState{ - Indent: " ", - DisablePointerAddresses: true, - DisableCapacities: true, - SortKeys: true, -} diff --git a/components/cli/vendor/github.com/stretchr/testify/assert/doc.go b/components/cli/vendor/github.com/stretchr/testify/assert/doc.go deleted file mode 100644 index c9dccc4d6c..0000000000 --- a/components/cli/vendor/github.com/stretchr/testify/assert/doc.go +++ /dev/null @@ -1,45 +0,0 @@ -// Package assert provides a set of comprehensive testing tools for use with the normal Go testing system. -// -// Example Usage -// -// The following is a complete example using assert in a standard test function: -// import ( -// "testing" -// "github.com/stretchr/testify/assert" -// ) -// -// func TestSomething(t *testing.T) { -// -// var a string = "Hello" -// var b string = "Hello" -// -// assert.Equal(t, a, b, "The two words should be the same.") -// -// } -// -// if you assert many times, use the format below: -// -// import ( -// "testing" -// "github.com/stretchr/testify/assert" -// ) -// -// func TestSomething(t *testing.T) { -// assert := assert.New(t) -// -// var a string = "Hello" -// var b string = "Hello" -// -// assert.Equal(a, b, "The two words should be the same.") -// } -// -// Assertions -// -// Assertions allow you to easily write test code, and are global funcs in the `assert` package. -// All assertion functions take, as the first argument, the `*testing.T` object provided by the -// testing framework. This allows the assertion funcs to write the failings and other details to -// the correct place. -// -// Every assertion function also takes an optional string message as the final argument, -// allowing custom error messages to be appended to the message the assertion method outputs. -package assert diff --git a/components/cli/vendor/github.com/stretchr/testify/assert/errors.go b/components/cli/vendor/github.com/stretchr/testify/assert/errors.go deleted file mode 100644 index ac9dc9d1d6..0000000000 --- a/components/cli/vendor/github.com/stretchr/testify/assert/errors.go +++ /dev/null @@ -1,10 +0,0 @@ -package assert - -import ( - "errors" -) - -// AnError is an error instance useful for testing. If the code does not care -// about error specifics, and only needs to return the error for example, this -// error should be used to make the test code more readable. -var AnError = errors.New("assert.AnError general error for testing") diff --git a/components/cli/vendor/github.com/stretchr/testify/assert/forward_assertions.go b/components/cli/vendor/github.com/stretchr/testify/assert/forward_assertions.go deleted file mode 100644 index b867e95ea5..0000000000 --- a/components/cli/vendor/github.com/stretchr/testify/assert/forward_assertions.go +++ /dev/null @@ -1,16 +0,0 @@ -package assert - -// Assertions provides assertion methods around the -// TestingT interface. -type Assertions struct { - t TestingT -} - -// New makes a new Assertions object for the specified TestingT. -func New(t TestingT) *Assertions { - return &Assertions{ - t: t, - } -} - -//go:generate go run ../_codegen/main.go -output-package=assert -template=assertion_forward.go.tmpl diff --git a/components/cli/vendor/github.com/stretchr/testify/assert/http_assertions.go b/components/cli/vendor/github.com/stretchr/testify/assert/http_assertions.go deleted file mode 100644 index fa7ab89b18..0000000000 --- a/components/cli/vendor/github.com/stretchr/testify/assert/http_assertions.go +++ /dev/null @@ -1,106 +0,0 @@ -package assert - -import ( - "fmt" - "net/http" - "net/http/httptest" - "net/url" - "strings" -) - -// httpCode is a helper that returns HTTP code of the response. It returns -1 -// if building a new request fails. -func httpCode(handler http.HandlerFunc, method, url string, values url.Values) int { - w := httptest.NewRecorder() - req, err := http.NewRequest(method, url+"?"+values.Encode(), nil) - if err != nil { - return -1 - } - handler(w, req) - return w.Code -} - -// HTTPSuccess asserts that a specified handler returns a success status code. -// -// assert.HTTPSuccess(t, myHandler, "POST", "http://www.google.com", nil) -// -// Returns whether the assertion was successful (true) or not (false). -func HTTPSuccess(t TestingT, handler http.HandlerFunc, method, url string, values url.Values) bool { - code := httpCode(handler, method, url, values) - if code == -1 { - return false - } - return code >= http.StatusOK && code <= http.StatusPartialContent -} - -// HTTPRedirect asserts that a specified handler returns a redirect status code. -// -// assert.HTTPRedirect(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} -// -// Returns whether the assertion was successful (true) or not (false). -func HTTPRedirect(t TestingT, handler http.HandlerFunc, method, url string, values url.Values) bool { - code := httpCode(handler, method, url, values) - if code == -1 { - return false - } - return code >= http.StatusMultipleChoices && code <= http.StatusTemporaryRedirect -} - -// HTTPError asserts that a specified handler returns an error status code. -// -// assert.HTTPError(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} -// -// Returns whether the assertion was successful (true) or not (false). -func HTTPError(t TestingT, handler http.HandlerFunc, method, url string, values url.Values) bool { - code := httpCode(handler, method, url, values) - if code == -1 { - return false - } - return code >= http.StatusBadRequest -} - -// HTTPBody is a helper that returns HTTP body of the response. It returns -// empty string if building a new request fails. -func HTTPBody(handler http.HandlerFunc, method, url string, values url.Values) string { - w := httptest.NewRecorder() - req, err := http.NewRequest(method, url+"?"+values.Encode(), nil) - if err != nil { - return "" - } - handler(w, req) - return w.Body.String() -} - -// HTTPBodyContains asserts that a specified handler returns a -// body that contains a string. -// -// assert.HTTPBodyContains(t, myHandler, "www.google.com", nil, "I'm Feeling Lucky") -// -// Returns whether the assertion was successful (true) or not (false). -func HTTPBodyContains(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, str interface{}) bool { - body := HTTPBody(handler, method, url, values) - - contains := strings.Contains(body, fmt.Sprint(str)) - if !contains { - Fail(t, fmt.Sprintf("Expected response body for \"%s\" to contain \"%s\" but found \"%s\"", url+"?"+values.Encode(), str, body)) - } - - return contains -} - -// HTTPBodyNotContains asserts that a specified handler returns a -// body that does not contain a string. -// -// assert.HTTPBodyNotContains(t, myHandler, "www.google.com", nil, "I'm Feeling Lucky") -// -// Returns whether the assertion was successful (true) or not (false). -func HTTPBodyNotContains(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, str interface{}) bool { - body := HTTPBody(handler, method, url, values) - - contains := strings.Contains(body, fmt.Sprint(str)) - if contains { - Fail(t, fmt.Sprintf("Expected response body for \"%s\" to NOT contain \"%s\" but found \"%s\"", url+"?"+values.Encode(), str, body)) - } - - return !contains -} diff --git a/components/cli/vendor/github.com/stretchr/testify/require/doc.go b/components/cli/vendor/github.com/stretchr/testify/require/doc.go deleted file mode 100644 index 169de39221..0000000000 --- a/components/cli/vendor/github.com/stretchr/testify/require/doc.go +++ /dev/null @@ -1,28 +0,0 @@ -// Package require implements the same assertions as the `assert` package but -// stops test execution when a test fails. -// -// Example Usage -// -// The following is a complete example using require in a standard test function: -// import ( -// "testing" -// "github.com/stretchr/testify/require" -// ) -// -// func TestSomething(t *testing.T) { -// -// var a string = "Hello" -// var b string = "Hello" -// -// require.Equal(t, a, b, "The two words should be the same.") -// -// } -// -// Assertions -// -// The `require` package have same global functions as in the `assert` package, -// but instead of returning a boolean result they call `t.FailNow()`. -// -// Every assertion function also takes an optional string message as the final argument, -// allowing custom error messages to be appended to the message the assertion method outputs. -package require diff --git a/components/cli/vendor/github.com/stretchr/testify/require/forward_requirements.go b/components/cli/vendor/github.com/stretchr/testify/require/forward_requirements.go deleted file mode 100644 index d3c2ab9bc7..0000000000 --- a/components/cli/vendor/github.com/stretchr/testify/require/forward_requirements.go +++ /dev/null @@ -1,16 +0,0 @@ -package require - -// Assertions provides assertion methods around the -// TestingT interface. -type Assertions struct { - t TestingT -} - -// New makes a new Assertions object for the specified TestingT. -func New(t TestingT) *Assertions { - return &Assertions{ - t: t, - } -} - -//go:generate go run ../_codegen/main.go -output-package=require -template=require_forward.go.tmpl diff --git a/components/cli/vendor/github.com/stretchr/testify/require/require.go b/components/cli/vendor/github.com/stretchr/testify/require/require.go deleted file mode 100644 index fc567f140a..0000000000 --- a/components/cli/vendor/github.com/stretchr/testify/require/require.go +++ /dev/null @@ -1,429 +0,0 @@ -/* -* CODE GENERATED AUTOMATICALLY WITH github.com/stretchr/testify/_codegen -* THIS FILE MUST NOT BE EDITED BY HAND - */ - -package require - -import ( - assert "github.com/stretchr/testify/assert" - http "net/http" - url "net/url" - time "time" -) - -// Condition uses a Comparison to assert a complex condition. -func Condition(t TestingT, comp assert.Comparison, msgAndArgs ...interface{}) { - if !assert.Condition(t, comp, msgAndArgs...) { - t.FailNow() - } -} - -// Contains asserts that the specified string, list(array, slice...) or map contains the -// specified substring or element. -// -// assert.Contains(t, "Hello World", "World", "But 'Hello World' does contain 'World'") -// assert.Contains(t, ["Hello", "World"], "World", "But ["Hello", "World"] does contain 'World'") -// assert.Contains(t, {"Hello": "World"}, "Hello", "But {'Hello': 'World'} does contain 'Hello'") -// -// Returns whether the assertion was successful (true) or not (false). -func Contains(t TestingT, s interface{}, contains interface{}, msgAndArgs ...interface{}) { - if !assert.Contains(t, s, contains, msgAndArgs...) { - t.FailNow() - } -} - -// Empty asserts that the specified object is empty. I.e. nil, "", false, 0 or either -// a slice or a channel with len == 0. -// -// assert.Empty(t, obj) -// -// Returns whether the assertion was successful (true) or not (false). -func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) { - if !assert.Empty(t, object, msgAndArgs...) { - t.FailNow() - } -} - -// Equal asserts that two objects are equal. -// -// assert.Equal(t, 123, 123, "123 and 123 should be equal") -// -// Returns whether the assertion was successful (true) or not (false). -// -// Pointer variable equality is determined based on the equality of the -// referenced values (as opposed to the memory addresses). -func Equal(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) { - if !assert.Equal(t, expected, actual, msgAndArgs...) { - t.FailNow() - } -} - -// EqualError asserts that a function returned an error (i.e. not `nil`) -// and that it is equal to the provided error. -// -// actualObj, err := SomeFunction() -// assert.EqualError(t, err, expectedErrorString, "An error was expected") -// -// Returns whether the assertion was successful (true) or not (false). -func EqualError(t TestingT, theError error, errString string, msgAndArgs ...interface{}) { - if !assert.EqualError(t, theError, errString, msgAndArgs...) { - t.FailNow() - } -} - -// EqualValues asserts that two objects are equal or convertable to the same types -// and equal. -// -// assert.EqualValues(t, uint32(123), int32(123), "123 and 123 should be equal") -// -// Returns whether the assertion was successful (true) or not (false). -func EqualValues(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) { - if !assert.EqualValues(t, expected, actual, msgAndArgs...) { - t.FailNow() - } -} - -// Error asserts that a function returned an error (i.e. not `nil`). -// -// actualObj, err := SomeFunction() -// if assert.Error(t, err, "An error was expected") { -// assert.Equal(t, err, expectedError) -// } -// -// Returns whether the assertion was successful (true) or not (false). -func Error(t TestingT, err error, msgAndArgs ...interface{}) { - if !assert.Error(t, err, msgAndArgs...) { - t.FailNow() - } -} - -// Exactly asserts that two objects are equal is value and type. -// -// assert.Exactly(t, int32(123), int64(123), "123 and 123 should NOT be equal") -// -// Returns whether the assertion was successful (true) or not (false). -func Exactly(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) { - if !assert.Exactly(t, expected, actual, msgAndArgs...) { - t.FailNow() - } -} - -// Fail reports a failure through -func Fail(t TestingT, failureMessage string, msgAndArgs ...interface{}) { - if !assert.Fail(t, failureMessage, msgAndArgs...) { - t.FailNow() - } -} - -// FailNow fails test -func FailNow(t TestingT, failureMessage string, msgAndArgs ...interface{}) { - if !assert.FailNow(t, failureMessage, msgAndArgs...) { - t.FailNow() - } -} - -// False asserts that the specified value is false. -// -// assert.False(t, myBool, "myBool should be false") -// -// Returns whether the assertion was successful (true) or not (false). -func False(t TestingT, value bool, msgAndArgs ...interface{}) { - if !assert.False(t, value, msgAndArgs...) { - t.FailNow() - } -} - -// HTTPBodyContains asserts that a specified handler returns a -// body that contains a string. -// -// assert.HTTPBodyContains(t, myHandler, "www.google.com", nil, "I'm Feeling Lucky") -// -// Returns whether the assertion was successful (true) or not (false). -func HTTPBodyContains(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}) { - if !assert.HTTPBodyContains(t, handler, method, url, values, str) { - t.FailNow() - } -} - -// HTTPBodyNotContains asserts that a specified handler returns a -// body that does not contain a string. -// -// assert.HTTPBodyNotContains(t, myHandler, "www.google.com", nil, "I'm Feeling Lucky") -// -// Returns whether the assertion was successful (true) or not (false). -func HTTPBodyNotContains(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}) { - if !assert.HTTPBodyNotContains(t, handler, method, url, values, str) { - t.FailNow() - } -} - -// HTTPError asserts that a specified handler returns an error status code. -// -// assert.HTTPError(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} -// -// Returns whether the assertion was successful (true) or not (false). -func HTTPError(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values) { - if !assert.HTTPError(t, handler, method, url, values) { - t.FailNow() - } -} - -// HTTPRedirect asserts that a specified handler returns a redirect status code. -// -// assert.HTTPRedirect(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} -// -// Returns whether the assertion was successful (true) or not (false). -func HTTPRedirect(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values) { - if !assert.HTTPRedirect(t, handler, method, url, values) { - t.FailNow() - } -} - -// HTTPSuccess asserts that a specified handler returns a success status code. -// -// assert.HTTPSuccess(t, myHandler, "POST", "http://www.google.com", nil) -// -// Returns whether the assertion was successful (true) or not (false). -func HTTPSuccess(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values) { - if !assert.HTTPSuccess(t, handler, method, url, values) { - t.FailNow() - } -} - -// Implements asserts that an object is implemented by the specified interface. -// -// assert.Implements(t, (*MyInterface)(nil), new(MyObject), "MyObject") -func Implements(t TestingT, interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) { - if !assert.Implements(t, interfaceObject, object, msgAndArgs...) { - t.FailNow() - } -} - -// InDelta asserts that the two numerals are within delta of each other. -// -// assert.InDelta(t, math.Pi, (22 / 7.0), 0.01) -// -// Returns whether the assertion was successful (true) or not (false). -func InDelta(t TestingT, expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) { - if !assert.InDelta(t, expected, actual, delta, msgAndArgs...) { - t.FailNow() - } -} - -// InDeltaSlice is the same as InDelta, except it compares two slices. -func InDeltaSlice(t TestingT, expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) { - if !assert.InDeltaSlice(t, expected, actual, delta, msgAndArgs...) { - t.FailNow() - } -} - -// InEpsilon asserts that expected and actual have a relative error less than epsilon -// -// Returns whether the assertion was successful (true) or not (false). -func InEpsilon(t TestingT, expected interface{}, actual interface{}, epsilon float64, msgAndArgs ...interface{}) { - if !assert.InEpsilon(t, expected, actual, epsilon, msgAndArgs...) { - t.FailNow() - } -} - -// InEpsilonSlice is the same as InEpsilon, except it compares each value from two slices. -func InEpsilonSlice(t TestingT, expected interface{}, actual interface{}, epsilon float64, msgAndArgs ...interface{}) { - if !assert.InEpsilonSlice(t, expected, actual, epsilon, msgAndArgs...) { - t.FailNow() - } -} - -// IsType asserts that the specified objects are of the same type. -func IsType(t TestingT, expectedType interface{}, object interface{}, msgAndArgs ...interface{}) { - if !assert.IsType(t, expectedType, object, msgAndArgs...) { - t.FailNow() - } -} - -// JSONEq asserts that two JSON strings are equivalent. -// -// assert.JSONEq(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) -// -// Returns whether the assertion was successful (true) or not (false). -func JSONEq(t TestingT, expected string, actual string, msgAndArgs ...interface{}) { - if !assert.JSONEq(t, expected, actual, msgAndArgs...) { - t.FailNow() - } -} - -// Len asserts that the specified object has specific length. -// Len also fails if the object has a type that len() not accept. -// -// assert.Len(t, mySlice, 3, "The size of slice is not 3") -// -// Returns whether the assertion was successful (true) or not (false). -func Len(t TestingT, object interface{}, length int, msgAndArgs ...interface{}) { - if !assert.Len(t, object, length, msgAndArgs...) { - t.FailNow() - } -} - -// Nil asserts that the specified object is nil. -// -// assert.Nil(t, err, "err should be nothing") -// -// Returns whether the assertion was successful (true) or not (false). -func Nil(t TestingT, object interface{}, msgAndArgs ...interface{}) { - if !assert.Nil(t, object, msgAndArgs...) { - t.FailNow() - } -} - -// NoError asserts that a function returned no error (i.e. `nil`). -// -// actualObj, err := SomeFunction() -// if assert.NoError(t, err) { -// assert.Equal(t, actualObj, expectedObj) -// } -// -// Returns whether the assertion was successful (true) or not (false). -func NoError(t TestingT, err error, msgAndArgs ...interface{}) { - if !assert.NoError(t, err, msgAndArgs...) { - t.FailNow() - } -} - -// NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the -// specified substring or element. -// -// assert.NotContains(t, "Hello World", "Earth", "But 'Hello World' does NOT contain 'Earth'") -// assert.NotContains(t, ["Hello", "World"], "Earth", "But ['Hello', 'World'] does NOT contain 'Earth'") -// assert.NotContains(t, {"Hello": "World"}, "Earth", "But {'Hello': 'World'} does NOT contain 'Earth'") -// -// Returns whether the assertion was successful (true) or not (false). -func NotContains(t TestingT, s interface{}, contains interface{}, msgAndArgs ...interface{}) { - if !assert.NotContains(t, s, contains, msgAndArgs...) { - t.FailNow() - } -} - -// NotEmpty asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either -// a slice or a channel with len == 0. -// -// if assert.NotEmpty(t, obj) { -// assert.Equal(t, "two", obj[1]) -// } -// -// Returns whether the assertion was successful (true) or not (false). -func NotEmpty(t TestingT, object interface{}, msgAndArgs ...interface{}) { - if !assert.NotEmpty(t, object, msgAndArgs...) { - t.FailNow() - } -} - -// NotEqual asserts that the specified values are NOT equal. -// -// assert.NotEqual(t, obj1, obj2, "two objects shouldn't be equal") -// -// Returns whether the assertion was successful (true) or not (false). -// -// Pointer variable equality is determined based on the equality of the -// referenced values (as opposed to the memory addresses). -func NotEqual(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) { - if !assert.NotEqual(t, expected, actual, msgAndArgs...) { - t.FailNow() - } -} - -// NotNil asserts that the specified object is not nil. -// -// assert.NotNil(t, err, "err should be something") -// -// Returns whether the assertion was successful (true) or not (false). -func NotNil(t TestingT, object interface{}, msgAndArgs ...interface{}) { - if !assert.NotNil(t, object, msgAndArgs...) { - t.FailNow() - } -} - -// NotPanics asserts that the code inside the specified PanicTestFunc does NOT panic. -// -// assert.NotPanics(t, func(){ -// RemainCalm() -// }, "Calling RemainCalm() should NOT panic") -// -// Returns whether the assertion was successful (true) or not (false). -func NotPanics(t TestingT, f assert.PanicTestFunc, msgAndArgs ...interface{}) { - if !assert.NotPanics(t, f, msgAndArgs...) { - t.FailNow() - } -} - -// NotRegexp asserts that a specified regexp does not match a string. -// -// assert.NotRegexp(t, regexp.MustCompile("starts"), "it's starting") -// assert.NotRegexp(t, "^start", "it's not starting") -// -// Returns whether the assertion was successful (true) or not (false). -func NotRegexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface{}) { - if !assert.NotRegexp(t, rx, str, msgAndArgs...) { - t.FailNow() - } -} - -// NotZero asserts that i is not the zero value for its type and returns the truth. -func NotZero(t TestingT, i interface{}, msgAndArgs ...interface{}) { - if !assert.NotZero(t, i, msgAndArgs...) { - t.FailNow() - } -} - -// Panics asserts that the code inside the specified PanicTestFunc panics. -// -// assert.Panics(t, func(){ -// GoCrazy() -// }, "Calling GoCrazy() should panic") -// -// Returns whether the assertion was successful (true) or not (false). -func Panics(t TestingT, f assert.PanicTestFunc, msgAndArgs ...interface{}) { - if !assert.Panics(t, f, msgAndArgs...) { - t.FailNow() - } -} - -// Regexp asserts that a specified regexp matches a string. -// -// assert.Regexp(t, regexp.MustCompile("start"), "it's starting") -// assert.Regexp(t, "start...$", "it's not starting") -// -// Returns whether the assertion was successful (true) or not (false). -func Regexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface{}) { - if !assert.Regexp(t, rx, str, msgAndArgs...) { - t.FailNow() - } -} - -// True asserts that the specified value is true. -// -// assert.True(t, myBool, "myBool should be true") -// -// Returns whether the assertion was successful (true) or not (false). -func True(t TestingT, value bool, msgAndArgs ...interface{}) { - if !assert.True(t, value, msgAndArgs...) { - t.FailNow() - } -} - -// WithinDuration asserts that the two times are within duration delta of each other. -// -// assert.WithinDuration(t, time.Now(), time.Now(), 10*time.Second, "The difference should not be more than 10s") -// -// Returns whether the assertion was successful (true) or not (false). -func WithinDuration(t TestingT, expected time.Time, actual time.Time, delta time.Duration, msgAndArgs ...interface{}) { - if !assert.WithinDuration(t, expected, actual, delta, msgAndArgs...) { - t.FailNow() - } -} - -// Zero asserts that i is the zero value for its type and returns the truth. -func Zero(t TestingT, i interface{}, msgAndArgs ...interface{}) { - if !assert.Zero(t, i, msgAndArgs...) { - t.FailNow() - } -} diff --git a/components/cli/vendor/github.com/stretchr/testify/require/require_forward.go b/components/cli/vendor/github.com/stretchr/testify/require/require_forward.go deleted file mode 100644 index caa18793df..0000000000 --- a/components/cli/vendor/github.com/stretchr/testify/require/require_forward.go +++ /dev/null @@ -1,353 +0,0 @@ -/* -* CODE GENERATED AUTOMATICALLY WITH github.com/stretchr/testify/_codegen -* THIS FILE MUST NOT BE EDITED BY HAND - */ - -package require - -import ( - assert "github.com/stretchr/testify/assert" - http "net/http" - url "net/url" - time "time" -) - -// Condition uses a Comparison to assert a complex condition. -func (a *Assertions) Condition(comp assert.Comparison, msgAndArgs ...interface{}) { - Condition(a.t, comp, msgAndArgs...) -} - -// Contains asserts that the specified string, list(array, slice...) or map contains the -// specified substring or element. -// -// a.Contains("Hello World", "World", "But 'Hello World' does contain 'World'") -// a.Contains(["Hello", "World"], "World", "But ["Hello", "World"] does contain 'World'") -// a.Contains({"Hello": "World"}, "Hello", "But {'Hello': 'World'} does contain 'Hello'") -// -// Returns whether the assertion was successful (true) or not (false). -func (a *Assertions) Contains(s interface{}, contains interface{}, msgAndArgs ...interface{}) { - Contains(a.t, s, contains, msgAndArgs...) -} - -// Empty asserts that the specified object is empty. I.e. nil, "", false, 0 or either -// a slice or a channel with len == 0. -// -// a.Empty(obj) -// -// Returns whether the assertion was successful (true) or not (false). -func (a *Assertions) Empty(object interface{}, msgAndArgs ...interface{}) { - Empty(a.t, object, msgAndArgs...) -} - -// Equal asserts that two objects are equal. -// -// a.Equal(123, 123, "123 and 123 should be equal") -// -// Returns whether the assertion was successful (true) or not (false). -// -// Pointer variable equality is determined based on the equality of the -// referenced values (as opposed to the memory addresses). -func (a *Assertions) Equal(expected interface{}, actual interface{}, msgAndArgs ...interface{}) { - Equal(a.t, expected, actual, msgAndArgs...) -} - -// EqualError asserts that a function returned an error (i.e. not `nil`) -// and that it is equal to the provided error. -// -// actualObj, err := SomeFunction() -// a.EqualError(err, expectedErrorString, "An error was expected") -// -// Returns whether the assertion was successful (true) or not (false). -func (a *Assertions) EqualError(theError error, errString string, msgAndArgs ...interface{}) { - EqualError(a.t, theError, errString, msgAndArgs...) -} - -// EqualValues asserts that two objects are equal or convertable to the same types -// and equal. -// -// a.EqualValues(uint32(123), int32(123), "123 and 123 should be equal") -// -// Returns whether the assertion was successful (true) or not (false). -func (a *Assertions) EqualValues(expected interface{}, actual interface{}, msgAndArgs ...interface{}) { - EqualValues(a.t, expected, actual, msgAndArgs...) -} - -// Error asserts that a function returned an error (i.e. not `nil`). -// -// actualObj, err := SomeFunction() -// if a.Error(err, "An error was expected") { -// assert.Equal(t, err, expectedError) -// } -// -// Returns whether the assertion was successful (true) or not (false). -func (a *Assertions) Error(err error, msgAndArgs ...interface{}) { - Error(a.t, err, msgAndArgs...) -} - -// Exactly asserts that two objects are equal is value and type. -// -// a.Exactly(int32(123), int64(123), "123 and 123 should NOT be equal") -// -// Returns whether the assertion was successful (true) or not (false). -func (a *Assertions) Exactly(expected interface{}, actual interface{}, msgAndArgs ...interface{}) { - Exactly(a.t, expected, actual, msgAndArgs...) -} - -// Fail reports a failure through -func (a *Assertions) Fail(failureMessage string, msgAndArgs ...interface{}) { - Fail(a.t, failureMessage, msgAndArgs...) -} - -// FailNow fails test -func (a *Assertions) FailNow(failureMessage string, msgAndArgs ...interface{}) { - FailNow(a.t, failureMessage, msgAndArgs...) -} - -// False asserts that the specified value is false. -// -// a.False(myBool, "myBool should be false") -// -// Returns whether the assertion was successful (true) or not (false). -func (a *Assertions) False(value bool, msgAndArgs ...interface{}) { - False(a.t, value, msgAndArgs...) -} - -// HTTPBodyContains asserts that a specified handler returns a -// body that contains a string. -// -// a.HTTPBodyContains(myHandler, "www.google.com", nil, "I'm Feeling Lucky") -// -// Returns whether the assertion was successful (true) or not (false). -func (a *Assertions) HTTPBodyContains(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}) { - HTTPBodyContains(a.t, handler, method, url, values, str) -} - -// HTTPBodyNotContains asserts that a specified handler returns a -// body that does not contain a string. -// -// a.HTTPBodyNotContains(myHandler, "www.google.com", nil, "I'm Feeling Lucky") -// -// Returns whether the assertion was successful (true) or not (false). -func (a *Assertions) HTTPBodyNotContains(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}) { - HTTPBodyNotContains(a.t, handler, method, url, values, str) -} - -// HTTPError asserts that a specified handler returns an error status code. -// -// a.HTTPError(myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} -// -// Returns whether the assertion was successful (true) or not (false). -func (a *Assertions) HTTPError(handler http.HandlerFunc, method string, url string, values url.Values) { - HTTPError(a.t, handler, method, url, values) -} - -// HTTPRedirect asserts that a specified handler returns a redirect status code. -// -// a.HTTPRedirect(myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} -// -// Returns whether the assertion was successful (true) or not (false). -func (a *Assertions) HTTPRedirect(handler http.HandlerFunc, method string, url string, values url.Values) { - HTTPRedirect(a.t, handler, method, url, values) -} - -// HTTPSuccess asserts that a specified handler returns a success status code. -// -// a.HTTPSuccess(myHandler, "POST", "http://www.google.com", nil) -// -// Returns whether the assertion was successful (true) or not (false). -func (a *Assertions) HTTPSuccess(handler http.HandlerFunc, method string, url string, values url.Values) { - HTTPSuccess(a.t, handler, method, url, values) -} - -// Implements asserts that an object is implemented by the specified interface. -// -// a.Implements((*MyInterface)(nil), new(MyObject), "MyObject") -func (a *Assertions) Implements(interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) { - Implements(a.t, interfaceObject, object, msgAndArgs...) -} - -// InDelta asserts that the two numerals are within delta of each other. -// -// a.InDelta(math.Pi, (22 / 7.0), 0.01) -// -// Returns whether the assertion was successful (true) or not (false). -func (a *Assertions) InDelta(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) { - InDelta(a.t, expected, actual, delta, msgAndArgs...) -} - -// InDeltaSlice is the same as InDelta, except it compares two slices. -func (a *Assertions) InDeltaSlice(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) { - InDeltaSlice(a.t, expected, actual, delta, msgAndArgs...) -} - -// InEpsilon asserts that expected and actual have a relative error less than epsilon -// -// Returns whether the assertion was successful (true) or not (false). -func (a *Assertions) InEpsilon(expected interface{}, actual interface{}, epsilon float64, msgAndArgs ...interface{}) { - InEpsilon(a.t, expected, actual, epsilon, msgAndArgs...) -} - -// InEpsilonSlice is the same as InEpsilon, except it compares each value from two slices. -func (a *Assertions) InEpsilonSlice(expected interface{}, actual interface{}, epsilon float64, msgAndArgs ...interface{}) { - InEpsilonSlice(a.t, expected, actual, epsilon, msgAndArgs...) -} - -// IsType asserts that the specified objects are of the same type. -func (a *Assertions) IsType(expectedType interface{}, object interface{}, msgAndArgs ...interface{}) { - IsType(a.t, expectedType, object, msgAndArgs...) -} - -// JSONEq asserts that two JSON strings are equivalent. -// -// a.JSONEq(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) -// -// Returns whether the assertion was successful (true) or not (false). -func (a *Assertions) JSONEq(expected string, actual string, msgAndArgs ...interface{}) { - JSONEq(a.t, expected, actual, msgAndArgs...) -} - -// Len asserts that the specified object has specific length. -// Len also fails if the object has a type that len() not accept. -// -// a.Len(mySlice, 3, "The size of slice is not 3") -// -// Returns whether the assertion was successful (true) or not (false). -func (a *Assertions) Len(object interface{}, length int, msgAndArgs ...interface{}) { - Len(a.t, object, length, msgAndArgs...) -} - -// Nil asserts that the specified object is nil. -// -// a.Nil(err, "err should be nothing") -// -// Returns whether the assertion was successful (true) or not (false). -func (a *Assertions) Nil(object interface{}, msgAndArgs ...interface{}) { - Nil(a.t, object, msgAndArgs...) -} - -// NoError asserts that a function returned no error (i.e. `nil`). -// -// actualObj, err := SomeFunction() -// if a.NoError(err) { -// assert.Equal(t, actualObj, expectedObj) -// } -// -// Returns whether the assertion was successful (true) or not (false). -func (a *Assertions) NoError(err error, msgAndArgs ...interface{}) { - NoError(a.t, err, msgAndArgs...) -} - -// NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the -// specified substring or element. -// -// a.NotContains("Hello World", "Earth", "But 'Hello World' does NOT contain 'Earth'") -// a.NotContains(["Hello", "World"], "Earth", "But ['Hello', 'World'] does NOT contain 'Earth'") -// a.NotContains({"Hello": "World"}, "Earth", "But {'Hello': 'World'} does NOT contain 'Earth'") -// -// Returns whether the assertion was successful (true) or not (false). -func (a *Assertions) NotContains(s interface{}, contains interface{}, msgAndArgs ...interface{}) { - NotContains(a.t, s, contains, msgAndArgs...) -} - -// NotEmpty asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either -// a slice or a channel with len == 0. -// -// if a.NotEmpty(obj) { -// assert.Equal(t, "two", obj[1]) -// } -// -// Returns whether the assertion was successful (true) or not (false). -func (a *Assertions) NotEmpty(object interface{}, msgAndArgs ...interface{}) { - NotEmpty(a.t, object, msgAndArgs...) -} - -// NotEqual asserts that the specified values are NOT equal. -// -// a.NotEqual(obj1, obj2, "two objects shouldn't be equal") -// -// Returns whether the assertion was successful (true) or not (false). -// -// Pointer variable equality is determined based on the equality of the -// referenced values (as opposed to the memory addresses). -func (a *Assertions) NotEqual(expected interface{}, actual interface{}, msgAndArgs ...interface{}) { - NotEqual(a.t, expected, actual, msgAndArgs...) -} - -// NotNil asserts that the specified object is not nil. -// -// a.NotNil(err, "err should be something") -// -// Returns whether the assertion was successful (true) or not (false). -func (a *Assertions) NotNil(object interface{}, msgAndArgs ...interface{}) { - NotNil(a.t, object, msgAndArgs...) -} - -// NotPanics asserts that the code inside the specified PanicTestFunc does NOT panic. -// -// a.NotPanics(func(){ -// RemainCalm() -// }, "Calling RemainCalm() should NOT panic") -// -// Returns whether the assertion was successful (true) or not (false). -func (a *Assertions) NotPanics(f assert.PanicTestFunc, msgAndArgs ...interface{}) { - NotPanics(a.t, f, msgAndArgs...) -} - -// NotRegexp asserts that a specified regexp does not match a string. -// -// a.NotRegexp(regexp.MustCompile("starts"), "it's starting") -// a.NotRegexp("^start", "it's not starting") -// -// Returns whether the assertion was successful (true) or not (false). -func (a *Assertions) NotRegexp(rx interface{}, str interface{}, msgAndArgs ...interface{}) { - NotRegexp(a.t, rx, str, msgAndArgs...) -} - -// NotZero asserts that i is not the zero value for its type and returns the truth. -func (a *Assertions) NotZero(i interface{}, msgAndArgs ...interface{}) { - NotZero(a.t, i, msgAndArgs...) -} - -// Panics asserts that the code inside the specified PanicTestFunc panics. -// -// a.Panics(func(){ -// GoCrazy() -// }, "Calling GoCrazy() should panic") -// -// Returns whether the assertion was successful (true) or not (false). -func (a *Assertions) Panics(f assert.PanicTestFunc, msgAndArgs ...interface{}) { - Panics(a.t, f, msgAndArgs...) -} - -// Regexp asserts that a specified regexp matches a string. -// -// a.Regexp(regexp.MustCompile("start"), "it's starting") -// a.Regexp("start...$", "it's not starting") -// -// Returns whether the assertion was successful (true) or not (false). -func (a *Assertions) Regexp(rx interface{}, str interface{}, msgAndArgs ...interface{}) { - Regexp(a.t, rx, str, msgAndArgs...) -} - -// True asserts that the specified value is true. -// -// a.True(myBool, "myBool should be true") -// -// Returns whether the assertion was successful (true) or not (false). -func (a *Assertions) True(value bool, msgAndArgs ...interface{}) { - True(a.t, value, msgAndArgs...) -} - -// WithinDuration asserts that the two times are within duration delta of each other. -// -// a.WithinDuration(time.Now(), time.Now(), 10*time.Second, "The difference should not be more than 10s") -// -// Returns whether the assertion was successful (true) or not (false). -func (a *Assertions) WithinDuration(expected time.Time, actual time.Time, delta time.Duration, msgAndArgs ...interface{}) { - WithinDuration(a.t, expected, actual, delta, msgAndArgs...) -} - -// Zero asserts that i is the zero value for its type and returns the truth. -func (a *Assertions) Zero(i interface{}, msgAndArgs ...interface{}) { - Zero(a.t, i, msgAndArgs...) -} diff --git a/components/cli/vendor/github.com/stretchr/testify/require/requirements.go b/components/cli/vendor/github.com/stretchr/testify/require/requirements.go deleted file mode 100644 index 41147562d8..0000000000 --- a/components/cli/vendor/github.com/stretchr/testify/require/requirements.go +++ /dev/null @@ -1,9 +0,0 @@ -package require - -// TestingT is an interface wrapper around *testing.T -type TestingT interface { - Errorf(format string, args ...interface{}) - FailNow() -} - -//go:generate go run ../_codegen/main.go -output-package=require -template=require.go.tmpl diff --git a/components/engine/integration-cli/check_test.go b/components/engine/integration-cli/check_test.go index dd802fbc11..8df93dc2f6 100644 --- a/components/engine/integration-cli/check_test.go +++ b/components/engine/integration-cli/check_test.go @@ -14,7 +14,6 @@ import ( "time" "github.com/docker/docker/api/types/swarm" - "github.com/docker/docker/cli/config" "github.com/docker/docker/integration-cli/checker" "github.com/docker/docker/integration-cli/cli" "github.com/docker/docker/integration-cli/cli/build/fakestorage" @@ -386,74 +385,6 @@ func (s *DockerSwarmSuite) TearDownTest(c *check.C) { s.ds.TearDownTest(c) } -func init() { - check.Suite(&DockerTrustSuite{ - ds: &DockerSuite{}, - }) -} - -type DockerTrustSuite struct { - ds *DockerSuite - reg *registry.V2 - not *testNotary -} - -func (s *DockerTrustSuite) OnTimeout(c *check.C) { - s.ds.OnTimeout(c) -} - -func (s *DockerTrustSuite) SetUpTest(c *check.C) { - testRequires(c, registry.Hosting, NotaryServerHosting) - s.reg = setupRegistry(c, false, "", "") - s.not = setupNotary(c) -} - -func (s *DockerTrustSuite) TearDownTest(c *check.C) { - if s.reg != nil { - s.reg.Close() - } - if s.not != nil { - s.not.Close() - } - - // Remove trusted keys and metadata after test - os.RemoveAll(filepath.Join(config.Dir(), "trust")) - s.ds.TearDownTest(c) -} - -func init() { - ds := &DockerSuite{} - check.Suite(&DockerTrustedSwarmSuite{ - trustSuite: DockerTrustSuite{ - ds: ds, - }, - swarmSuite: DockerSwarmSuite{ - ds: ds, - }, - }) -} - -type DockerTrustedSwarmSuite struct { - swarmSuite DockerSwarmSuite - trustSuite DockerTrustSuite - reg *registry.V2 - not *testNotary -} - -func (s *DockerTrustedSwarmSuite) SetUpTest(c *check.C) { - s.swarmSuite.SetUpTest(c) - s.trustSuite.SetUpTest(c) -} - -func (s *DockerTrustedSwarmSuite) TearDownTest(c *check.C) { - s.trustSuite.TearDownTest(c) - s.swarmSuite.TearDownTest(c) -} - -func (s *DockerTrustedSwarmSuite) OnTimeout(c *check.C) { - s.swarmSuite.OnTimeout(c) -} - func init() { check.Suite(&DockerPluginSuite{ ds: &DockerSuite{}, diff --git a/components/engine/integration-cli/docker_cli_build_test.go b/components/engine/integration-cli/docker_cli_build_test.go index 335aaed706..65c63abdb6 100644 --- a/components/engine/integration-cli/docker_cli_build_test.go +++ b/components/engine/integration-cli/docker_cli_build_test.go @@ -4048,142 +4048,6 @@ func (s *DockerSuite) TestBuildRUNErrMsg(c *check.C) { }) } -func (s *DockerTrustSuite) TestTrustedBuild(c *check.C) { - repoName := s.setupTrustedImage(c, "trusted-build") - dockerFile := fmt.Sprintf(` - FROM %s - RUN [] - `, repoName) - - name := "testtrustedbuild" - - buildImage(name, trustedBuild, build.WithDockerfile(dockerFile)).Assert(c, icmd.Expected{ - Out: fmt.Sprintf("FROM %s@sha", repoName[:len(repoName)-7]), - }) - - // We should also have a tag reference for the image. - dockerCmd(c, "inspect", repoName) - - // We should now be able to remove the tag reference. - dockerCmd(c, "rmi", repoName) -} - -func (s *DockerTrustSuite) TestTrustedBuildUntrustedTag(c *check.C) { - repoName := fmt.Sprintf("%v/dockercli/build-untrusted-tag:latest", privateRegistryURL) - dockerFile := fmt.Sprintf(` - FROM %s - RUN [] - `, repoName) - - name := "testtrustedbuilduntrustedtag" - - buildImage(name, trustedBuild, build.WithDockerfile(dockerFile)).Assert(c, icmd.Expected{ - ExitCode: 1, - Err: "does not have trust data for", - }) -} - -// FIXME(vdemeester) should migrate to docker/cli e2e tests -func (s *DockerTrustSuite) TestBuildContextDirIsSymlink(c *check.C) { - testRequires(c, DaemonIsLinux) - tempDir, err := ioutil.TempDir("", "test-build-dir-is-symlink-") - c.Assert(err, check.IsNil) - defer os.RemoveAll(tempDir) - - // Make a real context directory in this temp directory with a simple - // Dockerfile. - realContextDirname := filepath.Join(tempDir, "context") - if err := os.Mkdir(realContextDirname, os.FileMode(0755)); err != nil { - c.Fatal(err) - } - - if err = ioutil.WriteFile( - filepath.Join(realContextDirname, "Dockerfile"), - []byte(` - FROM busybox - RUN echo hello world - `), - os.FileMode(0644), - ); err != nil { - c.Fatal(err) - } - - // Make a symlink to the real context directory. - contextSymlinkName := filepath.Join(tempDir, "context_link") - if err := os.Symlink(realContextDirname, contextSymlinkName); err != nil { - c.Fatal(err) - } - - // Executing the build with the symlink as the specified context should - // *not* fail. - dockerCmd(c, "build", contextSymlinkName) -} - -func (s *DockerTrustSuite) TestTrustedBuildTagFromReleasesRole(c *check.C) { - c.Skip("Blacklisting for Docker CE") - testRequires(c, NotaryHosting) - - latestTag := s.setupTrustedImage(c, "trusted-build-releases-role") - repoName := strings.TrimSuffix(latestTag, ":latest") - - // Now create the releases role - s.notaryCreateDelegation(c, repoName, "targets/releases", s.not.keys[0].Public) - s.notaryImportKey(c, repoName, "targets/releases", s.not.keys[0].Private) - s.notaryPublish(c, repoName) - - // push a different tag to the releases role - otherTag := fmt.Sprintf("%s:other", repoName) - cli.DockerCmd(c, "tag", "busybox", otherTag) - - cli.Docker(cli.Args("push", otherTag), trustedCmd).Assert(c, icmd.Success) - s.assertTargetInRoles(c, repoName, "other", "targets/releases") - s.assertTargetNotInRoles(c, repoName, "other", "targets") - - cli.DockerCmd(c, "rmi", otherTag) - - dockerFile := fmt.Sprintf(` - FROM %s - RUN [] - `, otherTag) - name := "testtrustedbuildreleasesrole" - cli.BuildCmd(c, name, trustedCmd, build.WithDockerfile(dockerFile)).Assert(c, icmd.Expected{ - Out: fmt.Sprintf("FROM %s@sha", repoName), - }) -} - -func (s *DockerTrustSuite) TestTrustedBuildTagIgnoresOtherDelegationRoles(c *check.C) { - c.Skip("Blacklisting for Docker CE") - testRequires(c, NotaryHosting) - - latestTag := s.setupTrustedImage(c, "trusted-build-releases-role") - repoName := strings.TrimSuffix(latestTag, ":latest") - - // Now create a non-releases delegation role - s.notaryCreateDelegation(c, repoName, "targets/other", s.not.keys[0].Public) - s.notaryImportKey(c, repoName, "targets/other", s.not.keys[0].Private) - s.notaryPublish(c, repoName) - - // push a different tag to the other role - otherTag := fmt.Sprintf("%s:other", repoName) - cli.DockerCmd(c, "tag", "busybox", otherTag) - - cli.Docker(cli.Args("push", otherTag), trustedCmd).Assert(c, icmd.Success) - s.assertTargetInRoles(c, repoName, "other", "targets/other") - s.assertTargetNotInRoles(c, repoName, "other", "targets") - - cli.DockerCmd(c, "rmi", otherTag) - - dockerFile := fmt.Sprintf(` - FROM %s - RUN [] - `, otherTag) - - name := "testtrustedbuildotherrole" - cli.Docker(cli.Build(name), trustedCmd, build.WithDockerfile(dockerFile)).Assert(c, icmd.Expected{ - ExitCode: 1, - }) -} - // Issue #15634: COPY fails when path starts with "null" func (s *DockerSuite) TestBuildNullStringInAddCopyVolume(c *check.C) { name := "testbuildnullstringinaddcopyvolume" @@ -6020,28 +5884,6 @@ func (s *DockerSuite) TestBuildMultiStageNameVariants(c *check.C) { cli.Docker(cli.Args("run", "build1", "cat", "f2")).Assert(c, icmd.Expected{Out: "bar2"}) } -func (s *DockerTrustSuite) TestBuildMultiStageTrusted(c *check.C) { - img1 := s.setupTrustedImage(c, "trusted-build1") - img2 := s.setupTrustedImage(c, "trusted-build2") - dockerFile := fmt.Sprintf(` - FROM %s AS build-base - RUN echo ok > /foo - FROM %s - COPY --from=build-base foo bar`, img1, img2) - - name := "testcopyfromtrustedbuild" - - r := buildImage(name, trustedBuild, build.WithDockerfile(dockerFile)) - r.Assert(c, icmd.Expected{ - Out: fmt.Sprintf("FROM %s@sha", img1[:len(img1)-7]), - }) - r.Assert(c, icmd.Expected{ - Out: fmt.Sprintf("FROM %s@sha", img2[:len(img2)-7]), - }) - - dockerCmdWithResult("run", name, "cat", "bar").Assert(c, icmd.Expected{Out: "ok"}) -} - func (s *DockerSuite) TestBuildMultiStageMultipleBuildsWindows(c *check.C) { testRequires(c, DaemonIsWindows) dockerfile := ` diff --git a/components/engine/integration-cli/docker_cli_create_test.go b/components/engine/integration-cli/docker_cli_create_test.go index 448af9f199..120b62bc0f 100644 --- a/components/engine/integration-cli/docker_cli_create_test.go +++ b/components/engine/integration-cli/docker_cli_create_test.go @@ -3,7 +3,6 @@ package main import ( "encoding/json" "fmt" - "io/ioutil" "os" "reflect" "strings" @@ -16,7 +15,6 @@ import ( "github.com/docker/docker/pkg/stringid" "github.com/docker/go-connections/nat" "github.com/go-check/check" - "github.com/gotestyourself/gotestyourself/icmd" ) // Make sure we can create a simple container with some args @@ -292,75 +290,6 @@ func (s *DockerSuite) TestCreateByImageID(c *check.C) { } } -func (s *DockerTrustSuite) TestTrustedCreate(c *check.C) { - repoName := s.setupTrustedImage(c, "trusted-create") - - // Try create - cli.Docker(cli.Args("create", repoName), trustedCmd).Assert(c, SuccessTagging) - cli.DockerCmd(c, "rmi", repoName) - - // Try untrusted create to ensure we pushed the tag to the registry - cli.Docker(cli.Args("create", "--disable-content-trust=true", repoName)).Assert(c, SuccessDownloadedOnStderr) -} - -func (s *DockerTrustSuite) TestUntrustedCreate(c *check.C) { - repoName := fmt.Sprintf("%v/dockercliuntrusted/createtest", privateRegistryURL) - withTagName := fmt.Sprintf("%s:latest", repoName) - // tag the image and upload it to the private registry - cli.DockerCmd(c, "tag", "busybox", withTagName) - cli.DockerCmd(c, "push", withTagName) - cli.DockerCmd(c, "rmi", withTagName) - - // Try trusted create on untrusted tag - cli.Docker(cli.Args("create", withTagName), trustedCmd).Assert(c, icmd.Expected{ - ExitCode: 1, - Err: fmt.Sprintf("does not have trust data for %s", repoName), - }) -} - -func (s *DockerTrustSuite) TestTrustedIsolatedCreate(c *check.C) { - repoName := s.setupTrustedImage(c, "trusted-isolated-create") - - // Try create - cli.Docker(cli.Args("--config", "/tmp/docker-isolated-create", "create", repoName), trustedCmd).Assert(c, SuccessTagging) - defer os.RemoveAll("/tmp/docker-isolated-create") - - cli.DockerCmd(c, "rmi", repoName) -} - -func (s *DockerTrustSuite) TestTrustedCreateFromBadTrustServer(c *check.C) { - repoName := fmt.Sprintf("%v/dockerclievilcreate/trusted:latest", privateRegistryURL) - evilLocalConfigDir, err := ioutil.TempDir("", "evilcreate-local-config-dir") - c.Assert(err, check.IsNil) - - // tag the image and upload it to the private registry - cli.DockerCmd(c, "tag", "busybox", repoName) - cli.Docker(cli.Args("push", repoName), trustedCmd).Assert(c, SuccessSigningAndPushing) - cli.DockerCmd(c, "rmi", repoName) - - // Try create - cli.Docker(cli.Args("create", repoName), trustedCmd).Assert(c, SuccessTagging) - cli.DockerCmd(c, "rmi", repoName) - - // Kill the notary server, start a new "evil" one. - s.not.Close() - s.not, err = newTestNotary(c) - c.Assert(err, check.IsNil) - - // In order to make an evil server, lets re-init a client (with a different trust dir) and push new data. - // tag an image and upload it to the private registry - cli.DockerCmd(c, "--config", evilLocalConfigDir, "tag", "busybox", repoName) - - // Push up to the new server - cli.Docker(cli.Args("--config", evilLocalConfigDir, "push", repoName), trustedCmd).Assert(c, SuccessSigningAndPushing) - - // Now, try creating with the original client from this new trust server. This should fail because the new root is invalid. - cli.Docker(cli.Args("create", repoName), trustedCmd).Assert(c, icmd.Expected{ - ExitCode: 1, - Err: "could not rotate trust to a new trusted root", - }) -} - func (s *DockerSuite) TestCreateStopSignal(c *check.C) { name := "test_create_stop_signal" dockerCmd(c, "create", "--name", name, "--stop-signal", "9", "busybox") diff --git a/components/engine/integration-cli/docker_cli_plugins_test.go b/components/engine/integration-cli/docker_cli_plugins_test.go index 8ca7254440..8a936e3e44 100644 --- a/components/engine/integration-cli/docker_cli_plugins_test.go +++ b/components/engine/integration-cli/docker_cli_plugins_test.go @@ -16,7 +16,6 @@ import ( "github.com/docker/docker/integration-cli/daemon" "github.com/docker/docker/integration-cli/fixtures/plugin" "github.com/go-check/check" - "github.com/gotestyourself/gotestyourself/icmd" "golang.org/x/net/context" ) @@ -352,51 +351,6 @@ func (s *DockerSuite) TestPluginInspectOnWindows(c *check.C) { c.Assert(err.Error(), checker.Contains, "plugins are not supported on this platform") } -func (s *DockerTrustSuite) TestPluginTrustedInstall(c *check.C) { - testRequires(c, DaemonIsLinux, IsAmd64, Network) - - trustedName := s.setupTrustedplugin(c, pNameWithTag, "trusted-plugin-install") - - cli.Docker(cli.Args("plugin", "install", "--grant-all-permissions", trustedName), trustedCmd).Assert(c, icmd.Expected{ - Out: trustedName, - }) - - out := cli.DockerCmd(c, "plugin", "ls").Combined() - c.Assert(out, checker.Contains, "true") - - out = cli.DockerCmd(c, "plugin", "disable", trustedName).Combined() - c.Assert(strings.TrimSpace(out), checker.Contains, trustedName) - - out = cli.DockerCmd(c, "plugin", "enable", trustedName).Combined() - c.Assert(strings.TrimSpace(out), checker.Contains, trustedName) - - out = cli.DockerCmd(c, "plugin", "rm", "-f", trustedName).Combined() - c.Assert(strings.TrimSpace(out), checker.Contains, trustedName) - - // Try untrusted pull to ensure we pushed the tag to the registry - cli.Docker(cli.Args("plugin", "install", "--disable-content-trust=true", "--grant-all-permissions", trustedName), trustedCmd).Assert(c, SuccessDownloaded) - - out = cli.DockerCmd(c, "plugin", "ls").Combined() - c.Assert(out, checker.Contains, "true") - -} - -func (s *DockerTrustSuite) TestPluginUntrustedInstall(c *check.C) { - testRequires(c, DaemonIsLinux, IsAmd64, Network) - - pluginName := fmt.Sprintf("%v/dockercliuntrusted/plugintest:latest", privateRegistryURL) - // install locally and push to private registry - cli.DockerCmd(c, "plugin", "install", "--grant-all-permissions", "--alias", pluginName, pNameWithTag) - cli.DockerCmd(c, "plugin", "push", pluginName) - cli.DockerCmd(c, "plugin", "rm", "-f", pluginName) - - // Try trusted install on untrusted plugin - cli.Docker(cli.Args("plugin", "install", "--grant-all-permissions", pluginName), trustedCmd).Assert(c, icmd.Expected{ - ExitCode: 1, - Err: "Error: remote trust data does not exist", - }) -} - func (ps *DockerPluginSuite) TestPluginIDPrefix(c *check.C) { name := "test" client := testEnv.APIClient() diff --git a/components/engine/integration-cli/docker_cli_pull_trusted_test.go b/components/engine/integration-cli/docker_cli_pull_trusted_test.go deleted file mode 100644 index 49402fe9ab..0000000000 --- a/components/engine/integration-cli/docker_cli_pull_trusted_test.go +++ /dev/null @@ -1,224 +0,0 @@ -package main - -import ( - "fmt" - "io/ioutil" - - "github.com/docker/docker/integration-cli/checker" - "github.com/docker/docker/integration-cli/cli" - "github.com/docker/docker/integration-cli/cli/build" - "github.com/go-check/check" - "github.com/gotestyourself/gotestyourself/icmd" -) - -func (s *DockerTrustSuite) TestTrustedPull(c *check.C) { - repoName := s.setupTrustedImage(c, "trusted-pull") - - // Try pull - cli.Docker(cli.Args("pull", repoName), trustedCmd).Assert(c, SuccessTagging) - - cli.DockerCmd(c, "rmi", repoName) - // Try untrusted pull to ensure we pushed the tag to the registry - cli.Docker(cli.Args("pull", "--disable-content-trust=true", repoName), trustedCmd).Assert(c, SuccessDownloaded) -} - -func (s *DockerTrustSuite) TestTrustedIsolatedPull(c *check.C) { - repoName := s.setupTrustedImage(c, "trusted-isolated-pull") - - // Try pull (run from isolated directory without trust information) - cli.Docker(cli.Args("--config", "/tmp/docker-isolated", "pull", repoName), trustedCmd).Assert(c, SuccessTagging) - - cli.DockerCmd(c, "rmi", repoName) -} - -func (s *DockerTrustSuite) TestUntrustedPull(c *check.C) { - repoName := fmt.Sprintf("%v/dockercliuntrusted/pulltest:latest", privateRegistryURL) - // tag the image and upload it to the private registry - cli.DockerCmd(c, "tag", "busybox", repoName) - cli.DockerCmd(c, "push", repoName) - cli.DockerCmd(c, "rmi", repoName) - - // Try trusted pull on untrusted tag - cli.Docker(cli.Args("pull", repoName), trustedCmd).Assert(c, icmd.Expected{ - ExitCode: 1, - Err: "Error: remote trust data does not exist", - }) -} - -func (s *DockerTrustSuite) TestTrustedPullFromBadTrustServer(c *check.C) { - repoName := fmt.Sprintf("%v/dockerclievilpull/trusted:latest", privateRegistryURL) - evilLocalConfigDir, err := ioutil.TempDir("", "evil-local-config-dir") - if err != nil { - c.Fatalf("Failed to create local temp dir") - } - - // tag the image and upload it to the private registry - cli.DockerCmd(c, "tag", "busybox", repoName) - - cli.Docker(cli.Args("push", repoName), trustedCmd).Assert(c, SuccessSigningAndPushing) - cli.DockerCmd(c, "rmi", repoName) - - // Try pull - cli.Docker(cli.Args("pull", repoName), trustedCmd).Assert(c, SuccessTagging) - cli.DockerCmd(c, "rmi", repoName) - - // Kill the notary server, start a new "evil" one. - s.not.Close() - s.not, err = newTestNotary(c) - - c.Assert(err, check.IsNil, check.Commentf("Restarting notary server failed.")) - - // In order to make an evil server, lets re-init a client (with a different trust dir) and push new data. - // tag an image and upload it to the private registry - cli.DockerCmd(c, "--config", evilLocalConfigDir, "tag", "busybox", repoName) - - // Push up to the new server - cli.Docker(cli.Args("--config", evilLocalConfigDir, "push", repoName), trustedCmd).Assert(c, SuccessSigningAndPushing) - - // Now, try pulling with the original client from this new trust server. This should fail because the new root is invalid. - cli.Docker(cli.Args("pull", repoName), trustedCmd).Assert(c, icmd.Expected{ - ExitCode: 1, - Err: "could not rotate trust to a new trusted root", - }) -} - -func (s *DockerTrustSuite) TestTrustedOfflinePull(c *check.C) { - repoName := s.setupTrustedImage(c, "trusted-offline-pull") - - cli.Docker(cli.Args("pull", repoName), trustedCmdWithServer("https://invalidnotaryserver")).Assert(c, icmd.Expected{ - ExitCode: 1, - Err: "error contacting notary server", - }) - // Do valid trusted pull to warm cache - cli.Docker(cli.Args("pull", repoName), trustedCmd).Assert(c, SuccessTagging) - cli.DockerCmd(c, "rmi", repoName) - - // Try pull again with invalid notary server, should use cache - cli.Docker(cli.Args("pull", repoName), trustedCmdWithServer("https://invalidnotaryserver")).Assert(c, SuccessTagging) -} - -func (s *DockerTrustSuite) TestTrustedPullDelete(c *check.C) { - repoName := fmt.Sprintf("%v/dockercli/%s:latest", privateRegistryURL, "trusted-pull-delete") - // tag the image and upload it to the private registry - cli.BuildCmd(c, repoName, build.WithDockerfile(` - FROM busybox - CMD echo trustedpulldelete - `)) - cli.Docker(cli.Args("push", repoName), trustedCmd).Assert(c, SuccessSigningAndPushing) - - cli.DockerCmd(c, "rmi", repoName) - - // Try pull - result := cli.Docker(cli.Args("pull", repoName), trustedCmd).Assert(c, icmd.Success) - - matches := digestRegex.FindStringSubmatch(result.Combined()) - c.Assert(matches, checker.HasLen, 2, check.Commentf("unable to parse digest from pull output: %s", result.Combined())) - pullDigest := matches[1] - - imageID := inspectField(c, repoName, "Id") - - imageByDigest := repoName + "@" + pullDigest - byDigestID := inspectField(c, imageByDigest, "Id") - - c.Assert(byDigestID, checker.Equals, imageID) - - // rmi of tag should also remove the digest reference - cli.DockerCmd(c, "rmi", repoName) - - _, err := inspectFieldWithError(imageByDigest, "Id") - c.Assert(err, checker.NotNil, check.Commentf("digest reference should have been removed")) - - _, err = inspectFieldWithError(imageID, "Id") - c.Assert(err, checker.NotNil, check.Commentf("image should have been deleted")) -} - -func (s *DockerTrustSuite) TestTrustedPullReadsFromReleasesRole(c *check.C) { - c.Skip("Blacklisting for Docker CE") - testRequires(c, NotaryHosting) - repoName := fmt.Sprintf("%v/dockerclireleasesdelegationpulling/trusted", privateRegistryURL) - targetName := fmt.Sprintf("%s:latest", repoName) - - // Push with targets first, initializing the repo - cli.DockerCmd(c, "tag", "busybox", targetName) - cli.Docker(cli.Args("push", targetName), trustedCmd).Assert(c, icmd.Success) - s.assertTargetInRoles(c, repoName, "latest", "targets") - - // Try pull, check we retrieve from targets role - cli.Docker(cli.Args("-D", "pull", repoName), trustedCmd).Assert(c, icmd.Expected{ - Err: "retrieving target for targets role", - }) - - // Now we'll create the releases role, and try pushing and pulling - s.notaryCreateDelegation(c, repoName, "targets/releases", s.not.keys[0].Public) - s.notaryImportKey(c, repoName, "targets/releases", s.not.keys[0].Private) - s.notaryPublish(c, repoName) - - // try a pull, check that we can still pull because we can still read the - // old tag in the targets role - cli.Docker(cli.Args("-D", "pull", repoName), trustedCmd).Assert(c, icmd.Expected{ - Err: "retrieving target for targets role", - }) - - // try a pull -a, check that it succeeds because we can still pull from the - // targets role - cli.Docker(cli.Args("-D", "pull", "-a", repoName), trustedCmd).Assert(c, icmd.Success) - - // Push, should sign with targets/releases - cli.DockerCmd(c, "tag", "busybox", targetName) - cli.Docker(cli.Args("push", targetName), trustedCmd).Assert(c, icmd.Success) - s.assertTargetInRoles(c, repoName, "latest", "targets", "targets/releases") - - // Try pull, check we retrieve from targets/releases role - cli.Docker(cli.Args("-D", "pull", repoName), trustedCmd).Assert(c, icmd.Expected{ - Err: "retrieving target for targets/releases role", - }) - - // Create another delegation that we'll sign with - s.notaryCreateDelegation(c, repoName, "targets/other", s.not.keys[1].Public) - s.notaryImportKey(c, repoName, "targets/other", s.not.keys[1].Private) - s.notaryPublish(c, repoName) - - cli.DockerCmd(c, "tag", "busybox", targetName) - cli.Docker(cli.Args("push", targetName), trustedCmd).Assert(c, icmd.Success) - s.assertTargetInRoles(c, repoName, "latest", "targets", "targets/releases", "targets/other") - - // Try pull, check we retrieve from targets/releases role - cli.Docker(cli.Args("-D", "pull", repoName), trustedCmd).Assert(c, icmd.Expected{ - Err: "retrieving target for targets/releases role", - }) -} - -func (s *DockerTrustSuite) TestTrustedPullIgnoresOtherDelegationRoles(c *check.C) { - c.Skip("Blacklisting for Docker CE") - testRequires(c, NotaryHosting) - repoName := fmt.Sprintf("%v/dockerclipullotherdelegation/trusted", privateRegistryURL) - targetName := fmt.Sprintf("%s:latest", repoName) - - // We'll create a repo first with a non-release delegation role, so that when we - // push we'll sign it into the delegation role - s.notaryInitRepo(c, repoName) - s.notaryCreateDelegation(c, repoName, "targets/other", s.not.keys[0].Public) - s.notaryImportKey(c, repoName, "targets/other", s.not.keys[0].Private) - s.notaryPublish(c, repoName) - - // Push should write to the delegation role, not targets - cli.DockerCmd(c, "tag", "busybox", targetName) - cli.Docker(cli.Args("push", targetName), trustedCmd).Assert(c, icmd.Success) - s.assertTargetInRoles(c, repoName, "latest", "targets/other") - s.assertTargetNotInRoles(c, repoName, "latest", "targets") - - // Try pull - we should fail, since pull will only pull from the targets/releases - // role or the targets role - cli.DockerCmd(c, "tag", "busybox", targetName) - cli.Docker(cli.Args("-D", "pull", repoName), trustedCmd).Assert(c, icmd.Expected{ - ExitCode: 1, - Err: "No trust data for", - }) - - // try a pull -a: we should fail since pull will only pull from the targets/releases - // role or the targets role - cli.Docker(cli.Args("-D", "pull", "-a", repoName), trustedCmd).Assert(c, icmd.Expected{ - ExitCode: 1, - Err: "No trusted tags for", - }) -} diff --git a/components/engine/integration-cli/docker_cli_push_test.go b/components/engine/integration-cli/docker_cli_push_test.go index b602cb849b..48d6be2ac1 100644 --- a/components/engine/integration-cli/docker_cli_push_test.go +++ b/components/engine/integration-cli/docker_cli_push_test.go @@ -7,14 +7,11 @@ import ( "net/http" "net/http/httptest" "os" - "path/filepath" "strings" "sync" "github.com/docker/distribution/reference" - "github.com/docker/docker/cli/config" "github.com/docker/docker/integration-cli/checker" - "github.com/docker/docker/integration-cli/cli" "github.com/docker/docker/integration-cli/cli/build" "github.com/go-check/check" "github.com/gotestyourself/gotestyourself/icmd" @@ -281,227 +278,6 @@ func (s *DockerSchema1RegistrySuite) TestCrossRepositoryLayerPushNotSupported(c c.Assert(out3, check.Equals, "hello world") } -func (s *DockerTrustSuite) TestTrustedPush(c *check.C) { - c.Skip("Blacklisting for Docker CE") - repoName := fmt.Sprintf("%v/dockerclitrusted/pushtest:latest", privateRegistryURL) - // tag the image and upload it to the private registry - cli.DockerCmd(c, "tag", "busybox", repoName) - - cli.Docker(cli.Args("push", repoName), trustedCmd).Assert(c, SuccessSigningAndPushing) - - // Try pull after push - cli.Docker(cli.Args("pull", repoName), trustedCmd).Assert(c, icmd.Expected{ - Out: "Status: Image is up to date", - }) - - // Assert that we rotated the snapshot key to the server by checking our local keystore - contents, err := ioutil.ReadDir(filepath.Join(config.Dir(), "trust/private/tuf_keys", privateRegistryURL, "dockerclitrusted/pushtest")) - c.Assert(err, check.IsNil, check.Commentf("Unable to read local tuf key files")) - // Check that we only have 1 key (targets key) - c.Assert(contents, checker.HasLen, 1) -} - -func (s *DockerTrustSuite) TestTrustedPushWithEnvPasswords(c *check.C) { - repoName := fmt.Sprintf("%v/dockerclienv/trusted:latest", privateRegistryURL) - // tag the image and upload it to the private registry - cli.DockerCmd(c, "tag", "busybox", repoName) - - cli.Docker(cli.Args("push", repoName), trustedCmdWithPassphrases("12345678", "12345678")).Assert(c, SuccessSigningAndPushing) - - // Try pull after push - cli.Docker(cli.Args("pull", repoName), trustedCmd).Assert(c, icmd.Expected{ - Out: "Status: Image is up to date", - }) -} - -func (s *DockerTrustSuite) TestTrustedPushWithFailingServer(c *check.C) { - repoName := fmt.Sprintf("%v/dockerclitrusted/failingserver:latest", privateRegistryURL) - // tag the image and upload it to the private registry - cli.DockerCmd(c, "tag", "busybox", repoName) - - // Using a name that doesn't resolve to an address makes this test faster - cli.Docker(cli.Args("push", repoName), trustedCmdWithServer("https://server.invalid:81/")).Assert(c, icmd.Expected{ - ExitCode: 1, - Err: "error contacting notary server", - }) -} - -func (s *DockerTrustSuite) TestTrustedPushWithoutServerAndUntrusted(c *check.C) { - repoName := fmt.Sprintf("%v/dockerclitrusted/trustedandnot:latest", privateRegistryURL) - // tag the image and upload it to the private registry - cli.DockerCmd(c, "tag", "busybox", repoName) - - result := cli.Docker(cli.Args("push", "--disable-content-trust", repoName), trustedCmdWithServer("https://server.invalid:81/")) - result.Assert(c, icmd.Success) - c.Assert(result.Combined(), check.Not(checker.Contains), "Error establishing connection to notary repository", check.Commentf("Missing expected output on trusted push with --disable-content-trust:")) -} - -func (s *DockerTrustSuite) TestTrustedPushWithExistingTag(c *check.C) { - repoName := fmt.Sprintf("%v/dockerclitag/trusted:latest", privateRegistryURL) - // tag the image and upload it to the private registry - cli.DockerCmd(c, "tag", "busybox", repoName) - cli.DockerCmd(c, "push", repoName) - - cli.Docker(cli.Args("push", repoName), trustedCmd).Assert(c, SuccessSigningAndPushing) - - // Try pull after push - cli.Docker(cli.Args("pull", repoName), trustedCmd).Assert(c, icmd.Expected{ - Out: "Status: Image is up to date", - }) -} - -func (s *DockerTrustSuite) TestTrustedPushWithExistingSignedTag(c *check.C) { - repoName := fmt.Sprintf("%v/dockerclipushpush/trusted:latest", privateRegistryURL) - // tag the image and upload it to the private registry - cli.DockerCmd(c, "tag", "busybox", repoName) - - // Do a trusted push - cli.Docker(cli.Args("push", repoName), trustedCmd).Assert(c, SuccessSigningAndPushing) - - // Do another trusted push - cli.Docker(cli.Args("push", repoName), trustedCmd).Assert(c, SuccessSigningAndPushing) - cli.DockerCmd(c, "rmi", repoName) - - // Try pull to ensure the double push did not break our ability to pull - cli.Docker(cli.Args("pull", repoName), trustedCmd).Assert(c, SuccessDownloaded) -} - -func (s *DockerTrustSuite) TestTrustedPushWithIncorrectPassphraseForNonRoot(c *check.C) { - c.Skip("Blacklisting for Docker CE") - repoName := fmt.Sprintf("%v/dockercliincorretpwd/trusted:latest", privateRegistryURL) - // tag the image and upload it to the private registry - cli.DockerCmd(c, "tag", "busybox", repoName) - - // Push with default passphrases - cli.Docker(cli.Args("push", repoName), trustedCmd).Assert(c, SuccessSigningAndPushing) - - // Push with wrong passphrases - cli.Docker(cli.Args("push", repoName), trustedCmdWithPassphrases("12345678", "87654321")).Assert(c, icmd.Expected{ - ExitCode: 1, - Err: "could not find necessary signing keys", - }) -} - -func (s *DockerTrustSuite) TestTrustedPushWithReleasesDelegationOnly(c *check.C) { - testRequires(c, NotaryHosting) - repoName := fmt.Sprintf("%v/dockerclireleasedelegationinitfirst/trusted", privateRegistryURL) - targetName := fmt.Sprintf("%s:latest", repoName) - s.notaryInitRepo(c, repoName) - s.notaryCreateDelegation(c, repoName, "targets/releases", s.not.keys[0].Public) - s.notaryPublish(c, repoName) - - s.notaryImportKey(c, repoName, "targets/releases", s.not.keys[0].Private) - - // tag the image and upload it to the private registry - cli.DockerCmd(c, "tag", "busybox", targetName) - - cli.Docker(cli.Args("push", targetName), trustedCmd).Assert(c, SuccessSigningAndPushing) - // check to make sure that the target has been added to targets/releases and not targets - s.assertTargetInRoles(c, repoName, "latest", "targets/releases") - s.assertTargetNotInRoles(c, repoName, "latest", "targets") - - // Try pull after push - os.RemoveAll(filepath.Join(config.Dir(), "trust")) - - cli.Docker(cli.Args("pull", targetName), trustedCmd).Assert(c, icmd.Expected{ - Out: "Status: Image is up to date", - }) -} - -func (s *DockerTrustSuite) TestTrustedPushSignsAllFirstLevelRolesWeHaveKeysFor(c *check.C) { - testRequires(c, NotaryHosting) - repoName := fmt.Sprintf("%v/dockerclimanyroles/trusted", privateRegistryURL) - targetName := fmt.Sprintf("%s:latest", repoName) - s.notaryInitRepo(c, repoName) - s.notaryCreateDelegation(c, repoName, "targets/role1", s.not.keys[0].Public) - s.notaryCreateDelegation(c, repoName, "targets/role2", s.not.keys[1].Public) - s.notaryCreateDelegation(c, repoName, "targets/role3", s.not.keys[2].Public) - - // import everything except the third key - s.notaryImportKey(c, repoName, "targets/role1", s.not.keys[0].Private) - s.notaryImportKey(c, repoName, "targets/role2", s.not.keys[1].Private) - - s.notaryCreateDelegation(c, repoName, "targets/role1/subrole", s.not.keys[3].Public) - s.notaryImportKey(c, repoName, "targets/role1/subrole", s.not.keys[3].Private) - - s.notaryPublish(c, repoName) - - // tag the image and upload it to the private registry - cli.DockerCmd(c, "tag", "busybox", targetName) - - cli.Docker(cli.Args("push", targetName), trustedCmd).Assert(c, SuccessSigningAndPushing) - - // check to make sure that the target has been added to targets/role1 and targets/role2, and - // not targets (because there are delegations) or targets/role3 (due to missing key) or - // targets/role1/subrole (due to it being a second level delegation) - s.assertTargetInRoles(c, repoName, "latest", "targets/role1", "targets/role2") - s.assertTargetNotInRoles(c, repoName, "latest", "targets") - - // Try pull after push - os.RemoveAll(filepath.Join(config.Dir(), "trust")) - - // pull should fail because none of these are the releases role - cli.Docker(cli.Args("pull", targetName), trustedCmd).Assert(c, icmd.Expected{ - ExitCode: 1, - }) -} - -func (s *DockerTrustSuite) TestTrustedPushSignsForRolesWithKeysAndValidPaths(c *check.C) { - repoName := fmt.Sprintf("%v/dockerclirolesbykeysandpaths/trusted", privateRegistryURL) - targetName := fmt.Sprintf("%s:latest", repoName) - s.notaryInitRepo(c, repoName) - s.notaryCreateDelegation(c, repoName, "targets/role1", s.not.keys[0].Public, "l", "z") - s.notaryCreateDelegation(c, repoName, "targets/role2", s.not.keys[1].Public, "x", "y") - s.notaryCreateDelegation(c, repoName, "targets/role3", s.not.keys[2].Public, "latest") - s.notaryCreateDelegation(c, repoName, "targets/role4", s.not.keys[3].Public, "latest") - - // import everything except the third key - s.notaryImportKey(c, repoName, "targets/role1", s.not.keys[0].Private) - s.notaryImportKey(c, repoName, "targets/role2", s.not.keys[1].Private) - s.notaryImportKey(c, repoName, "targets/role4", s.not.keys[3].Private) - - s.notaryPublish(c, repoName) - - // tag the image and upload it to the private registry - cli.DockerCmd(c, "tag", "busybox", targetName) - - cli.Docker(cli.Args("push", targetName), trustedCmd).Assert(c, SuccessSigningAndPushing) - - // check to make sure that the target has been added to targets/role1 and targets/role4, and - // not targets (because there are delegations) or targets/role2 (due to path restrictions) or - // targets/role3 (due to missing key) - s.assertTargetInRoles(c, repoName, "latest", "targets/role1", "targets/role4") - s.assertTargetNotInRoles(c, repoName, "latest", "targets") - - // Try pull after push - os.RemoveAll(filepath.Join(config.Dir(), "trust")) - - // pull should fail because none of these are the releases role - cli.Docker(cli.Args("pull", targetName), trustedCmd).Assert(c, icmd.Expected{ - ExitCode: 1, - }) -} - -func (s *DockerTrustSuite) TestTrustedPushDoesntSignTargetsIfDelegationsExist(c *check.C) { - testRequires(c, NotaryHosting) - repoName := fmt.Sprintf("%v/dockerclireleasedelegationnotsignable/trusted", privateRegistryURL) - targetName := fmt.Sprintf("%s:latest", repoName) - s.notaryInitRepo(c, repoName) - s.notaryCreateDelegation(c, repoName, "targets/role1", s.not.keys[0].Public) - s.notaryPublish(c, repoName) - - // do not import any delegations key - - // tag the image and upload it to the private registry - cli.DockerCmd(c, "tag", "busybox", targetName) - - cli.Docker(cli.Args("push", targetName), trustedCmd).Assert(c, icmd.Expected{ - ExitCode: 1, - Err: "no valid signing keys", - }) - s.assertTargetNotInRoles(c, repoName, "latest", "targets", "targets/role1") -} - func (s *DockerRegistryAuthHtpasswdSuite) TestPushNoCredentialsNoRetry(c *check.C) { repoName := fmt.Sprintf("%s/busybox", privateRegistryURL) dockerCmd(c, "tag", "busybox", repoName) diff --git a/components/engine/integration-cli/docker_cli_run_test.go b/components/engine/integration-cli/docker_cli_run_test.go index 776f0e5ba5..4f71cc0f45 100644 --- a/components/engine/integration-cli/docker_cli_run_test.go +++ b/components/engine/integration-cli/docker_cli_run_test.go @@ -3140,75 +3140,6 @@ func (s *DockerSuite) TestRunNetworkFilesBindMountROFilesystem(c *check.C) { } } -func (s *DockerTrustSuite) TestTrustedRun(c *check.C) { - // Windows does not support this functionality - testRequires(c, DaemonIsLinux) - repoName := s.setupTrustedImage(c, "trusted-run") - - // Try run - cli.Docker(cli.Args("run", repoName), trustedCmd).Assert(c, SuccessTagging) - cli.DockerCmd(c, "rmi", repoName) - - // Try untrusted run to ensure we pushed the tag to the registry - cli.Docker(cli.Args("run", "--disable-content-trust=true", repoName), trustedCmd).Assert(c, SuccessDownloadedOnStderr) -} - -func (s *DockerTrustSuite) TestUntrustedRun(c *check.C) { - // Windows does not support this functionality - testRequires(c, DaemonIsLinux) - repoName := fmt.Sprintf("%v/dockercliuntrusted/runtest:latest", privateRegistryURL) - // tag the image and upload it to the private registry - cli.DockerCmd(c, "tag", "busybox", repoName) - cli.DockerCmd(c, "push", repoName) - cli.DockerCmd(c, "rmi", repoName) - - // Try trusted run on untrusted tag - cli.Docker(cli.Args("run", repoName), trustedCmd).Assert(c, icmd.Expected{ - ExitCode: 125, - Err: "does not have trust data for", - }) -} - -func (s *DockerTrustSuite) TestTrustedRunFromBadTrustServer(c *check.C) { - // Windows does not support this functionality - testRequires(c, DaemonIsLinux) - repoName := fmt.Sprintf("%v/dockerclievilrun/trusted:latest", privateRegistryURL) - evilLocalConfigDir, err := ioutil.TempDir("", "evilrun-local-config-dir") - if err != nil { - c.Fatalf("Failed to create local temp dir") - } - - // tag the image and upload it to the private registry - cli.DockerCmd(c, "tag", "busybox", repoName) - - cli.Docker(cli.Args("push", repoName), trustedCmd).Assert(c, SuccessSigningAndPushing) - cli.DockerCmd(c, "rmi", repoName) - - // Try run - cli.Docker(cli.Args("run", repoName), trustedCmd).Assert(c, SuccessTagging) - cli.DockerCmd(c, "rmi", repoName) - - // Kill the notary server, start a new "evil" one. - s.not.Close() - s.not, err = newTestNotary(c) - if err != nil { - c.Fatalf("Restarting notary server failed.") - } - - // In order to make an evil server, lets re-init a client (with a different trust dir) and push new data. - // tag an image and upload it to the private registry - cli.DockerCmd(c, "--config", evilLocalConfigDir, "tag", "busybox", repoName) - - // Push up to the new server - cli.Docker(cli.Args("--config", evilLocalConfigDir, "push", repoName), trustedCmd).Assert(c, SuccessSigningAndPushing) - - // Now, try running with the original client from this new trust server. This should fail because the new root is invalid. - cli.Docker(cli.Args("run", repoName), trustedCmd).Assert(c, icmd.Expected{ - ExitCode: 125, - Err: "could not rotate trust to a new trusted root", - }) -} - func (s *DockerSuite) TestPtraceContainerProcsFromHost(c *check.C) { // Not applicable on Windows as uses Unix specific functionality testRequires(c, DaemonIsLinux, SameHostDaemon) diff --git a/components/engine/integration-cli/docker_cli_swarm_test.go b/components/engine/integration-cli/docker_cli_swarm_test.go index 4a5ec9a566..5ee3881bc2 100644 --- a/components/engine/integration-cli/docker_cli_swarm_test.go +++ b/components/engine/integration-cli/docker_cli_swarm_test.go @@ -1560,78 +1560,6 @@ func (s *DockerSwarmSuite) TestSwarmNetworkIPAMOptions(c *check.C) { c.Assert(strings.TrimSpace(out), checker.Contains, "com.docker.network.ipam.serial:true") } -func (s *DockerTrustedSwarmSuite) TestTrustedServiceCreate(c *check.C) { - d := s.swarmSuite.AddDaemon(c, true, true) - - // Attempt creating a service from an image that is known to notary. - repoName := s.trustSuite.setupTrustedImage(c, "trusted-pull") - - name := "trusted" - cli.Docker(cli.Args("-D", "service", "create", "--detach", "--no-resolve-image", "--name", name, repoName, "top"), trustedCmd, cli.Daemon(d.Daemon)).Assert(c, icmd.Expected{ - Err: "resolved image tag to", - }) - - out, err := d.Cmd("service", "inspect", "--pretty", name) - c.Assert(err, checker.IsNil, check.Commentf(out)) - c.Assert(out, checker.Contains, repoName+"@", check.Commentf(out)) - - // Try trusted service create on an untrusted tag. - - repoName = fmt.Sprintf("%v/untrustedservicecreate/createtest:latest", privateRegistryURL) - // tag the image and upload it to the private registry - cli.DockerCmd(c, "tag", "busybox", repoName) - cli.DockerCmd(c, "push", repoName) - cli.DockerCmd(c, "rmi", repoName) - - name = "untrusted" - cli.Docker(cli.Args("service", "create", "--detach", "--no-resolve-image", "--name", name, repoName, "top"), trustedCmd, cli.Daemon(d.Daemon)).Assert(c, icmd.Expected{ - ExitCode: 1, - Err: "Error: remote trust data does not exist", - }) - - out, err = d.Cmd("service", "inspect", "--pretty", name) - c.Assert(err, checker.NotNil, check.Commentf(out)) -} - -func (s *DockerTrustedSwarmSuite) TestTrustedServiceUpdate(c *check.C) { - d := s.swarmSuite.AddDaemon(c, true, true) - - // Attempt creating a service from an image that is known to notary. - repoName := s.trustSuite.setupTrustedImage(c, "trusted-pull") - - name := "myservice" - - // Create a service without content trust - cli.Docker(cli.Args("service", "create", "--detach", "--no-resolve-image", "--name", name, repoName, "top"), cli.Daemon(d.Daemon)).Assert(c, icmd.Success) - - result := cli.Docker(cli.Args("service", "inspect", "--pretty", name), cli.Daemon(d.Daemon)) - c.Assert(result.Error, checker.IsNil, check.Commentf(result.Combined())) - // Daemon won't insert the digest because this is disabled by - // DOCKER_SERVICE_PREFER_OFFLINE_IMAGE. - c.Assert(result.Combined(), check.Not(checker.Contains), repoName+"@", check.Commentf(result.Combined())) - - cli.Docker(cli.Args("-D", "service", "update", "--detach", "--no-resolve-image", "--image", repoName, name), trustedCmd, cli.Daemon(d.Daemon)).Assert(c, icmd.Expected{ - Err: "resolved image tag to", - }) - - cli.Docker(cli.Args("service", "inspect", "--pretty", name), cli.Daemon(d.Daemon)).Assert(c, icmd.Expected{ - Out: repoName + "@", - }) - - // Try trusted service update on an untrusted tag. - - repoName = fmt.Sprintf("%v/untrustedservicecreate/createtest:latest", privateRegistryURL) - // tag the image and upload it to the private registry - cli.DockerCmd(c, "tag", "busybox", repoName) - cli.DockerCmd(c, "push", repoName) - cli.DockerCmd(c, "rmi", repoName) - - cli.Docker(cli.Args("service", "update", "--detach", "--no-resolve-image", "--image", repoName, name), trustedCmd, cli.Daemon(d.Daemon)).Assert(c, icmd.Expected{ - ExitCode: 1, - Err: "Error: remote trust data does not exist", - }) -} - // Test case for issue #27866, which did not allow NW name that is the prefix of a swarm NW ID. // e.g. if the ingress ID starts with "n1", it was impossible to create a NW named "n1". func (s *DockerSwarmSuite) TestSwarmNetworkCreateIssue27866(c *check.C) { diff --git a/components/engine/integration-cli/docker_utils_test.go b/components/engine/integration-cli/docker_utils_test.go index ea780cc6e8..1475558239 100644 --- a/components/engine/integration-cli/docker_utils_test.go +++ b/components/engine/integration-cli/docker_utils_test.go @@ -202,12 +202,6 @@ func buildImage(name string, cmdOperators ...cli.CmdOperator) *icmd.Result { return cli.Docker(cli.Build(name), cmdOperators...) } -// Deprecated: use trustedcmd -func trustedBuild(cmd *icmd.Cmd) func() { - trustedCmd(cmd) - return nil -} - // Write `content` to the file at path `dst`, creating it if necessary, // as well as any missing directories. // The file is truncated if it already exists. @@ -306,13 +300,6 @@ func setupRegistry(c *check.C, schema1 bool, auth, tokenURL string) *registry.V2 return reg } -func setupNotary(c *check.C) *testNotary { - ts, err := newTestNotary(c) - c.Assert(err, check.IsNil) - - return ts -} - // appendBaseEnv appends the minimum set of environment variables to exec the // docker cli binary for testing with correct configuration to the given env // list. diff --git a/components/engine/integration-cli/requirements_test.go b/components/engine/integration-cli/requirements_test.go index 838977e70e..b0a95d42f8 100644 --- a/components/engine/integration-cli/requirements_test.go +++ b/components/engine/integration-cli/requirements_test.go @@ -112,22 +112,6 @@ func Apparmor() bool { return err == nil && len(buf) > 1 && buf[0] == 'Y' } -func NotaryHosting() bool { - // for now notary binary is built only if we're running inside - // container through `make test`. Figure that out by testing if - // notary-server binary is in PATH. - _, err := exec.LookPath(notaryServerBinary) - return err == nil -} - -func NotaryServerHosting() bool { - // for now notary-server binary is built only if we're running inside - // container through `make test`. Figure that out by testing if - // notary-server binary is in PATH. - _, err := exec.LookPath(notaryServerBinary) - return err == nil -} - func Devicemapper() bool { return strings.HasPrefix(testEnv.DaemonInfo.Driver, "devicemapper") } diff --git a/components/engine/integration-cli/trust_server_test.go b/components/engine/integration-cli/trust_server_test.go deleted file mode 100644 index 937babdaa7..0000000000 --- a/components/engine/integration-cli/trust_server_test.go +++ /dev/null @@ -1,334 +0,0 @@ -package main - -import ( - "context" - "fmt" - "io/ioutil" - "net" - "net/http" - "os" - "os/exec" - "path/filepath" - "strings" - "time" - - "github.com/docker/docker/api/types" - cliconfig "github.com/docker/docker/cli/config" - "github.com/docker/docker/integration-cli/checker" - "github.com/docker/docker/integration-cli/cli" - "github.com/docker/docker/integration-cli/fixtures/plugin" - "github.com/docker/go-connections/tlsconfig" - "github.com/go-check/check" - "github.com/gotestyourself/gotestyourself/icmd" -) - -var notaryBinary = "notary" -var notaryServerBinary = "notary-server" - -type keyPair struct { - Public string - Private string -} - -type testNotary struct { - cmd *exec.Cmd - dir string - keys []keyPair -} - -const notaryHost = "localhost:4443" -const notaryURL = "https://" + notaryHost - -var SuccessTagging = icmd.Expected{ - Err: "Tagging", -} - -var SuccessSigningAndPushing = icmd.Expected{ - Out: "Signing and pushing trust metadata", -} - -var SuccessDownloaded = icmd.Expected{ - Out: "Status: Downloaded", -} - -var SuccessDownloadedOnStderr = icmd.Expected{ - Err: "Status: Downloaded", -} - -func newTestNotary(c *check.C) (*testNotary, error) { - // generate server config - template := `{ - "server": { - "http_addr": "%s", - "tls_key_file": "%s", - "tls_cert_file": "%s" - }, - "trust_service": { - "type": "local", - "hostname": "", - "port": "", - "key_algorithm": "ed25519" - }, - "logging": { - "level": "debug" - }, - "storage": { - "backend": "memory" - } -}` - tmp, err := ioutil.TempDir("", "notary-test-") - if err != nil { - return nil, err - } - confPath := filepath.Join(tmp, "config.json") - config, err := os.Create(confPath) - if err != nil { - return nil, err - } - defer config.Close() - - workingDir, err := os.Getwd() - if err != nil { - return nil, err - } - if _, err := fmt.Fprintf(config, template, notaryHost, filepath.Join(workingDir, "fixtures/notary/localhost.key"), filepath.Join(workingDir, "fixtures/notary/localhost.cert")); err != nil { - os.RemoveAll(tmp) - return nil, err - } - - // generate client config - clientConfPath := filepath.Join(tmp, "client-config.json") - clientConfig, err := os.Create(clientConfPath) - if err != nil { - return nil, err - } - defer clientConfig.Close() - - template = `{ - "trust_dir" : "%s", - "remote_server": { - "url": "%s", - "skipTLSVerify": true - } -}` - if _, err = fmt.Fprintf(clientConfig, template, filepath.Join(cliconfig.Dir(), "trust"), notaryURL); err != nil { - os.RemoveAll(tmp) - return nil, err - } - - // load key fixture filenames - var keys []keyPair - for i := 1; i < 5; i++ { - keys = append(keys, keyPair{ - Public: filepath.Join(workingDir, fmt.Sprintf("fixtures/notary/delgkey%v.crt", i)), - Private: filepath.Join(workingDir, fmt.Sprintf("fixtures/notary/delgkey%v.key", i)), - }) - } - - // run notary-server - cmd := exec.Command(notaryServerBinary, "-config", confPath) - if err := cmd.Start(); err != nil { - os.RemoveAll(tmp) - if os.IsNotExist(err) { - c.Skip(err.Error()) - } - return nil, err - } - - testNotary := &testNotary{ - cmd: cmd, - dir: tmp, - keys: keys, - } - - // Wait for notary to be ready to serve requests. - for i := 1; i <= 20; i++ { - if err = testNotary.Ping(); err == nil { - break - } - time.Sleep(10 * time.Millisecond * time.Duration(i*i)) - } - - if err != nil { - c.Fatalf("Timeout waiting for test notary to become available: %s", err) - } - - return testNotary, nil -} - -func (t *testNotary) Ping() error { - tlsConfig := tlsconfig.ClientDefault() - tlsConfig.InsecureSkipVerify = true - client := http.Client{ - Transport: &http.Transport{ - Proxy: http.ProxyFromEnvironment, - Dial: (&net.Dialer{ - Timeout: 30 * time.Second, - KeepAlive: 30 * time.Second, - }).Dial, - TLSHandshakeTimeout: 10 * time.Second, - TLSClientConfig: tlsConfig, - }, - } - resp, err := client.Get(fmt.Sprintf("%s/v2/", notaryURL)) - if err != nil { - return err - } - if resp.StatusCode != http.StatusOK { - return fmt.Errorf("notary ping replied with an unexpected status code %d", resp.StatusCode) - } - return nil -} - -func (t *testNotary) Close() { - t.cmd.Process.Kill() - t.cmd.Process.Wait() - os.RemoveAll(t.dir) -} - -func trustedCmd(cmd *icmd.Cmd) func() { - pwd := "12345678" - cmd.Env = append(cmd.Env, trustEnv(notaryURL, pwd, pwd)...) - return nil -} - -func trustedCmdWithServer(server string) func(*icmd.Cmd) func() { - return func(cmd *icmd.Cmd) func() { - pwd := "12345678" - cmd.Env = append(cmd.Env, trustEnv(server, pwd, pwd)...) - return nil - } -} - -func trustedCmdWithPassphrases(rootPwd, repositoryPwd string) func(*icmd.Cmd) func() { - return func(cmd *icmd.Cmd) func() { - cmd.Env = append(cmd.Env, trustEnv(notaryURL, rootPwd, repositoryPwd)...) - return nil - } -} - -func trustEnv(server, rootPwd, repositoryPwd string) []string { - env := append(os.Environ(), []string{ - "DOCKER_CONTENT_TRUST=1", - fmt.Sprintf("DOCKER_CONTENT_TRUST_SERVER=%s", server), - fmt.Sprintf("DOCKER_CONTENT_TRUST_ROOT_PASSPHRASE=%s", rootPwd), - fmt.Sprintf("DOCKER_CONTENT_TRUST_REPOSITORY_PASSPHRASE=%s", repositoryPwd), - }...) - return env -} - -func (s *DockerTrustSuite) setupTrustedImage(c *check.C, name string) string { - repoName := fmt.Sprintf("%v/dockercli/%s:latest", privateRegistryURL, name) - // tag the image and upload it to the private registry - cli.DockerCmd(c, "tag", "busybox", repoName) - cli.Docker(cli.Args("push", repoName), trustedCmd).Assert(c, SuccessSigningAndPushing) - cli.DockerCmd(c, "rmi", repoName) - return repoName -} - -func (s *DockerTrustSuite) setupTrustedplugin(c *check.C, source, name string) string { - repoName := fmt.Sprintf("%v/dockercli/%s:latest", privateRegistryURL, name) - - client := testEnv.APIClient() - - ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) - err := plugin.Create(ctx, client, repoName) - cancel() - c.Assert(err, checker.IsNil, check.Commentf("could not create test plugin")) - - // tag the image and upload it to the private registry - // TODO: shouldn't need to use the CLI to do trust - cli.Docker(cli.Args("plugin", "push", repoName), trustedCmd).Assert(c, SuccessSigningAndPushing) - - ctx, cancel = context.WithTimeout(context.Background(), 60*time.Second) - err = client.PluginRemove(ctx, repoName, types.PluginRemoveOptions{Force: true}) - cancel() - c.Assert(err, checker.IsNil, check.Commentf("failed to cleanup test plugin for trust suite")) - return repoName -} - -func (s *DockerTrustSuite) notaryCmd(c *check.C, args ...string) string { - pwd := "12345678" - env := []string{ - fmt.Sprintf("NOTARY_ROOT_PASSPHRASE=%s", pwd), - fmt.Sprintf("NOTARY_TARGETS_PASSPHRASE=%s", pwd), - fmt.Sprintf("NOTARY_SNAPSHOT_PASSPHRASE=%s", pwd), - fmt.Sprintf("NOTARY_DELEGATION_PASSPHRASE=%s", pwd), - } - result := icmd.RunCmd(icmd.Cmd{ - Command: append([]string{notaryBinary, "-c", filepath.Join(s.not.dir, "client-config.json")}, args...), - Env: append(os.Environ(), env...), - }) - result.Assert(c, icmd.Success) - return result.Combined() -} - -func (s *DockerTrustSuite) notaryInitRepo(c *check.C, repoName string) { - s.notaryCmd(c, "init", repoName) -} - -func (s *DockerTrustSuite) notaryCreateDelegation(c *check.C, repoName, role string, pubKey string, paths ...string) { - pathsArg := "--all-paths" - if len(paths) > 0 { - pathsArg = "--paths=" + strings.Join(paths, ",") - } - - s.notaryCmd(c, "delegation", "add", repoName, role, pubKey, pathsArg) -} - -func (s *DockerTrustSuite) notaryPublish(c *check.C, repoName string) { - s.notaryCmd(c, "publish", repoName) -} - -func (s *DockerTrustSuite) notaryImportKey(c *check.C, repoName, role string, privKey string) { - s.notaryCmd(c, "key", "import", privKey, "-g", repoName, "-r", role) -} - -func (s *DockerTrustSuite) notaryListTargetsInRole(c *check.C, repoName, role string) map[string]string { - out := s.notaryCmd(c, "list", repoName, "-r", role) - - // should look something like: - // NAME DIGEST SIZE (BYTES) ROLE - // ------------------------------------------------------------------------------------------------------ - // latest 24a36bbc059b1345b7e8be0df20f1b23caa3602e85d42fff7ecd9d0bd255de56 1377 targets - - targets := make(map[string]string) - - // no target - lines := strings.Split(strings.TrimSpace(out), "\n") - if len(lines) == 1 && strings.Contains(out, "No targets present in this repository.") { - return targets - } - - // otherwise, there is at least one target - c.Assert(len(lines), checker.GreaterOrEqualThan, 3) - - for _, line := range lines[2:] { - tokens := strings.Fields(line) - c.Assert(tokens, checker.HasLen, 4) - targets[tokens[0]] = tokens[3] - } - - return targets -} - -func (s *DockerTrustSuite) assertTargetInRoles(c *check.C, repoName, target string, roles ...string) { - // check all the roles - for _, role := range roles { - targets := s.notaryListTargetsInRole(c, repoName, role) - roleName, ok := targets[target] - c.Assert(ok, checker.True) - c.Assert(roleName, checker.Equals, role) - } -} - -func (s *DockerTrustSuite) assertTargetNotInRoles(c *check.C, repoName, target string, roles ...string) { - targets := s.notaryListTargetsInRole(c, repoName, "targets") - - roleName, ok := targets[target] - if ok { - for _, role := range roles { - c.Assert(roleName, checker.Not(checker.Equals), role) - } - } -} diff --git a/components/engine/registry/config.go b/components/engine/registry/config.go index 68bd01c481..de5a526b69 100644 --- a/components/engine/registry/config.go +++ b/components/engine/registry/config.go @@ -45,9 +45,6 @@ var ( // IndexName is the name of the index IndexName = "docker.io" - // NotaryServer is the endpoint serving the Notary trust server - NotaryServer = "https://notary.docker.io" - // DefaultV2Registry is the URI of the default v2 registry DefaultV2Registry = &url.URL{ Scheme: "https",