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 }