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 }