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 }