github.com/panekj/cli@v0.0.0-20230304125325-467dd2f3797e/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  			ImageContext{
   126  				Context: Context{
   127  					Format: "{{nil}}",
   128  				},
   129  			},
   130  			`template parsing error: template: :1:2: executing "" at <nil>: nil is not a command`,
   131  		},
   132  		// Table Format
   133  		{
   134  			ImageContext{
   135  				Context: Context{
   136  					Format: NewImageFormat("table", false, false),
   137  				},
   138  			},
   139  			`REPOSITORY   TAG       IMAGE ID   CREATED        SIZE
   140  image        tag1      imageID1   24 hours ago   0B
   141  image        tag2      imageID2   N/A            0B
   142  <none>       <none>    imageID3   24 hours ago   0B
   143  `,
   144  		},
   145  		{
   146  			ImageContext{
   147  				Context: Context{
   148  					Format: NewImageFormat("table {{.Repository}}", false, false),
   149  				},
   150  			},
   151  			"REPOSITORY\nimage\nimage\n<none>\n",
   152  		},
   153  		{
   154  			ImageContext{
   155  				Context: Context{
   156  					Format: NewImageFormat("table {{.Repository}}", false, true),
   157  				},
   158  				Digest: true,
   159  			},
   160  			`REPOSITORY   DIGEST
   161  image        sha256:cbbf2f9a99b47fc460d422812b6a5adff7dfee951d8fa2e4a98caa0382cfbdbf
   162  image        <none>
   163  <none>       <none>
   164  `,
   165  		},
   166  		{
   167  			ImageContext{
   168  				Context: Context{
   169  					Format: NewImageFormat("table {{.Repository}}", true, false),
   170  				},
   171  			},
   172  			"REPOSITORY\nimage\nimage\n<none>\n",
   173  		},
   174  		{
   175  			ImageContext{
   176  				Context: Context{
   177  					Format: NewImageFormat("table {{.Digest}}", true, false),
   178  				},
   179  			},
   180  			"DIGEST\nsha256:cbbf2f9a99b47fc460d422812b6a5adff7dfee951d8fa2e4a98caa0382cfbdbf\n<none>\n<none>\n",
   181  		},
   182  		{
   183  			ImageContext{
   184  				Context: Context{
   185  					Format: NewImageFormat("table", true, false),
   186  				},
   187  			},
   188  			"imageID1\nimageID2\nimageID3\n",
   189  		},
   190  		{
   191  			ImageContext{
   192  				Context: Context{
   193  					Format: NewImageFormat("table", false, true),
   194  				},
   195  				Digest: true,
   196  			},
   197  			`REPOSITORY   TAG       DIGEST                                                                    IMAGE ID   CREATED        SIZE
   198  image        tag1      sha256:cbbf2f9a99b47fc460d422812b6a5adff7dfee951d8fa2e4a98caa0382cfbdbf   imageID1   24 hours ago   0B
   199  image        tag2      <none>                                                                    imageID2   N/A            0B
   200  <none>       <none>    <none>                                                                    imageID3   24 hours ago   0B
   201  `,
   202  		},
   203  		{
   204  			ImageContext{
   205  				Context: Context{
   206  					Format: NewImageFormat("table", true, true),
   207  				},
   208  				Digest: true,
   209  			},
   210  			"imageID1\nimageID2\nimageID3\n",
   211  		},
   212  		// Raw Format
   213  		{
   214  			ImageContext{
   215  				Context: Context{
   216  					Format: NewImageFormat("raw", false, false),
   217  				},
   218  			},
   219  			fmt.Sprintf(`repository: image
   220  tag: tag1
   221  image_id: imageID1
   222  created_at: %s
   223  virtual_size: 0B
   224  
   225  repository: image
   226  tag: tag2
   227  image_id: imageID2
   228  created_at: %s
   229  virtual_size: 0B
   230  
   231  repository: <none>
   232  tag: <none>
   233  image_id: imageID3
   234  created_at: %s
   235  virtual_size: 0B
   236  
   237  `, expectedTime, expectedZeroTime, expectedTime),
   238  		},
   239  		{
   240  			ImageContext{
   241  				Context: Context{
   242  					Format: NewImageFormat("raw", false, true),
   243  				},
   244  				Digest: true,
   245  			},
   246  			fmt.Sprintf(`repository: image
   247  tag: tag1
   248  digest: sha256:cbbf2f9a99b47fc460d422812b6a5adff7dfee951d8fa2e4a98caa0382cfbdbf
   249  image_id: imageID1
   250  created_at: %s
   251  virtual_size: 0B
   252  
   253  repository: image
   254  tag: tag2
   255  digest: <none>
   256  image_id: imageID2
   257  created_at: %s
   258  virtual_size: 0B
   259  
   260  repository: <none>
   261  tag: <none>
   262  digest: <none>
   263  image_id: imageID3
   264  created_at: %s
   265  virtual_size: 0B
   266  
   267  `, expectedTime, expectedZeroTime, expectedTime),
   268  		},
   269  		{
   270  			ImageContext{
   271  				Context: Context{
   272  					Format: NewImageFormat("raw", true, false),
   273  				},
   274  			},
   275  			`image_id: imageID1
   276  image_id: imageID2
   277  image_id: imageID3
   278  `,
   279  		},
   280  		// Custom Format
   281  		{
   282  			ImageContext{
   283  				Context: Context{
   284  					Format: NewImageFormat("{{.Repository}}", false, false),
   285  				},
   286  			},
   287  			"image\nimage\n<none>\n",
   288  		},
   289  		{
   290  			ImageContext{
   291  				Context: Context{
   292  					Format: NewImageFormat("{{.Repository}}", false, true),
   293  				},
   294  				Digest: true,
   295  			},
   296  			"image\nimage\n<none>\n",
   297  		},
   298  	}
   299  
   300  	images := []types.ImageSummary{
   301  		{ID: "imageID1", RepoTags: []string{"image:tag1"}, RepoDigests: []string{"image@sha256:cbbf2f9a99b47fc460d422812b6a5adff7dfee951d8fa2e4a98caa0382cfbdbf"}, Created: unixTime},
   302  		{ID: "imageID2", RepoTags: []string{"image:tag2"}, Created: zeroTime},
   303  		{ID: "imageID3", RepoTags: []string{"<none>:<none>"}, RepoDigests: []string{"<none>@<none>"}, Created: unixTime},
   304  	}
   305  
   306  	for _, tc := range cases {
   307  		tc := tc
   308  		t.Run(string(tc.context.Format), func(t *testing.T) {
   309  			var out bytes.Buffer
   310  			tc.context.Output = &out
   311  			err := ImageWrite(tc.context, images)
   312  			if err != nil {
   313  				assert.Error(t, err, tc.expected)
   314  			} else {
   315  				assert.Equal(t, out.String(), tc.expected)
   316  			}
   317  		})
   318  	}
   319  }
   320  
   321  func TestImageContextWriteWithNoImage(t *testing.T) {
   322  	out := bytes.NewBufferString("")
   323  	images := []types.ImageSummary{}
   324  
   325  	cases := []struct {
   326  		context  ImageContext
   327  		expected string
   328  	}{
   329  		{
   330  			ImageContext{
   331  				Context: Context{
   332  					Format: NewImageFormat("{{.Repository}}", false, false),
   333  					Output: out,
   334  				},
   335  			},
   336  			"",
   337  		},
   338  		{
   339  			ImageContext{
   340  				Context: Context{
   341  					Format: NewImageFormat("table {{.Repository}}", false, false),
   342  					Output: out,
   343  				},
   344  			},
   345  			"REPOSITORY\n",
   346  		},
   347  		{
   348  			ImageContext{
   349  				Context: Context{
   350  					Format: NewImageFormat("{{.Repository}}", false, true),
   351  					Output: out,
   352  				},
   353  			},
   354  			"",
   355  		},
   356  		{
   357  			ImageContext{
   358  				Context: Context{
   359  					Format: NewImageFormat("table {{.Repository}}", false, true),
   360  					Output: out,
   361  				},
   362  			},
   363  			"REPOSITORY   DIGEST\n",
   364  		},
   365  	}
   366  
   367  	for _, tc := range cases {
   368  		tc := tc
   369  		t.Run(string(tc.context.Format), func(t *testing.T) {
   370  			err := ImageWrite(tc.context, images)
   371  			assert.NilError(t, err)
   372  			assert.Equal(t, out.String(), tc.expected)
   373  			// Clean buffer
   374  			out.Reset()
   375  		})
   376  	}
   377  }