github.com/thanos-io/thanos@v0.32.5/pkg/store/storepb/custom_test.go (about)

     1  // Copyright (c) The Thanos Authors.
     2  // Licensed under the Apache License 2.0.
     3  
     4  package storepb
     5  
     6  import (
     7  	"fmt"
     8  	"path/filepath"
     9  	"sort"
    10  	"testing"
    11  	"time"
    12  
    13  	"github.com/pkg/errors"
    14  	"github.com/prometheus/prometheus/model/labels"
    15  	"github.com/prometheus/prometheus/promql/parser"
    16  	"github.com/prometheus/prometheus/tsdb/chunkenc"
    17  
    18  	"github.com/efficientgo/core/testutil"
    19  	"github.com/thanos-io/thanos/pkg/store/labelpb"
    20  )
    21  
    22  type sample struct {
    23  	t int64
    24  	v float64
    25  }
    26  
    27  type listSeriesSet struct {
    28  	series []Series
    29  	idx    int
    30  }
    31  
    32  func newSeries(tb testing.TB, lset labels.Labels, smplChunks [][]sample) Series {
    33  	s := Series{
    34  		Labels: labelpb.ZLabelsFromPromLabels(lset),
    35  	}
    36  
    37  	for _, smpls := range smplChunks {
    38  		c := chunkenc.NewXORChunk()
    39  		a, err := c.Appender()
    40  		testutil.Ok(tb, err)
    41  
    42  		for _, smpl := range smpls {
    43  			a.Append(smpl.t, smpl.v)
    44  		}
    45  
    46  		ch := AggrChunk{
    47  			MinTime: smpls[0].t,
    48  			MaxTime: smpls[len(smpls)-1].t,
    49  			Raw:     &Chunk{Type: Chunk_XOR, Data: c.Bytes()},
    50  		}
    51  
    52  		s.Chunks = append(s.Chunks, ch)
    53  	}
    54  	return s
    55  }
    56  
    57  func newListSeriesSet(tb testing.TB, raw []rawSeries) *listSeriesSet {
    58  	var series []Series
    59  	for _, s := range raw {
    60  		series = append(series, newSeries(tb, s.lset, s.chunks))
    61  	}
    62  	return &listSeriesSet{
    63  		series: series,
    64  		idx:    -1,
    65  	}
    66  }
    67  
    68  func (s *listSeriesSet) Next() bool {
    69  	s.idx++
    70  	return s.idx < len(s.series)
    71  }
    72  
    73  func (s *listSeriesSet) At() (labels.Labels, []AggrChunk) {
    74  	if s.idx < 0 || s.idx >= len(s.series) {
    75  		return nil, nil
    76  	}
    77  
    78  	return s.series[s.idx].PromLabels(), s.series[s.idx].Chunks
    79  }
    80  
    81  func (s *listSeriesSet) Err() error { return nil }
    82  
    83  type errSeriesSet struct{ err error }
    84  
    85  func (errSeriesSet) Next() bool { return false }
    86  
    87  func (errSeriesSet) At() (labels.Labels, []AggrChunk) { return nil, nil }
    88  
    89  func (e errSeriesSet) Err() error { return e.err }
    90  
    91  func TestMergeSeriesSets(t *testing.T) {
    92  	for _, tcase := range []struct {
    93  		desc     string
    94  		in       [][]rawSeries
    95  		expected []rawSeries
    96  	}{
    97  		{
    98  			desc:     "nils",
    99  			in:       nil,
   100  			expected: nil,
   101  		},
   102  		{
   103  			desc: "single seriesSet, distinct series",
   104  			in: [][]rawSeries{{{
   105  				lset:   labels.FromStrings("a", "a"),
   106  				chunks: [][]sample{{{1, 1}, {2, 2}}, {{3, 3}, {4, 4}}},
   107  			}, {
   108  				lset:   labels.FromStrings("a", "c"),
   109  				chunks: [][]sample{{{11, 1}, {12, 2}}, {{13, 3}, {14, 4}}},
   110  			}}},
   111  
   112  			expected: []rawSeries{
   113  				{
   114  					lset:   labels.FromStrings("a", "a"),
   115  					chunks: [][]sample{{{1, 1}, {2, 2}}, {{3, 3}, {4, 4}}},
   116  				}, {
   117  					lset:   labels.FromStrings("a", "c"),
   118  					chunks: [][]sample{{{11, 1}, {12, 2}}, {{13, 3}, {14, 4}}},
   119  				},
   120  			},
   121  		},
   122  		{
   123  			desc: "two seriesSets, distinct series",
   124  			in: [][]rawSeries{{{
   125  				lset:   labels.FromStrings("a", "a"),
   126  				chunks: [][]sample{{{1, 1}, {2, 2}}, {{3, 3}, {4, 4}}},
   127  			}}, {{
   128  				lset:   labels.FromStrings("a", "c"),
   129  				chunks: [][]sample{{{11, 1}, {12, 2}}, {{13, 3}, {14, 4}}},
   130  			}}},
   131  
   132  			expected: []rawSeries{
   133  				{
   134  					lset:   labels.FromStrings("a", "a"),
   135  					chunks: [][]sample{{{1, 1}, {2, 2}}, {{3, 3}, {4, 4}}},
   136  				}, {
   137  					lset:   labels.FromStrings("a", "c"),
   138  					chunks: [][]sample{{{11, 1}, {12, 2}}, {{13, 3}, {14, 4}}},
   139  				},
   140  			},
   141  		},
   142  		{
   143  			desc: "two seriesSets, {a=c} series to merge, sorted",
   144  			in: [][]rawSeries{
   145  				{
   146  					{
   147  						lset:   labels.FromStrings("a", "a"),
   148  						chunks: [][]sample{{{1, 1}, {2, 2}}, {{3, 3}, {4, 4}}},
   149  					},
   150  					{
   151  						lset:   labels.FromStrings("a", "c"),
   152  						chunks: [][]sample{{{11, 1}, {12, 2}}, {{13, 3}, {14, 4}}},
   153  					},
   154  				},
   155  				{
   156  					{
   157  						lset:   labels.FromStrings("a", "c"),
   158  						chunks: [][]sample{{{7, 1}, {8, 2}}, {{9, 3}, {10, 4}, {11, 4444}}}, // Last sample overlaps, merge ignores that.
   159  					},
   160  				},
   161  			},
   162  
   163  			expected: []rawSeries{
   164  				{
   165  					lset:   labels.FromStrings("a", "a"),
   166  					chunks: [][]sample{{{1, 1}, {2, 2}}, {{3, 3}, {4, 4}}},
   167  				}, {
   168  					lset:   labels.FromStrings("a", "c"),
   169  					chunks: [][]sample{{{7, 1}, {8, 2}}, {{9, 3}, {10, 4}, {11, 4444}}, {{11, 1}, {12, 2}}, {{13, 3}, {14, 4}}},
   170  				},
   171  			},
   172  		},
   173  		{
   174  			desc: "single seriesSets, {a=c} series to merge, sorted",
   175  			in: [][]rawSeries{
   176  				{
   177  					{
   178  						lset:   labels.FromStrings("a", "a"),
   179  						chunks: [][]sample{{{1, 1}, {2, 2}}, {{3, 3}, {4, 4}}},
   180  					},
   181  					{
   182  						lset:   labels.FromStrings("a", "c"),
   183  						chunks: [][]sample{{{7, 1}, {8, 2}}, {{9, 3}, {10, 4}, {11, 4444}}},
   184  					},
   185  					{
   186  						lset:   labels.FromStrings("a", "c"),
   187  						chunks: [][]sample{{{11, 1}, {12, 2}}, {{13, 3}, {14, 4}}},
   188  					},
   189  				},
   190  			},
   191  
   192  			expected: []rawSeries{
   193  				{
   194  					lset:   labels.FromStrings("a", "a"),
   195  					chunks: [][]sample{{{1, 1}, {2, 2}}, {{3, 3}, {4, 4}}},
   196  				}, {
   197  					lset:   labels.FromStrings("a", "c"),
   198  					chunks: [][]sample{{{7, 1}, {8, 2}}, {{9, 3}, {10, 4}, {11, 4444}}, {{11, 1}, {12, 2}}, {{13, 3}, {14, 4}}},
   199  				},
   200  			},
   201  		},
   202  		{
   203  			desc: "four seriesSets, {a=c} series to merge AND deduplicate exactly the same chunks",
   204  			in: [][]rawSeries{
   205  				{
   206  					{
   207  						lset:   labels.FromStrings("a", "a"),
   208  						chunks: [][]sample{{{1, 1}, {2, 2}}, {{3, 3}, {4, 4}}},
   209  					},
   210  					{
   211  						lset: labels.FromStrings("a", "c"),
   212  						chunks: [][]sample{
   213  							{{11, 11}, {12, 12}, {13, 13}, {14, 14}},
   214  							{{15, 15}, {16, 16}, {17, 17}, {18, 18}},
   215  						},
   216  					},
   217  					{
   218  						lset: labels.FromStrings("a", "c"),
   219  						chunks: [][]sample{
   220  							{{20, 20}, {21, 21}, {22, 22}, {24, 24}},
   221  						},
   222  					},
   223  				},
   224  				{
   225  					{
   226  						lset: labels.FromStrings("a", "c"),
   227  						chunks: [][]sample{
   228  							{{1, 1}, {2, 2}, {3, 3}, {4, 4}},
   229  							{{11, 11}, {12, 12}, {13, 13}, {14, 14}}, // Same chunk as in set 1.
   230  						},
   231  					},
   232  					{
   233  						lset:   labels.FromStrings("a", "d"),
   234  						chunks: [][]sample{{{11, 1}, {12, 2}}, {{13, 3}, {14, 4}}},
   235  					},
   236  				},
   237  				{
   238  					{
   239  						lset: labels.FromStrings("a", "c"),
   240  						chunks: [][]sample{
   241  							{{11, 11}, {12, 12}, {13, 13}, {14, 14}}, // Same chunk as in set 1.
   242  							{{20, 20}, {21, 21}, {22, 23}, {24, 24}}, // Almost same chunk as in set 1 (one value is different).
   243  						},
   244  					},
   245  				},
   246  				{
   247  					{
   248  						lset: labels.FromStrings("a", "c"),
   249  						chunks: [][]sample{
   250  							{{11, 11}, {12, 12}, {14, 14}},           // Almost same chunk as in set 1 (one sample is missing).
   251  							{{20, 20}, {21, 21}, {22, 22}, {24, 24}}, // Same chunk as in set 1.
   252  						},
   253  					},
   254  				},
   255  			},
   256  
   257  			expected: []rawSeries{
   258  				{
   259  					lset:   labels.Labels{labels.Label{Name: "a", Value: "a"}},
   260  					chunks: [][]sample{{{t: 1, v: 1}, {t: 2, v: 2}}, {{t: 3, v: 3}, {t: 4, v: 4}}},
   261  				}, {
   262  					lset: labels.Labels{labels.Label{Name: "a", Value: "c"}},
   263  					chunks: [][]sample{
   264  						{{t: 1, v: 1}, {t: 2, v: 2}, {t: 3, v: 3}, {t: 4, v: 4}},
   265  						{{t: 11, v: 11}, {t: 12, v: 12}, {t: 13, v: 13}, {t: 14, v: 14}},
   266  						{{t: 11, v: 11}, {t: 12, v: 12}, {t: 14, v: 14}},
   267  						{{t: 15, v: 15}, {t: 16, v: 16}, {t: 17, v: 17}, {t: 18, v: 18}},
   268  						{{t: 20, v: 20}, {t: 21, v: 21}, {t: 22, v: 22}, {t: 24, v: 24}},
   269  						{{t: 20, v: 20}, {t: 21, v: 21}, {t: 22, v: 23}, {t: 24, v: 24}},
   270  					},
   271  				}, {
   272  					lset:   labels.Labels{labels.Label{Name: "a", Value: "d"}},
   273  					chunks: [][]sample{{{t: 11, v: 1}, {t: 12, v: 2}}, {{t: 13, v: 3}, {t: 14, v: 4}}},
   274  				},
   275  			},
   276  		},
   277  		{
   278  			desc: "four seriesSets, {a=c} series to merge, unsorted chunks, so dedup is expected to not be fully done",
   279  			in: [][]rawSeries{
   280  				{
   281  					{
   282  						lset: labels.FromStrings("a", "c"),
   283  						chunks: [][]sample{
   284  							{{20, 20}, {21, 21}, {22, 22}, {24, 24}},
   285  						},
   286  					},
   287  					{
   288  						lset: labels.FromStrings("a", "c"),
   289  						chunks: [][]sample{
   290  							{{11, 11}, {12, 12}, {13, 13}, {14, 14}},
   291  							{{15, 15}, {16, 16}, {17, 17}, {18, 18}},
   292  						},
   293  					},
   294  				},
   295  				{
   296  					{
   297  						lset: labels.FromStrings("a", "c"),
   298  						chunks: [][]sample{
   299  							{{11, 11}, {12, 12}, {13, 13}, {14, 14}}, // Same chunk as in set 1.
   300  							{{1, 1}, {2, 2}, {3, 3}, {4, 4}},
   301  						},
   302  					},
   303  				},
   304  				{
   305  					{
   306  						lset: labels.FromStrings("a", "c"),
   307  						chunks: [][]sample{
   308  							{{20, 20}, {21, 21}, {22, 23}, {24, 24}}, // Almost same chunk as in set 1 (one value is different).
   309  							{{11, 11}, {12, 12}, {13, 13}, {14, 14}}, // Same chunk as in set 1.
   310  						},
   311  					},
   312  				},
   313  			},
   314  
   315  			expected: []rawSeries{
   316  				{
   317  					lset: labels.Labels{labels.Label{Name: "a", Value: "c"}},
   318  					chunks: [][]sample{
   319  						{{t: 11, v: 11}, {t: 12, v: 12}, {t: 13, v: 13}, {t: 14, v: 14}},
   320  						{{t: 1, v: 1}, {t: 2, v: 2}, {t: 3, v: 3}, {t: 4, v: 4}},
   321  						{{t: 20, v: 20}, {t: 21, v: 21}, {t: 22, v: 22}, {t: 24, v: 24}},
   322  						{{t: 11, v: 11}, {t: 12, v: 12}, {t: 13, v: 13}, {t: 14, v: 14}},
   323  						{{t: 15, v: 15}, {t: 16, v: 16}, {t: 17, v: 17}, {t: 18, v: 18}},
   324  						{{t: 20, v: 20}, {t: 21, v: 21}, {t: 22, v: 23}, {t: 24, v: 24}},
   325  						{{t: 11, v: 11}, {t: 12, v: 12}, {t: 13, v: 13}, {t: 14, v: 14}},
   326  					},
   327  				},
   328  			},
   329  		},
   330  	} {
   331  		t.Run(tcase.desc, func(t *testing.T) {
   332  			var input []SeriesSet
   333  			for _, iss := range tcase.in {
   334  				input = append(input, newListSeriesSet(t, iss))
   335  			}
   336  			testutil.Equals(t, tcase.expected, expandSeriesSet(t, MergeSeriesSets(input...)))
   337  		})
   338  	}
   339  }
   340  
   341  func TestMergeSeriesSetError(t *testing.T) {
   342  	var input []SeriesSet
   343  	for _, iss := range [][]rawSeries{{{
   344  		lset:   labels.FromStrings("a", "a"),
   345  		chunks: [][]sample{{{1, 1}, {2, 2}}, {{3, 3}, {4, 4}}},
   346  	}}, {{
   347  		lset:   labels.FromStrings("a", "c"),
   348  		chunks: [][]sample{{{11, 1}, {12, 2}}, {{13, 3}, {14, 4}}},
   349  	}}} {
   350  		input = append(input, newListSeriesSet(t, iss))
   351  	}
   352  	expectedErr := errors.New("test error")
   353  	ss := MergeSeriesSets(append(input, errSeriesSet{err: expectedErr})...)
   354  	testutil.Equals(t, expectedErr, ss.Err())
   355  }
   356  
   357  type rawSeries struct {
   358  	lset   labels.Labels
   359  	chunks [][]sample
   360  }
   361  
   362  func expandSeriesSet(t *testing.T, gotSS SeriesSet) (ret []rawSeries) {
   363  	for gotSS.Next() {
   364  		lset, chks := gotSS.At()
   365  
   366  		r := rawSeries{lset: lset, chunks: make([][]sample, len(chks))}
   367  		for i, chk := range chks {
   368  			c, err := chunkenc.FromData(chunkenc.EncXOR, chk.Raw.Data)
   369  			testutil.Ok(t, err)
   370  
   371  			iter := c.Iterator(nil)
   372  			for iter.Next() != chunkenc.ValNone {
   373  				t, v := iter.At()
   374  				r.chunks[i] = append(r.chunks[i], sample{t: t, v: v})
   375  			}
   376  			testutil.Ok(t, iter.Err())
   377  		}
   378  		ret = append(ret, r)
   379  	}
   380  	testutil.Ok(t, gotSS.Err())
   381  	return ret
   382  }
   383  
   384  // Test the cost of merging series sets for different number of merged sets and their size.
   385  func BenchmarkMergedSeriesSet(b *testing.B) {
   386  	b.Run("overlapping chunks", func(b *testing.B) {
   387  		benchmarkMergedSeriesSet(testutil.NewTB(b), true)
   388  	})
   389  	b.Run("non-overlapping chunks", func(b *testing.B) {
   390  		benchmarkMergedSeriesSet(testutil.NewTB(b), false)
   391  	})
   392  }
   393  
   394  func TestMergedSeriesSet_Labels(t *testing.T) {
   395  	t.Run("overlapping chunks", func(t *testing.T) {
   396  		benchmarkMergedSeriesSet(testutil.NewTB(t), true)
   397  	})
   398  	t.Run("non-overlapping chunks", func(t *testing.T) {
   399  		benchmarkMergedSeriesSet(testutil.NewTB(t), false)
   400  	})
   401  }
   402  
   403  func benchmarkMergedSeriesSet(b testutil.TB, overlappingChunks bool) {
   404  	var sel func(sets []SeriesSet) SeriesSet
   405  	sel = func(sets []SeriesSet) SeriesSet {
   406  		if len(sets) == 0 {
   407  			return EmptySeriesSet()
   408  		}
   409  		if len(sets) == 1 {
   410  			return sets[0]
   411  		}
   412  		l := len(sets) / 2
   413  		return newMergedSeriesSet(sel(sets[:l]), sel(sets[l:]))
   414  	}
   415  
   416  	chunks := [][]sample{{{1, 1}, {2, 2}}, {{3, 3}, {4, 4}}}
   417  	for _, k := range []int{
   418  		100,
   419  		1000,
   420  		10000,
   421  		20000,
   422  	} {
   423  		for _, j := range []int{1, 2, 4, 8, 16, 32} {
   424  			b.Run(fmt.Sprintf("series=%d,blocks=%d", k, j), func(b testutil.TB) {
   425  				lbls, err := labels.ReadLabels(filepath.Join("../../testutil/testdata", "20kseries.json"), k)
   426  				testutil.Ok(b, err)
   427  
   428  				sort.Sort(labels.Slice(lbls))
   429  
   430  				blocks := make([][]rawSeries, j)
   431  				for _, l := range lbls {
   432  					for j := range blocks {
   433  						if overlappingChunks {
   434  							blocks[j] = append(blocks[j], rawSeries{lset: l, chunks: chunks})
   435  							continue
   436  						}
   437  						blocks[j] = append(blocks[j], rawSeries{
   438  							lset: l,
   439  							chunks: [][]sample{
   440  								{{int64(4*j) + 1, 1}, {int64(4*j) + 2, 2}},
   441  								{{int64(4*j) + 3, 3}, {int64(4*j) + 4, 4}},
   442  							},
   443  						})
   444  					}
   445  				}
   446  
   447  				b.ResetTimer()
   448  
   449  				for i := 0; i < b.N(); i++ {
   450  					var sets []SeriesSet
   451  					for _, s := range blocks {
   452  						sets = append(sets, newListSeriesSet(b, s))
   453  					}
   454  					ms := sel(sets)
   455  
   456  					i := 0
   457  					for ms.Next() {
   458  						i++
   459  					}
   460  					testutil.Ok(b, ms.Err())
   461  					testutil.Equals(b, len(lbls), i)
   462  				}
   463  			})
   464  		}
   465  	}
   466  }
   467  
   468  func TestMatchersToString_Translate(t *testing.T) {
   469  	for _, c := range []struct {
   470  		ms       []LabelMatcher
   471  		expected string
   472  	}{
   473  		{
   474  			ms: []LabelMatcher{
   475  				{Name: "__name__", Type: LabelMatcher_EQ, Value: "up"},
   476  			},
   477  			expected: `{__name__="up"}`,
   478  		},
   479  		{
   480  			ms: []LabelMatcher{
   481  				{Name: "__name__", Type: LabelMatcher_NEQ, Value: "up"},
   482  				{Name: "job", Type: LabelMatcher_EQ, Value: "test"},
   483  			},
   484  			expected: `{__name__!="up", job="test"}`,
   485  		},
   486  		{
   487  			ms: []LabelMatcher{
   488  				{Name: "__name__", Type: LabelMatcher_EQ, Value: "up"},
   489  				{Name: "job", Type: LabelMatcher_RE, Value: "test"},
   490  			},
   491  			expected: `{__name__="up", job=~"test"}`,
   492  		},
   493  		{
   494  			ms: []LabelMatcher{
   495  				{Name: "job", Type: LabelMatcher_NRE, Value: "test"},
   496  			},
   497  			expected: `{job!~"test"}`,
   498  		},
   499  		{
   500  			ms: []LabelMatcher{
   501  				{Name: "__name__", Type: LabelMatcher_EQ, Value: "up"},
   502  				{Name: "__name__", Type: LabelMatcher_NEQ, Value: "up"},
   503  			},
   504  			// We cannot use up{__name__!="up"} in this case.
   505  			expected: `{__name__="up", __name__!="up"}`,
   506  		},
   507  	} {
   508  		t.Run(c.expected, func(t *testing.T) {
   509  			testutil.Equals(t, c.expected, MatchersToString(c.ms...))
   510  
   511  			promMs, err := MatchersToPromMatchers(c.ms...)
   512  			testutil.Ok(t, err)
   513  
   514  			testutil.Equals(t, c.expected, PromMatchersToString(promMs...))
   515  
   516  			ms, err := PromMatchersToMatchers(promMs...)
   517  			testutil.Ok(t, err)
   518  
   519  			testutil.Equals(t, c.ms, ms)
   520  			testutil.Equals(t, c.expected, MatchersToString(ms...))
   521  
   522  			// Is this parsable?
   523  			promMsParsed, err := parser.ParseMetricSelector(c.expected)
   524  			testutil.Ok(t, err)
   525  			testutil.Equals(t, promMs, promMsParsed)
   526  		})
   527  
   528  	}
   529  }
   530  
   531  func TestSeriesRequestToPromQL(t *testing.T) {
   532  	ts := []struct {
   533  		name     string
   534  		r        *SeriesRequest
   535  		expected string
   536  	}{
   537  		{
   538  			name: "Single matcher regular expression",
   539  			r: &SeriesRequest{
   540  				Matchers: []LabelMatcher{
   541  					{
   542  						Type:  LabelMatcher_RE,
   543  						Name:  "namespace",
   544  						Value: "kube-.+",
   545  					},
   546  				},
   547  				QueryHints: &QueryHints{
   548  					Func: &Func{
   549  						Name: "max",
   550  					},
   551  				},
   552  			},
   553  			expected: `max ({namespace=~"kube-.+"})`,
   554  		},
   555  		{
   556  			name: "Single matcher regular expression with grouping",
   557  			r: &SeriesRequest{
   558  				Matchers: []LabelMatcher{
   559  					{
   560  						Type:  LabelMatcher_RE,
   561  						Name:  "namespace",
   562  						Value: "kube-.+",
   563  					},
   564  				},
   565  				QueryHints: &QueryHints{
   566  					Func: &Func{
   567  						Name: "max",
   568  					},
   569  					Grouping: &Grouping{
   570  						By:     false,
   571  						Labels: []string{"container", "pod"},
   572  					},
   573  				},
   574  			},
   575  			expected: `max without (container,pod) ({namespace=~"kube-.+"})`,
   576  		},
   577  		{
   578  			name: "Multiple matchers with grouping",
   579  			r: &SeriesRequest{
   580  				Matchers: []LabelMatcher{
   581  					{
   582  						Type:  LabelMatcher_EQ,
   583  						Name:  "__name__",
   584  						Value: "kube_pod_info",
   585  					},
   586  					{
   587  						Type:  LabelMatcher_RE,
   588  						Name:  "namespace",
   589  						Value: "kube-.+",
   590  					},
   591  				},
   592  				QueryHints: &QueryHints{
   593  					Func: &Func{
   594  						Name: "max",
   595  					},
   596  					Grouping: &Grouping{
   597  						By:     false,
   598  						Labels: []string{"container", "pod"},
   599  					},
   600  				},
   601  			},
   602  			expected: `max without (container,pod) ({__name__="kube_pod_info", namespace=~"kube-.+"})`,
   603  		},
   604  		{
   605  			name: "Query with vector range selector",
   606  			r: &SeriesRequest{
   607  				Matchers: []LabelMatcher{
   608  					{
   609  						Type:  LabelMatcher_EQ,
   610  						Name:  "__name__",
   611  						Value: "kube_pod_info",
   612  					},
   613  					{
   614  						Type:  LabelMatcher_RE,
   615  						Name:  "namespace",
   616  						Value: "kube-.+",
   617  					},
   618  				},
   619  				QueryHints: &QueryHints{
   620  					Func: &Func{
   621  						Name: "max_over_time",
   622  					},
   623  					Range: &Range{
   624  						Millis: 10 * time.Minute.Milliseconds(),
   625  					},
   626  				},
   627  			},
   628  			expected: `max_over_time ({__name__="kube_pod_info", namespace=~"kube-.+"}[600000ms])`,
   629  		},
   630  		{
   631  			name: "Query with grouping and vector range selector",
   632  			r: &SeriesRequest{
   633  				Matchers: []LabelMatcher{
   634  					{
   635  						Type:  LabelMatcher_EQ,
   636  						Name:  "__name__",
   637  						Value: "kube_pod_info",
   638  					},
   639  					{
   640  						Type:  LabelMatcher_RE,
   641  						Name:  "namespace",
   642  						Value: "kube-.+",
   643  					},
   644  				},
   645  				QueryHints: &QueryHints{
   646  					Func: &Func{
   647  						Name: "max",
   648  					},
   649  					Grouping: &Grouping{
   650  						By:     false,
   651  						Labels: []string{"container", "pod"},
   652  					},
   653  					Range: &Range{
   654  						Millis: 10 * time.Minute.Milliseconds(),
   655  					},
   656  				},
   657  			},
   658  			expected: `max without (container,pod) ({__name__="kube_pod_info", namespace=~"kube-.+"}[600000ms])`,
   659  		},
   660  	}
   661  
   662  	for _, tc := range ts {
   663  		t.Run(tc.name, func(t *testing.T) {
   664  			actual := tc.r.ToPromQL()
   665  			if tc.expected != actual {
   666  				t.Fatalf("invalid promql result, got %s, want %s", actual, tc.expected)
   667  			}
   668  		})
   669  	}
   670  }