github.com/opcr-io/oras-go/v2@v2.0.0-20231122155130-eb4260d8a0ae/registry/remote/internal/errutil/errutil_test.go (about)

     1  /*
     2  Copyright The ORAS Authors.
     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  
    16  package errutil
    17  
    18  import (
    19  	"errors"
    20  	"net/http"
    21  	"net/http/httptest"
    22  	"strings"
    23  	"testing"
    24  
    25  	"github.com/opcr-io/oras-go/v2/registry/remote/errcode"
    26  )
    27  
    28  func Test_ParseErrorResponse(t *testing.T) {
    29  	path := "/test"
    30  	expectedErrs := errcode.Errors{
    31  		{
    32  			Code:    "UNAUTHORIZED",
    33  			Message: "authentication required",
    34  		},
    35  		{
    36  			Code:    "NAME_UNKNOWN",
    37  			Message: "repository name not known to registry",
    38  		},
    39  	}
    40  	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    41  		switch r.URL.Path {
    42  		case path:
    43  			msg := `{ "errors": [ { "code": "UNAUTHORIZED", "message": "authentication required", "detail": [ { "Type": "repository", "Class": "", "Name": "library/hello-world", "Action": "pull" } ] }, { "code": "NAME_UNKNOWN", "message": "repository name not known to registry" } ] }`
    44  			w.WriteHeader(http.StatusUnauthorized)
    45  			if _, err := w.Write([]byte(msg)); err != nil {
    46  				t.Errorf("failed to write %q: %v", r.URL, err)
    47  			}
    48  		default:
    49  			t.Errorf("unexpected access: %s %s", r.Method, r.URL)
    50  			w.WriteHeader(http.StatusNotFound)
    51  		}
    52  
    53  	}))
    54  	defer ts.Close()
    55  
    56  	resp, err := http.Get(ts.URL + path)
    57  	if err != nil {
    58  		t.Fatalf("failed to do request: %v", err)
    59  	}
    60  	err = ParseErrorResponse(resp)
    61  	if err == nil {
    62  		t.Errorf("ParseErrorResponse() error = %v, wantErr %v", err, true)
    63  	}
    64  
    65  	var errResp *errcode.ErrorResponse
    66  	if ok := errors.As(err, &errResp); !ok {
    67  		t.Errorf("errors.As(err, &UnexpectedStatusCodeError) = %v, want %v", ok, true)
    68  	}
    69  	if want := http.MethodGet; errResp.Method != want {
    70  		t.Errorf("ParseErrorResponse() Method = %v, want Method %v", errResp.Method, want)
    71  	}
    72  	if want := http.StatusUnauthorized; errResp.StatusCode != want {
    73  		t.Errorf("ParseErrorResponse() StatusCode = %v, want StatusCode %v", errResp.StatusCode, want)
    74  	}
    75  	if want := path; errResp.URL.Path != want {
    76  		t.Errorf("ParseErrorResponse() URL = %v, want URL %v", errResp.URL.Path, want)
    77  	}
    78  	for i, e := range errResp.Errors {
    79  		if want := expectedErrs[i].Code; e.Code != expectedErrs[i].Code {
    80  			t.Errorf("ParseErrorResponse() Code = %v, want Code %v", e.Code, want)
    81  		}
    82  		if want := expectedErrs[i].Message; e.Message != want {
    83  			t.Errorf("ParseErrorResponse() Message = %v, want Code %v", e.Code, want)
    84  		}
    85  	}
    86  
    87  	errmsg := err.Error()
    88  	if want := "401"; !strings.Contains(errmsg, want) {
    89  		t.Errorf("ParseErrorResponse() error = %v, want err message %v", err, want)
    90  	}
    91  	// first error
    92  	if want := "unauthorized"; !strings.Contains(errmsg, want) {
    93  		t.Errorf("ParseErrorResponse() error = %v, want err message %v", err, want)
    94  	}
    95  	if want := "authentication required"; !strings.Contains(errmsg, want) {
    96  		t.Errorf("ParseErrorResponse() error = %v, want err message %v", err, want)
    97  	}
    98  	// second error
    99  	if want := "name unknown"; !strings.Contains(errmsg, want) {
   100  		t.Errorf("ParseErrorResponse() error = %v, want err message %v", err, want)
   101  	}
   102  	if want := "repository name not known to registry"; !strings.Contains(errmsg, want) {
   103  		t.Errorf("ParseErrorResponse() error = %v, want err message %v", err, want)
   104  	}
   105  }
   106  
   107  func Test_ParseErrorResponse_plain(t *testing.T) {
   108  	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   109  		w.WriteHeader(http.StatusUnauthorized)
   110  	}))
   111  	defer ts.Close()
   112  
   113  	resp, err := http.Get(ts.URL)
   114  	if err != nil {
   115  		t.Fatalf("failed to do request: %v", err)
   116  	}
   117  	err = ParseErrorResponse(resp)
   118  	if err == nil {
   119  		t.Errorf("ParseErrorResponse() error = %v, wantErr %v", err, true)
   120  	}
   121  	errmsg := err.Error()
   122  	if want := "401"; !strings.Contains(errmsg, want) {
   123  		t.Errorf("ParseErrorResponse() error = %v, want err message %v", err, want)
   124  	}
   125  	if want := http.StatusText(http.StatusUnauthorized); !strings.Contains(errmsg, want) {
   126  		t.Errorf("ParseErrorResponse() error = %v, want err message %v", err, want)
   127  	}
   128  }
   129  
   130  func TestIsErrorCode(t *testing.T) {
   131  	tests := []struct {
   132  		name string
   133  		err  error
   134  		code string
   135  		want bool
   136  	}{
   137  		{
   138  			name: "test errcode.Error, same code",
   139  			err: errcode.Error{
   140  				Code: errcode.ErrorCodeNameUnknown,
   141  			},
   142  			code: errcode.ErrorCodeNameUnknown,
   143  			want: true,
   144  		},
   145  		{
   146  			name: "test errcode.Error, different code",
   147  			err: errcode.Error{
   148  				Code: errcode.ErrorCodeUnauthorized,
   149  			},
   150  			code: errcode.ErrorCodeNameUnknown,
   151  			want: false,
   152  		},
   153  		{
   154  			name: "test errcode.Errors containing single error, same code",
   155  			err: errcode.Errors{
   156  				{
   157  					Code: errcode.ErrorCodeNameUnknown,
   158  				},
   159  			},
   160  			code: errcode.ErrorCodeNameUnknown,
   161  			want: true,
   162  		},
   163  		{
   164  			name: "test errcode.Errors containing single error, different code",
   165  			err: errcode.Errors{
   166  				{
   167  					Code: errcode.ErrorCodeNameUnknown,
   168  				},
   169  			},
   170  			code: errcode.ErrorCodeNameUnknown,
   171  			want: true,
   172  		},
   173  		{
   174  			name: "test errcode.Errors containing multiple errors, same code",
   175  			err: errcode.Errors{
   176  				{
   177  					Code: errcode.ErrorCodeNameUnknown,
   178  				},
   179  				{
   180  					Code: errcode.ErrorCodeUnauthorized,
   181  				},
   182  			},
   183  			code: errcode.ErrorCodeNameUnknown,
   184  			want: false,
   185  		},
   186  		{
   187  			name: "test errcode.ErrorResponse containing single error, same code",
   188  			err: &errcode.ErrorResponse{
   189  				Errors: errcode.Errors{
   190  					{
   191  						Code: errcode.ErrorCodeNameUnknown,
   192  					},
   193  				},
   194  			},
   195  			code: errcode.ErrorCodeNameUnknown,
   196  			want: true,
   197  		},
   198  		{
   199  			name: "test errcode.ErrorResponse containing single error, different code",
   200  			err: &errcode.ErrorResponse{
   201  				Errors: errcode.Errors{
   202  					{
   203  						Code: errcode.ErrorCodeUnauthorized,
   204  					},
   205  				},
   206  			},
   207  			code: errcode.ErrorCodeNameUnknown,
   208  			want: false,
   209  		},
   210  		{
   211  			name: "test errcode.ErrorResponse containing multiple errors, same code",
   212  			err: &errcode.ErrorResponse{
   213  				Errors: errcode.Errors{
   214  					{
   215  						Code: errcode.ErrorCodeNameUnknown,
   216  					},
   217  					{
   218  						Code: errcode.ErrorCodeUnauthorized,
   219  					},
   220  				},
   221  			},
   222  			code: errcode.ErrorCodeNameUnknown,
   223  			want: false,
   224  		},
   225  		{
   226  			name: "test unstructured error",
   227  			err:  errors.New(errcode.ErrorCodeNameUnknown),
   228  			code: errcode.ErrorCodeNameUnknown,
   229  			want: false,
   230  		},
   231  	}
   232  	for _, tt := range tests {
   233  		t.Run(tt.name, func(t *testing.T) {
   234  			if got := IsErrorCode(tt.err, tt.code); got != tt.want {
   235  				t.Errorf("IsErrorCode() = %v, want %v", got, tt.want)
   236  			}
   237  		})
   238  	}
   239  }