Files
docker-cli/cli/command/formatter/image_test.go
Sebastiaan van Stijn f02301ab5d remove uses of deprecated VirtualSize field
The VirtualSize field is deprecated and the upcoming API version v1.44
will no longer propagate the field. See:
1261fe69a3,

Given that in docker 1.10 and up (API v1.22), the VirtualSize and Size
fields contain the same value, and the "df" endpoint was not supported
until API v1.25, we can "safely" use Size instead; see:

- 4ae7176ffb
- 4352da7803

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2023-04-28 14:58:53 +02:00

378 lines
8.6 KiB
Go

package formatter
import (
"bytes"
"fmt"
"strings"
"testing"
"time"
"github.com/docker/cli/internal/test"
"github.com/docker/docker/api/types"
"github.com/docker/docker/pkg/stringid"
"gotest.tools/v3/assert"
is "gotest.tools/v3/assert/cmp"
)
func TestImageContext(t *testing.T) {
imageID := stringid.GenerateRandomID()
unix := time.Now().Unix()
zeroTime := int64(-62135596800)
var ctx imageContext
cases := []struct {
imageCtx imageContext
expValue string
call func() string
}{
{
imageCtx: imageContext{i: types.ImageSummary{ID: imageID}, trunc: true},
expValue: stringid.TruncateID(imageID),
call: ctx.ID,
},
{
imageCtx: imageContext{i: types.ImageSummary{ID: imageID}, trunc: false},
expValue: imageID,
call: ctx.ID,
},
{
imageCtx: imageContext{i: types.ImageSummary{Size: 10}, trunc: true},
expValue: "10B",
call: ctx.Size,
},
{
imageCtx: imageContext{i: types.ImageSummary{Created: unix}, trunc: true},
expValue: time.Unix(unix, 0).String(), call: ctx.CreatedAt,
},
// FIXME
// {imageContext{
// i: types.ImageSummary{Created: unix},
// trunc: true,
// }, units.HumanDuration(time.Unix(unix, 0)), createdSinceHeader, ctx.CreatedSince},
{
imageCtx: imageContext{i: types.ImageSummary{}, repo: "busybox"},
expValue: "busybox",
call: ctx.Repository,
},
{
imageCtx: imageContext{i: types.ImageSummary{}, tag: "latest"},
expValue: "latest",
call: ctx.Tag,
},
{
imageCtx: imageContext{i: types.ImageSummary{}, digest: "sha256:d149ab53f8718e987c3a3024bb8aa0e2caadf6c0328f1d9d850b2a2a67f2819a"},
expValue: "sha256:d149ab53f8718e987c3a3024bb8aa0e2caadf6c0328f1d9d850b2a2a67f2819a",
call: ctx.Digest,
},
{
imageCtx: imageContext{i: types.ImageSummary{Containers: 10}},
expValue: "10",
call: ctx.Containers,
},
{
imageCtx: imageContext{i: types.ImageSummary{Size: 10000}},
expValue: "10kB",
call: ctx.VirtualSize, //nolint:staticcheck // ignore SA1019: field is deprecated, but still set on API < v1.44.
},
{
imageCtx: imageContext{i: types.ImageSummary{SharedSize: 10000}},
expValue: "10kB",
call: ctx.SharedSize,
},
{
imageCtx: imageContext{i: types.ImageSummary{SharedSize: 5000, Size: 20000}},
expValue: "15kB",
call: ctx.UniqueSize,
},
{
imageCtx: imageContext{i: types.ImageSummary{Created: zeroTime}},
expValue: "",
call: ctx.CreatedSince,
},
}
for _, c := range cases {
ctx = c.imageCtx
v := c.call()
if strings.Contains(v, ",") {
test.CompareMultipleValues(t, v, c.expValue)
} else {
assert.Check(t, is.Equal(c.expValue, v))
}
}
}
func TestImageContextWrite(t *testing.T) {
unixTime := time.Now().AddDate(0, 0, -1).Unix()
zeroTime := int64(-62135596800)
expectedTime := time.Unix(unixTime, 0).String()
expectedZeroTime := time.Unix(zeroTime, 0).String()
cases := []struct {
context ImageContext
expected string
}{
// Errors
{
ImageContext{
Context: Context{
Format: "{{InvalidFunction}}",
},
},
`template parsing error: template: :1: function "InvalidFunction" not defined`,
},
{
ImageContext{
Context: Context{
Format: "{{nil}}",
},
},
`template parsing error: template: :1:2: executing "" at <nil>: nil is not a command`,
},
// Table Format
{
ImageContext{
Context: Context{
Format: NewImageFormat("table", false, false),
},
},
`REPOSITORY TAG IMAGE ID CREATED SIZE
image tag1 imageID1 24 hours ago 0B
image tag2 imageID2 N/A 0B
<none> <none> imageID3 24 hours ago 0B
`,
},
{
ImageContext{
Context: Context{
Format: NewImageFormat("table {{.Repository}}", false, false),
},
},
"REPOSITORY\nimage\nimage\n<none>\n",
},
{
ImageContext{
Context: Context{
Format: NewImageFormat("table {{.Repository}}", false, true),
},
Digest: true,
},
`REPOSITORY DIGEST
image sha256:cbbf2f9a99b47fc460d422812b6a5adff7dfee951d8fa2e4a98caa0382cfbdbf
image <none>
<none> <none>
`,
},
{
ImageContext{
Context: Context{
Format: NewImageFormat("table {{.Repository}}", true, false),
},
},
"REPOSITORY\nimage\nimage\n<none>\n",
},
{
ImageContext{
Context: Context{
Format: NewImageFormat("table {{.Digest}}", true, false),
},
},
"DIGEST\nsha256:cbbf2f9a99b47fc460d422812b6a5adff7dfee951d8fa2e4a98caa0382cfbdbf\n<none>\n<none>\n",
},
{
ImageContext{
Context: Context{
Format: NewImageFormat("table", true, false),
},
},
"imageID1\nimageID2\nimageID3\n",
},
{
ImageContext{
Context: Context{
Format: NewImageFormat("table", false, true),
},
Digest: true,
},
`REPOSITORY TAG DIGEST IMAGE ID CREATED SIZE
image tag1 sha256:cbbf2f9a99b47fc460d422812b6a5adff7dfee951d8fa2e4a98caa0382cfbdbf imageID1 24 hours ago 0B
image tag2 <none> imageID2 N/A 0B
<none> <none> <none> imageID3 24 hours ago 0B
`,
},
{
ImageContext{
Context: Context{
Format: NewImageFormat("table", true, true),
},
Digest: true,
},
"imageID1\nimageID2\nimageID3\n",
},
// Raw Format
{
ImageContext{
Context: Context{
Format: NewImageFormat("raw", false, false),
},
},
fmt.Sprintf(`repository: image
tag: tag1
image_id: imageID1
created_at: %s
virtual_size: 0B
repository: image
tag: tag2
image_id: imageID2
created_at: %s
virtual_size: 0B
repository: <none>
tag: <none>
image_id: imageID3
created_at: %s
virtual_size: 0B
`, expectedTime, expectedZeroTime, expectedTime),
},
{
ImageContext{
Context: Context{
Format: NewImageFormat("raw", false, true),
},
Digest: true,
},
fmt.Sprintf(`repository: image
tag: tag1
digest: sha256:cbbf2f9a99b47fc460d422812b6a5adff7dfee951d8fa2e4a98caa0382cfbdbf
image_id: imageID1
created_at: %s
virtual_size: 0B
repository: image
tag: tag2
digest: <none>
image_id: imageID2
created_at: %s
virtual_size: 0B
repository: <none>
tag: <none>
digest: <none>
image_id: imageID3
created_at: %s
virtual_size: 0B
`, expectedTime, expectedZeroTime, expectedTime),
},
{
ImageContext{
Context: Context{
Format: NewImageFormat("raw", true, false),
},
},
`image_id: imageID1
image_id: imageID2
image_id: imageID3
`,
},
// Custom Format
{
ImageContext{
Context: Context{
Format: NewImageFormat("{{.Repository}}", false, false),
},
},
"image\nimage\n<none>\n",
},
{
ImageContext{
Context: Context{
Format: NewImageFormat("{{.Repository}}", false, true),
},
Digest: true,
},
"image\nimage\n<none>\n",
},
}
images := []types.ImageSummary{
{ID: "imageID1", RepoTags: []string{"image:tag1"}, RepoDigests: []string{"image@sha256:cbbf2f9a99b47fc460d422812b6a5adff7dfee951d8fa2e4a98caa0382cfbdbf"}, Created: unixTime},
{ID: "imageID2", RepoTags: []string{"image:tag2"}, Created: zeroTime},
{ID: "imageID3", RepoTags: []string{"<none>:<none>"}, RepoDigests: []string{"<none>@<none>"}, Created: unixTime},
}
for _, tc := range cases {
tc := tc
t.Run(string(tc.context.Format), func(t *testing.T) {
var out bytes.Buffer
tc.context.Output = &out
err := ImageWrite(tc.context, images)
if err != nil {
assert.Error(t, err, tc.expected)
} else {
assert.Equal(t, out.String(), tc.expected)
}
})
}
}
func TestImageContextWriteWithNoImage(t *testing.T) {
out := bytes.NewBufferString("")
images := []types.ImageSummary{}
cases := []struct {
context ImageContext
expected string
}{
{
ImageContext{
Context: Context{
Format: NewImageFormat("{{.Repository}}", false, false),
Output: out,
},
},
"",
},
{
ImageContext{
Context: Context{
Format: NewImageFormat("table {{.Repository}}", false, false),
Output: out,
},
},
"REPOSITORY\n",
},
{
ImageContext{
Context: Context{
Format: NewImageFormat("{{.Repository}}", false, true),
Output: out,
},
},
"",
},
{
ImageContext{
Context: Context{
Format: NewImageFormat("table {{.Repository}}", false, true),
Output: out,
},
},
"REPOSITORY DIGEST\n",
},
}
for _, tc := range cases {
tc := tc
t.Run(string(tc.context.Format), func(t *testing.T) {
err := ImageWrite(tc.context, images)
assert.NilError(t, err)
assert.Equal(t, out.String(), tc.expected)
// Clean buffer
out.Reset()
})
}
}