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  }