1 package gval
2
3
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
24 {
25 name: "PLUS literal number to literal bool",
26 expression: "1 + true",
27 want: "1true",
28 },
29 {
30 name: "PLUS number to bool",
31 expression: "number + bool",
32 want: "1true",
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
95 {
96 name: "AND number to number",
97 expression: "number && number",
98 want: true,
99 },
100 {
101
102 name: "OR number to number",
103 expression: "number || number",
104 want: 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
137 {
138 name: "GT literal bool to literal bool",
139 expression: "true > true",
140 want: false,
141 },
142 {
143 name: "GT bool to bool",
144 expression: "bool > bool",
145 want: false,
146 },
147 {
148 name: "GTE bool to bool",
149 expression: "bool >= bool",
150 want: true,
151 },
152 {
153 name: "LT bool to bool",
154 expression: "bool < bool",
155 want: false,
156 },
157 {
158 name: "LTE bool to bool",
159 expression: "bool <= bool",
160 want: true,
161 },
162 {
163 name: "GT number to string",
164 expression: "number > string",
165 want: false,
166 },
167 {
168
169 name: "GTE number to string",
170 expression: "number >= string",
171 want: false,
172 },
173 {
174 name: "LT number to string",
175 expression: "number < string",
176 want: true,
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
236 {
237 name: "Ternary with number",
238 expression: "10 ? true",
239 want: true,
240 },
241 {
242 name: "Ternary with string",
243 expression: `"foo" ? true`,
244 want: true,
245 },
246
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
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
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