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 }