github.com/go-graphite/carbonapi@v0.17.0/expr/functions/groupByNode/function_test.go (about) 1 package groupByNode 2 3 import ( 4 "context" 5 "testing" 6 "time" 7 8 "github.com/go-graphite/carbonapi/expr/functions/aggregate" 9 "github.com/go-graphite/carbonapi/expr/interfaces" 10 "github.com/go-graphite/carbonapi/expr/metadata" 11 "github.com/go-graphite/carbonapi/expr/types" 12 "github.com/go-graphite/carbonapi/pkg/parser" 13 th "github.com/go-graphite/carbonapi/tests" 14 ) 15 16 var ( 17 md []interfaces.FunctionMetadata = New("") 18 s []interfaces.FunctionMetadata = aggregate.New("") 19 ) 20 21 func init() { 22 for _, m := range s { 23 metadata.RegisterFunction(m.Name, m.F) 24 } 25 for _, m := range md { 26 metadata.RegisterFunction(m.Name, m.F) 27 } 28 } 29 30 // Note: some of these tests are influenced by the testcases for groupByNode and groupByNodes functions 31 // in Graphite-web. See: https://github.com/graphite-project/graphite-web/blob/master/webapp/tests/test_functions.py 32 func TestGroupByNode(t *testing.T) { 33 now32 := int64(time.Now().Unix()) 34 35 mr := parser.MetricRequest{Metric: "metric1.foo.*.*", From: 0, Until: 1} 36 37 tests := []th.MultiReturnEvalTestItem{ 38 { 39 Target: "groupByNode(metric1.foo.*.*,3,\"sum\")", 40 M: map[parser.MetricRequest][]*types.MetricData{ 41 mr: { 42 types.MakeMetricData("metric1.foo.bar1.baz", []float64{1, 2, 3, 4, 5}, 1, now32), 43 types.MakeMetricData("metric1.foo.bar1.qux", []float64{6, 7, 8, 9, 10}, 1, now32), 44 types.MakeMetricData("metric1.foo.bar2.baz", []float64{11, 12, 13, 14, 15}, 1, now32), 45 types.MakeMetricData("metric1.foo.bar2.qux", []float64{7, 8, 9, 10, 11}, 1, now32), 46 }, 47 }, 48 Name: "groupByNode", 49 Results: map[string][]*types.MetricData{ 50 "baz": {types.MakeMetricData("baz", []float64{12, 14, 16, 18, 20}, 1, now32).SetTag("aggregatedBy", "sum")}, 51 "qux": {types.MakeMetricData("qux", []float64{13, 15, 17, 19, 21}, 1, now32).SetTag("aggregatedBy", "sum")}, 52 }, 53 }, 54 { 55 Name: "groupByNode_with_tag", 56 Target: `groupByNode(metric1.foo.*.*,"name","sum")`, 57 M: map[parser.MetricRequest][]*types.MetricData{ 58 mr: { 59 types.MakeMetricData("metric1.foo.bar1.baz", []float64{1, 2, 3, 4, 5}, 1, now32), 60 }, 61 }, 62 Results: map[string][]*types.MetricData{ 63 "metric1.foo.bar1.baz": {types.MakeMetricData("metric1.foo.bar1.baz", []float64{1, 2, 3, 4, 5}, 1, now32).SetTag("aggregatedBy", "sum")}, 64 }, 65 }, 66 { 67 Target: "groupByNode(metric1.foo.*.*,3,\"sum\")", 68 M: map[parser.MetricRequest][]*types.MetricData{ 69 mr: { 70 types.MakeMetricData("metric1.foo.bar1.01", []float64{1, 2, 3, 4, 5}, 1, now32), 71 types.MakeMetricData("metric1.foo.bar1.10", []float64{6, 7, 8, 9, 10}, 1, now32), 72 types.MakeMetricData("metric1.foo.bar2.01", []float64{11, 12, 13, 14, 15}, 1, now32), 73 types.MakeMetricData("metric1.foo.bar2.10", []float64{7, 8, 9, 10, 11}, 1, now32), 74 }, 75 }, 76 Name: "groupByNode_names_with_int", 77 Results: map[string][]*types.MetricData{ 78 "01": {types.MakeMetricData("01", []float64{12, 14, 16, 18, 20}, 1, now32).SetTag("aggregatedBy", "sum")}, 79 "10": {types.MakeMetricData("10", []float64{13, 15, 17, 19, 21}, 1, now32).SetTag("aggregatedBy", "sum")}, 80 }, 81 }, 82 { 83 Target: "groupByNode(metric1.foo.*.*,3,\"sum\")", 84 M: map[parser.MetricRequest][]*types.MetricData{ 85 mr: { 86 types.MakeMetricData("metric1.foo.bar1.127_0_0_1:2003", []float64{1, 2, 3, 4, 5}, 1, now32), 87 types.MakeMetricData("metric1.foo.bar1.127_0_0_1:2004", []float64{6, 7, 8, 9, 10}, 1, now32), 88 types.MakeMetricData("metric1.foo.bar2.127_0_0_1:2003", []float64{11, 12, 13, 14, 15}, 1, now32), 89 types.MakeMetricData("metric1.foo.bar2.127_0_0_1:2004", []float64{7, 8, 9, 10, 11}, 1, now32), 90 }, 91 }, 92 Name: "groupByNode_names_with_colons", 93 Results: map[string][]*types.MetricData{ 94 "127_0_0_1:2003": {types.MakeMetricData("127_0_0_1:2003", []float64{12, 14, 16, 18, 20}, 1, now32).SetTag("aggregatedBy", "sum")}, 95 "127_0_0_1:2004": {types.MakeMetricData("127_0_0_1:2004", []float64{13, 15, 17, 19, 21}, 1, now32).SetTag("aggregatedBy", "sum")}, 96 }, 97 }, 98 { 99 Target: "groupByNode(metric1.foo.*.*,-2,\"sum\")", 100 M: map[parser.MetricRequest][]*types.MetricData{ 101 mr: { 102 types.MakeMetricData("metric1.foo.bar1.baz", []float64{1, 2, 3, 4, 5}, 1, now32), 103 types.MakeMetricData("metric1.foo.bar1.qux", []float64{6, 7, 8, 9, 10}, 1, now32), 104 types.MakeMetricData("metric1.foo.bar2.baz", []float64{11, 12, 13, 14, 15}, 1, now32), 105 types.MakeMetricData("metric1.foo.bar2.qux", []float64{7, 8, 9, 10, 11}, 1, now32), 106 }, 107 }, 108 Name: "groupByNode_with_negative_index", 109 Results: map[string][]*types.MetricData{ 110 "bar1": {types.MakeMetricData("bar1", []float64{7, 9, 11, 13, 15}, 1, now32).SetTag("aggregatedBy", "sum")}, 111 "bar2": {types.MakeMetricData("bar2", []float64{18, 20, 22, 24, 26}, 1, now32).SetTag("aggregatedBy", "sum")}, 112 }, 113 }, 114 { 115 Target: "groupByNode(metric1.foo.*.*,2)", 116 M: map[parser.MetricRequest][]*types.MetricData{ 117 mr: { 118 types.MakeMetricData("metric1.foo.bar1.baz", []float64{1, 2, 3, 4, 5}, 1, now32), 119 types.MakeMetricData("metric1.foo.bar1.qux", []float64{6, 7, 8, 9, 10}, 1, now32), 120 types.MakeMetricData("metric1.foo.bar2.baz", []float64{11, 12, 13, 14, 15}, 1, now32), 121 types.MakeMetricData("metric1.foo.bar2.qux", []float64{7, 8, 9, 10, 11}, 1, now32), 122 }, 123 }, 124 Name: "groupByNode_with_no_callback_arg", 125 Results: map[string][]*types.MetricData{ 126 "bar1": {types.MakeMetricData("bar1", []float64{3.5, 4.5, 5.5, 6.5, 7.5}, 1, now32).SetTag("aggregatedBy", "avg")}, 127 "bar2": {types.MakeMetricData("bar2", []float64{9, 10, 11, 12, 13}, 1, now32).SetTag("aggregatedBy", "avg")}, 128 }, 129 }, 130 { 131 Target: "groupByNodes(metric1.foo.*.*,\"sum\",0,1,3)", 132 M: map[parser.MetricRequest][]*types.MetricData{ 133 mr: { 134 types.MakeMetricData("metric1.foo.bar1.baz", []float64{1, 2, 3, 4, 5}, 1, now32), 135 types.MakeMetricData("metric1.foo.bar1.qux", []float64{6, 7, 8, 9, 10}, 1, now32), 136 types.MakeMetricData("metric1.foo.bar2.baz", []float64{11, 12, 13, 14, 15}, 1, now32), 137 types.MakeMetricData("metric1.foo.bar2.qux", []float64{7, 8, 9, 10, 11}, 1, now32), 138 }, 139 }, 140 Name: "groupByNodes", 141 Results: map[string][]*types.MetricData{ 142 "metric1.foo.baz": {types.MakeMetricData("metric1.foo.baz", []float64{12, 14, 16, 18, 20}, 1, now32).SetTag("aggregatedBy", "sum")}, 143 "metric1.foo.qux": {types.MakeMetricData("metric1.foo.qux", []float64{13, 15, 17, 19, 21}, 1, now32).SetTag("aggregatedBy", "sum")}, 144 }, 145 }, 146 { 147 Name: "groupByNodes_out_of_range_node_is_ignored", 148 Target: "groupByNodes(metric1.foo.*.*,\"sum\",0,5,2)", 149 M: map[parser.MetricRequest][]*types.MetricData{ 150 mr: { 151 types.MakeMetricData("metric1.foo.bar1.baz", []float64{1, 2, 3, 4, 5}, 1, now32), 152 }, 153 }, 154 Results: map[string][]*types.MetricData{ 155 "metric1.bar1": {types.MakeMetricData("metric1.bar1", []float64{1, 2, 3, 4, 5}, 1, now32).SetTag("aggregatedBy", "sum")}, 156 }, 157 }, 158 { 159 Name: "groupByNodes_tags_and_nodes_combined", 160 Target: `groupByNodes(metric1.foo.*.*,"sum","name",1)`, 161 M: map[parser.MetricRequest][]*types.MetricData{ 162 mr: { 163 types.MakeMetricData("metric1.foo.bar1.baz", []float64{1, 2, 3, 4, 5}, 1, now32), 164 types.MakeMetricData("metric1.foo.bar1.bla", []float64{1, 2, 3, 4, 5}, 1, now32), 165 }, 166 }, 167 Results: map[string][]*types.MetricData{ 168 "metric1.foo.bar1.baz.foo": {types.MakeMetricData("metric1.foo.bar1.baz.foo", []float64{1, 2, 3, 4, 5}, 1, now32).SetTag("aggregatedBy", "sum")}, 169 "metric1.foo.bar1.bla.foo": {types.MakeMetricData("metric1.foo.bar1.bla.foo", []float64{1, 2, 3, 4, 5}, 1, now32).SetTag("aggregatedBy", "sum")}, 170 }, 171 }, 172 { 173 Name: "groupByNodes_no_nodes", 174 Target: `groupByNodes(metric1.foo.*.*,"sum")`, 175 M: map[parser.MetricRequest][]*types.MetricData{ 176 mr: { 177 types.MakeMetricData("metric1.foo.bar1.baz", []float64{1, 2, 3, 4, 5}, 1, now32), 178 types.MakeMetricData("metric1.foo.bar1.bla", []float64{1, 2, 3, 4, 5}, 1, now32), 179 }, 180 }, 181 // If no nodes are specified, all metrics are combined to the empty string 182 Results: map[string][]*types.MetricData{ 183 "": {types.MakeMetricData("", []float64{2, 4, 6, 8, 10}, 1, now32).SetTag("aggregatedBy", "sum")}, 184 }, 185 }, 186 { 187 Target: "groupByNode(metric1.foo.*.*,2,\"sum\")", 188 M: map[parser.MetricRequest][]*types.MetricData{ 189 mr: { 190 types.MakeMetricData("metric1.foo.Ab1==.lag", []float64{1, 2, 3, 4, 5}, 1, now32), 191 types.MakeMetricData("metric1.foo.bC2=.lag", []float64{6, 7, 8, 9, 10}, 1, now32), 192 types.MakeMetricData("metric1.foo.Ab1==.lag", []float64{11, 12, 13, 14, 15}, 1, now32), 193 types.MakeMetricData("metric1.foo.bC2=.lag=", []float64{7, 8, 9, 10, 11}, 1, now32), 194 }, 195 }, 196 Name: "groupByNode_names_with_special_symbol_equal", 197 Results: map[string][]*types.MetricData{ 198 "Ab1==": {types.MakeMetricData("Ab1==", []float64{12, 14, 16, 18, 20}, 1, now32).SetTag("aggregatedBy", "sum")}, 199 "bC2=": {types.MakeMetricData("bC2=", []float64{13, 15, 17, 19, 21}, 1, now32).SetTag("aggregatedBy", "sum")}, 200 }, 201 }, 202 { 203 Target: "groupByNode(metric1.foo.*.*,3,\"sum\")", 204 M: map[parser.MetricRequest][]*types.MetricData{ 205 mr: { 206 types.MakeMetricData("metric1.foo.Ab1==.lag;tag1=value1", []float64{1, 2, 3, 4, 5}, 1, now32), 207 types.MakeMetricData("metric1.foo.Ab2.lag=;tag1=value1", []float64{1, 0, 3, 4, 5}, 1, now32), 208 }, 209 }, 210 Name: "groupByNode_tagged_names_with_special_symbol_equal", 211 Results: map[string][]*types.MetricData{ 212 "lag": {types.MakeMetricData("lag", []float64{1, 2, 3, 4, 5}, 1, now32).SetTag("aggregatedBy", "sum")}, 213 "lag=": {types.MakeMetricData("lag=", []float64{1, 0, 3, 4, 5}, 1, now32).SetTag("aggregatedBy", "sum")}, 214 }, 215 }, 216 { 217 Name: "groupByNodes_range", 218 Target: `groupByNodes(test.metric*.foo*,"range",1,0)`, 219 M: map[parser.MetricRequest][]*types.MetricData{ 220 {Metric: "test.metric*.foo*", From: 0, Until: 1}: { 221 types.MakeMetricData("test.metric1.foo1", []float64{0}, 1, now32), 222 types.MakeMetricData("test.metric1.foo2", []float64{0}, 1, now32), 223 types.MakeMetricData("test.metric2.foo1", []float64{0}, 1, now32), 224 types.MakeMetricData("test.metric2.foo2", []float64{0}, 1, now32), 225 }, 226 }, 227 Results: map[string][]*types.MetricData{ 228 "metric1.test": {types.MakeMetricData("metric1.test", []float64{0}, 1, now32).SetTag("aggregatedBy", "range")}, 229 "metric2.test": {types.MakeMetricData("metric2.test", []float64{0}, 1, now32).SetTag("aggregatedBy", "range")}, 230 }, 231 }, 232 { 233 Name: "groupByNodes_average_no_nodes", 234 Target: `groupByNodes(test.metric*.foo*,"average")`, 235 M: map[parser.MetricRequest][]*types.MetricData{ 236 {Metric: "test.metric*.foo*", From: 0, Until: 1}: { 237 types.MakeMetricData("test.metric1.foo1", []float64{0}, 1, now32), 238 types.MakeMetricData("test.metric1.foo2", []float64{0}, 1, now32), 239 types.MakeMetricData("test.metric2.foo1", []float64{0}, 1, now32), 240 types.MakeMetricData("test.metric2.foo2", []float64{0}, 1, now32), 241 }, 242 }, 243 Results: map[string][]*types.MetricData{ 244 "": {types.MakeMetricData("", []float64{0}, 1, now32).SetTag("aggregatedBy", "average")}, 245 }, 246 }, 247 } 248 249 for _, tt := range tests { 250 testName := tt.Target 251 t.Run(testName, func(t *testing.T) { 252 eval := th.EvaluatorFromFuncWithMetadata(metadata.FunctionMD.Functions) 253 th.TestMultiReturnEvalExpr(t, eval, &tt) 254 }) 255 } 256 257 } 258 259 func TestGroupByNodeError(t *testing.T) { 260 now32 := int64(time.Now().Unix()) 261 262 mr := parser.MetricRequest{Metric: "metric1.foo.*.*", From: 0, Until: 1} 263 264 tests := []th.EvalTestItemWithError{ 265 { 266 Target: "groupByNode(metric1.foo.*.*,3,\"4\")", 267 M: map[parser.MetricRequest][]*types.MetricData{ 268 mr: { 269 types.MakeMetricData("metric1.foo.bar1.baz", []float64{1, 2, 3, 4, 5}, 1, now32), 270 types.MakeMetricData("metric1.foo.bar1.qux", []float64{6, 7, 8, 9, 10}, 1, now32), 271 types.MakeMetricData("metric1.foo.bar2.baz", []float64{11, 12, 13, 14, 15}, 1, now32), 272 types.MakeMetricData("metric1.foo.bar2.qux", []float64{7, 8, 9, 10, 11}, 1, now32), 273 }, 274 }, 275 Error: parser.ErrInvalidArg, 276 }, 277 } 278 279 for _, tt := range tests { 280 testName := tt.Target 281 t.Run(testName, func(t *testing.T) { 282 eval := th.EvaluatorFromFuncWithMetadata(metadata.FunctionMD.Functions) 283 th.TestEvalExprWithError(t, eval, &tt) 284 }) 285 } 286 287 } 288 289 func BenchmarkGroupByNode(b *testing.B) { 290 target := "groupByNodes(metric1.foo.bar.*,\"sum\",0,2)" 291 metrics := map[parser.MetricRequest][]*types.MetricData{ 292 {Metric: "metric1.foo.bar.*", From: 0, Until: 1}: { 293 types.MakeMetricData("metric1.foo.bar.baz1", []float64{1, 2, 3, 4, 5}, 1, 1), 294 types.MakeMetricData("metric1.foo.bar.baz2", []float64{1, 2, 3, 4, 5}, 1, 1), 295 types.MakeMetricData("metric1.foo.bar.baz3", []float64{1, 2, 3, 4, 5}, 1, 1), 296 types.MakeMetricData("metric1.foo.bar.baz4", []float64{1, 2, 3, 4, 5}, 1, 1), 297 }, 298 } 299 300 eval := th.EvaluatorFromFuncWithMetadata(metadata.FunctionMD.Functions) 301 exp, _, err := parser.ParseExpr(target) 302 if err != nil { 303 b.Fatalf("failed to parse %s: %+v", target, err) 304 } 305 306 b.ResetTimer() 307 for n := 0; n < b.N; n++ { 308 g, err := eval.Eval(context.Background(), exp, 0, 1, metrics) 309 if err != nil { 310 b.Fatalf("failed to eval %s: %+v", target, err) 311 } 312 _ = g 313 } 314 }