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  }