github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/m3ninx/index/batch_test.go (about) 1 // Copyright (c) 2018 Uber Technologies, Inc. 2 // 3 // Permission is hereby granted, free of charge, to any person obtaining a copy 4 // of this software and associated documentation files (the "Software"), to deal 5 // in the Software without restriction, including without limitation the rights 6 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 // copies of the Software, and to permit persons to whom the Software is 8 // furnished to do so, subject to the following conditions: 9 // 10 // The above copyright notice and this permission notice shall be included in 11 // all copies or substantial portions of the Software. 12 // 13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 // THE SOFTWARE. 20 21 package index 22 23 import ( 24 "errors" 25 "fmt" 26 "math/rand" 27 "os" 28 "testing" 29 "time" 30 31 "github.com/leanovate/gopter" 32 "github.com/leanovate/gopter/gen" 33 "github.com/leanovate/gopter/prop" 34 "github.com/stretchr/testify/require" 35 ) 36 37 func TestBatchAllowPartialUpdates(t *testing.T) { 38 tests := []struct { 39 name string 40 batch Batch 41 expected bool 42 }{ 43 { 44 name: "off", 45 batch: NewBatch(nil), 46 expected: false, 47 }, 48 { 49 name: "on", 50 batch: NewBatch(nil, AllowPartialUpdates()), 51 expected: true, 52 }, 53 } 54 55 for _, test := range tests { 56 t.Run(test.name, func(t *testing.T) { 57 require.Equal(t, test.expected, test.batch.AllowPartialUpdates) 58 }) 59 } 60 } 61 62 func TestBatchPartialError(t *testing.T) { 63 var ( 64 idxs = []int{3, 7, 13} 65 err = NewBatchPartialError() 66 ) 67 require.True(t, err.IsEmpty()) 68 69 for _, idx := range idxs { 70 err.Add(BatchError{errors.New("error"), idx}) 71 } 72 require.False(t, err.IsEmpty()) 73 74 var actualIdxs []int 75 for _, err := range err.Errs() { 76 actualIdxs = append(actualIdxs, err.Idx) 77 } 78 require.Equal(t, idxs, actualIdxs) 79 80 require.True(t, IsBatchPartialError(err)) 81 require.False(t, IsBatchPartialError(errors.New("error"))) 82 } 83 84 func TestBatchPartialErrorDupeFilter(t *testing.T) { 85 var ( 86 idxs = []int{3, 7, 13} 87 err = NewBatchPartialError() 88 ) 89 require.True(t, err.IsEmpty()) 90 91 for _, idx := range idxs { 92 err.Add(BatchError{ErrDuplicateID, idx}) 93 } 94 require.False(t, err.IsEmpty()) 95 96 var actualIdxs []int 97 for _, err := range err.Errs() { 98 actualIdxs = append(actualIdxs, err.Idx) 99 } 100 101 filtered := err.FilterDuplicateIDErrors() 102 require.Nil(t, filtered) 103 } 104 105 func TestBatchPartialErrorDupeFilterIncludeOther(t *testing.T) { 106 testErr := fmt.Errorf("testerr") 107 108 err := NewBatchPartialError() 109 err.Add(BatchError{ErrDuplicateID, 0}) 110 filtered := err.FilterDuplicateIDErrors() 111 require.Nil(t, filtered) 112 113 err.Add(BatchError{testErr, 1}) 114 filtered = err.FilterDuplicateIDErrors() 115 require.NotNil(t, filtered) 116 117 } 118 119 func TestBatchPartialErrorDupeFilterSingleElement(t *testing.T) { 120 testErr := fmt.Errorf("testerr") 121 122 err := NewBatchPartialError() 123 err.Add(BatchError{testErr, 1}) 124 original := err.FilterDuplicateIDErrors() 125 require.NotNil(t, original) 126 127 err.Add(BatchError{ErrDuplicateID, 0}) 128 after := err.FilterDuplicateIDErrors() 129 require.Equal(t, original.Error(), after.Error()) 130 } 131 132 func TestBatchPartialErrorFilterPropTest(t *testing.T) { 133 parameters := gopter.DefaultTestParameters() 134 seed := time.Now().UnixNano() 135 parameters.MinSuccessfulTests = 1000 136 parameters.MaxSize = 40 137 parameters.Rng = rand.New(rand.NewSource(seed)) 138 parameters.Workers = 1 139 properties := gopter.NewProperties(parameters) 140 141 properties.Property("Filtering errors works", prop.ForAll( 142 func(errStrings []string) bool { 143 errs := make([]error, 0, len(errStrings)) 144 for _, err := range errStrings { 145 errs = append(errs, errors.New(err)) 146 } 147 numDupeErrors := 0 148 b := NewBatchPartialError() 149 for i := range errs { 150 if errs[i] == ErrDuplicateID { 151 numDupeErrors++ 152 } 153 b.Add(BatchError{errs[i], i}) 154 } 155 nb := b.FilterDuplicateIDErrors() 156 if nb == nil { 157 return numDupeErrors == len(errs) 158 } 159 nbb := nb.(*BatchPartialError) 160 return len(nbb.errs) == len(errs)-numDupeErrors 161 }, 162 gen.SliceOf(genError()), 163 )) 164 165 reporter := gopter.NewFormatedReporter(true, 160, os.Stdout) 166 if !properties.Run(reporter) { 167 t.Errorf("failed with initial seed: %d", seed) 168 } 169 } 170 171 func genError() gopter.Gen { 172 return gen.OneConstOf(ErrDocNotFound.Error(), ErrDuplicateID.Error()) 173 }