github.com/m3db/m3@v1.5.0/src/query/graphite/native/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 native
    22  
    23  import (
    24  	"fmt"
    25  	"reflect"
    26  	"sort"
    27  	"testing"
    28  
    29  	"github.com/m3db/m3/src/query/graphite/common"
    30  	xtest "github.com/m3db/m3/src/query/graphite/testing"
    31  
    32  	"github.com/stretchr/testify/assert"
    33  	"github.com/stretchr/testify/require"
    34  )
    35  
    36  func f1(ctx *common.Context, a float64, b string, c bool) (string, error) {
    37  	return fmt.Sprintf("%.3f %s %t", a, b, c), nil
    38  }
    39  
    40  func f2(ctx *common.Context, a ...string) ([]string, error) {
    41  	sort.Strings(a)
    42  	return a, nil
    43  }
    44  
    45  func f3(ctx *common.Context, values ...float64) (float64, error) {
    46  	sum := float64(0)
    47  	for _, n := range values {
    48  		sum += n
    49  	}
    50  
    51  	return sum, nil
    52  }
    53  
    54  type testFunction struct {
    55  	f              interface{}
    56  	input          []interface{}
    57  	expectedOutput interface{}
    58  }
    59  
    60  var testFunctions = []testFunction{
    61  	{f1, []interface{}{635.6, "Hello", false}, "635.600 Hello false"},
    62  	{f2, []interface{}{"b", "c", "a"}, []string{"a", "b", "c"}},
    63  	{f3, []interface{}{10.0, 20.0, 30.0}, 60},
    64  }
    65  
    66  func TestFunctions(t *testing.T) {
    67  	ctx := common.NewTestContext()
    68  	defer ctx.Close()
    69  
    70  	for _, tf := range testFunctions {
    71  		f, err := buildFunction(tf.f)
    72  		require.Nil(t, err, "could not build function %s", reflect.TypeOf(tf.f).Name())
    73  
    74  		out, err := f.call(ctx, tf.input)
    75  		require.Nil(t, err, "Could not call function %s", reflect.TypeOf(tf.f).Name())
    76  		xtest.Equalish(t, tf.expectedOutput, out)
    77  	}
    78  }
    79  
    80  func errorf(ctx *common.Context) ([]float64, error) {
    81  	return nil, fmt.Errorf("this failed")
    82  }
    83  
    84  func TestFunctionReturningError(t *testing.T) {
    85  	f, err := buildFunction(errorf)
    86  	require.Nil(t, err)
    87  
    88  	_, err = f.call(nil, nil)
    89  	require.NotNil(t, err)
    90  	assert.Equal(t, "this failed", err.Error())
    91  }
    92  
    93  type invalidFunction struct {
    94  	name          string
    95  	f             interface{}
    96  	expectedError string
    97  }
    98  
    99  func badf1() (float64, error)                                               { return 0, nil }
   100  func badf2(ctx *common.Context)                                             {}
   101  func badf3(ctx *common.Context) (float32, error)                            { return 0, nil }
   102  func badf4(ctx *common.Context) (string, string)                            { return "", "" }
   103  func badf5(ctx *common.Context, n byte) (string, error)                     { return "", nil }
   104  func badf6(ctx *common.Context, n float64) (byte, error)                    { return 0, nil }
   105  func badf7(ctx *common.Context, foo, bar multiplePathSpecs) (string, error) { return "", nil }
   106  
   107  func TestInvalidFunctions(t *testing.T) {
   108  	invalidFunctions := []invalidFunction{
   109  		{"badf1", badf1, "functions must take at least 1 argument"},
   110  		{"badf2", badf2, "functions must return a value and an error"},
   111  		{"badf3", badf3, "invalid return type float32"},
   112  		{"badf4", badf4, "functions must return a value and an error"},
   113  		{"badf5", badf5, "invalid arg 1: uint8 is not supported"},
   114  		{"badf6", badf6, "invalid return type uint8"},
   115  		{"24", 24, "not a function"},
   116  		{"badf7", badf7, "invalid arg 1: multiplePathSpecs must be the last arg"},
   117  	}
   118  
   119  	for i, fn := range invalidFunctions {
   120  		f, err := buildFunction(fn.f)
   121  		require.NotNil(t, err, "invalid error for %s (%d)", fn.name, i)
   122  		assert.Equal(t, fn.expectedError, err.Error(), "invalid error for %s (%d)", fn.name, i)
   123  		assert.Nil(t, f)
   124  	}
   125  }