fuel-web/nailgun/nailgun/test/unit/test_yaql_ext.py

237 lines
7.9 KiB
Python

# -*- coding: utf-8 -*-
# Copyright 2016 Mirantis, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import mock
from oslo_serialization import jsonutils
import six
import yaml
import yaql
from yaql.language import exceptions
from nailgun.extensions import BaseExtension
from nailgun.test.base import BaseUnitTest
from nailgun import yaql_ext
class TestYaqlExt(BaseUnitTest):
@classmethod
def setUpClass(cls):
cls.variables = {
'$%new': {
'nodes': [
{'uid': '1', 'role': 'compute'},
{'uid': '2', 'role': 'controller'}
],
'configs': {
'nova': {
'value': 1,
'value2': None,
}
},
'cluster': {
'status': 'operational'
},
},
'$%old': {
'nodes': [
{'uid': '1', 'role': 'controller'},
{'uid': '2', 'role': 'compute'},
],
'configs': {
'nova': {
'value': 2,
'value2': None
}
},
'cluster': {
'status': 'new'
},
}
}
cls.variables['$'] = cls.variables['$%new']
def evaluate(self, expression, variables=None, engine=None):
context = yaql_ext.create_context(
add_datadiff=True, add_serializers=True
)
for k, v in six.iteritems(variables or self.variables):
context[k] = v
engine = engine or yaql_ext.create_engine()
parsed_exp = engine(expression)
return parsed_exp.evaluate(context=context)
def test_new(self):
result = self.evaluate(
'new($.nodes.where($.role=compute))'
)
self.assertEqual([{'uid': '1', 'role': 'compute'}], result)
def test_old(self):
result = self.evaluate(
'old($.nodes.where($.role=compute))'
)
self.assertEqual([{'uid': '2', 'role': 'compute'}], result)
def test_added(self):
self.assertEqual(
[{'uid': '1', 'role': 'compute'}],
self.evaluate('added($.nodes.where($.role=compute))')
)
def test_deleted(self):
self.assertItemsEqual(
[{'uid': '2', 'role': 'compute'}],
self.evaluate('deleted($.nodes.where($.role=compute))')
)
def test_changed(self):
self.assertTrue(self.evaluate('changed($.configs.nova.value)'))
self.assertFalse(self.evaluate('changed($.configs.nova.value2)'))
def test_added_if_no_old(self):
variables = self.variables.copy()
variables['$%old'] = {}
self.assertItemsEqual(
[{'uid': '1', 'role': 'compute'}],
self.evaluate('added($.nodes.where($.role=compute))', variables)
)
def test_delete_if_no_old(self):
variables = self.variables.copy()
variables['$%old'] = {}
self.assertIsNone(
self.evaluate('deleted($.nodes.where($.role=compute))', variables)
)
def test_changed_if_no_old(self):
variables = self.variables.copy()
variables['$%old'] = {}
self.assertTrue(
self.evaluate('changed($.configs.nova.value)', variables)
)
self.assertTrue(
self.evaluate('changed($.configs.nova.value2)', variables)
)
def test_changed_many(self):
expressions = '$.configs.nova.value, $.configs.nova.value2'
self.assertTrue(self.evaluate('changedAny({0})'.format(expressions)))
self.assertFalse(self.evaluate('changedAll({0})'.format(expressions)))
def test_undefined(self):
variables = self.variables.copy()
variables['$%old'] = {}
self.assertTrue(
self.evaluate('old($.configs.nova.value).isUndef()', variables),
)
def test_to_yaml(self):
expected = yaml.safe_dump(
self.variables['$%new']['configs'], default_flow_style=False
)
actual = self.evaluate('$.configs.toYaml()')
self.assertEqual(expected, actual)
def test_to_json(self):
expected = jsonutils.dumps(self.variables['$%new']['configs'])
actual = self.evaluate('$.configs.toJson()')
self.assertEqual(expected, actual)
def test_limit_iterables(self):
engine = yaql.YaqlFactory().create({
'yaql.limitIterators': 1,
'yaql.convertTuplesToLists': True,
'yaql.convertSetsToLists': True
})
functions = ['added', 'deleted', 'changed']
expressions = ['$.nodes', '$.configs.nova']
for exp in expressions:
for func in functions:
with self.assertRaises(exceptions.CollectionTooLargeException):
self.evaluate('{0}({1})'.format(func, exp), engine=engine)
expressions = ['$.configs.nova.value', '$.cluster.status']
for exp in expressions:
for func in functions:
self.evaluate('{0}({1})'.format(func, exp), engine=engine)
def test_create_engine(self):
engine1 = yaql_ext.create_engine()
engine2 = yaql_ext.create_engine()
self.assertIsNot(engine1, engine2)
@mock.patch('nailgun.yaql_ext.yaql')
def test_create_engine_with_non_default_options(self, yaql_m):
yaql_ext.create_engine(limit_iterators=10, memory_quota=20)
yaql_m.YaqlFactory().create.assert_called_once_with({
'yaql.limitIterators': 10,
'yaql.memoryQuota': 20,
'yaql.convertTuplesToLists': True,
'yaql.convertSetsToLists': True
})
@mock.patch('nailgun.yaql_ext.yaql')
@mock.patch('nailgun.yaql_ext.settings')
def test_create_engine_with_default_options(self, settings_m, yaql_m):
settings_m.YAQL_LIMIT_ITERATORS = 100
settings_m.YAQL_MEMORY_QUOTA = 200
yaql_ext.create_engine()
yaql_m.YaqlFactory().create.assert_called_once_with({
'yaql.limitIterators': 100,
'yaql.memoryQuota': 200,
'yaql.convertTuplesToLists': True,
'yaql.convertSetsToLists': True
})
def test_get_default_engine(self):
engine1 = yaql_ext.get_default_engine()
engine2 = yaql_ext.get_default_engine()
self.assertIs(engine1, engine2)
class TestYaqlExtWithExtensions(BaseUnitTest):
class YaqlUtils(BaseExtension):
name = 'yaql_utils'
version = '1.0.0'
description = "A set of helper functions for YAQL"
@classmethod
def setup_yaql_context(cls, context):
def is_odd(n):
return bool(n % 2)
context.register_function(is_odd)
def get_interpreter(self):
with mock.patch('nailgun.extensions.manager.get_all_extensions',
return_value=[self.YaqlUtils()]):
context = yaql_ext.create_context(add_extensions=True)
engine = yaql_ext.create_engine()
def evaluate(expression):
parsed_exp = engine(expression)
return parsed_exp.evaluate(context=context)
return evaluate
def test_function_from_extension(self):
interpreter = self.get_interpreter()
self.assertTrue(interpreter('isOdd(1)'))
self.assertFalse(interpreter('isOdd(2)'))