github.com/tonistiigi/docker@v0.10.1-0.20240229224939-974013b0dc6a/client/image_list_test.go (about) 1 package client // import "github.com/docker/docker/client" 2 3 import ( 4 "bytes" 5 "context" 6 "encoding/json" 7 "fmt" 8 "io" 9 "net/http" 10 "net/url" 11 "strings" 12 "testing" 13 14 "github.com/docker/docker/api/types/filters" 15 "github.com/docker/docker/api/types/image" 16 "github.com/docker/docker/errdefs" 17 "gotest.tools/v3/assert" 18 is "gotest.tools/v3/assert/cmp" 19 ) 20 21 func TestImageListError(t *testing.T) { 22 client := &Client{ 23 client: newMockClient(errorMock(http.StatusInternalServerError, "Server error")), 24 } 25 26 _, err := client.ImageList(context.Background(), image.ListOptions{}) 27 assert.Check(t, is.ErrorType(err, errdefs.IsSystem)) 28 } 29 30 // TestImageListConnectionError verifies that connection errors occurring 31 // during API-version negotiation are not shadowed by API-version errors. 32 // 33 // Regression test for https://github.com/docker/cli/issues/4890 34 func TestImageListConnectionError(t *testing.T) { 35 client, err := NewClientWithOpts(WithAPIVersionNegotiation(), WithHost("tcp://no-such-host.invalid")) 36 assert.NilError(t, err) 37 38 _, err = client.ImageList(context.Background(), image.ListOptions{}) 39 assert.Check(t, is.ErrorType(err, IsErrConnectionFailed)) 40 } 41 42 func TestImageList(t *testing.T) { 43 const expectedURL = "/images/json" 44 45 listCases := []struct { 46 options image.ListOptions 47 expectedQueryParams map[string]string 48 }{ 49 { 50 options: image.ListOptions{}, 51 expectedQueryParams: map[string]string{ 52 "all": "", 53 "filter": "", 54 "filters": "", 55 }, 56 }, 57 { 58 options: image.ListOptions{ 59 Filters: filters.NewArgs( 60 filters.Arg("label", "label1"), 61 filters.Arg("label", "label2"), 62 filters.Arg("dangling", "true"), 63 ), 64 }, 65 expectedQueryParams: map[string]string{ 66 "all": "", 67 "filter": "", 68 "filters": `{"dangling":{"true":true},"label":{"label1":true,"label2":true}}`, 69 }, 70 }, 71 { 72 options: image.ListOptions{ 73 Filters: filters.NewArgs(filters.Arg("dangling", "false")), 74 }, 75 expectedQueryParams: map[string]string{ 76 "all": "", 77 "filter": "", 78 "filters": `{"dangling":{"false":true}}`, 79 }, 80 }, 81 } 82 for _, listCase := range listCases { 83 client := &Client{ 84 client: newMockClient(func(req *http.Request) (*http.Response, error) { 85 if !strings.HasPrefix(req.URL.Path, expectedURL) { 86 return nil, fmt.Errorf("Expected URL '%s', got '%s'", expectedURL, req.URL) 87 } 88 query := req.URL.Query() 89 for key, expected := range listCase.expectedQueryParams { 90 actual := query.Get(key) 91 if actual != expected { 92 return nil, fmt.Errorf("%s not set in URL query properly. Expected '%s', got %s", key, expected, actual) 93 } 94 } 95 content, err := json.Marshal([]image.Summary{ 96 { 97 ID: "image_id2", 98 }, 99 { 100 ID: "image_id2", 101 }, 102 }) 103 if err != nil { 104 return nil, err 105 } 106 return &http.Response{ 107 StatusCode: http.StatusOK, 108 Body: io.NopCloser(bytes.NewReader(content)), 109 }, nil 110 }), 111 } 112 113 images, err := client.ImageList(context.Background(), listCase.options) 114 if err != nil { 115 t.Fatal(err) 116 } 117 if len(images) != 2 { 118 t.Fatalf("expected 2 images, got %v", images) 119 } 120 } 121 } 122 123 func TestImageListApiBefore125(t *testing.T) { 124 expectedFilter := "image:tag" 125 client := &Client{ 126 client: newMockClient(func(req *http.Request) (*http.Response, error) { 127 query := req.URL.Query() 128 actualFilter := query.Get("filter") 129 if actualFilter != expectedFilter { 130 return nil, fmt.Errorf("filter not set in URL query properly. Expected '%s', got %s", expectedFilter, actualFilter) 131 } 132 actualFilters := query.Get("filters") 133 if actualFilters != "" { 134 return nil, fmt.Errorf("filters should have not been present, were with value: %s", actualFilters) 135 } 136 content, err := json.Marshal([]image.Summary{ 137 { 138 ID: "image_id2", 139 }, 140 { 141 ID: "image_id2", 142 }, 143 }) 144 if err != nil { 145 return nil, err 146 } 147 return &http.Response{ 148 StatusCode: http.StatusOK, 149 Body: io.NopCloser(bytes.NewReader(content)), 150 }, nil 151 }), 152 version: "1.24", 153 } 154 155 options := image.ListOptions{ 156 Filters: filters.NewArgs(filters.Arg("reference", "image:tag")), 157 } 158 159 images, err := client.ImageList(context.Background(), options) 160 if err != nil { 161 t.Fatal(err) 162 } 163 if len(images) != 2 { 164 t.Fatalf("expected 2 images, got %v", images) 165 } 166 } 167 168 // Checks if shared-size query parameter is set/not being set correctly 169 // for /images/json. 170 func TestImageListWithSharedSize(t *testing.T) { 171 t.Parallel() 172 const sharedSize = "shared-size" 173 for _, tc := range []struct { 174 name string 175 version string 176 options image.ListOptions 177 sharedSize string // expected value for the shared-size query param, or empty if it should not be set. 178 }{ 179 {name: "unset after 1.42, no options set", version: "1.42"}, 180 {name: "set after 1.42, if requested", version: "1.42", options: image.ListOptions{SharedSize: true}, sharedSize: "1"}, 181 {name: "unset before 1.42, even if requested", version: "1.41", options: image.ListOptions{SharedSize: true}}, 182 } { 183 tc := tc 184 t.Run(tc.name, func(t *testing.T) { 185 t.Parallel() 186 var query url.Values 187 client := &Client{ 188 client: newMockClient(func(req *http.Request) (*http.Response, error) { 189 query = req.URL.Query() 190 return &http.Response{ 191 StatusCode: http.StatusOK, 192 Body: io.NopCloser(strings.NewReader("[]")), 193 }, nil 194 }), 195 version: tc.version, 196 } 197 _, err := client.ImageList(context.Background(), tc.options) 198 assert.Check(t, err) 199 expectedSet := tc.sharedSize != "" 200 assert.Check(t, is.Equal(query.Has(sharedSize), expectedSet)) 201 assert.Check(t, is.Equal(query.Get(sharedSize), tc.sharedSize)) 202 }) 203 } 204 }