cuelabs.dev/go/oci/ociregistry@v0.0.0-20240906074133-82eb438dd565/error_test.go (about) 1 package ociregistry 2 3 import ( 4 "encoding/json" 5 "errors" 6 "fmt" 7 "net/http" 8 "testing" 9 10 "github.com/go-quicktest/qt" 11 ) 12 13 var errorTests = []struct { 14 testName string 15 err error 16 wantMsg string 17 wantMarshalData rawJSONMessage 18 wantMarshalHTTPStatus int 19 }{{ 20 testName: "RegularGoError", 21 err: fmt.Errorf("unknown error"), 22 wantMsg: "unknown error", 23 wantMarshalData: `{"errors":[{"code":"UNKNOWN","message":"unknown error"}]}`, 24 wantMarshalHTTPStatus: http.StatusInternalServerError, 25 }, { 26 testName: "RegistryError", 27 err: ErrBlobUnknown, 28 wantMsg: "blob unknown: blob unknown to registry", 29 wantMarshalData: `{"errors":[{"code":"BLOB_UNKNOWN","message":"blob unknown to registry"}]}`, 30 wantMarshalHTTPStatus: http.StatusNotFound, 31 }, { 32 testName: "WrappedRegistryErrorWithContextAtStart", 33 err: fmt.Errorf("some context: %w", ErrBlobUnknown), 34 wantMsg: "some context: blob unknown: blob unknown to registry", 35 wantMarshalData: `{"errors":[{"code":"BLOB_UNKNOWN","message":"some context: blob unknown: blob unknown to registry"}]}`, 36 wantMarshalHTTPStatus: http.StatusNotFound, 37 }, { 38 testName: "WrappedRegistryErrorWithContextAtEnd", 39 err: fmt.Errorf("%w: some context", ErrBlobUnknown), 40 wantMsg: "blob unknown: blob unknown to registry: some context", 41 wantMarshalData: `{"errors":[{"code":"BLOB_UNKNOWN","message":"blob unknown to registry: some context"}]}`, 42 wantMarshalHTTPStatus: http.StatusNotFound, 43 }, { 44 testName: "HTTPStatusIgnoredWithKnownCode", 45 err: NewHTTPError(fmt.Errorf("%w: some context", ErrBlobUnknown), http.StatusUnauthorized, nil, nil), 46 wantMsg: "401 Unauthorized: blob unknown: blob unknown to registry: some context", 47 // Note: the "401 Unauthorized" remains intact because it's not redundant with respect 48 // to the 404 HTTP response code. 49 wantMarshalData: `{"errors":[{"code":"BLOB_UNKNOWN","message":"401 Unauthorized: blob unknown: blob unknown to registry: some context"}]}`, 50 wantMarshalHTTPStatus: http.StatusNotFound, 51 }, { 52 testName: "HTTPStatusUsedWithUnknownCode", 53 err: NewHTTPError(NewError("a message with a code", "SOME_CODE", nil), http.StatusUnauthorized, nil, nil), 54 wantMsg: "401 Unauthorized: some code: a message with a code", 55 wantMarshalData: `{"errors":[{"code":"SOME_CODE","message":"a message with a code"}]}`, 56 wantMarshalHTTPStatus: http.StatusUnauthorized, 57 }, { 58 testName: "ErrorWithDetail", 59 err: NewError("a message with some detail", "SOME_CODE", json.RawMessage(`{"foo": true}`)), 60 wantMsg: `some code: a message with some detail`, 61 wantMarshalData: `{"errors":[{"code":"SOME_CODE","message":"a message with some detail","detail":{"foo":true}}]}`, 62 wantMarshalHTTPStatus: http.StatusInternalServerError, 63 }} 64 65 func TestError(t *testing.T) { 66 for _, test := range errorTests { 67 t.Run(test.testName, func(t *testing.T) { 68 qt.Check(t, qt.ErrorMatches(test.err, test.wantMsg)) 69 data, httpStatus := MarshalError(test.err) 70 qt.Check(t, qt.Equals(httpStatus, test.wantMarshalHTTPStatus)) 71 qt.Check(t, qt.JSONEquals(data, test.wantMarshalData), qt.Commentf("marshal data: %s", data)) 72 73 // Check that the marshaled error unmarshals into WireError OK and 74 // that the code matches appropriately. 75 var errs *WireErrors 76 err := json.Unmarshal(data, &errs) 77 qt.Assert(t, qt.IsNil(err)) 78 if ociErr := Error(nil); errors.As(test.err, &ociErr) { 79 qt.Assert(t, qt.IsTrue(errors.Is(errs, NewError("something", ociErr.Code(), nil)))) 80 } 81 }) 82 } 83 } 84 85 type rawJSONMessage string 86 87 func (m rawJSONMessage) MarshalJSON() ([]byte, error) { 88 return []byte(m), nil 89 } 90 91 func (m *rawJSONMessage) UnmarshalJSON(data []byte) error { 92 *m = rawJSONMessage(data) 93 return nil 94 }