1import unittest
2
3from graphite.errors import InputParameterError
4from graphite.functions.params import Param, ParamTypes, validateParams
5
6
7class TestParam(unittest.TestCase):
8    params = [
9        Param('one', ParamTypes.string, required=True),
10        Param('two', ParamTypes.string, required=True),
11        Param('three', ParamTypes.string, required=True),
12    ]
13
14    def test_simple_args(self):
15        self.assertEqual(validateParams(
16            'TestParam',
17            self.params,
18            ['arg1', 'arg2', 'arg3'],
19            {}),
20            (['arg1', 'arg2', 'arg3'], {}),
21        )
22
23        self.assertRaises(
24            InputParameterError,
25            validateParams,
26            'TestParam',
27            self.params,
28            ['arg1', 'arg2'],
29            {},
30        )
31
32    def test_simple_kwargs(self):
33        self.assertEqual(validateParams(
34            'TestParam',
35            self.params,
36            [],
37            {'one': '1', 'two': '2', 'three': '3'}),
38            ([], {'one': '1', 'two': '2', 'three': '3'}),
39        )
40
41        self.assertRaises(
42            InputParameterError,
43            validateParams,
44            'TestParam',
45            self.params,
46            [],
47            {'one': '1', 'two': '2'},
48        )
49
50        self.assertRaises(
51            InputParameterError,
52            validateParams,
53            'TestParam',
54            self.params,
55            [],
56            {'one': '1', 'two': '2', 'four': '4'},
57        )
58
59    def test_mixed_cases(self):
60        self.assertEqual(validateParams(
61            'TestParam',
62            self.params,
63            ['one', 'two'],
64            {'three': '3'}),
65            (['one', 'two'],
66            {'three': '3'})
67        )
68
69        self.assertEqual(validateParams(
70            'TestParam',
71            self.params,
72            ['one'],
73            {'three': '3', 'two': '2'}),
74            (['one'], {'three': '3', 'two': '2'}),
75        )
76
77        # positional args don't check the name
78        self.assertEqual(validateParams(
79            'TestParam',
80            self.params,
81            ['one', 'two', 'four'],
82            {}),
83            (['one', 'two', 'four'], {})
84        )
85
86        self.assertRaises(
87            InputParameterError,
88            validateParams,
89            'TestParam',
90            self.params,
91            [],
92            {'three': '3', 'two': '2'},
93        )
94
95        self.assertRaises(
96            InputParameterError,
97            validateParams,
98            'TestParam',
99            self.params,
100            ['one', 'three'],
101            {'two': '2'},
102        )
103
104        self.assertRaises(
105            InputParameterError,
106            validateParams,
107            'TestParam',
108            self.params,
109            ['three'],
110            {'one': '1', 'two': '2'},
111        )
112
113    def test_repeated_args(self):
114        self.assertRaises(
115            InputParameterError,
116            validateParams,
117            'TestParam',
118            self.params,
119            ['one'],
120            {'three': '3', 'one': '1'},
121        )
122
123        self.assertRaises(
124            InputParameterError,
125            validateParams,
126            'TestParam',
127            self.params,
128            ['one', 'two'],
129            {'three': '3', 'two': '2'},
130        )
131
132        self.assertRaises(
133            InputParameterError,
134            validateParams,
135            'TestParam',
136            self.params,
137            ['one', 'two', 'three'],
138            {'one': '1'},
139        )
140
141    def test_multiple_property(self):
142        self.assertRaises(
143            InputParameterError,
144            validateParams,
145            'TestParam',
146            [
147                Param('one', ParamTypes.string, required=True),
148                Param('two', ParamTypes.string, required=True),
149                Param('three', ParamTypes.string, required=True, multiple=False),
150            ],
151            ['one', 'two', 'three', 'four'],
152            {},
153        )
154
155        self.assertEqual(validateParams(
156            'TestParam',
157            [
158                Param('one', ParamTypes.string, required=True),
159                Param('two', ParamTypes.string, required=True),
160                Param('three', ParamTypes.string, required=True, multiple=True),
161            ],
162            ['one', 'two', 'three', 'four'],
163            {}),
164            (['one', 'two', 'three', 'four'], {}),
165        )
166
167        self.assertRaises(
168            InputParameterError,
169            validateParams,
170            'TestParam',
171            [
172                Param('one', ParamTypes.string, required=True),
173                Param('two', ParamTypes.string, required=True),
174                Param('three', ParamTypes.string, required=True, multiple=True),
175            ],
176            ['one', 'two', 'three'],
177            # should fail because parameters which are specified multiple times
178            # cannot be in kwargs, only args
179            {'three': '3'},
180        )
181
182    def test_options_property(self):
183        self.assertEqual(validateParams(
184            'TestParam',
185            [
186                Param('one', ParamTypes.string, required=True),
187                Param('two', ParamTypes.string, required=True),
188                Param('three', ParamTypes.string, required=True, options=['3', 'three']),
189            ],
190            ['one', 'two', '3'],
191            {}),
192            (['one', 'two', '3'], {}),
193        )
194
195        self.assertEqual(validateParams(
196            'TestParam',
197            [
198                Param('one', ParamTypes.string, required=True),
199                Param('two', ParamTypes.string, required=True),
200                Param('three', ParamTypes.string, required=True, options=['3', 'three']),
201            ],
202            ['one', 'two', 'three'],
203            {}),
204            (['one', 'two', 'three'], {}),
205        )
206
207        self.assertRaises(
208            InputParameterError,
209            validateParams,
210            'TestParam',
211            [
212                Param('one', ParamTypes.string, required=True),
213                Param('two', ParamTypes.string, required=True),
214                Param('three', ParamTypes.string, required=True, options=['3', 'three']),
215            ],
216            ['one', 'two', 'four'],
217            {},
218        )
219
220    def test_use_series_function_as_aggregator(self):
221        # powSeries is a series function which is marked as a valid aggregator
222        self.assertEqual(validateParams(
223            'TestParam',
224            [
225                Param('func', ParamTypes.aggOrSeriesFunc, required=True),
226            ],
227            ['powSeries'],
228            {}),
229            (['powSeries'], {}),
230        )
231
232        # squareRoot is a series function which is not marked as a valid aggregator
233        self.assertRaises(
234            InputParameterError,
235            validateParams,
236            'TestParam',
237            [
238                Param('func', ParamTypes.aggOrSeriesFunc, required=True),
239            ],
240            ['squareRoot'],
241            {},
242        )
243
244    def test_param_type_int_or_inf(self):
245        self.assertEqual(validateParams(
246            'TestParam',
247            [Param('param', ParamTypes.intOrInf)],
248            [1],
249            {}),
250            ([1], {}),
251        )
252
253        self.assertEqual(validateParams(
254            'TestParam',
255            [Param('param', ParamTypes.intOrInf)],
256            [float('inf')],
257            {}),
258            ([float('inf')], {}),
259        )
260
261        self.assertRaises(
262            InputParameterError,
263            validateParams,
264            'TestParam',
265            [Param('param', ParamTypes.intOrInf)],
266            [1.2],
267            {},
268        )
269
270    def test_unexpected_kwargs(self):
271        self.assertRaises(
272            InputParameterError,
273            validateParams,
274            'TestParam',
275            [Param('param', ParamTypes.integer)],
276            [],
277            {'param': 1, 'param2': 2},
278        )
279
280    def test_default_value(self):
281        # if no value is specified, but there is a default value, we don't
282        # want the validator to raise an exception because 'None' is invalid
283        self.assertEqual(validateParams(
284            'TestParam',
285            [
286                Param('one', ParamTypes.aggFunc, default='sum'),
287            ],
288            [],
289            {}),
290            ([], {}),
291        )
292
293    def test_various_type_conversions(self):
294        self.assertEqual(validateParams(
295            'TestParam',
296            [
297                Param('bool', ParamTypes.boolean),
298            ],
299            ['true'],
300            {}),
301            ([True], {}),
302        )
303
304        self.assertEqual(validateParams(
305            'TestParam',
306            [
307                Param('bool', ParamTypes.boolean),
308            ],
309            [],
310            {'bool': 'false'}),
311            ([], {'bool': False}),
312        )
313
314        self.assertEqual(validateParams(
315            'TestParam',
316            [
317                Param('bool1', ParamTypes.boolean),
318                Param('bool2', ParamTypes.boolean),
319            ],
320            [0],
321            {'bool2': 1}),
322            ([False], {'bool2': True}),
323        )
324
325        self.assertEqual(validateParams(
326            'TestParam',
327            [
328                Param('float', ParamTypes.float),
329            ],
330            ['1e3'],
331            {}),
332            ([float(1000)], {}),
333        )
334
335        self.assertEqual(validateParams(
336            'TestParam',
337            [
338                Param('float', ParamTypes.float),
339            ],
340            ['0.123'],
341            {}),
342            ([float(0.123)], {}),
343        )
344
345        self.assertEqual(validateParams(
346            'TestParam',
347            [
348                Param('float', ParamTypes.float),
349            ],
350            [],
351            {'float': 'inf'}),
352            ([], {'float': float('inf')}),
353        )
354
355        self.assertEqual(validateParams(
356            'TestParam',
357            [
358                Param('int', ParamTypes.integer),
359            ],
360            ['123'],
361            {}),
362            ([123], {}),
363        )
364
365        self.assertEqual(validateParams(
366            'TestParam',
367            [
368                Param('int', ParamTypes.integer),
369            ],
370            [],
371            {'int': '-123'}),
372            ([], {'int': -123}),
373        )
374
375        self.assertEqual(validateParams(
376            'TestParam',
377            [
378                Param('intOrInf', ParamTypes.intOrInf),
379            ],
380            ['123'],
381            {}),
382            ([123], {}),
383        )
384
385        self.assertEqual(validateParams(
386            'TestParam',
387            [
388                Param('intOrInf', ParamTypes.intOrInf),
389            ],
390            [],
391            {'intOrInf': float('inf')}),
392            ([], {'intOrInf': float('inf')}),
393        )
394
395    def test_impossible_type_conversions(self):
396        self.assertRaises(
397            InputParameterError,
398            validateParams,
399            'TestParam',
400            [Param('param', ParamTypes.integer)],
401            ['inf'],
402            {},
403        )
404
405        self.assertRaises(
406            InputParameterError,
407            validateParams,
408            'TestParam',
409            [Param('param', ParamTypes.boolean)],
410            ['1'],
411            {},
412        )
413
414        self.assertRaises(
415            InputParameterError,
416            validateParams,
417            'TestParam',
418            [Param('param', ParamTypes.boolean)],
419            [3],
420            {},
421        )
422