github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/pgwire/pgerror/internal_errors_test.go (about) 1 // Copyright 2019 The Cockroach Authors. 2 // 3 // Use of this software is governed by the Business Source License 4 // included in the file licenses/BSL.txt. 5 // 6 // As of the Change Date specified in that file, in accordance with 7 // the Business Source License, use of this software will be governed 8 // by the Apache License, Version 2.0, included in the file 9 // licenses/APL.txt. 10 11 package pgerror 12 13 import ( 14 "fmt" 15 "regexp" 16 "strings" 17 "testing" 18 19 "github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgcode" 20 "github.com/cockroachdb/errors" 21 "github.com/kr/pretty" 22 ) 23 24 // f formats an error. 25 func f(format string, e *Error) string { 26 return fmt.Sprintf(format, e) 27 } 28 29 // m checks a match against a single regexp. 30 func m(t *testing.T, s, re string) { 31 t.Helper() 32 matched, err := regexp.MatchString(re, s) 33 if err != nil { 34 t.Fatal(err) 35 } 36 if !matched { 37 t.Errorf("string does not match pattern %q:\n%s", re, s) 38 } 39 } 40 41 // ml checks a match against an array of regexps. 42 func ml(t *testing.T, s string, re []string) { 43 t.Helper() 44 lines := strings.Split(s, "\n") 45 for i := 0; i < len(lines) && i < len(re); i++ { 46 matched, err := regexp.MatchString(re[i], lines[i]) 47 if err != nil { 48 t.Fatal(err) 49 } 50 if !matched { 51 t.Errorf("line %d: %q: does not match pattern %q; s:\n%s", i+1, lines[i], re[i], s) 52 } 53 } 54 if len(lines) < len(re) { 55 t.Errorf("too few lines in message: expected %d, got %d; s:\n%s", len(re), len(lines), s) 56 } 57 } 58 59 // eq checks a string equality. 60 func eq(t *testing.T, s, exp string) { 61 t.Helper() 62 if s != exp { 63 t.Errorf("got %q, expected %q", s, exp) 64 } 65 } 66 67 func TestInternalError(t *testing.T) { 68 type pred struct { 69 name string 70 fn func(*testing.T, *Error) 71 } 72 73 const ie = "internal error: " 74 75 testData := []struct { 76 err error 77 preds []pred 78 }{ 79 { 80 errors.AssertionFailedf("woo %s", "waa"), 81 []pred{ 82 // Verify that the information is captured. 83 {"basefields", func(t *testing.T, e *Error) { 84 eq(t, e.Message, ie+"woo waa") 85 eq(t, e.Code, pgcode.Internal) 86 m(t, e.Source.File, ".*internal_errors_test.go") 87 eq(t, e.Source.Function, "TestInternalError") 88 ml(t, e.Detail, []string{"stack trace:", 89 ".*internal_errors_test.go.*TestInternalError.*", 90 ".*testing.go.*tRunner()"}) 91 }}, 92 93 // Verify that formatting works. 94 {"format", func(t *testing.T, e *Error) { 95 eq(t, f("%v", e), ie+"woo waa") 96 eq(t, f("%s", e), ie+"woo waa") 97 eq(t, f("%#v", e), "(XX000) "+ie+"woo waa") 98 m(t, f("%+v", e), 99 // Heading line: source, code, error message. 100 `.*internal_errors_test.go:\d+ in TestInternalError\(\): \(XX000\) `+ 101 ie+"woo waa") 102 }}, 103 }, 104 }, 105 { 106 // Check that the non-formatting constructor preserves the 107 // format spec without making a mess. 108 New(pgcode.Syntax, "syn %s"), 109 []pred{ 110 {"basefields", func(t *testing.T, e *Error) { 111 eq(t, e.Message, "syn %s") 112 eq(t, e.Code, pgcode.Syntax) 113 m(t, e.Source.File, ".*internal_errors_test.go") 114 eq(t, e.Source.Function, "TestInternalError") 115 }}, 116 }, 117 }, 118 { 119 // Check that a wrapped pgerr with errors.Wrap is wrapped properly. 120 Wrap(errors.Wrap(makeNormal(), "wrapA"), pgcode.Syntax, "wrapB"), 121 []pred{ 122 {"basefields", func(t *testing.T, e *Error) { 123 eq(t, e.Code, pgcode.Syntax) 124 eq(t, e.Message, "wrapB: wrapA: syn") 125 // Source info is stored. 126 m(t, e.Source.File, ".*internal_errors_test.go") 127 eq(t, e.Source.Function, "makeNormal") 128 }}, 129 }, 130 }, 131 { 132 // Check that a wrapped internal error with errors.Wrap is wrapped properly. 133 Wrap(errors.Wrap(makeBoo(), "wrapA"), pgcode.Syntax, "wrapB"), 134 []pred{ 135 {"basefields", func(t *testing.T, e *Error) { 136 eq(t, e.Code, pgcode.Internal) 137 eq(t, e.Message, ie+"wrapB: wrapA: boo") 138 // Source info is stored. 139 m(t, e.Source.File, ".*internal_errors_test.go") 140 eq(t, e.Source.Function, "makeBoo") 141 }}, 142 {"no-details", func(t *testing.T, e *Error) { 143 // Simple errors do not store stack trace details. 144 ml(t, e.Detail, []string{ 145 `stack trace:`, 146 `.*makeBoo.*`, 147 }) 148 }}, 149 }, 150 }, 151 { 152 // Check that Wrapf respects the original code for regular errors. 153 Wrapf(New(pgcode.Syntax, "syn foo"), pgcode.AdminShutdown, "wrap %s", "waa"), 154 []pred{ 155 {"basefields", func(t *testing.T, e *Error) { 156 // Wrap adds a prefix to the message. 157 eq(t, e.Message, "wrap waa: syn foo") 158 // Original code is preserved. 159 eq(t, e.Code, pgcode.Syntax) 160 // Source info is stored. 161 m(t, e.Source.File, ".*internal_errors_test.go") 162 eq(t, e.Source.Function, "TestInternalError") 163 }}, 164 }, 165 }, 166 { 167 // Check that Wrapf around a regular fmt.Error makes sense. 168 Wrapf(fmt.Errorf("fmt err"), pgcode.AdminShutdown, "wrap %s", "waa"), 169 []pred{ 170 {"basefields", func(t *testing.T, e *Error) { 171 // Wrap adds a prefix to the message. 172 eq(t, e.Message, "wrap waa: fmt err") 173 // New code was added. 174 eq(t, e.Code, pgcode.AdminShutdown) 175 // Source info is stored. 176 m(t, e.Source.File, ".*internal_errors_test.go") 177 eq(t, e.Source.Function, "TestInternalError") 178 }}, 179 }, 180 }, 181 { 182 // Check that Wrap does the same thing 183 Wrap(fmt.Errorf("fmt err"), pgcode.AdminShutdown, "wrapx waa"), 184 []pred{ 185 {"basefields", func(t *testing.T, e *Error) { 186 // Wrap adds a prefix to the message. 187 eq(t, e.Message, "wrapx waa: fmt err") 188 // New code was added. 189 eq(t, e.Code, pgcode.AdminShutdown) 190 // Source info is stored. 191 m(t, e.Source.File, ".*internal_errors_test.go") 192 eq(t, e.Source.Function, "TestInternalError") 193 }}, 194 {"no-details", func(t *testing.T, e *Error) { 195 eq(t, e.Detail, "") 196 }}, 197 }, 198 }, 199 { 200 // Check that Wrapf around an error.Wrap extracts something useful. 201 Wrapf(errors.Wrap(fmt.Errorf("fmt"), "wrap1"), pgcode.AdminShutdown, "wrap2 %s", "waa"), 202 []pred{ 203 {"basefields", func(t *testing.T, e *Error) { 204 // Wrap adds a prefix to the message. 205 eq(t, e.Message, "wrap2 waa: wrap1: fmt") 206 // New code was added. 207 eq(t, e.Code, pgcode.AdminShutdown) 208 // Source info is stored. 209 m(t, e.Source.File, ".*internal_errors_test.go") 210 eq(t, e.Source.Function, "TestInternalError") 211 }}, 212 {"no-details", func(t *testing.T, e *Error) { 213 // Simple errors do not store stack trace details. 214 eq(t, e.Detail, "") 215 }}, 216 }, 217 }, 218 { 219 // Check that a Wrap around an internal error preserves the internal error. 220 doWrap(makeBoo()), 221 []pred{ 222 {"basefields", func(t *testing.T, e *Error) { 223 // Wrap adds a prefix to the message. 224 eq(t, e.Message, ie+"wrap woo: boo") 225 // Internal error was preserved. 226 eq(t, e.Code, pgcode.Internal) 227 // Source info is preserved from original error. 228 m(t, e.Source.File, ".*internal_errors_test.go") 229 eq(t, e.Source.Function, "makeBoo") 230 }}, 231 {"retained-details", func(t *testing.T, e *Error) { 232 ml(t, e.Detail, 233 []string{ 234 // Ensure that the original stack trace remains at the top. 235 "stack trace:", 236 ".*makeBoo.*", 237 ".*TestInternalError.*", 238 ".*tRunner.*", 239 }, 240 ) 241 }}, 242 }, 243 }, 244 { 245 // Check that an internal error Wrap around a regular error 246 // creates internal error details. 247 errors.NewAssertionErrorWithWrappedErrf(New(pgcode.Syntax, "syn err"), "iewrap %s", "waa"), 248 []pred{ 249 {"basefields", func(t *testing.T, e *Error) { 250 // Wrap adds a prefix to the message. 251 eq(t, e.Message, ie+"iewrap waa: syn err") 252 // Internal error was preserved. 253 eq(t, e.Code, pgcode.Internal) 254 // Source info is preserved from original error. 255 m(t, e.Source.File, ".*internal_errors_test.go") 256 eq(t, e.Source.Function, "TestInternalError") 257 }}, 258 {"retained-details", func(t *testing.T, e *Error) { 259 ml(t, e.Detail, 260 []string{ 261 // Ensure that the assertion catcher is captured in details. 262 "stack trace:", 263 ".*TestInternalError.*", 264 ".*tRunner.*", 265 }, 266 ) 267 }}, 268 }, 269 }, 270 { 271 // Check that an internal error Wrap around another internal 272 // error creates internal error details and a sane error 273 // message. 274 errors.NewAssertionErrorWithWrappedErrf( 275 makeBoo(), "iewrap2 %s", "waa"), 276 []pred{ 277 {"basefields", func(t *testing.T, e *Error) { 278 // Ensure the "internal error" prefix only occurs once. 279 eq(t, e.Message, ie+"iewrap2 waa: boo") 280 // Internal error was preserved. 281 eq(t, e.Code, pgcode.Internal) 282 // Source info is preserved from original error. 283 m(t, e.Source.File, ".*internal_errors_test.go") 284 // The original cause is masked by the barrier. 285 eq(t, e.Source.Function, "TestInternalError") 286 }}, 287 {"retained-details", func(t *testing.T, e *Error) { 288 ml(t, e.Detail, 289 []string{ 290 // Ensure that the assertion catcher is captured in details. 291 // Also makeBoo() is masked here. 292 "stack trace:", 293 ".*TestInternalError.*", 294 ".*tRunner.*", 295 }, 296 ) 297 }}, 298 }, 299 }, 300 } 301 302 for i, test := range testData { 303 t.Run(fmt.Sprintf("%d %s", i, test.err), func(t *testing.T) { 304 for _, pred := range test.preds { 305 t.Run(pred.name, func(t *testing.T) { 306 pgErr := Flatten(test.err) 307 pred.fn(t, pgErr) 308 if t.Failed() { 309 t.Logf("input error: %# v", pretty.Formatter(test.err)) 310 t.Logf("pg error: %# v", pretty.Formatter(pgErr)) 311 } 312 }) 313 } 314 }) 315 } 316 } 317 318 func makeNormal() error { 319 return New(pgcode.Syntax, "syn") 320 } 321 322 func doWrap(err error) error { 323 return Wrapf(err, pgcode.AdminShutdown, "wrap %s", "woo") 324 } 325 326 func makeBoo() error { 327 return errors.AssertionFailedf("boo") 328 }