2023-10-13 11:54:33 +00:00
|
|
|
package unittest
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"fmt"
|
|
|
|
"net/url"
|
|
|
|
"reflect"
|
|
|
|
"sort"
|
2023-10-16 12:19:29 +00:00
|
|
|
"time"
|
2023-10-13 11:54:33 +00:00
|
|
|
|
|
|
|
"github.com/VictoriaMetrics/VictoriaMetrics/app/vmalert/datasource"
|
|
|
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/promutils"
|
|
|
|
"github.com/VictoriaMetrics/metricsql"
|
|
|
|
)
|
|
|
|
|
|
|
|
// metricsqlTestCase holds metricsql_expr_test cases defined in test file
|
|
|
|
type metricsqlTestCase struct {
|
|
|
|
Expr string `yaml:"expr"`
|
|
|
|
EvalTime *promutils.Duration `yaml:"eval_time"`
|
|
|
|
ExpSamples []expSample `yaml:"exp_samples"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type expSample struct {
|
|
|
|
Labels string `yaml:"labels"`
|
|
|
|
Value float64 `yaml:"value"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// checkMetricsqlCase will check metricsql_expr_test cases
|
|
|
|
func checkMetricsqlCase(cases []metricsqlTestCase, q datasource.QuerierBuilder) (checkErrs []error) {
|
|
|
|
queries := q.BuildWithParams(datasource.QuerierParams{QueryParams: url.Values{"nocache": {"1"}, "latency_offset": {"1ms"}}, DataSourceType: "prometheus"})
|
|
|
|
Outer:
|
|
|
|
for _, mt := range cases {
|
2023-10-16 12:19:29 +00:00
|
|
|
result, _, err := queries.Query(context.Background(), mt.Expr, durationToTime(mt.EvalTime))
|
2023-10-13 11:54:33 +00:00
|
|
|
if err != nil {
|
|
|
|
checkErrs = append(checkErrs, fmt.Errorf(" expr: %q, time: %s, err: %w", mt.Expr,
|
|
|
|
mt.EvalTime.Duration().String(), err))
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
var gotSamples []parsedSample
|
|
|
|
for _, s := range result.Data {
|
|
|
|
sort.Slice(s.Labels, func(i, j int) bool {
|
|
|
|
return s.Labels[i].Name < s.Labels[j].Name
|
|
|
|
})
|
|
|
|
gotSamples = append(gotSamples, parsedSample{
|
|
|
|
Labels: s.Labels,
|
|
|
|
Value: s.Values[0],
|
|
|
|
})
|
|
|
|
}
|
|
|
|
var expSamples []parsedSample
|
|
|
|
for _, s := range mt.ExpSamples {
|
|
|
|
expLb := datasource.Labels{}
|
|
|
|
if s.Labels != "" {
|
|
|
|
metricsqlExpr, err := metricsql.Parse(s.Labels)
|
|
|
|
if err != nil {
|
|
|
|
checkErrs = append(checkErrs, fmt.Errorf("\n expr: %q, time: %s, err: %v", mt.Expr,
|
|
|
|
mt.EvalTime.Duration().String(), fmt.Errorf("failed to parse labels %q: %w", s.Labels, err)))
|
|
|
|
continue Outer
|
|
|
|
}
|
|
|
|
metricsqlMetricExpr, ok := metricsqlExpr.(*metricsql.MetricExpr)
|
2024-10-17 09:00:34 +00:00
|
|
|
if !ok || len(metricsqlMetricExpr.LabelFilterss) > 1 {
|
2023-10-13 11:54:33 +00:00
|
|
|
checkErrs = append(checkErrs, fmt.Errorf("\n expr: %q, time: %s, err: %v", mt.Expr,
|
2024-10-17 09:00:34 +00:00
|
|
|
mt.EvalTime.Duration().String(), fmt.Errorf("got invalid exp_samples: %q", s.Labels)))
|
2023-10-13 11:54:33 +00:00
|
|
|
continue Outer
|
|
|
|
}
|
2024-10-17 09:00:34 +00:00
|
|
|
if len(metricsqlMetricExpr.LabelFilterss) > 0 {
|
|
|
|
for _, l := range metricsqlMetricExpr.LabelFilterss[0] {
|
|
|
|
expLb = append(expLb, datasource.Label{
|
|
|
|
Name: l.Label,
|
|
|
|
Value: l.Value,
|
|
|
|
})
|
|
|
|
}
|
2023-10-13 11:54:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
sort.Slice(expLb, func(i, j int) bool {
|
|
|
|
return expLb[i].Name < expLb[j].Name
|
|
|
|
})
|
|
|
|
expSamples = append(expSamples, parsedSample{
|
|
|
|
Labels: expLb,
|
|
|
|
Value: s.Value,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
sort.Slice(expSamples, func(i, j int) bool {
|
|
|
|
return datasource.LabelCompare(expSamples[i].Labels, expSamples[j].Labels) <= 0
|
|
|
|
})
|
|
|
|
sort.Slice(gotSamples, func(i, j int) bool {
|
|
|
|
return datasource.LabelCompare(gotSamples[i].Labels, gotSamples[j].Labels) <= 0
|
|
|
|
})
|
|
|
|
if !reflect.DeepEqual(expSamples, gotSamples) {
|
|
|
|
checkErrs = append(checkErrs, fmt.Errorf("\n expr: %q, time: %s,\n exp: %v\n got: %v", mt.Expr,
|
|
|
|
mt.EvalTime.Duration().String(), parsedSamplesString(expSamples), parsedSamplesString(gotSamples)))
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
2023-10-16 12:19:29 +00:00
|
|
|
|
|
|
|
func durationToTime(pd *promutils.Duration) time.Time {
|
|
|
|
if pd == nil {
|
|
|
|
return time.Time{}
|
|
|
|
}
|
|
|
|
return time.UnixMilli(pd.Duration().Milliseconds())
|
|
|
|
}
|