github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/query/graphite/common/basic_functions_test.go (about)

     1  // Copyright (c) 2019 Uber Technologies, Inc.
     2  //
     3  // Permission is hereby granted, free of charge, to any person obtaining a copy
     4  // of this software and associated documentation files (the "Software"), to deal
     5  // in the Software without restriction, including without limitation the rights
     6  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     7  // copies of the Software, and to permit persons to whom the Software is
     8  // furnished to do so, subject to the following conditions:
     9  //
    10  // The above copyright notice and this permission notice shall be included in
    11  // all copies or substantial portions of the Software.
    12  //
    13  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    14  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    15  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    16  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    17  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    18  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    19  // THE SOFTWARE.
    20  
    21  package common
    22  
    23  import (
    24  	"math"
    25  	"testing"
    26  	"time"
    27  
    28  	"github.com/m3db/m3/src/query/graphite/ts"
    29  	"github.com/m3db/m3/src/x/errors"
    30  
    31  	"github.com/stretchr/testify/assert"
    32  	"github.com/stretchr/testify/require"
    33  )
    34  
    35  var (
    36  	consolidationStartTime = time.Now().Truncate(time.Minute).Add(10 * time.Second)
    37  	consolidationEndTime   = consolidationStartTime.Add(1 * time.Minute)
    38  )
    39  
    40  type limitFunc func(series ts.SeriesList, n int) (ts.SeriesList, error)
    41  
    42  func validateOutputs(t *testing.T, step int, start time.Time, expected []TestSeries, actual []*ts.Series) {
    43  	require.Equal(t, len(expected), len(actual))
    44  
    45  	for i := range expected {
    46  		a, e := actual[i], expected[i].Data
    47  
    48  		require.Equal(t, len(e), a.Len())
    49  
    50  		for step := 0; step < a.Len(); step++ {
    51  			v := a.ValueAt(step)
    52  			assert.Equal(t, e[step], v, "invalid value for %d", step)
    53  		}
    54  
    55  		assert.Equal(t, expected[i].Name, a.Name())
    56  		assert.Equal(t, step, a.MillisPerStep())
    57  		assert.Equal(t, start, a.StartTime())
    58  	}
    59  }
    60  
    61  func TestLimitFunctions(t *testing.T) {
    62  	ctx := NewTestContext()
    63  	defer ctx.Close()
    64  
    65  	var (
    66  		// Values are not required in this test.
    67  		testInput = []TestSeries{
    68  			{"foo", []float64{}}, {"bar", []float64{}},
    69  			{"baz", []float64{}}, {"qux", []float64{}},
    70  		}
    71  
    72  		tests = []struct {
    73  			f      limitFunc
    74  			n      int
    75  			inputs []TestSeries
    76  			output []TestSeries
    77  			err    error
    78  		}{
    79  			{Head, 2, testInput, testInput[:2], nil},
    80  			{Head, 100, testInput, testInput, nil},
    81  			{Head, -2, testInput, nil, ErrNegativeCount},
    82  		}
    83  
    84  		startTime = time.Now()
    85  		step      = 100
    86  	)
    87  
    88  	for _, test := range tests {
    89  		series := ts.SeriesList{Values: NewTestSeriesList(ctx, startTime, test.inputs, step)}
    90  		output, err := test.f(series, test.n)
    91  		assert.Equal(t, err, test.err)
    92  
    93  		validateOutputs(t, step, startTime, test.output, output.Values)
    94  	}
    95  }
    96  
    97  func TestNormalize(t *testing.T) {
    98  	ctx, input := NewConsolidationTestSeries(consolidationStartTime, consolidationEndTime, 30*time.Second)
    99  	defer ctx.Close()
   100  
   101  	normalized, start, end, step, err := Normalize(ctx, ts.SeriesList{
   102  		Values: input,
   103  	})
   104  	expectedStart := ctx.StartTime.Add(-30 * time.Second)
   105  	expectedEnd := ctx.StartTime.Add(90 * time.Second)
   106  	expectedStep := 10000
   107  	nan := math.NaN()
   108  	require.Nil(t, err)
   109  	require.Equal(t, expectedStart, start)
   110  	require.Equal(t, expectedEnd, end)
   111  	require.Equal(t, expectedStep, step)
   112  	expected := []TestSeries{
   113  		{Name: "a", Data: []float64{nan, nan, nan, 10, 10, 10, 10, 10, 10, nan, nan, nan}},
   114  		{Name: "b", Data: []float64{15, 15, 15, 15, 15, 15, nan, nan, nan, nan, nan, nan}},
   115  		{Name: "c", Data: []float64{nan, nan, nan, nan, nan, nan, 17, 17, 17, 17, 17, 17}},
   116  		{Name: "d", Data: []float64{nan, nan, nan, 3, 3, 3, 3, 3, 3, nan, nan, nan}},
   117  	}
   118  
   119  	CompareOutputsAndExpected(t, expectedStep, expectedStart, expected, normalized.Values)
   120  }
   121  
   122  func TestParseInterval(t *testing.T) {
   123  	tests := map[string]time.Duration{
   124  		"5s":        time.Second * 5,
   125  		"20sec":     time.Second * 20,
   126  		"60seconds": time.Second * 60,
   127  		"1min":      time.Minute * 1,
   128  		"10min":     time.Minute * 10,
   129  		"2minutes":  time.Minute * 2,
   130  		"3minute":   time.Minute * 3,
   131  		"36h":       time.Hour * 36,
   132  		"9hours":    time.Hour * 9,
   133  		"1hour":     time.Hour * 1,
   134  		"12hr":      time.Hour * 12,
   135  		"1d":        time.Hour * 24,
   136  		"2days":     time.Hour * 24 * 2,
   137  		"1mon":      time.Hour * 24 * 30,
   138  		"4W":        time.Hour * 24 * 7 * 4,
   139  		"40weeks":   time.Hour * 24 * 7 * 40,
   140  		"6months":   time.Hour * 24 * 30 * 6,
   141  		"2y":        time.Hour * 24 * 365 * 2,
   142  		"10years":   time.Hour * 24 * 365 * 10,
   143  		"1w5min":    (time.Hour * 24 * 7) + (time.Minute * 5),
   144  	}
   145  
   146  	for s, d := range tests {
   147  		actual, err := ParseInterval(s)
   148  		require.NoError(t, err, "got error parsing interval %s", s)
   149  
   150  		assert.Equal(t, d, actual, "invalid parse result for %s", s)
   151  	}
   152  
   153  	actual, err := ParseInterval("122222222222222222222222222222s")
   154  	assert.True(t, errors.IsInvalidParams(err))
   155  	assert.Equal(t, time.Duration(0), actual)
   156  }
   157  
   158  func TestCount(t *testing.T) {
   159  	ctx, valid := NewConsolidationTestSeries(consolidationStartTime, consolidationEndTime, 30*time.Second)
   160  	defer ctx.Close()
   161  
   162  	longCtx := NewContext(ContextOptions{
   163  		Start: consolidationStartTime,
   164  		End:   consolidationStartTime.Add(10 * time.Minute),
   165  	})
   166  	defer longCtx.Close()
   167  
   168  	renamer := func(series ts.SeriesList) string {
   169  		return "woot"
   170  	}
   171  
   172  	tests := []struct {
   173  		context        *Context
   174  		input          []*ts.Series
   175  		renamer        SeriesListRenamer
   176  		expectedSeries TestSeries
   177  		expectedStart  time.Time
   178  		expectedStep   int
   179  	}{{
   180  		ctx,
   181  		valid,
   182  		renamer,
   183  		TestSeries{
   184  			Name: "woot",
   185  			Data: []float64{4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4},
   186  		},
   187  		valid[1].StartTime(),
   188  		valid[1].MillisPerStep(),
   189  	},
   190  		{
   191  			longCtx,
   192  			nil,
   193  			renamer,
   194  			TestSeries{
   195  				"woot",
   196  				[]float64{0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
   197  			},
   198  			ctx.StartTime,
   199  			MillisPerMinute,
   200  		},
   201  	}
   202  
   203  	for _, test := range tests {
   204  		series := ts.SeriesList{Values: test.input}
   205  		results, err := Count(test.context, series, test.renamer)
   206  		require.Nil(t, err)
   207  
   208  		CompareOutputsAndExpected(t, test.expectedStep, test.expectedStart,
   209  			[]TestSeries{test.expectedSeries}, results.Values)
   210  	}
   211  }