github.com/xeptore/docker-cli@v20.10.14+incompatible/cli/command/formatter/image_test.go (about)

     1  package formatter
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"strings"
     7  	"testing"
     8  	"time"
     9  
    10  	"github.com/docker/cli/internal/test"
    11  	"github.com/docker/docker/api/types"
    12  	"github.com/docker/docker/pkg/stringid"
    13  	"gotest.tools/v3/assert"
    14  	is "gotest.tools/v3/assert/cmp"
    15  )
    16  
    17  func TestImageContext(t *testing.T) {
    18  	imageID := stringid.GenerateRandomID()
    19  	unix := time.Now().Unix()
    20  	zeroTime := int64(-62135596800)
    21  
    22  	var ctx imageContext
    23  	cases := []struct {
    24  		imageCtx imageContext
    25  		expValue string
    26  		call     func() string
    27  	}{
    28  		{
    29  			imageCtx: imageContext{i: types.ImageSummary{ID: imageID}, trunc: true},
    30  			expValue: stringid.TruncateID(imageID),
    31  			call:     ctx.ID,
    32  		},
    33  		{
    34  			imageCtx: imageContext{i: types.ImageSummary{ID: imageID}, trunc: false},
    35  			expValue: imageID,
    36  			call:     ctx.ID,
    37  		},
    38  		{
    39  			imageCtx: imageContext{i: types.ImageSummary{Size: 10, VirtualSize: 10}, trunc: true},
    40  			expValue: "10B",
    41  			call:     ctx.Size,
    42  		},
    43  		{
    44  			imageCtx: imageContext{i: types.ImageSummary{Created: unix}, trunc: true},
    45  			expValue: time.Unix(unix, 0).String(), call: ctx.CreatedAt,
    46  		},
    47  		// FIXME
    48  		// {imageContext{
    49  		// 	i:     types.ImageSummary{Created: unix},
    50  		// 	trunc: true,
    51  		// }, units.HumanDuration(time.Unix(unix, 0)), createdSinceHeader, ctx.CreatedSince},
    52  		{
    53  			imageCtx: imageContext{i: types.ImageSummary{}, repo: "busybox"},
    54  			expValue: "busybox",
    55  			call:     ctx.Repository,
    56  		},
    57  		{
    58  			imageCtx: imageContext{i: types.ImageSummary{}, tag: "latest"},
    59  			expValue: "latest",
    60  			call:     ctx.Tag,
    61  		},
    62  		{
    63  			imageCtx: imageContext{i: types.ImageSummary{}, digest: "sha256:d149ab53f8718e987c3a3024bb8aa0e2caadf6c0328f1d9d850b2a2a67f2819a"},
    64  			expValue: "sha256:d149ab53f8718e987c3a3024bb8aa0e2caadf6c0328f1d9d850b2a2a67f2819a",
    65  			call:     ctx.Digest,
    66  		},
    67  		{
    68  			imageCtx: imageContext{i: types.ImageSummary{Containers: 10}},
    69  			expValue: "10",
    70  			call:     ctx.Containers,
    71  		},
    72  		{
    73  			imageCtx: imageContext{i: types.ImageSummary{VirtualSize: 10000}},
    74  			expValue: "10kB",
    75  			call:     ctx.VirtualSize,
    76  		},
    77  		{
    78  			imageCtx: imageContext{i: types.ImageSummary{SharedSize: 10000}},
    79  			expValue: "10kB",
    80  			call:     ctx.SharedSize,
    81  		},
    82  		{
    83  			imageCtx: imageContext{i: types.ImageSummary{SharedSize: 5000, VirtualSize: 20000}},
    84  			expValue: "15kB",
    85  			call:     ctx.UniqueSize,
    86  		},
    87  		{
    88  			imageCtx: imageContext{i: types.ImageSummary{Created: zeroTime}},
    89  			expValue: "",
    90  			call:     ctx.CreatedSince,
    91  		},
    92  	}
    93  
    94  	for _, c := range cases {
    95  		ctx = c.imageCtx
    96  		v := c.call()
    97  		if strings.Contains(v, ",") {
    98  			test.CompareMultipleValues(t, v, c.expValue)
    99  		} else {
   100  			assert.Check(t, is.Equal(c.expValue, v))
   101  		}
   102  	}
   103  }
   104  
   105  func TestImageContextWrite(t *testing.T) {
   106  	unixTime := time.Now().AddDate(0, 0, -1).Unix()
   107  	zeroTime := int64(-62135596800)
   108  	expectedTime := time.Unix(unixTime, 0).String()
   109  	expectedZeroTime := time.Unix(zeroTime, 0).String()
   110  
   111  	cases := []struct {
   112  		context  ImageContext
   113  		expected string
   114  	}{
   115  		// Errors
   116  		{
   117  			ImageContext{
   118  				Context: Context{
   119  					Format: "{{InvalidFunction}}",
   120  				},
   121  			},
   122  			`Template parsing error: template: :1: function "InvalidFunction" not defined
   123  `,
   124  		},
   125  		{
   126  			ImageContext{
   127  				Context: Context{
   128  					Format: "{{nil}}",
   129  				},
   130  			},
   131  			`Template parsing error: template: :1:2: executing "" at <nil>: nil is not a command
   132  `,
   133  		},
   134  		// Table Format
   135  		{
   136  			ImageContext{
   137  				Context: Context{
   138  					Format: NewImageFormat("table", false, false),
   139  				},
   140  			},
   141  			`REPOSITORY   TAG       IMAGE ID   CREATED        SIZE
   142  image        tag1      imageID1   24 hours ago   0B
   143  image        tag2      imageID2   N/A            0B
   144  <none>       <none>    imageID3   24 hours ago   0B
   145  `,
   146  		},
   147  		{
   148  			ImageContext{
   149  				Context: Context{
   150  					Format: NewImageFormat("table {{.Repository}}", false, false),
   151  				},
   152  			},
   153  			"REPOSITORY\nimage\nimage\n<none>\n",
   154  		},
   155  		{
   156  			ImageContext{
   157  				Context: Context{
   158  					Format: NewImageFormat("table {{.Repository}}", false, true),
   159  				},
   160  				Digest: true,
   161  			},
   162  			`REPOSITORY   DIGEST
   163  image        sha256:cbbf2f9a99b47fc460d422812b6a5adff7dfee951d8fa2e4a98caa0382cfbdbf
   164  image        <none>
   165  <none>       <none>
   166  `,
   167  		},
   168  		{
   169  			ImageContext{
   170  				Context: Context{
   171  					Format: NewImageFormat("table {{.Repository}}", true, false),
   172  				},
   173  			},
   174  			"REPOSITORY\nimage\nimage\n<none>\n",
   175  		},
   176  		{
   177  			ImageContext{
   178  				Context: Context{
   179  					Format: NewImageFormat("table {{.Digest}}", true, false),
   180  				},
   181  			},
   182  			"DIGEST\nsha256:cbbf2f9a99b47fc460d422812b6a5adff7dfee951d8fa2e4a98caa0382cfbdbf\n<none>\n<none>\n",
   183  		},
   184  		{
   185  			ImageContext{
   186  				Context: Context{
   187  					Format: NewImageFormat("table", true, false),
   188  				},
   189  			},
   190  			"imageID1\nimageID2\nimageID3\n",
   191  		},
   192  		{
   193  			ImageContext{
   194  				Context: Context{
   195  					Format: NewImageFormat("table", false, true),
   196  				},
   197  				Digest: true,
   198  			},
   199  			`REPOSITORY   TAG       DIGEST                                                                    IMAGE ID   CREATED        SIZE
   200  image        tag1      sha256:cbbf2f9a99b47fc460d422812b6a5adff7dfee951d8fa2e4a98caa0382cfbdbf   imageID1   24 hours ago   0B
   201  image        tag2      <none>                                                                    imageID2   N/A            0B
   202  <none>       <none>    <none>                                                                    imageID3   24 hours ago   0B
   203  `,
   204  		},
   205  		{
   206  			ImageContext{
   207  				Context: Context{
   208  					Format: NewImageFormat("table", true, true),
   209  				},
   210  				Digest: true,
   211  			},
   212  			"imageID1\nimageID2\nimageID3\n",
   213  		},
   214  		// Raw Format
   215  		{
   216  			ImageContext{
   217  				Context: Context{
   218  					Format: NewImageFormat("raw", false, false),
   219  				},
   220  			},
   221  			fmt.Sprintf(`repository: image
   222  tag: tag1
   223  image_id: imageID1
   224  created_at: %s
   225  virtual_size: 0B
   226  
   227  repository: image
   228  tag: tag2
   229  image_id: imageID2
   230  created_at: %s
   231  virtual_size: 0B
   232  
   233  repository: <none>
   234  tag: <none>
   235  image_id: imageID3
   236  created_at: %s
   237  virtual_size: 0B
   238  
   239  `, expectedTime, expectedZeroTime, expectedTime),
   240  		},
   241  		{
   242  			ImageContext{
   243  				Context: Context{
   244  					Format: NewImageFormat("raw", false, true),
   245  				},
   246  				Digest: true,
   247  			},
   248  			fmt.Sprintf(`repository: image
   249  tag: tag1
   250  digest: sha256:cbbf2f9a99b47fc460d422812b6a5adff7dfee951d8fa2e4a98caa0382cfbdbf
   251  image_id: imageID1
   252  created_at: %s
   253  virtual_size: 0B
   254  
   255  repository: image
   256  tag: tag2
   257  digest: <none>
   258  image_id: imageID2
   259  created_at: %s
   260  virtual_size: 0B
   261  
   262  repository: <none>
   263  tag: <none>
   264  digest: <none>
   265  image_id: imageID3
   266  created_at: %s
   267  virtual_size: 0B
   268  
   269  `, expectedTime, expectedZeroTime, expectedTime),
   270  		},
   271  		{
   272  			ImageContext{
   273  				Context: Context{
   274  					Format: NewImageFormat("raw", true, false),
   275  				},
   276  			},
   277  			`image_id: imageID1
   278  image_id: imageID2
   279  image_id: imageID3
   280  `,
   281  		},
   282  		// Custom Format
   283  		{
   284  			ImageContext{
   285  				Context: Context{
   286  					Format: NewImageFormat("{{.Repository}}", false, false),
   287  				},
   288  			},
   289  			"image\nimage\n<none>\n",
   290  		},
   291  		{
   292  			ImageContext{
   293  				Context: Context{
   294  					Format: NewImageFormat("{{.Repository}}", false, true),
   295  				},
   296  				Digest: true,
   297  			},
   298  			"image\nimage\n<none>\n",
   299  		},
   300  	}
   301  
   302  	images := []types.ImageSummary{
   303  		{ID: "imageID1", RepoTags: []string{"image:tag1"}, RepoDigests: []string{"image@sha256:cbbf2f9a99b47fc460d422812b6a5adff7dfee951d8fa2e4a98caa0382cfbdbf"}, Created: unixTime},
   304  		{ID: "imageID2", RepoTags: []string{"image:tag2"}, Created: zeroTime},
   305  		{ID: "imageID3", RepoTags: []string{"<none>:<none>"}, RepoDigests: []string{"<none>@<none>"}, Created: unixTime},
   306  	}
   307  
   308  	for _, tc := range cases {
   309  		tc := tc
   310  		t.Run(string(tc.context.Format), func(t *testing.T) {
   311  			var out bytes.Buffer
   312  			tc.context.Output = &out
   313  			err := ImageWrite(tc.context, images)
   314  			if err != nil {
   315  				assert.Error(t, err, tc.expected)
   316  			} else {
   317  				assert.Equal(t, out.String(), tc.expected)
   318  			}
   319  		})
   320  	}
   321  }
   322  
   323  func TestImageContextWriteWithNoImage(t *testing.T) {
   324  	out := bytes.NewBufferString("")
   325  	images := []types.ImageSummary{}
   326  
   327  	cases := []struct {
   328  		context  ImageContext
   329  		expected string
   330  	}{
   331  		{
   332  			ImageContext{
   333  				Context: Context{
   334  					Format: NewImageFormat("{{.Repository}}", false, false),
   335  					Output: out,
   336  				},
   337  			},
   338  			"",
   339  		},
   340  		{
   341  			ImageContext{
   342  				Context: Context{
   343  					Format: NewImageFormat("table {{.Repository}}", false, false),
   344  					Output: out,
   345  				},
   346  			},
   347  			"REPOSITORY\n",
   348  		},
   349  		{
   350  			ImageContext{
   351  				Context: Context{
   352  					Format: NewImageFormat("{{.Repository}}", false, true),
   353  					Output: out,
   354  				},
   355  			},
   356  			"",
   357  		},
   358  		{
   359  			ImageContext{
   360  				Context: Context{
   361  					Format: NewImageFormat("table {{.Repository}}", false, true),
   362  					Output: out,
   363  				},
   364  			},
   365  			"REPOSITORY   DIGEST\n",
   366  		},
   367  	}
   368  
   369  	for _, tc := range cases {
   370  		tc := tc
   371  		t.Run(string(tc.context.Format), func(t *testing.T) {
   372  			err := ImageWrite(tc.context, images)
   373  			assert.NilError(t, err)
   374  			assert.Equal(t, out.String(), tc.expected)
   375  			// Clean buffer
   376  			out.Reset()
   377  		})
   378  	}
   379  }