...

Source file src/github.com/google/go-containerregistry/pkg/v1/remote/transport/error_test.go

Documentation: github.com/google/go-containerregistry/pkg/v1/remote/transport

     1  // Copyright 2018 Google LLC All Rights Reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //    http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package transport
    16  
    17  import (
    18  	"bytes"
    19  	"errors"
    20  	"io"
    21  	"net/http"
    22  	"net/url"
    23  	"testing"
    24  
    25  	"github.com/google/go-cmp/cmp"
    26  )
    27  
    28  func TestTemporary(t *testing.T) {
    29  	tests := []struct {
    30  		error *Error
    31  		retry bool
    32  	}{{
    33  		error: &Error{},
    34  		retry: false,
    35  	}, {
    36  		error: &Error{
    37  			Errors: []Diagnostic{{
    38  				Code: BlobUploadInvalidErrorCode,
    39  			}},
    40  		},
    41  		retry: true,
    42  	}, {
    43  		error: &Error{
    44  			Errors: []Diagnostic{{
    45  				Code: BlobUploadInvalidErrorCode,
    46  			}, {
    47  				Code: DeniedErrorCode,
    48  			}},
    49  		},
    50  		retry: false,
    51  	}, {
    52  		error: &Error{
    53  			Errors: []Diagnostic{{
    54  				Code: TooManyRequestsErrorCode,
    55  			}},
    56  		},
    57  		retry: true,
    58  	}, {
    59  		error: &Error{
    60  			Errors: []Diagnostic{{
    61  				Code: UnavailableErrorCode,
    62  			}},
    63  		},
    64  		retry: true,
    65  	}, {
    66  		error: &Error{
    67  			StatusCode: http.StatusInternalServerError,
    68  		},
    69  		retry: true,
    70  	}}
    71  
    72  	for _, test := range tests {
    73  		retry := test.error.Temporary()
    74  
    75  		if test.retry != retry {
    76  			t.Errorf("Temporary(%s) = %t, wanted %t", test.error, retry, test.retry)
    77  		}
    78  	}
    79  }
    80  
    81  func TestCheckErrorNil(t *testing.T) {
    82  	tests := []int{
    83  		http.StatusOK,
    84  		http.StatusAccepted,
    85  		http.StatusCreated,
    86  		http.StatusMovedPermanently,
    87  		http.StatusInternalServerError,
    88  	}
    89  
    90  	for _, code := range tests {
    91  		resp := &http.Response{StatusCode: code}
    92  
    93  		if err := CheckError(resp, code); err != nil {
    94  			t.Errorf("CheckError(%d) = %v", code, err)
    95  		}
    96  	}
    97  }
    98  
    99  func TestCheckErrorNotError(t *testing.T) {
   100  	tests := []struct {
   101  		code    int
   102  		body    string
   103  		msg     string
   104  		request *http.Request
   105  	}{{
   106  		code: http.StatusBadRequest,
   107  		body: "",
   108  		msg:  "unexpected status code 400 Bad Request",
   109  	}, {
   110  		code: http.StatusUnauthorized,
   111  		// Valid JSON, but not a structured error -- we should still print the body.
   112  		body: `{"details":"incorrect username or password"}`,
   113  		msg:  `unexpected status code 401 Unauthorized: {"details":"incorrect username or password"}`,
   114  	}, {
   115  		code: http.StatusUnauthorized,
   116  		body: "Not JSON",
   117  		msg:  "GET https://example.com/somepath?access_token=REDACTED&scope=foo&service=bar: unexpected status code 401 Unauthorized: Not JSON",
   118  		request: &http.Request{
   119  			Method: http.MethodGet,
   120  			URL: &url.URL{
   121  				Scheme: "https",
   122  				Host:   "example.com",
   123  				Path:   "somepath",
   124  				RawQuery: url.Values{
   125  					"scope":        []string{"foo"},
   126  					"service":      []string{"bar"},
   127  					"access_token": []string{"hunter2"},
   128  				}.Encode(),
   129  			},
   130  		},
   131  	}, {
   132  		code: http.StatusUnauthorized,
   133  		body: "",
   134  		msg:  "HEAD https://example.com/somepath: unexpected status code 401 Unauthorized (HEAD responses have no body, use GET for details)",
   135  		request: &http.Request{
   136  			Method: http.MethodHead,
   137  			URL: &url.URL{
   138  				Scheme: "https",
   139  				Host:   "example.com",
   140  				Path:   "somepath",
   141  			},
   142  		},
   143  	}}
   144  
   145  	for _, test := range tests {
   146  		resp := &http.Response{
   147  			StatusCode: test.code,
   148  			Body:       io.NopCloser(bytes.NewBufferString(test.body)),
   149  			Request:    test.request,
   150  		}
   151  
   152  		err := CheckError(resp, http.StatusOK)
   153  		if err == nil {
   154  			t.Fatalf("CheckError(%d, %s) = nil, wanted error", test.code, test.body)
   155  		}
   156  		var terr *Error
   157  		if !errors.As(err, &terr) {
   158  			t.Fatalf("CheckError(%d, %s) = %v, wanted error type", test.code, test.body, err)
   159  		}
   160  
   161  		if terr.StatusCode != test.code {
   162  			t.Errorf("Incorrect status code, got %d, want %d", terr.StatusCode, test.code)
   163  		}
   164  
   165  		if terr.Error() != test.msg {
   166  			t.Errorf("Incorrect message, got %q, want %q", terr.Error(), test.msg)
   167  		}
   168  	}
   169  }
   170  
   171  func TestCheckErrorWithError(t *testing.T) {
   172  	tests := []struct {
   173  		name      string
   174  		code      int
   175  		errorBody string
   176  		msg       string
   177  	}{{
   178  		name:      "Invalid name error",
   179  		code:      http.StatusBadRequest,
   180  		errorBody: `{"errors":[{"code":"NAME_INVALID","message":"a message for you"}],"StatusCode":400}`,
   181  		msg:       "NAME_INVALID: a message for you",
   182  	}, {
   183  		name:      "Only status code is provided",
   184  		code:      http.StatusBadRequest,
   185  		errorBody: `{"StatusCode":400}`,
   186  		msg:       "unexpected status code 400 Bad Request: {\"StatusCode\":400}",
   187  	}, {
   188  		name:      "Multiple diagnostics",
   189  		code:      http.StatusBadRequest,
   190  		errorBody: `{"errors":[{"code":"NAME_INVALID","message":"a message for you"}, {"code":"SIZE_INVALID","message":"another message for you", "detail": "with some details"}],"StatusCode":400,"Request":null}`,
   191  		msg:       "multiple errors returned: NAME_INVALID: a message for you; SIZE_INVALID: another message for you; with some details",
   192  	}}
   193  
   194  	for _, test := range tests {
   195  		t.Run(test.name, func(t *testing.T) {
   196  			resp := &http.Response{
   197  				StatusCode: test.code,
   198  				Body:       io.NopCloser(bytes.NewBuffer([]byte(test.errorBody))),
   199  			}
   200  
   201  			var terr *Error
   202  			if err := CheckError(resp, http.StatusOK); err == nil {
   203  				t.Errorf("CheckError(%d, %s) = nil, wanted error", test.code, test.errorBody)
   204  			} else if !errors.As(err, &terr) {
   205  				t.Errorf("CheckError(%d, %s) = %T, wanted *transport.Error", test.code, test.errorBody, err)
   206  			} else if diff := cmp.Diff(test.msg, err.Error()); diff != "" {
   207  				t.Errorf("CheckError(%d, %s).Error(); (-want +got) %s", test.code, test.errorBody, diff)
   208  			}
   209  		})
   210  	}
   211  }
   212  
   213  func TestBodyError(t *testing.T) {
   214  	expectedErr := errors.New("whoops")
   215  	resp := &http.Response{
   216  		StatusCode: http.StatusOK,
   217  		Body:       &errReadCloser{expectedErr},
   218  	}
   219  	if err := CheckError(resp, http.StatusNotFound); err == nil {
   220  		t.Errorf("CheckError() = nil, wanted error %v", expectedErr)
   221  	} else if !errors.Is(err, expectedErr) {
   222  		t.Errorf("CheckError() = %v, wanted %v", err, expectedErr)
   223  	}
   224  }
   225  
   226  type errReadCloser struct {
   227  	err error
   228  }
   229  
   230  func (e *errReadCloser) Read(_ []byte) (int, error) {
   231  	return 0, e.err
   232  }
   233  
   234  func (e *errReadCloser) Close() error {
   235  	return e.err
   236  }
   237  

View as plain text