...

Source file src/github.com/PaesslerAG/gval/gval_evaluationFailure_test.go

Documentation: github.com/PaesslerAG/gval

     1  package gval
     2  
     3  /*
     4  	Tests to make sure evaluation fails in the expected ways.
     5  */
     6  import (
     7  	"errors"
     8  	"fmt"
     9  	"testing"
    10  )
    11  
    12  func TestModifierTyping(test *testing.T) {
    13  	var (
    14  		invalidOperator      = "invalid operation"
    15  		unknownParameter     = "unknown parameter"
    16  		invalidRegex         = "error parsing regex"
    17  		tooFewArguments      = "reflect: Call with too few input arguments"
    18  		tooManyArguments     = "reflect: Call with too many input arguments"
    19  		mismatchedParameters = "reflect: Call using"
    20  		custom               = "test error"
    21  	)
    22  	evaluationTests := []evaluationTest{
    23  		//ModifierTyping
    24  		{
    25  			name:       "PLUS literal number to literal bool",
    26  			expression: "1 + true",
    27  			want:       "1true", // + on string is defined
    28  		},
    29  		{
    30  			name:       "PLUS number to bool",
    31  			expression: "number + bool",
    32  			want:       "1true", // + on string is defined
    33  		},
    34  		{
    35  			name:       "MINUS number to bool",
    36  			expression: "number - bool",
    37  			wantErr:    invalidOperator,
    38  		},
    39  		{
    40  			name:       "MINUS number to bool",
    41  			expression: "number - bool",
    42  			wantErr:    invalidOperator,
    43  		},
    44  		{
    45  			name:       "MULTIPLY number to bool",
    46  			expression: "number * bool",
    47  			wantErr:    invalidOperator,
    48  		},
    49  		{
    50  			name:       "DIVIDE number to bool",
    51  			expression: "number / bool",
    52  			wantErr:    invalidOperator,
    53  		},
    54  		{
    55  			name:       "EXPONENT number to bool",
    56  			expression: "number ** bool",
    57  			wantErr:    invalidOperator,
    58  		},
    59  		{
    60  			name:       "MODULUS number to bool",
    61  			expression: "number % bool",
    62  			wantErr:    invalidOperator,
    63  		},
    64  		{
    65  			name:       "XOR number to bool",
    66  			expression: "number % bool",
    67  			wantErr:    invalidOperator,
    68  		},
    69  		{
    70  			name:       "BITWISE_OR number to bool",
    71  			expression: "number | bool",
    72  			wantErr:    invalidOperator,
    73  		},
    74  		{
    75  			name:       "BITWISE_AND number to bool",
    76  			expression: "number & bool",
    77  			wantErr:    invalidOperator,
    78  		},
    79  		{
    80  			name:       "BITWISE_XOR number to bool",
    81  			expression: "number ^ bool",
    82  			wantErr:    invalidOperator,
    83  		},
    84  		{
    85  			name:       "BITWISE_LSHIFT number to bool",
    86  			expression: "number << bool",
    87  			wantErr:    invalidOperator,
    88  		},
    89  		{
    90  			name:       "BITWISE_RSHIFT number to bool",
    91  			expression: "number >> bool",
    92  			wantErr:    invalidOperator,
    93  		},
    94  		//LogicalOperatorTyping
    95  		{
    96  			name:       "AND number to number",
    97  			expression: "number && number",
    98  			want:       true, // number != 0 is true
    99  		},
   100  		{
   101  
   102  			name:       "OR number to number",
   103  			expression: "number || number",
   104  			want:       true, // number != 0 is true
   105  		},
   106  		{
   107  			name:       "AND string to string",
   108  			expression: "string && string",
   109  			wantErr:    invalidOperator,
   110  		},
   111  		{
   112  			name:       "OR string to string",
   113  			expression: "string || string",
   114  			wantErr:    invalidOperator,
   115  		},
   116  		{
   117  			name:       "AND number to string",
   118  			expression: "number && string",
   119  			wantErr:    invalidOperator,
   120  		},
   121  		{
   122  			name:       "OR number to string",
   123  			expression: "number || string",
   124  			wantErr:    invalidOperator,
   125  		},
   126  		{
   127  			name:       "AND bool to string",
   128  			expression: "bool && string",
   129  			wantErr:    invalidOperator,
   130  		},
   131  		{
   132  			name:       "OR string to bool",
   133  			expression: "string || bool",
   134  			wantErr:    invalidOperator,
   135  		},
   136  		//ComparatorTyping
   137  		{
   138  			name:       "GT literal bool to literal bool",
   139  			expression: "true > true",
   140  			want:       false, //lexical order on "true"
   141  		},
   142  		{
   143  			name:       "GT bool to bool",
   144  			expression: "bool > bool",
   145  			want:       false, //lexical order on "true"
   146  		},
   147  		{
   148  			name:       "GTE bool to bool",
   149  			expression: "bool >= bool",
   150  			want:       true, //lexical order on "true"
   151  		},
   152  		{
   153  			name:       "LT bool to bool",
   154  			expression: "bool < bool",
   155  			want:       false, //lexical order on "true"
   156  		},
   157  		{
   158  			name:       "LTE bool to bool",
   159  			expression: "bool <= bool",
   160  			want:       true, //lexical order on "true"
   161  		},
   162  		{
   163  			name:       "GT number to string",
   164  			expression: "number > string",
   165  			want:       false, //lexical order "1" < "foo"
   166  		},
   167  		{
   168  
   169  			name:       "GTE number to string",
   170  			expression: "number >= string",
   171  			want:       false, //lexical order "1" < "foo"
   172  		},
   173  		{
   174  			name:       "LT number to string",
   175  			expression: "number < string",
   176  			want:       true, //lexical order "1" < "foo"
   177  		},
   178  		{
   179  			name:       "REQ number to string",
   180  			expression: "number =~ string",
   181  			want:       false,
   182  		},
   183  		{
   184  			name:       "REQ number to bool",
   185  			expression: "number =~ bool",
   186  			want:       false,
   187  		},
   188  		{
   189  			name:       "REQ bool to number",
   190  			expression: "bool =~ number",
   191  			want:       false,
   192  		},
   193  		{
   194  			name:       "REQ bool to string",
   195  			expression: "bool =~ string",
   196  			want:       false,
   197  		},
   198  		{
   199  			name:       "NREQ number to string",
   200  			expression: "number !~ string",
   201  			want:       true,
   202  		},
   203  		{
   204  			name:       "NREQ number to bool",
   205  			expression: "number !~ bool",
   206  			want:       true,
   207  		},
   208  		{
   209  			name:       "NREQ bool to number",
   210  			expression: "bool !~ number",
   211  			want:       true,
   212  		},
   213  		{
   214  
   215  			name:       "NREQ bool to string",
   216  			expression: "bool !~ string",
   217  			want:       true,
   218  		},
   219  		{
   220  			name:       "IN non-array numeric",
   221  			expression: "1 in 2",
   222  			wantErr:    "expected type []interface{} for in operator but got float64",
   223  		},
   224  		{
   225  			name:       "IN non-array string",
   226  			expression: `1 in "foo"`,
   227  			wantErr:    "expected type []interface{} for in operator but got string",
   228  		},
   229  		{
   230  
   231  			name:       "IN non-array boolean",
   232  			expression: "1 in true",
   233  			wantErr:    "expected type []interface{} for in operator but got bool",
   234  		},
   235  		//TernaryTyping
   236  		{
   237  			name:       "Ternary with number",
   238  			expression: "10 ? true",
   239  			want:       true, // 10 != nil && 10 != false
   240  		},
   241  		{
   242  			name:       "Ternary with string",
   243  			expression: `"foo" ? true`,
   244  			want:       true, // "foo" != nil && "foo" != false
   245  		},
   246  		//RegexParameterCompilation
   247  		{
   248  			name:       "Regex equality runtime parsing",
   249  			expression: `"foo" =~ foo`,
   250  			parameter: map[string]interface{}{
   251  				"foo": "[foo",
   252  			},
   253  			wantErr: invalidRegex,
   254  		},
   255  		{
   256  			name:       "Regex inequality runtime parsing",
   257  			expression: `"foo" !~ foo`,
   258  			parameter: map[string]interface{}{
   259  				"foo": "[foo",
   260  			},
   261  			wantErr: invalidRegex,
   262  		},
   263  		{
   264  			name:       "Regex equality runtime right side evaluation",
   265  			expression: `"foo" =~ error()`,
   266  			wantErr:    custom,
   267  		},
   268  		{
   269  			name:       "Regex inequality runtime right side evaluation",
   270  			expression: `"foo" !~ error()`,
   271  			wantErr:    custom,
   272  		},
   273  		{
   274  			name:       "Regex equality runtime left side evaluation",
   275  			expression: `error() =~ "."`,
   276  			wantErr:    custom,
   277  		},
   278  		{
   279  			name:       "Regex inequality runtime left side evaluation",
   280  			expression: `error() !~ "."`,
   281  			wantErr:    custom,
   282  		},
   283  		//FuncExecution
   284  		{
   285  			name:       "Func error bubbling",
   286  			expression: "error()",
   287  			extension: Function("error", func(arguments ...interface{}) (interface{}, error) {
   288  				return nil, errors.New("Huge problems")
   289  			}),
   290  			wantErr: "Huge problems",
   291  		},
   292  		//InvalidParameterCalls
   293  		{
   294  			name:       "Missing parameter field reference",
   295  			expression: "foo.NotExists",
   296  			parameter:  fooFailureParameters,
   297  			wantErr:    unknownParameter,
   298  		},
   299  		{
   300  			name:       "Parameter method call on missing function",
   301  			expression: "foo.NotExist()",
   302  			parameter:  fooFailureParameters,
   303  			wantErr:    unknownParameter,
   304  		},
   305  		{
   306  			name:       "Nested missing parameter field reference",
   307  			expression: "foo.Nested.NotExists",
   308  			parameter:  fooFailureParameters,
   309  			wantErr:    unknownParameter,
   310  		},
   311  		{
   312  			name:       "Parameter method call returns error",
   313  			expression: "foo.AlwaysFail()",
   314  			parameter:  fooFailureParameters,
   315  			wantErr:    "function should always fail",
   316  		},
   317  		{
   318  			name:       "Too few arguments to parameter call",
   319  			expression: "foo.FuncArgStr()",
   320  			parameter:  fooFailureParameters,
   321  			wantErr:    tooFewArguments,
   322  		},
   323  		{
   324  			name:       "Too many arguments to parameter call",
   325  			expression: `foo.FuncArgStr("foo", "bar", 15)`,
   326  			parameter:  fooFailureParameters,
   327  			wantErr:    tooManyArguments,
   328  		},
   329  		{
   330  			name:       "Mismatched parameters",
   331  			expression: "foo.FuncArgStr(5)",
   332  			parameter:  fooFailureParameters,
   333  			wantErr:    mismatchedParameters,
   334  		},
   335  		{
   336  			name:       "Negative Array Index",
   337  			expression: "foo[-1]",
   338  			parameter: map[string]interface{}{
   339  				"foo": []int{1, 2, 3},
   340  			},
   341  			wantErr: unknownParameter,
   342  		},
   343  		{
   344  			name:       "Nested slice call index out of bound",
   345  			expression: `foo.Nested.Slice[10]`,
   346  			parameter:  map[string]interface{}{"foo": foo},
   347  			wantErr:    unknownParameter,
   348  		},
   349  		{
   350  			name:       "Nested map call missing key",
   351  			expression: `foo.Nested.Map["d"]`,
   352  			parameter:  map[string]interface{}{"foo": foo},
   353  			wantErr:    unknownParameter,
   354  		},
   355  		{
   356  			name:       "invalid selector",
   357  			expression: "hello[world()]",
   358  			extension: NewLanguage(Base(), Function("world", func() (int, error) {
   359  				return 0, fmt.Errorf("test error")
   360  			})),
   361  			wantErr: "test error",
   362  		},
   363  		{
   364  			name:       "eval `nil > 1` returns true #23",
   365  			expression: `nil > 1`,
   366  			wantErr:    "invalid operation (<nil>) > (float64)",
   367  		},
   368  	}
   369  
   370  	for i := range evaluationTests {
   371  		if evaluationTests[i].parameter == nil {
   372  			evaluationTests[i].parameter = map[string]interface{}{
   373  				"number": 1,
   374  				"string": "foo",
   375  				"bool":   true,
   376  				"error": func() (int, error) {
   377  					return 0, fmt.Errorf("test error")
   378  				},
   379  			}
   380  		}
   381  	}
   382  
   383  	testEvaluate(evaluationTests, test)
   384  }
   385  

View as plain text