github.com/spotmaxtech/k8s-apimachinery-v0260@v0.0.1/pkg/util/errors/errors_test.go (about) 1 /* 2 Copyright 2015 The Kubernetes 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 errors 18 19 import ( 20 "errors" 21 "fmt" 22 "reflect" 23 "sort" 24 "testing" 25 ) 26 27 func TestEmptyAggregate(t *testing.T) { 28 var slice []error 29 var agg Aggregate 30 var err error 31 32 agg = NewAggregate(slice) 33 if agg != nil { 34 t.Errorf("expected nil, got %#v", agg) 35 } 36 err = NewAggregate(slice) 37 if err != nil { 38 t.Errorf("expected nil, got %#v", err) 39 } 40 41 // This is not normally possible, but pedantry demands I test it. 42 agg = aggregate(slice) // empty aggregate 43 if s := agg.Error(); s != "" { 44 t.Errorf("expected empty string, got %q", s) 45 } 46 if s := agg.Errors(); len(s) != 0 { 47 t.Errorf("expected empty slice, got %#v", s) 48 } 49 err = agg.(error) 50 if s := err.Error(); s != "" { 51 t.Errorf("expected empty string, got %q", s) 52 } 53 } 54 55 func TestAggregateWithNil(t *testing.T) { 56 var slice []error 57 slice = []error{nil} 58 var agg Aggregate 59 var err error 60 61 agg = NewAggregate(slice) 62 if agg != nil { 63 t.Errorf("expected nil, got %#v", agg) 64 } 65 err = NewAggregate(slice) 66 if err != nil { 67 t.Errorf("expected nil, got %#v", err) 68 } 69 70 // Append a non-nil error 71 slice = append(slice, fmt.Errorf("err")) 72 agg = NewAggregate(slice) 73 if agg == nil { 74 t.Errorf("expected non-nil") 75 } 76 if s := agg.Error(); s != "err" { 77 t.Errorf("expected 'err', got %q", s) 78 } 79 if s := agg.Errors(); len(s) != 1 { 80 t.Errorf("expected one-element slice, got %#v", s) 81 } 82 if s := agg.Errors()[0].Error(); s != "err" { 83 t.Errorf("expected 'err', got %q", s) 84 } 85 86 err = agg.(error) 87 if err == nil { 88 t.Errorf("expected non-nil") 89 } 90 if s := err.Error(); s != "err" { 91 t.Errorf("expected 'err', got %q", s) 92 } 93 } 94 95 func TestSingularAggregate(t *testing.T) { 96 var slice []error = []error{fmt.Errorf("err")} 97 var agg Aggregate 98 var err error 99 100 agg = NewAggregate(slice) 101 if agg == nil { 102 t.Errorf("expected non-nil") 103 } 104 if s := agg.Error(); s != "err" { 105 t.Errorf("expected 'err', got %q", s) 106 } 107 if s := agg.Errors(); len(s) != 1 { 108 t.Errorf("expected one-element slice, got %#v", s) 109 } 110 if s := agg.Errors()[0].Error(); s != "err" { 111 t.Errorf("expected 'err', got %q", s) 112 } 113 114 err = agg.(error) 115 if err == nil { 116 t.Errorf("expected non-nil") 117 } 118 if s := err.Error(); s != "err" { 119 t.Errorf("expected 'err', got %q", s) 120 } 121 } 122 123 func TestPluralAggregate(t *testing.T) { 124 var slice []error = []error{fmt.Errorf("abc"), fmt.Errorf("123")} 125 var agg Aggregate 126 var err error 127 128 agg = NewAggregate(slice) 129 if agg == nil { 130 t.Errorf("expected non-nil") 131 } 132 if s := agg.Error(); s != "[abc, 123]" { 133 t.Errorf("expected '[abc, 123]', got %q", s) 134 } 135 if s := agg.Errors(); len(s) != 2 { 136 t.Errorf("expected two-elements slice, got %#v", s) 137 } 138 if s := agg.Errors()[0].Error(); s != "abc" { 139 t.Errorf("expected '[abc, 123]', got %q", s) 140 } 141 142 err = agg.(error) 143 if err == nil { 144 t.Errorf("expected non-nil") 145 } 146 if s := err.Error(); s != "[abc, 123]" { 147 t.Errorf("expected '[abc, 123]', got %q", s) 148 } 149 } 150 151 func TestDedupeAggregate(t *testing.T) { 152 var slice []error = []error{fmt.Errorf("abc"), fmt.Errorf("abc")} 153 var agg Aggregate 154 155 agg = NewAggregate(slice) 156 if agg == nil { 157 t.Errorf("expected non-nil") 158 } 159 if s := agg.Error(); s != "abc" { 160 t.Errorf("expected 'abc', got %q", s) 161 } 162 if s := agg.Errors(); len(s) != 2 { 163 t.Errorf("expected two-elements slice, got %#v", s) 164 } 165 } 166 167 func TestDedupePluralAggregate(t *testing.T) { 168 var slice []error = []error{fmt.Errorf("abc"), fmt.Errorf("abc"), fmt.Errorf("123")} 169 var agg Aggregate 170 171 agg = NewAggregate(slice) 172 if agg == nil { 173 t.Errorf("expected non-nil") 174 } 175 if s := agg.Error(); s != "[abc, 123]" { 176 t.Errorf("expected '[abc, 123]', got %q", s) 177 } 178 if s := agg.Errors(); len(s) != 3 { 179 t.Errorf("expected three-elements slice, got %#v", s) 180 } 181 } 182 183 func TestFlattenAndDedupeAggregate(t *testing.T) { 184 var slice []error = []error{fmt.Errorf("abc"), fmt.Errorf("abc"), NewAggregate([]error{fmt.Errorf("abc")})} 185 var agg Aggregate 186 187 agg = NewAggregate(slice) 188 if agg == nil { 189 t.Errorf("expected non-nil") 190 } 191 if s := agg.Error(); s != "abc" { 192 t.Errorf("expected 'abc', got %q", s) 193 } 194 if s := agg.Errors(); len(s) != 3 { 195 t.Errorf("expected three-elements slice, got %#v", s) 196 } 197 } 198 199 func TestFlattenAggregate(t *testing.T) { 200 var slice []error = []error{fmt.Errorf("abc"), fmt.Errorf("abc"), NewAggregate([]error{fmt.Errorf("abc"), fmt.Errorf("def"), NewAggregate([]error{fmt.Errorf("def"), fmt.Errorf("ghi")})})} 201 var agg Aggregate 202 203 agg = NewAggregate(slice) 204 if agg == nil { 205 t.Errorf("expected non-nil") 206 } 207 if s := agg.Error(); s != "[abc, def, ghi]" { 208 t.Errorf("expected '[abc, def, ghi]', got %q", s) 209 } 210 if s := agg.Errors(); len(s) != 3 { 211 t.Errorf("expected three-elements slice, got %#v", s) 212 } 213 } 214 215 func TestFilterOut(t *testing.T) { 216 testCases := []struct { 217 err error 218 filter []Matcher 219 expected error 220 }{ 221 { 222 nil, 223 []Matcher{}, 224 nil, 225 }, 226 { 227 aggregate{}, 228 []Matcher{}, 229 nil, 230 }, 231 { 232 aggregate{fmt.Errorf("abc")}, 233 []Matcher{}, 234 aggregate{fmt.Errorf("abc")}, 235 }, 236 { 237 aggregate{fmt.Errorf("abc")}, 238 []Matcher{func(err error) bool { return false }}, 239 aggregate{fmt.Errorf("abc")}, 240 }, 241 { 242 aggregate{fmt.Errorf("abc")}, 243 []Matcher{func(err error) bool { return true }}, 244 nil, 245 }, 246 { 247 aggregate{fmt.Errorf("abc")}, 248 []Matcher{func(err error) bool { return false }, func(err error) bool { return false }}, 249 aggregate{fmt.Errorf("abc")}, 250 }, 251 { 252 aggregate{fmt.Errorf("abc")}, 253 []Matcher{func(err error) bool { return false }, func(err error) bool { return true }}, 254 nil, 255 }, 256 { 257 aggregate{fmt.Errorf("abc"), fmt.Errorf("def"), fmt.Errorf("ghi")}, 258 []Matcher{func(err error) bool { return err.Error() == "def" }}, 259 aggregate{fmt.Errorf("abc"), fmt.Errorf("ghi")}, 260 }, 261 { 262 aggregate{aggregate{fmt.Errorf("abc")}}, 263 []Matcher{}, 264 aggregate{aggregate{fmt.Errorf("abc")}}, 265 }, 266 { 267 aggregate{aggregate{fmt.Errorf("abc"), aggregate{fmt.Errorf("def")}}}, 268 []Matcher{}, 269 aggregate{aggregate{fmt.Errorf("abc"), aggregate{fmt.Errorf("def")}}}, 270 }, 271 { 272 aggregate{aggregate{fmt.Errorf("abc"), aggregate{fmt.Errorf("def")}}}, 273 []Matcher{func(err error) bool { return err.Error() == "def" }}, 274 aggregate{aggregate{fmt.Errorf("abc")}}, 275 }, 276 } 277 for i, testCase := range testCases { 278 err := FilterOut(testCase.err, testCase.filter...) 279 if !reflect.DeepEqual(testCase.expected, err) { 280 t.Errorf("%d: expected %v, got %v", i, testCase.expected, err) 281 } 282 } 283 } 284 285 func TestFlatten(t *testing.T) { 286 testCases := []struct { 287 agg Aggregate 288 expected Aggregate 289 }{ 290 { 291 nil, 292 nil, 293 }, 294 { 295 aggregate{}, 296 nil, 297 }, 298 { 299 aggregate{fmt.Errorf("abc")}, 300 aggregate{fmt.Errorf("abc")}, 301 }, 302 { 303 aggregate{fmt.Errorf("abc"), fmt.Errorf("def"), fmt.Errorf("ghi")}, 304 aggregate{fmt.Errorf("abc"), fmt.Errorf("def"), fmt.Errorf("ghi")}, 305 }, 306 { 307 aggregate{aggregate{fmt.Errorf("abc")}}, 308 aggregate{fmt.Errorf("abc")}, 309 }, 310 { 311 aggregate{aggregate{aggregate{fmt.Errorf("abc")}}}, 312 aggregate{fmt.Errorf("abc")}, 313 }, 314 { 315 aggregate{aggregate{fmt.Errorf("abc"), aggregate{fmt.Errorf("def")}}}, 316 aggregate{fmt.Errorf("abc"), fmt.Errorf("def")}, 317 }, 318 { 319 aggregate{aggregate{aggregate{fmt.Errorf("abc")}, fmt.Errorf("def"), aggregate{fmt.Errorf("ghi")}}}, 320 aggregate{fmt.Errorf("abc"), fmt.Errorf("def"), fmt.Errorf("ghi")}, 321 }, 322 } 323 for i, testCase := range testCases { 324 agg := Flatten(testCase.agg) 325 if !reflect.DeepEqual(testCase.expected, agg) { 326 t.Errorf("%d: expected %v, got %v", i, testCase.expected, agg) 327 } 328 } 329 } 330 331 func TestCreateAggregateFromMessageCountMap(t *testing.T) { 332 testCases := []struct { 333 name string 334 mcm MessageCountMap 335 expected Aggregate 336 }{ 337 { 338 "input has single instance of one message", 339 MessageCountMap{"abc": 1}, 340 aggregate{fmt.Errorf("abc")}, 341 }, 342 { 343 "input has multiple messages", 344 MessageCountMap{"abc": 2, "ghi": 1}, 345 aggregate{fmt.Errorf("abc (repeated 2 times)"), fmt.Errorf("ghi")}, 346 }, 347 { 348 "input has multiple messages", 349 MessageCountMap{"ghi": 1, "abc": 2}, 350 aggregate{fmt.Errorf("abc (repeated 2 times)"), fmt.Errorf("ghi")}, 351 }, 352 } 353 354 var expected, agg []error 355 for _, testCase := range testCases { 356 t.Run(testCase.name, func(t *testing.T) { 357 if testCase.expected != nil { 358 expected = testCase.expected.Errors() 359 sort.Slice(expected, func(i, j int) bool { return expected[i].Error() < expected[j].Error() }) 360 } 361 if testCase.mcm != nil { 362 agg = CreateAggregateFromMessageCountMap(testCase.mcm).Errors() 363 sort.Slice(agg, func(i, j int) bool { return agg[i].Error() < agg[j].Error() }) 364 } 365 if !reflect.DeepEqual(expected, agg) { 366 t.Errorf("expected %v, got %v", expected, agg) 367 } 368 }) 369 } 370 } 371 372 func TestAggregateGoroutines(t *testing.T) { 373 testCases := []struct { 374 errs []error 375 expected map[string]bool // can't compare directly to Aggregate due to non-deterministic ordering 376 }{ 377 { 378 []error{}, 379 nil, 380 }, 381 { 382 []error{nil}, 383 nil, 384 }, 385 { 386 []error{nil, nil}, 387 nil, 388 }, 389 { 390 []error{fmt.Errorf("1")}, 391 map[string]bool{"1": true}, 392 }, 393 { 394 []error{fmt.Errorf("1"), nil}, 395 map[string]bool{"1": true}, 396 }, 397 { 398 []error{fmt.Errorf("1"), fmt.Errorf("267")}, 399 map[string]bool{"1": true, "267": true}, 400 }, 401 { 402 []error{fmt.Errorf("1"), nil, fmt.Errorf("1234")}, 403 map[string]bool{"1": true, "1234": true}, 404 }, 405 { 406 []error{nil, fmt.Errorf("1"), nil, fmt.Errorf("1234"), fmt.Errorf("22")}, 407 map[string]bool{"1": true, "1234": true, "22": true}, 408 }, 409 } 410 for i, testCase := range testCases { 411 funcs := make([]func() error, len(testCase.errs)) 412 for i := range testCase.errs { 413 err := testCase.errs[i] 414 funcs[i] = func() error { return err } 415 } 416 agg := AggregateGoroutines(funcs...) 417 if agg == nil { 418 if len(testCase.expected) > 0 { 419 t.Errorf("%d: expected %v, got nil", i, testCase.expected) 420 } 421 continue 422 } 423 if len(agg.Errors()) != len(testCase.expected) { 424 t.Errorf("%d: expected %d errors in aggregate, got %v", i, len(testCase.expected), agg) 425 continue 426 } 427 for _, err := range agg.Errors() { 428 if !testCase.expected[err.Error()] { 429 t.Errorf("%d: expected %v, got aggregate containing %v", i, testCase.expected, err) 430 } 431 } 432 } 433 } 434 435 type alwaysMatchingError struct{} 436 437 func (_ alwaysMatchingError) Error() string { 438 return "error" 439 } 440 441 func (_ alwaysMatchingError) Is(_ error) bool { 442 return true 443 } 444 445 type someError struct{ msg string } 446 447 func (se someError) Error() string { 448 if se.msg != "" { 449 return se.msg 450 } 451 return "err" 452 } 453 454 func TestAggregateWithErrorsIs(t *testing.T) { 455 testCases := []struct { 456 name string 457 err error 458 matchAgainst error 459 expectMatch bool 460 }{ 461 { 462 name: "no match", 463 err: aggregate{errors.New("my-error"), errors.New("my-other-error")}, 464 matchAgainst: fmt.Errorf("no entry %s", "here"), 465 }, 466 { 467 name: "match via .Is()", 468 err: aggregate{errors.New("forbidden"), alwaysMatchingError{}}, 469 matchAgainst: errors.New("unauthorized"), 470 expectMatch: true, 471 }, 472 { 473 name: "match via equality", 474 err: aggregate{errors.New("err"), someError{}}, 475 matchAgainst: someError{}, 476 expectMatch: true, 477 }, 478 { 479 name: "match via nested aggregate", 480 err: aggregate{errors.New("closed today"), aggregate{aggregate{someError{}}}}, 481 matchAgainst: someError{}, 482 expectMatch: true, 483 }, 484 { 485 name: "match via wrapped aggregate", 486 err: fmt.Errorf("wrap: %w", aggregate{errors.New("err"), someError{}}), 487 matchAgainst: someError{}, 488 expectMatch: true, 489 }, 490 } 491 492 for _, tc := range testCases { 493 t.Run(tc.name, func(t *testing.T) { 494 result := errors.Is(tc.err, tc.matchAgainst) 495 if result != tc.expectMatch { 496 t.Errorf("expected match: %t, got match: %t", tc.expectMatch, result) 497 } 498 }) 499 } 500 } 501 502 type accessTrackingError struct { 503 wasAccessed bool 504 } 505 506 func (accessTrackingError) Error() string { 507 return "err" 508 } 509 510 func (ate *accessTrackingError) Is(_ error) bool { 511 ate.wasAccessed = true 512 return true 513 } 514 515 var _ error = &accessTrackingError{} 516 517 func TestErrConfigurationInvalidWithErrorsIsShortCircuitsOnFirstMatch(t *testing.T) { 518 errC := aggregate{&accessTrackingError{}, &accessTrackingError{}} 519 _ = errors.Is(errC, &accessTrackingError{}) 520 521 var numAccessed int 522 for _, err := range errC { 523 if ate := err.(*accessTrackingError); ate.wasAccessed { 524 numAccessed++ 525 } 526 } 527 if numAccessed != 1 { 528 t.Errorf("expected exactly one error to get accessed, got %d", numAccessed) 529 } 530 }