vitess.io/vitess@v0.16.2/go/vt/vterrors/errors_test.go (about) 1 /* 2 Copyright 2019 The Vitess Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package vterrors 18 19 import ( 20 "context" 21 "errors" 22 "fmt" 23 "io" 24 "math/rand" 25 "reflect" 26 "strings" 27 "testing" 28 29 "github.com/stretchr/testify/assert" 30 31 vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" 32 ) 33 34 func TestWrapNil(t *testing.T) { 35 got := Wrap(nil, "no error") 36 if got != nil { 37 t.Errorf("Wrap(nil, \"no error\"): got %#v, expected nil", got) 38 } 39 } 40 41 func TestWrap(t *testing.T) { 42 tests := []struct { 43 err error 44 message string 45 wantMessage string 46 wantCode vtrpcpb.Code 47 }{ 48 {io.EOF, "read error", "read error: EOF", vtrpcpb.Code_UNKNOWN}, 49 {New(vtrpcpb.Code_ALREADY_EXISTS, "oops"), "client error", "client error: oops", vtrpcpb.Code_ALREADY_EXISTS}, 50 } 51 52 for _, tt := range tests { 53 got := Wrap(tt.err, tt.message) 54 if got.Error() != tt.wantMessage { 55 t.Errorf("Wrap(%v, %q): got: [%v], want [%v]", tt.err, tt.message, got, tt.wantMessage) 56 } 57 if Code(got) != tt.wantCode { 58 t.Errorf("Wrap(%v, %v): got: [%v], want [%v]", tt.err, tt, Code(got), tt.wantCode) 59 } 60 } 61 } 62 63 func TestUnwrap(t *testing.T) { 64 tests := []struct { 65 err error 66 isWrapped bool 67 }{ 68 {fmt.Errorf("some error: %d", 17), false}, 69 {errors.New("some new error"), false}, 70 {Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "some msg %d", 19), false}, 71 {Wrapf(errors.New("some wrapped error"), "some msg"), true}, 72 {nil, false}, 73 } 74 75 for _, tt := range tests { 76 t.Run(fmt.Sprintf("%v", tt.err), func(t *testing.T) { 77 { 78 wasWrapped, unwrapped := Unwrap(tt.err) 79 assert.Equal(t, tt.isWrapped, wasWrapped) 80 if !wasWrapped { 81 assert.Equal(t, tt.err, unwrapped) 82 } 83 } 84 { 85 wrapped := Wrap(tt.err, "some message") 86 wasWrapped, unwrapped := Unwrap(wrapped) 87 assert.Equal(t, wasWrapped, (tt.err != nil)) 88 assert.Equal(t, tt.err, unwrapped) 89 } 90 }) 91 } 92 } 93 94 func TestUnwrapAll(t *testing.T) { 95 tests := []struct { 96 err error 97 }{ 98 {fmt.Errorf("some error: %d", 17)}, 99 {errors.New("some new error")}, 100 {Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "some msg %d", 19)}, 101 {nil}, 102 } 103 104 for _, tt := range tests { 105 t.Run(fmt.Sprintf("%v", tt.err), func(t *testing.T) { 106 { 107 // see that unwrapping a non-wrapped error just returns the same error 108 unwrapped := UnwrapAll(tt.err) 109 assert.Equal(t, tt.err, unwrapped) 110 } 111 { 112 // see that unwrapping a 5-times wrapped error returns the original error 113 wrapped := tt.err 114 for range rand.Perm(5) { 115 wrapped = Wrap(wrapped, "some message") 116 } 117 unwrapped := UnwrapAll(wrapped) 118 assert.Equal(t, tt.err, unwrapped) 119 } 120 }) 121 } 122 123 } 124 125 type nilError struct{} 126 127 func (nilError) Error() string { return "nil error" } 128 129 func TestRootCause(t *testing.T) { 130 x := New(vtrpcpb.Code_FAILED_PRECONDITION, "error") 131 tests := []struct { 132 err error 133 want error 134 }{{ 135 // nil error is nil 136 err: nil, 137 want: nil, 138 }, { 139 // explicit nil error is nil 140 err: (error)(nil), 141 want: nil, 142 }, { 143 // typed nil is nil 144 err: (*nilError)(nil), 145 want: (*nilError)(nil), 146 }, { 147 // uncaused error is unaffected 148 err: io.EOF, 149 want: io.EOF, 150 }, { 151 // caused error returns cause 152 err: Wrap(io.EOF, "ignored"), 153 want: io.EOF, 154 }, { 155 err: x, // return from errors.New 156 want: x, 157 }} 158 159 for i, tt := range tests { 160 got := RootCause(tt.err) 161 if !reflect.DeepEqual(got, tt.want) { 162 t.Errorf("test %d: got %#v, want %#v", i+1, got, tt.want) 163 } 164 } 165 } 166 167 func TestCause(t *testing.T) { 168 x := New(vtrpcpb.Code_FAILED_PRECONDITION, "error") 169 tests := []struct { 170 err error 171 want error 172 }{{ 173 // nil error is nil 174 err: nil, 175 want: nil, 176 }, { 177 // uncaused error is nil 178 err: io.EOF, 179 want: nil, 180 }, { 181 // caused error returns cause 182 err: Wrap(io.EOF, "ignored"), 183 want: io.EOF, 184 }, { 185 err: x, // return from errors.New 186 want: nil, 187 }} 188 189 for i, tt := range tests { 190 got := Cause(tt.err) 191 if !reflect.DeepEqual(got, tt.want) { 192 t.Errorf("test %d: got %#v, want %#v", i+1, got, tt.want) 193 } 194 } 195 } 196 197 func TestWrapfNil(t *testing.T) { 198 got := Wrapf(nil, "no error") 199 if got != nil { 200 t.Errorf("Wrapf(nil, \"no error\"): got %#v, expected nil", got) 201 } 202 } 203 204 func TestWrapf(t *testing.T) { 205 tests := []struct { 206 err error 207 message string 208 want string 209 }{ 210 {io.EOF, "read error", "read error: EOF"}, 211 {Wrapf(io.EOF, "read error without format specifiers"), "client error", "client error: read error without format specifiers: EOF"}, 212 {Wrapf(io.EOF, "read error with %d format specifier", 1), "client error", "client error: read error with 1 format specifier: EOF"}, 213 } 214 215 for _, tt := range tests { 216 got := Wrapf(tt.err, tt.message).Error() 217 if got != tt.want { 218 t.Errorf("Wrapf(%v, %q): got: %v, want %v", tt.err, tt.message, got, tt.want) 219 } 220 } 221 } 222 223 func TestErrorf(t *testing.T) { 224 tests := []struct { 225 err error 226 want string 227 }{ 228 {Errorf(vtrpcpb.Code_DATA_LOSS, "read error without format specifiers"), "read error without format specifiers"}, 229 {Errorf(vtrpcpb.Code_DATA_LOSS, "read error with %d format specifier", 1), "read error with 1 format specifier"}, 230 } 231 232 for _, tt := range tests { 233 got := tt.err.Error() 234 if got != tt.want { 235 t.Errorf("Errorf(%v): got: %q, want %q", tt.err, got, tt.want) 236 } 237 } 238 } 239 240 func innerMost() error { 241 return Wrap(io.ErrNoProgress, "oh noes") 242 } 243 244 func middle() error { 245 return innerMost() 246 } 247 248 func outer() error { 249 return middle() 250 } 251 252 func TestStackFormat(t *testing.T) { 253 err := outer() 254 got := fmt.Sprintf("%v", err) 255 256 assertContains(t, got, "innerMost", false) 257 assertContains(t, got, "middle", false) 258 assertContains(t, got, "outer", false) 259 260 logErrStacks = true 261 defer func() { logErrStacks = false }() 262 got = fmt.Sprintf("%v", err) 263 assertContains(t, got, "innerMost", true) 264 assertContains(t, got, "middle", true) 265 assertContains(t, got, "outer", true) 266 } 267 268 // errors.New, etc values are not expected to be compared by value 269 // but the change in errors#27 made them incomparable. Assert that 270 // various kinds of errors have a functional equality operator, even 271 // if the result of that equality is always false. 272 func TestErrorEquality(t *testing.T) { 273 vals := []error{ 274 nil, 275 io.EOF, 276 errors.New("EOF"), 277 New(vtrpcpb.Code_ALREADY_EXISTS, "EOF"), 278 Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "EOF"), 279 Wrap(io.EOF, "EOF"), 280 Wrapf(io.EOF, "EOF%d", 2), 281 } 282 283 for i := range vals { 284 for j := range vals { 285 _ = vals[i] == vals[j] // mustn't panic 286 } 287 } 288 } 289 290 func TestCreation(t *testing.T) { 291 testcases := []struct { 292 in, want vtrpcpb.Code 293 }{{ 294 in: vtrpcpb.Code_CANCELED, 295 want: vtrpcpb.Code_CANCELED, 296 }, { 297 in: vtrpcpb.Code_UNKNOWN, 298 want: vtrpcpb.Code_UNKNOWN, 299 }} 300 for _, tcase := range testcases { 301 if got := Code(New(tcase.in, "")); got != tcase.want { 302 t.Errorf("Code(New(%v)): %v, want %v", tcase.in, got, tcase.want) 303 } 304 if got := Code(Errorf(tcase.in, "")); got != tcase.want { 305 t.Errorf("Code(Errorf(%v)): %v, want %v", tcase.in, got, tcase.want) 306 } 307 } 308 } 309 310 func TestCode(t *testing.T) { 311 testcases := []struct { 312 in error 313 want vtrpcpb.Code 314 }{{ 315 in: nil, 316 want: vtrpcpb.Code_OK, 317 }, { 318 in: errors.New("generic"), 319 want: vtrpcpb.Code_UNKNOWN, 320 }, { 321 in: New(vtrpcpb.Code_CANCELED, "generic"), 322 want: vtrpcpb.Code_CANCELED, 323 }, { 324 in: context.Canceled, 325 want: vtrpcpb.Code_CANCELED, 326 }, { 327 in: context.DeadlineExceeded, 328 want: vtrpcpb.Code_DEADLINE_EXCEEDED, 329 }} 330 for _, tcase := range testcases { 331 if got := Code(tcase.in); got != tcase.want { 332 t.Errorf("Code(%v): %v, want %v", tcase.in, got, tcase.want) 333 } 334 } 335 } 336 337 func TestWrapping(t *testing.T) { 338 err1 := Errorf(vtrpcpb.Code_UNAVAILABLE, "foo") 339 err2 := Wrapf(err1, "bar") 340 err3 := Wrapf(err2, "baz") 341 errorWithoutStack := fmt.Sprintf("%v", err3) 342 343 logErrStacks = true 344 errorWithStack := fmt.Sprintf("%v", err3) 345 logErrStacks = false 346 347 assertEquals(t, err3.Error(), "baz: bar: foo") 348 assertContains(t, errorWithoutStack, "foo", true) 349 assertContains(t, errorWithoutStack, "bar", true) 350 assertContains(t, errorWithoutStack, "baz", true) 351 assertContains(t, errorWithoutStack, "TestWrapping", false) 352 353 assertContains(t, errorWithStack, "foo", true) 354 assertContains(t, errorWithStack, "bar", true) 355 assertContains(t, errorWithStack, "baz", true) 356 assertContains(t, errorWithStack, "TestWrapping", true) 357 358 } 359 360 func assertContains(t *testing.T, s, substring string, contains bool) { 361 t.Helper() 362 if doesContain := strings.Contains(s, substring); doesContain != contains { 363 t.Errorf("string `%v` contains `%v`: %v, want %v", s, substring, doesContain, contains) 364 } 365 } 366 367 func assertEquals(t *testing.T, a, b any) { 368 if a != b { 369 t.Fatalf("expected [%s] to be equal to [%s]", a, b) 370 } 371 }