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  }