github.1git.de/docker/cli@v26.1.3+incompatible/cli/command/container/list_test.go (about)

     1  package container
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"testing"
     7  
     8  	"github.com/docker/cli/cli/config/configfile"
     9  	"github.com/docker/cli/internal/test"
    10  	"github.com/docker/cli/internal/test/builders"
    11  	"github.com/docker/cli/opts"
    12  	"github.com/docker/docker/api/types"
    13  	"github.com/docker/docker/api/types/container"
    14  	"gotest.tools/v3/assert"
    15  	is "gotest.tools/v3/assert/cmp"
    16  	"gotest.tools/v3/golden"
    17  )
    18  
    19  func TestContainerListBuildContainerListOptions(t *testing.T) {
    20  	filters := opts.NewFilterOpt()
    21  	assert.NilError(t, filters.Set("foo=bar"))
    22  	assert.NilError(t, filters.Set("baz=foo"))
    23  
    24  	contexts := []struct {
    25  		psOpts          *psOptions
    26  		expectedAll     bool
    27  		expectedSize    bool
    28  		expectedLimit   int
    29  		expectedFilters map[string]string
    30  	}{
    31  		{
    32  			psOpts: &psOptions{
    33  				all:    true,
    34  				size:   true,
    35  				last:   5,
    36  				filter: filters,
    37  			},
    38  			expectedAll:   true,
    39  			expectedSize:  true,
    40  			expectedLimit: 5,
    41  			expectedFilters: map[string]string{
    42  				"foo": "bar",
    43  				"baz": "foo",
    44  			},
    45  		},
    46  		{
    47  			psOpts: &psOptions{
    48  				all:     true,
    49  				size:    true,
    50  				last:    -1,
    51  				nLatest: true,
    52  			},
    53  			expectedAll:     true,
    54  			expectedSize:    true,
    55  			expectedLimit:   1,
    56  			expectedFilters: make(map[string]string),
    57  		},
    58  		{
    59  			psOpts: &psOptions{
    60  				all:    true,
    61  				size:   false,
    62  				last:   5,
    63  				filter: filters,
    64  				// With .Size, size should be true
    65  				format: "{{.Size}}",
    66  			},
    67  			expectedAll:   true,
    68  			expectedSize:  true,
    69  			expectedLimit: 5,
    70  			expectedFilters: map[string]string{
    71  				"foo": "bar",
    72  				"baz": "foo",
    73  			},
    74  		},
    75  		{
    76  			psOpts: &psOptions{
    77  				all:    true,
    78  				size:   false,
    79  				last:   5,
    80  				filter: filters,
    81  				// With .Size, size should be true
    82  				format: "{{.Size}} {{.CreatedAt}} {{upper .Networks}}",
    83  			},
    84  			expectedAll:   true,
    85  			expectedSize:  true,
    86  			expectedLimit: 5,
    87  			expectedFilters: map[string]string{
    88  				"foo": "bar",
    89  				"baz": "foo",
    90  			},
    91  		},
    92  		{
    93  			psOpts: &psOptions{
    94  				all:    true,
    95  				size:   false,
    96  				last:   5,
    97  				filter: filters,
    98  				// Without .Size, size should be false
    99  				format: "{{.CreatedAt}} {{.Networks}}",
   100  			},
   101  			expectedAll:   true,
   102  			expectedSize:  false,
   103  			expectedLimit: 5,
   104  			expectedFilters: map[string]string{
   105  				"foo": "bar",
   106  				"baz": "foo",
   107  			},
   108  		},
   109  	}
   110  
   111  	for _, c := range contexts {
   112  		options, err := buildContainerListOptions(c.psOpts)
   113  		assert.NilError(t, err)
   114  
   115  		assert.Check(t, is.Equal(c.expectedAll, options.All))
   116  		assert.Check(t, is.Equal(c.expectedSize, options.Size))
   117  		assert.Check(t, is.Equal(c.expectedLimit, options.Limit))
   118  		assert.Check(t, is.Equal(len(c.expectedFilters), options.Filters.Len()))
   119  
   120  		for k, v := range c.expectedFilters {
   121  			f := options.Filters
   122  			if !f.ExactMatch(k, v) {
   123  				t.Fatalf("Expected filter with key %s to be %s but got %s", k, v, f.Get(k))
   124  			}
   125  		}
   126  	}
   127  }
   128  
   129  func TestContainerListErrors(t *testing.T) {
   130  	testCases := []struct {
   131  		args              []string
   132  		flags             map[string]string
   133  		containerListFunc func(container.ListOptions) ([]types.Container, error)
   134  		expectedError     string
   135  	}{
   136  		{
   137  			flags: map[string]string{
   138  				"format": "{{invalid}}",
   139  			},
   140  			expectedError: `function "invalid" not defined`,
   141  		},
   142  		{
   143  			flags: map[string]string{
   144  				"format": "{{join}}",
   145  			},
   146  			expectedError: `wrong number of args for join`,
   147  		},
   148  		{
   149  			containerListFunc: func(_ container.ListOptions) ([]types.Container, error) {
   150  				return nil, fmt.Errorf("error listing containers")
   151  			},
   152  			expectedError: "error listing containers",
   153  		},
   154  	}
   155  	for _, tc := range testCases {
   156  		cmd := newListCommand(
   157  			test.NewFakeCli(&fakeClient{
   158  				containerListFunc: tc.containerListFunc,
   159  			}),
   160  		)
   161  		cmd.SetArgs(tc.args)
   162  		for key, value := range tc.flags {
   163  			assert.Check(t, cmd.Flags().Set(key, value))
   164  		}
   165  		cmd.SetOut(io.Discard)
   166  		assert.ErrorContains(t, cmd.Execute(), tc.expectedError)
   167  	}
   168  }
   169  
   170  func TestContainerListWithoutFormat(t *testing.T) {
   171  	cli := test.NewFakeCli(&fakeClient{
   172  		containerListFunc: func(_ container.ListOptions) ([]types.Container, error) {
   173  			return []types.Container{
   174  				*builders.Container("c1"),
   175  				*builders.Container("c2", builders.WithName("foo")),
   176  				*builders.Container("c3", builders.WithPort(80, 80, builders.TCP), builders.WithPort(81, 81, builders.TCP), builders.WithPort(82, 82, builders.TCP)),
   177  				*builders.Container("c4", builders.WithPort(81, 81, builders.UDP)),
   178  				*builders.Container("c5", builders.WithPort(82, 82, builders.IP("8.8.8.8"), builders.TCP)),
   179  			}, nil
   180  		},
   181  	})
   182  	cmd := newListCommand(cli)
   183  	assert.NilError(t, cmd.Execute())
   184  	golden.Assert(t, cli.OutBuffer().String(), "container-list-without-format.golden")
   185  }
   186  
   187  func TestContainerListNoTrunc(t *testing.T) {
   188  	cli := test.NewFakeCli(&fakeClient{
   189  		containerListFunc: func(_ container.ListOptions) ([]types.Container, error) {
   190  			return []types.Container{
   191  				*builders.Container("c1"),
   192  				*builders.Container("c2", builders.WithName("foo/bar")),
   193  			}, nil
   194  		},
   195  	})
   196  	cmd := newListCommand(cli)
   197  	assert.Check(t, cmd.Flags().Set("no-trunc", "true"))
   198  	assert.NilError(t, cmd.Execute())
   199  	golden.Assert(t, cli.OutBuffer().String(), "container-list-without-format-no-trunc.golden")
   200  }
   201  
   202  // Test for GitHub issue docker/docker#21772
   203  func TestContainerListNamesMultipleTime(t *testing.T) {
   204  	cli := test.NewFakeCli(&fakeClient{
   205  		containerListFunc: func(_ container.ListOptions) ([]types.Container, error) {
   206  			return []types.Container{
   207  				*builders.Container("c1"),
   208  				*builders.Container("c2", builders.WithName("foo/bar")),
   209  			}, nil
   210  		},
   211  	})
   212  	cmd := newListCommand(cli)
   213  	assert.Check(t, cmd.Flags().Set("format", "{{.Names}} {{.Names}}"))
   214  	assert.NilError(t, cmd.Execute())
   215  	golden.Assert(t, cli.OutBuffer().String(), "container-list-format-name-name.golden")
   216  }
   217  
   218  // Test for GitHub issue docker/docker#30291
   219  func TestContainerListFormatTemplateWithArg(t *testing.T) {
   220  	cli := test.NewFakeCli(&fakeClient{
   221  		containerListFunc: func(_ container.ListOptions) ([]types.Container, error) {
   222  			return []types.Container{
   223  				*builders.Container("c1", builders.WithLabel("some.label", "value")),
   224  				*builders.Container("c2", builders.WithName("foo/bar"), builders.WithLabel("foo", "bar")),
   225  			}, nil
   226  		},
   227  	})
   228  	cmd := newListCommand(cli)
   229  	assert.Check(t, cmd.Flags().Set("format", `{{.Names}} {{.Label "some.label"}}`))
   230  	assert.NilError(t, cmd.Execute())
   231  	golden.Assert(t, cli.OutBuffer().String(), "container-list-format-with-arg.golden")
   232  }
   233  
   234  func TestContainerListFormatSizeSetsOption(t *testing.T) {
   235  	tests := []struct {
   236  		doc, format, sizeFlag string
   237  		sizeExpected          bool
   238  	}{
   239  		{
   240  			doc:          "detect with all fields",
   241  			format:       `{{json .}}`,
   242  			sizeExpected: true,
   243  		},
   244  		{
   245  			doc:          "detect with explicit field",
   246  			format:       `{{.Size}}`,
   247  			sizeExpected: true,
   248  		},
   249  		{
   250  			doc:          "detect no size",
   251  			format:       `{{.Names}}`,
   252  			sizeExpected: false,
   253  		},
   254  		{
   255  			doc:          "override enable",
   256  			format:       `{{.Names}}`,
   257  			sizeFlag:     "true",
   258  			sizeExpected: true,
   259  		},
   260  		{
   261  			doc:          "override disable",
   262  			format:       `{{.Size}}`,
   263  			sizeFlag:     "false",
   264  			sizeExpected: false,
   265  		},
   266  	}
   267  
   268  	for _, tc := range tests {
   269  		tc := tc
   270  		t.Run(tc.doc, func(t *testing.T) {
   271  			cli := test.NewFakeCli(&fakeClient{
   272  				containerListFunc: func(options container.ListOptions) ([]types.Container, error) {
   273  					assert.Check(t, is.Equal(options.Size, tc.sizeExpected))
   274  					return []types.Container{}, nil
   275  				},
   276  			})
   277  			cmd := newListCommand(cli)
   278  			assert.Check(t, cmd.Flags().Set("format", tc.format))
   279  			if tc.sizeFlag != "" {
   280  				assert.Check(t, cmd.Flags().Set("size", tc.sizeFlag))
   281  			}
   282  			assert.NilError(t, cmd.Execute())
   283  		})
   284  	}
   285  }
   286  
   287  func TestContainerListWithConfigFormat(t *testing.T) {
   288  	cli := test.NewFakeCli(&fakeClient{
   289  		containerListFunc: func(_ container.ListOptions) ([]types.Container, error) {
   290  			return []types.Container{
   291  				*builders.Container("c1", builders.WithLabel("some.label", "value"), builders.WithSize(10700000)),
   292  				*builders.Container("c2", builders.WithName("foo/bar"), builders.WithLabel("foo", "bar"), builders.WithSize(3200000)),
   293  			}, nil
   294  		},
   295  	})
   296  	cli.SetConfigFile(&configfile.ConfigFile{
   297  		PsFormat: "{{ .Names }} {{ .Image }} {{ .Labels }} {{ .Size}}",
   298  	})
   299  	cmd := newListCommand(cli)
   300  	assert.NilError(t, cmd.Execute())
   301  	golden.Assert(t, cli.OutBuffer().String(), "container-list-with-config-format.golden")
   302  }
   303  
   304  func TestContainerListWithFormat(t *testing.T) {
   305  	cli := test.NewFakeCli(&fakeClient{
   306  		containerListFunc: func(_ container.ListOptions) ([]types.Container, error) {
   307  			return []types.Container{
   308  				*builders.Container("c1", builders.WithLabel("some.label", "value")),
   309  				*builders.Container("c2", builders.WithName("foo/bar"), builders.WithLabel("foo", "bar")),
   310  			}, nil
   311  		},
   312  	})
   313  
   314  	t.Run("with format", func(t *testing.T) {
   315  		cli.OutBuffer().Reset()
   316  		cmd := newListCommand(cli)
   317  		assert.Check(t, cmd.Flags().Set("format", "{{ .Names }} {{ .Image }} {{ .Labels }}"))
   318  		assert.NilError(t, cmd.Execute())
   319  		golden.Assert(t, cli.OutBuffer().String(), "container-list-with-format.golden")
   320  	})
   321  
   322  	t.Run("with format and quiet", func(t *testing.T) {
   323  		cli.OutBuffer().Reset()
   324  		cmd := newListCommand(cli)
   325  		assert.Check(t, cmd.Flags().Set("format", "{{ .Names }} {{ .Image }} {{ .Labels }}"))
   326  		assert.Check(t, cmd.Flags().Set("quiet", "true"))
   327  		assert.NilError(t, cmd.Execute())
   328  		assert.Equal(t, cli.ErrBuffer().String(), "WARNING: Ignoring custom format, because both --format and --quiet are set.\n")
   329  		golden.Assert(t, cli.OutBuffer().String(), "container-list-quiet.golden")
   330  	})
   331  }