From c8201c119ec686e79797721156767685fe848aca Mon Sep 17 00:00:00 2001 From: shangxdy Date: Thu, 7 Apr 2016 14:08:49 -0400 Subject: Update tosca lib to version 0.5 Use tosca-parser and heat-translator to analyze to the basic nfv-tosca type definitions, and use simple tosca new feature such as policy, group and trigger, which are now supported by the latest version of tosca-parser and heat-translator. JIRA:PARSER-18 Change-Id: I797bcacbb5b32005d0aeb0f3f32851ac96e30f01 Signed--off-by: shangxdy Signed-off-by: shangxdy --- .../toscaparser/tests/test_datatypes.py | 408 +++++++++++++++++++++ 1 file changed, 408 insertions(+) create mode 100644 tosca2heat/tosca-parser/toscaparser/tests/test_datatypes.py (limited to 'tosca2heat/tosca-parser/toscaparser/tests/test_datatypes.py') diff --git a/tosca2heat/tosca-parser/toscaparser/tests/test_datatypes.py b/tosca2heat/tosca-parser/toscaparser/tests/test_datatypes.py new file mode 100644 index 0000000..0e613b2 --- /dev/null +++ b/tosca2heat/tosca-parser/toscaparser/tests/test_datatypes.py @@ -0,0 +1,408 @@ +# 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 os + +from testtools.testcase import skip +from toscaparser.common import exception +from toscaparser.dataentity import DataEntity +from toscaparser.elements.datatype import DataType +from toscaparser.parameters import Input +from toscaparser.tests.base import TestCase +from toscaparser.tosca_template import ToscaTemplate +from toscaparser.utils.gettextutils import _ +from toscaparser.utils import yamlparser + + +class DataTypeTest(TestCase): + + custom_type_schema = ''' + tosca.my.datatypes.PeopleBase: + properties: + name: + type: string + required: true + constraints: + - min_length: 2 + gender: + type: string + default: unknown + + tosca.my.datatypes.People: + derived_from: tosca.my.datatypes.PeopleBase + properties: + addresses: + type: map + required: false + entry_schema: + type: string + contacts: + type: list + required: false + entry_schema: + type: tosca.my.datatypes.ContactInfo + + tosca.my.datatypes.ContactInfo: + description: simple contact information + properties: + contact_name: + type: string + required: true + constraints: + - min_length: 2 + contact_email: + type: string + contact_phone: + type: string + + tosca.my.datatypes.TestLab: + properties: + temperature: + type: range + required: false + constraints: + - in_range: [-256, UNBOUNDED] + humidity: + type: range + required: false + constraints: + - in_range: [-256, INFINITY] + ''' + custom_type_def = yamlparser.simple_parse(custom_type_schema) + + def test_empty_template(self): + value_snippet = '' + value = yamlparser.simple_parse(value_snippet) + self.assertEqual(value, {}) + + # TODO(Matt) - opened as bug 1555300 + # Need a test for PortSpec normative data type + # that tests the spec. requirement: "A valid PortSpec + # must have at least one of the following properties: + # target, target_range, source or source_range." + # TODO(Matt) - opened as bug 1555310 + # test PortSpec value for source and target + # against the source_range and target_range + # when specified. + def test_built_in_datatype(self): + value_snippet = ''' + private_network: + network_name: private + network_id: 3e54214f-5c09-1bc9-9999-44100326da1b + addresses: [ 10.111.128.10 ] + ''' + value = yamlparser.simple_parse(value_snippet) + data = DataEntity('tosca.datatypes.network.NetworkInfo', + value.get('private_network')) + self.assertIsNotNone(data.validate()) + + value_snippet = ''' + portspec_valid: + protocol: tcp + ''' + value = yamlparser.simple_parse(value_snippet) + data = DataEntity('tosca.datatypes.network.PortSpec', + value.get('portspec_valid')) + self.assertIsNotNone(data.validate()) + + value_snippet = ''' + portspec_invalid: + protocol: xyz + ''' + value = yamlparser.simple_parse(value_snippet) + data = DataEntity('tosca.datatypes.network.PortSpec', + value.get('portspec_invalid')) + err = self.assertRaises(exception.ValidationError, data.validate) + self.assertEqual(_('The value "xyz" of property "protocol" is not ' + 'valid. Expected a value from "[udp, tcp, igmp]".' + ), + err.__str__()) + + def test_built_in_datatype_with_short_name(self): + value_snippet = ''' + ethernet_port: + port_name: port1 + port_id: 2c0c7a37-691a-23a6-7709-2d10ad041467 + network_id: 3e54214f-5c09-1bc9-9999-44100326da1b + mac_address: f1:18:3b:41:92:1e + addresses: [ 172.24.9.102 ] + ''' + value = yamlparser.simple_parse(value_snippet) + data = DataEntity('PortInfo', value.get('ethernet_port')) + self.assertIsNotNone(data.validate()) + + def test_built_in_datatype_without_properties(self): + value_snippet = ''' + 2 + ''' + value = yamlparser.simple_parse(value_snippet) + datatype = DataType('PortDef') + self.assertEqual('integer', datatype.value_type) + data = DataEntity('PortDef', value) + self.assertIsNotNone(data.validate()) + + @skip('The example in TOSCA spec may have some problem.') + def test_built_in_nested_datatype(self): + value_snippet = ''' + user_port: + protocol: tcp + target: [50000] + source: [9000] + ''' + value = yamlparser.simple_parse(value_snippet) + data = DataEntity('PortSpec', value.get('user_port')) + self.assertIsNotNone(data.validate()) + + def test_built_in_nested_datatype_portdef(self): + tpl_snippet = ''' + inputs: + db_port: + type: PortDef + description: Port for the MySQL database + ''' + inputs = yamlparser.simple_parse(tpl_snippet)['inputs'] + name, attrs = list(inputs.items())[0] + input = Input(name, attrs) + self.assertIsNone(input.validate(3360)) + err = self.assertRaises(exception.ValidationError, input.validate, + 336000) + self.assertEqual(_('The value "336000" of property "None" is out of ' + 'range "(min:1, max:65535)".'), + err.__str__()) + + def test_custom_datatype(self): + value_snippet = ''' + name: Mike + gender: male + ''' + value = yamlparser.simple_parse(value_snippet) + data = DataEntity('tosca.my.datatypes.PeopleBase', value, + DataTypeTest.custom_type_def) + self.assertIsNotNone(data.validate()) + + def test_custom_datatype_with_parent(self): + value_snippet = ''' + name: Mike + gender: male + contacts: + - {contact_name: Tom, + contact_email: tom@email.com, + contact_phone: '123456789'} + - {contact_name: Jerry, + contact_email: jerry@email.com, + contact_phone: '321654987'} + ''' + value = yamlparser.simple_parse(value_snippet) + data = DataEntity('tosca.my.datatypes.People', value, + DataTypeTest.custom_type_def) + self.assertIsNotNone(data.validate()) + + # [Tom, Jerry] is not a dict, it can't be a value of datatype PeopleBase + def test_non_dict_value_for_datatype(self): + value_snippet = ''' + [Tom, Jerry] + ''' + value = yamlparser.simple_parse(value_snippet) + data = DataEntity('tosca.my.datatypes.PeopleBase', value, + DataTypeTest.custom_type_def) + error = self.assertRaises(exception.TypeMismatchError, data.validate) + self.assertEqual(_('[\'Tom\', \'Jerry\'] must be of type ' + '"tosca.my.datatypes.PeopleBase".'), + error.__str__()) + + # 'nema' is an invalid field name + def test_field_error_in_dataentity(self): + value_snippet = ''' + nema: Mike + gender: male + ''' + value = yamlparser.simple_parse(value_snippet) + data = DataEntity('tosca.my.datatypes.PeopleBase', value, + DataTypeTest.custom_type_def) + error = self.assertRaises(exception.UnknownFieldError, data.validate) + self.assertEqual(_('Data value of type ' + '"tosca.my.datatypes.PeopleBase" contains unknown ' + 'field "nema". Refer to the definition to verify ' + 'valid values.'), + error.__str__()) + + def test_default_field_in_dataentity(self): + value_snippet = ''' + name: Mike + ''' + value = yamlparser.simple_parse(value_snippet) + data = DataEntity('tosca.my.datatypes.PeopleBase', value, + DataTypeTest.custom_type_def) + data = data.validate() + self.assertEqual('unknown', data.get('gender')) + + # required field 'name' is missing + def test_missing_field_in_dataentity(self): + value_snippet = ''' + gender: male + ''' + value = yamlparser.simple_parse(value_snippet) + data = DataEntity('tosca.my.datatypes.PeopleBase', value, + DataTypeTest.custom_type_def) + error = self.assertRaises(exception.MissingRequiredFieldError, + data.validate) + self.assertEqual(_('Data value of type ' + '"tosca.my.datatypes.PeopleBase" is missing ' + 'required field "[\'name\']".'), + error.__str__()) + + # the value of name field is not a string + def test_type_error_in_dataentity(self): + value_snippet = ''' + name: 123 + gender: male + ''' + value = yamlparser.simple_parse(value_snippet) + data = DataEntity('tosca.my.datatypes.PeopleBase', value, + DataTypeTest.custom_type_def) + error = self.assertRaises(ValueError, data.validate) + self.assertEqual(_('"123" is not a string.'), error.__str__()) + + # the value of name doesn't meet the defined constraint + def test_value_error_in_dataentity(self): + value_snippet = ''' + name: M + gender: male + ''' + value = yamlparser.simple_parse(value_snippet) + data = DataEntity('tosca.my.datatypes.PeopleBase', value, + DataTypeTest.custom_type_def) + error = self.assertRaises(exception.ValidationError, data.validate) + self.assertEqual(_('Length of value "M" of property "name" must be ' + 'at least "2".'), error.__str__()) + + # value of addresses doesn't fit the entry_schema + def test_validation_in_collection_entry(self): + value_snippet = ''' + name: Mike + gender: male + addresses: {Home: 1, Office: 9 bar avenue} + ''' + value = yamlparser.simple_parse(value_snippet) + data = DataEntity('tosca.my.datatypes.People', value, + DataTypeTest.custom_type_def) + error = self.assertRaises(ValueError, data.validate) + self.assertEqual(_('"1" is not a string.'), error.__str__()) + + # 'contact_pone' is an invalid attribute name in nested datatype below + def test_validation_in_nested_datatype(self): + value_snippet = ''' + name: Mike + gender: male + contacts: + - {contact_name: Tom, + contact_email: tom@email.com, + contact_pone: '123456789'} + - {contact_name: Jerry, + contact_email: jerry@email.com, + contact_phone: '321654987'} + ''' + value = yamlparser.simple_parse(value_snippet) + data = DataEntity('tosca.my.datatypes.People', value, + DataTypeTest.custom_type_def) + error = self.assertRaises(exception.UnknownFieldError, data.validate) + self.assertEqual(_('Data value of type ' + '"tosca.my.datatypes.ContactInfo" contains unknown ' + 'field "contact_pone". Refer to the definition to ' + 'verify valid values.'), + error.__str__()) + + def test_datatype_in_current_template(self): + tpl_path = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + "data/datatypes/test_custom_datatypes_in_current_template.yaml") + self.assertIsNotNone(ToscaTemplate(tpl_path)) + + def test_datatype_in_template_positive(self): + tpl_path = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + "data/datatypes/test_custom_datatypes_positive.yaml") + self.assertIsNotNone(ToscaTemplate(tpl_path)) + + def test_datatype_in_template_invalid_value(self): + tpl_path = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + "data/datatypes/test_custom_datatypes_value_error.yaml") + self.assertRaises(exception.ValidationError, ToscaTemplate, tpl_path) + exception.ExceptionCollector.assertExceptionMessage( + ValueError, + _('"[\'1 foo street\', \'9 bar avenue\']" is not a map.')) + + def test_datatype_in_template_nested_datatype_error(self): + tpl_path = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + "data/datatypes/test_custom_datatypes_nested_datatype_error.yaml") + self.assertRaises(exception.ValidationError, ToscaTemplate, tpl_path) + exception.ExceptionCollector.assertExceptionMessage( + ValueError, _('"123456789" is not a string.')) + + def test_valid_range_type(self): + value_snippet = ''' + user_port: + protocol: tcp + target_range: [20000, 60000] + source_range: [1000, 3000] + ''' + value = yamlparser.simple_parse(value_snippet) + data = DataEntity('PortSpec', value.get('user_port')) + self.assertIsNotNone(data.validate()) + + def test_invalid_range_datatype(self): + value_snippet = ''' + user_port: + protocol: tcp + target_range: [20000] + ''' + value = yamlparser.simple_parse(value_snippet) + data = DataEntity('PortSpec', value.get('user_port')) + err = self.assertRaises(ValueError, data.validate) + self.assertEqual(_('"[20000]" is not a valid range.' + ), + err.__str__()) + + value_snippet = ''' + user_port: + protocol: tcp + target_range: [20000, 3000] + ''' + value = yamlparser.simple_parse(value_snippet) + data = DataEntity('PortSpec', value.get('user_port')) + err = self.assertRaises(ValueError, data.validate) + self.assertEqual(_('"[20000, 3000]" is not a valid range.' + ), + err.__str__()) + + value_snippet = ''' + humidity: [-100, 100] + ''' + value = yamlparser.simple_parse(value_snippet) + data = DataEntity('tosca.my.datatypes.TestLab', + value, DataTypeTest.custom_type_def) + err = self.assertRaises(exception.InvalidSchemaError, + lambda: data.validate()) + self.assertEqual(_('The property "in_range" expects comparable values.' + ), + err.__str__()) + + def test_range_unbounded(self): + value_snippet = ''' + temperature: [-100, 999999] + ''' + value = yamlparser.simple_parse(value_snippet) + data = DataEntity('tosca.my.datatypes.TestLab', value, + DataTypeTest.custom_type_def) + self.assertIsNotNone(data.validate()) -- cgit 1.2.3-korg