github.com/go-graphite/carbonapi@v0.17.0/zipper/zipper_test.go (about) 1 package zipper 2 3 import ( 4 "fmt" 5 "math" 6 "testing" 7 8 "github.com/ansel1/merry" 9 10 "github.com/go-graphite/carbonapi/zipper/types" 11 protov3 "github.com/go-graphite/protocol/carbonapi_v3_pb" 12 ) 13 14 type mergeValuesData struct { 15 name string 16 m1 protov3.FetchResponse 17 m2 protov3.FetchResponse 18 expectedResult protov3.FetchResponse 19 expectedError merry.Error 20 } 21 22 var ( 23 errMetadataMismatchFmt = "%v mismatch, got %v, expected %v" 24 errLengthMismatchFmt = "length mismatch, got %v, expected %v" 25 errContentMismatchFmt = "content mismatch at pos %v, got %v, expected %v" 26 ) 27 28 func fetchResponseEquals(r1, r2 *protov3.FetchResponse) error { 29 if r1.StartTime != r2.StartTime { 30 return fmt.Errorf(errMetadataMismatchFmt, "StartTime", r1.StartTime, r2.StartTime) 31 } 32 33 if r1.StopTime != r2.StopTime { 34 return fmt.Errorf(errMetadataMismatchFmt, "StopTime", r1.StopTime, r2.StopTime) 35 } 36 37 if r1.XFilesFactor != r2.XFilesFactor { 38 return fmt.Errorf(errMetadataMismatchFmt, "XFilesFactor", r1.XFilesFactor, r2.XFilesFactor) 39 } 40 41 if r1.Name != r2.Name { 42 return fmt.Errorf(errMetadataMismatchFmt, "Name", r1.Name, r2.Name) 43 } 44 45 if r1.StepTime != r2.StepTime { 46 return fmt.Errorf(errMetadataMismatchFmt, "StepTime", r1.StepTime, r2.StepTime) 47 } 48 49 if r1.ConsolidationFunc != r2.ConsolidationFunc { 50 return fmt.Errorf(errMetadataMismatchFmt, "ConsolidationFunc", r1.ConsolidationFunc, r2.ConsolidationFunc) 51 } 52 53 if len(r1.Values) != len(r2.Values) { 54 return fmt.Errorf(errLengthMismatchFmt, r1.Values, r2.Values) 55 } 56 57 for i := range r1.Values { 58 if math.IsNaN(r1.Values[i]) && math.IsNaN(r2.Values[i]) { 59 continue 60 } 61 if r1.Values[i] != r2.Values[i] { 62 return fmt.Errorf(errContentMismatchFmt, i, r1.Values[i], r2.Values[i]) 63 } 64 } 65 66 return nil 67 } 68 69 func TestMergeValues(t *testing.T) { 70 tests := []mergeValuesData{ 71 { 72 name: "simple 1", 73 // 60 seconds 74 m1: protov3.FetchResponse{ 75 Name: "foo", 76 StartTime: 60, 77 StepTime: 60, 78 ConsolidationFunc: "average", 79 Values: []float64{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}, 80 }, 81 // 120 seconds 82 m2: protov3.FetchResponse{ 83 Name: "foo", 84 StartTime: 0, 85 StepTime: 120, 86 ConsolidationFunc: "average", 87 Values: []float64{1, 3, 5, 7, 9}, 88 }, 89 90 expectedResult: protov3.FetchResponse{ 91 Name: "foo", 92 StartTime: 60, 93 StepTime: 60, 94 ConsolidationFunc: "average", 95 Values: []float64{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}, 96 }, 97 98 expectedError: nil, 99 }, 100 { 101 name: "simple 2", 102 // 60 seconds 103 m1: protov3.FetchResponse{ 104 Name: "foo", 105 StartTime: 0, 106 StepTime: 120, 107 ConsolidationFunc: "average", 108 Values: []float64{1, 3, 5, 7, 9}, 109 }, 110 // 120 seconds 111 m2: protov3.FetchResponse{ 112 Name: "foo", 113 StartTime: 60, 114 StepTime: 60, 115 ConsolidationFunc: "average", 116 Values: []float64{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}, 117 }, 118 119 expectedResult: protov3.FetchResponse{ 120 Name: "foo", 121 StartTime: 60, 122 StepTime: 60, 123 ConsolidationFunc: "average", 124 Values: []float64{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}, 125 }, 126 127 expectedError: nil, 128 }, 129 { 130 name: "fill the gaps simple", 131 // 60 seconds 132 m1: protov3.FetchResponse{ 133 Name: "foo", 134 StartTime: 60, 135 StepTime: 60, 136 ConsolidationFunc: "average", 137 Values: []float64{1, 2, 3, 4, math.NaN(), 6, 7, 8, 9, math.NaN(), 11, 12, 13, 14, 15, 16, math.NaN(), math.NaN(), math.NaN(), 20}, 138 }, 139 // 120 seconds 140 m2: protov3.FetchResponse{ 141 Name: "foo", 142 StartTime: 60, 143 StepTime: 60, 144 ConsolidationFunc: "average", 145 Values: []float64{1, 2, math.NaN(), math.NaN(), 5, 6, 7, 8, 9, math.NaN(), 11, 12, math.NaN(), 14, 15, 16, 17, 18, math.NaN(), 20}, 146 }, 147 148 expectedResult: protov3.FetchResponse{ 149 Name: "foo", 150 StartTime: 60, 151 StepTime: 60, 152 ConsolidationFunc: "average", 153 Values: []float64{1, 2, 3, 4, 5, 6, 7, 8, 9, math.NaN(), 11, 12, 13, 14, 15, 16, 17, 18, math.NaN(), 20}, 154 }, 155 156 expectedError: nil, 157 }, 158 { 159 name: "fill end of metric", 160 // 60 seconds 161 m1: protov3.FetchResponse{ 162 Name: "foo", 163 StartTime: 60, 164 StepTime: 60, 165 ConsolidationFunc: "average", 166 Values: []float64{1, 2, 3, 4, math.NaN(), 6, 7, 8, 9, math.NaN(), 11, 12, 13, 14, 15, 16, math.NaN(), math.NaN(), math.NaN()}, 167 }, 168 // 120 seconds 169 m2: protov3.FetchResponse{ 170 Name: "foo", 171 StartTime: 60, 172 StepTime: 60, 173 ConsolidationFunc: "average", 174 Values: []float64{1, 2, math.NaN(), math.NaN(), 5, 6, 7, 8, 9, math.NaN(), 11, 12, math.NaN(), 14, 15, 16, 17, 18, math.NaN(), 20}, 175 }, 176 177 expectedResult: protov3.FetchResponse{ 178 Name: "foo", 179 StartTime: 60, 180 StepTime: 60, 181 ConsolidationFunc: "average", 182 Values: []float64{1, 2, 3, 4, 5, 6, 7, 8, 9, math.NaN(), 11, 12, 13, 14, 15, 16, 17, 18, math.NaN(), 20}, 183 }, 184 185 expectedError: nil, 186 }, 187 } 188 189 for _, test := range tests { 190 t.Run(test.name, func(t *testing.T) { 191 if test.m1.StopTime <= test.m1.StartTime { 192 test.m1.StopTime = test.m1.StartTime + int64(len(test.m1.Values))*test.m1.StepTime 193 } 194 if test.m2.StopTime <= test.m2.StartTime { 195 test.m2.StopTime = test.m2.StartTime + int64(len(test.m2.Values))*test.m2.StepTime 196 } 197 if test.expectedResult.StopTime <= test.expectedResult.StartTime { 198 test.expectedResult.StopTime = test.expectedResult.StartTime + int64(len(test.expectedResult.Values))*test.expectedResult.StepTime 199 } 200 err := types.MergeFetchResponses(&test.m1, &test.m2) 201 if !merry.Is(err, test.expectedError) { 202 t.Fatalf("unexpected error: '%v'", err) 203 } 204 205 err2 := fetchResponseEquals(&test.m1, &test.expectedResult) 206 if err2 != nil { 207 t.Fatalf("unexpected difference: '%v'\n got : %+v\n expected: %+v\n", err, test.m1, test.expectedResult) 208 } 209 }) 210 } 211 }