github.com/go-graphite/carbonapi@v0.17.0/expr/functions/slo/function_test.go (about)

     1  package slo
     2  
     3  import (
     4  	"math"
     5  	"testing"
     6  
     7  	th "github.com/go-graphite/carbonapi/tests"
     8  
     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  )
    14  
    15  var (
    16  	md []interfaces.FunctionMetadata = New("")
    17  )
    18  
    19  func init() {
    20  	for _, m := range md {
    21  		metadata.RegisterFunction(m.Name, m.F)
    22  	}
    23  }
    24  
    25  func TestSlo(t *testing.T) {
    26  	nan := math.NaN()
    27  	now32 := int64(1615737710)
    28  
    29  	testCases := []th.EvalTestItem{
    30  		{
    31  			"slo(x.y.z, \"10sec\", \"above\", 2)",
    32  			map[parser.MetricRequest][]*types.MetricData{
    33  				{
    34  					Metric: "x.y.z",
    35  					From:   0,
    36  					Until:  1,
    37  				}: {
    38  					types.MakeMetricData(
    39  						"x.y.z",
    40  						[]float64{1, 2, 3, 4, 5, nan, nan, 6, 7, 0, 8},
    41  						5,
    42  						now32,
    43  					),
    44  				},
    45  			},
    46  			[]*types.MetricData{
    47  				types.MakeMetricData(
    48  					"slo(x.y.z, 10sec, above, 2)",
    49  					// (1, 2) -> 0
    50  					// (3, 4) -> 1
    51  					// (5, nan) -> 1: all not-null elements are above 2
    52  					// (nan, 6) -> 1: the same
    53  					// (7, 0) -> 0.5: only 1 element of 2 is above 2
    54  					// (8) -> 1
    55  					[]float64{0, 1, 1, 1, 0.5, 1},
    56  					10,
    57  					now32,
    58  				),
    59  			},
    60  		},
    61  		{
    62  			"slo(x.y.z, \"4sec\", \"below\", 6)",
    63  			map[parser.MetricRequest][]*types.MetricData{
    64  				{
    65  					Metric: "x.y.z",
    66  					From:   0,
    67  					Until:  1,
    68  				}: {
    69  					types.MakeMetricData(
    70  						"x.y.z",
    71  						[]float64{1, 2, 3, 4, 5, 6, 7, 8, 9},
    72  						5,
    73  						now32,
    74  					),
    75  				},
    76  			},
    77  			[]*types.MetricData{
    78  				types.MakeMetricData(
    79  					"slo(x.y.z, 4sec, below, 6)",
    80  					// all data points are nan because interval (4 sec) is less than step time (5 sec)
    81  					[]float64{nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan},
    82  					4,
    83  					now32,
    84  				),
    85  			},
    86  		},
    87  	}
    88  
    89  	for _, testCase := range testCases {
    90  		testName := testCase.Target
    91  		t.Run(testName, func(t *testing.T) {
    92  			eval := th.EvaluatorFromFunc(md[0].F)
    93  			th.TestEvalExpr(t, eval, &testCase)
    94  		})
    95  	}
    96  }
    97  
    98  func TestSloErrorBudget(t *testing.T) {
    99  	nan := math.NaN()
   100  	now32 := int64(1615737710)
   101  
   102  	testCases := []th.EvalTestItem{
   103  		{
   104  			"sloErrorBudget(some.data.series, \"5sec\", \"aboveOrEqual\", 2, 0.6)",
   105  			map[parser.MetricRequest][]*types.MetricData{
   106  				{
   107  					Metric: "some.data.series",
   108  					From:   0,
   109  					Until:  1,
   110  				}: {
   111  					types.MakeMetricData(
   112  						"some.data.series",
   113  						[]float64{
   114  							1, 1.5, 2, 3, 4, // 3 of 5 points are greater or equal than 2
   115  							nan, 0, 1, 1.5, 2.1, // 1 of 4 points is greater or equal than 2
   116  							1, 2, 3, 4, 5, // 4 of 5 points are greater or equal than 2
   117  							1, 2, 3, 4, // 3 of 4 points are greater or equal than 2
   118  						},
   119  						1,
   120  						now32,
   121  					),
   122  				},
   123  			},
   124  			[]*types.MetricData{
   125  				types.MakeMetricData(
   126  					"sloErrorBudget(some.data.series, 5sec, aboveOrEqual, 2, 0.6)",
   127  					[]float64{
   128  						0,     // 3 of 5 points match, slo is 0.6, no error budget remains
   129  						-1.75, // 1 of 4 points match, slo is 0.6, error budget is exceeded by (0.25 - 0.6) * 5 = -1.75
   130  						1,     // 4 of 5 points match, slo is 0.6, amount of remained budget is (0.8 - 0.6) * 5 = 1
   131  						0.6,   // 3 of 4 points match, slo is 0.6, amount of remained budget is (0.75 - 0.6) * 4 = 0.6
   132  					},
   133  					5,
   134  					now32,
   135  				),
   136  			},
   137  		},
   138  		{
   139  			"sloErrorBudget(some.data.series, \"4sec\", \"aboveOrEqual\", 2, 0.6)",
   140  			map[parser.MetricRequest][]*types.MetricData{
   141  				{
   142  					Metric: "some.data.series",
   143  					From:   0,
   144  					Until:  1,
   145  				}: {
   146  					types.MakeMetricData(
   147  						"some.data.series",
   148  						[]float64{
   149  							1, 1.5, 2, 3, 4,
   150  							nan, 0, 1, 1.5, 2.1,
   151  							1, 2, 3, 4, 5,
   152  							1, 2, 3, 4,
   153  						},
   154  						5,
   155  						now32,
   156  					),
   157  				},
   158  			},
   159  			[]*types.MetricData{
   160  				types.MakeMetricData(
   161  					"sloErrorBudget(some.data.series, 4sec, aboveOrEqual, 2, 0.6)",
   162  					[]float64{
   163  						// all data points are nan because interval (4 sec) is less than step time (5 sec)
   164  						nan, nan, nan, nan, nan, nan, nan, nan,
   165  						nan, nan, nan, nan, nan, nan, nan, nan,
   166  						nan, nan, nan, nan, nan, nan, nan, nan,
   167  					},
   168  					4,
   169  					now32,
   170  				),
   171  			},
   172  		},
   173  	}
   174  
   175  	for _, testCase := range testCases {
   176  		testName := testCase.Target
   177  		t.Run(testName, func(t *testing.T) {
   178  			eval := th.EvaluatorFromFunc(md[0].F)
   179  			th.TestEvalExpr(t, eval, &testCase)
   180  		})
   181  	}
   182  }