github.com/cockroachdb/errors@v1.11.1/errbase/adapters_test.go (about) 1 // Copyright 2019 The Cockroach Authors. 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 12 // implied. See the License for the specific language governing 13 // permissions and limitations under the License. 14 15 package errbase_test 16 17 import ( 18 "context" 19 goErr "errors" 20 "fmt" 21 "os" 22 "reflect" 23 "strings" 24 "testing" 25 26 "github.com/cockroachdb/errors/errbase" 27 "github.com/cockroachdb/errors/errorspb" 28 "github.com/cockroachdb/errors/testutils" 29 "github.com/kr/pretty" 30 pkgErr "github.com/pkg/errors" 31 ) 32 33 func network(t *testing.T, err error) error { 34 t.Helper() 35 enc := errbase.EncodeError(context.Background(), err) 36 t.Logf("encoded: %# v", pretty.Formatter(enc)) 37 newErr := errbase.DecodeError(context.Background(), enc) 38 t.Logf("decoded: %# v", pretty.Formatter(newErr)) 39 return newErr 40 } 41 42 func TestAdaptBaseGoErr(t *testing.T) { 43 // Base Go errors are preserved completely. 44 origErr := goErr.New("world") 45 t.Logf("start err: %# v", pretty.Formatter(origErr)) 46 47 newErr := network(t, origErr) 48 49 tt := testutils.T{T: t} 50 // The library preserves the error message. 51 tt.CheckEqual(newErr.Error(), origErr.Error()) 52 53 // It actually preserves the full structure of the message, 54 // including its Go type. 55 tt.CheckDeepEqual(newErr, origErr) 56 } 57 58 func TestAdaptGoSingleWrapErr(t *testing.T) { 59 origErr := fmt.Errorf("an error %w", goErr.New("hello")) 60 t.Logf("start err: %# v", pretty.Formatter(origErr)) 61 62 newErr := network(t, origErr) 63 64 tt := testutils.T{T: t} 65 // The library preserves the cause. It's not possible to preserve the fmt 66 // string. 67 tt.CheckEqual(newErr.Error(), origErr.Error()) 68 tt.CheckContains(newErr.Error(), "hello") 69 } 70 71 func TestAdaptBaseGoJoinErr(t *testing.T) { 72 origErr := goErr.Join(goErr.New("hello"), goErr.New("world")) 73 t.Logf("start err: %# v", pretty.Formatter(origErr)) 74 75 newErr := network(t, origErr) 76 77 tt := testutils.T{T: t} 78 // The library preserves the error message. 79 tt.CheckEqual(newErr.Error(), origErr.Error()) 80 81 } 82 83 func TestAdaptGoMultiWrapErr(t *testing.T) { 84 origErr := fmt.Errorf("an error %w and also %w", goErr.New("hello"), goErr.New("world")) 85 t.Logf("start err: %# v", pretty.Formatter(origErr)) 86 87 newErr := network(t, origErr) 88 89 tt := testutils.T{T: t} 90 // The library preserves the causes. It's not possible to preserve the fmt string. 91 tt.CheckEqual(newErr.Error(), origErr.Error()) 92 tt.CheckContains(newErr.Error(), "hello") 93 tt.CheckContains(newErr.Error(), "world") 94 } 95 96 func TestAdaptPkgWithMessage(t *testing.T) { 97 // Simple message wrappers from github.com/pkg/errors are preserved 98 // completely. 99 origErr := pkgErr.WithMessage(goErr.New("world"), "hello") 100 t.Logf("start err: %# v", pretty.Formatter(origErr)) 101 102 newErr := network(t, origErr) 103 104 tt := testutils.T{T: t} 105 // The library preserves the error message. 106 tt.CheckEqual(newErr.Error(), origErr.Error()) 107 108 // It actually preserves the full structure of the message, 109 // including its Go type. 110 tt.CheckDeepEqual(newErr, origErr) 111 } 112 113 func TestAdaptPkgFundamental(t *testing.T) { 114 // The "simple error" from github.com/pkg/errors is not 115 // that simple because it contains a stack trace. However, 116 // we are happy to preserve this stack trace. 117 origErr := pkgErr.New("hello") 118 t.Logf("start err: %# v", pretty.Formatter(origErr)) 119 120 tt := testutils.T{T: t} 121 122 // Show that there is indeed a stack trace. 123 theStack := fmt.Sprintf("%+v", errbase.GetSafeDetails(origErr)) 124 tt.Check(strings.Contains(theStack, "adapters_test.go")) 125 126 newErr := network(t, origErr) 127 128 // In any case, the library preserves the error message. 129 tt.CheckEqual(newErr.Error(), origErr.Error()) 130 131 // The decoded error does not compare equal, since 132 // we had to change the type to preserve the stack trace. 133 tt.Check(!reflect.DeepEqual(origErr, newErr)) 134 135 // However, it remembers what type the error is coming from. 136 errV := fmt.Sprintf("%+v", newErr) 137 tt.Check(strings.Contains(errV, "github.com/pkg/errors/*errors.fundamental")) 138 139 // Also, the decoded error does include the stack trace. 140 details := errbase.GetSafeDetails(newErr).SafeDetails 141 tt.Check(len(details) > 0 && strings.Contains(details[0], "adapters_test.go")) 142 143 // Moreover, if we re-encode and re-decode, that will be preserved exactly! 144 newErr2 := network(t, newErr) 145 tt.CheckDeepEqual(newErr2, newErr) 146 } 147 148 func TestAdaptPkgWithStack(t *testing.T) { 149 // The "with stack" wrapper from github.com/pkg/errors cannot be 150 // serialized exactly, however we are happy to preserve this stack 151 // trace. 152 origErr := pkgErr.WithStack(goErr.New("hello")) 153 t.Logf("start err: %# v", pretty.Formatter(origErr)) 154 155 tt := testutils.T{T: t} 156 // Show that there is indeed a stack trace. 157 theStack := fmt.Sprintf("%+v", errbase.GetSafeDetails(origErr)) 158 tt.Check(strings.Contains(theStack, "adapters_test.go")) 159 160 newErr := network(t, origErr) 161 162 // In any case, the library preserves the error message. 163 tt.CheckEqual(newErr.Error(), origErr.Error()) 164 165 // The decoded error does not compare equal, since 166 // we had to change the type to preserve the stack trace. 167 tt.Check(!reflect.DeepEqual(newErr, origErr)) 168 169 // However, it does include the stack trace. 170 details := errbase.GetSafeDetails(newErr).SafeDetails 171 tt.Check(len(details) > 0 && strings.Contains(details[0], "adapters_test.go")) 172 173 // Moreover, if we re-encode and re-decode, that will be preserved exactly! 174 newErr2 := network(t, newErr) 175 tt.CheckDeepEqual(newErr2, newErr) 176 } 177 178 func TestAdaptProtoErrors(t *testing.T) { 179 // If an error type has a proto representation already, 180 // it will be preserved exactly. 181 origErr := &errorspb.TestError{} 182 t.Logf("start err: %# v", pretty.Formatter(origErr)) 183 184 newErr := network(t, origErr) 185 186 tt := testutils.T{T: t} 187 188 // In any case, the library preserves the error message. 189 tt.CheckEqual(newErr.Error(), origErr.Error()) 190 191 // Moreover, it preserves the entire structure. 192 tt.CheckDeepEqual(newErr, origErr) 193 } 194 195 func TestAdaptProtoErrorsWithWrapper(t *testing.T) { 196 // proto-native error types are preserved exactly 197 // together with their wrappers. 198 rErr := &errorspb.TestError{} 199 origErr := pkgErr.WithMessage(rErr, "hello roachpb") 200 t.Logf("start err: %# v", pretty.Formatter(origErr)) 201 202 newErr := network(t, origErr) 203 204 tt := testutils.T{T: t} 205 206 // In any case, the library preserves the error message. 207 tt.CheckEqual(newErr.Error(), origErr.Error()) 208 209 // Moreover, it preserves the entire structure. 210 tt.CheckDeepEqual(newErr, origErr) 211 } 212 213 func TestAdaptContextCanceled(t *testing.T) { 214 // context.DeadlineExceeded is preserved exactly. 215 216 tt := testutils.T{T: t} 217 newErr := network(t, context.DeadlineExceeded) 218 tt.CheckEqual(newErr, context.DeadlineExceeded) 219 } 220 221 func TestAdaptOsErrors(t *testing.T) { 222 // The special os error types are preserved exactly. 223 224 tt := testutils.T{T: t} 225 var origErr error 226 227 origErr = &os.PathError{Op: "hello", Path: "world", Err: goErr.New("woo")} 228 newErr := network(t, origErr) 229 tt.CheckDeepEqual(newErr, origErr) 230 231 origErr = &os.LinkError{Op: "hello", Old: "world", New: "universe", Err: goErr.New("woo")} 232 newErr = network(t, origErr) 233 tt.CheckDeepEqual(newErr, origErr) 234 235 origErr = &os.SyscallError{Syscall: "hello", Err: goErr.New("woo")} 236 newErr = network(t, origErr) 237 tt.CheckDeepEqual(newErr, origErr) 238 }