1 # Licensed under the Apache License, Version 2.0 (the "License"); you may
2 # not use this file except in compliance with the License. You may obtain
3 # a copy of the License at
5 # http://www.apache.org/licenses/LICENSE-2.0
7 # Unless required by applicable law or agreed to in writing, software
8 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
9 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
10 # License for the specific language governing permissions and limitations
15 from testtools.testcase import skip
16 from toscaparser.common import exception
17 from toscaparser.dataentity import DataEntity
18 from toscaparser.elements.datatype import DataType
19 from toscaparser.parameters import Input
20 from toscaparser.tests.base import TestCase
21 from toscaparser.tosca_template import ToscaTemplate
22 from toscaparser.utils.gettextutils import _
23 from toscaparser.utils import yamlparser
26 class DataTypeTest(TestCase):
28 custom_type_schema = '''
29 tosca.my.datatypes.PeopleBase:
40 tosca.my.datatypes.People:
41 derived_from: tosca.my.datatypes.PeopleBase
52 type: tosca.my.datatypes.ContactInfo
54 tosca.my.datatypes.ContactInfo:
55 description: simple contact information
67 tosca.my.datatypes.TestLab:
73 - in_range: [-256, UNBOUNDED]
78 - in_range: [-256, INFINITY]
80 custom_type_def = yamlparser.simple_parse(custom_type_schema)
82 def test_empty_template(self):
84 value = yamlparser.simple_parse(value_snippet)
85 self.assertEqual(value, {})
87 # TODO(Matt) - opened as bug 1555300
88 # Need a test for PortSpec normative data type
89 # that tests the spec. requirement: "A valid PortSpec
90 # must have at least one of the following properties:
91 # target, target_range, source or source_range."
92 # TODO(Matt) - opened as bug 1555310
93 # test PortSpec value for source and target
94 # against the source_range and target_range
96 def test_built_in_datatype(self):
100 network_id: 3e54214f-5c09-1bc9-9999-44100326da1b
101 addresses: [ 10.111.128.10 ]
103 value = yamlparser.simple_parse(value_snippet)
104 data = DataEntity('tosca.datatypes.network.NetworkInfo',
105 value.get('private_network'))
106 self.assertIsNotNone(data.validate())
112 value = yamlparser.simple_parse(value_snippet)
113 data = DataEntity('tosca.datatypes.network.PortSpec',
114 value.get('portspec_valid'))
115 self.assertIsNotNone(data.validate())
121 value = yamlparser.simple_parse(value_snippet)
122 data = DataEntity('tosca.datatypes.network.PortSpec',
123 value.get('portspec_invalid'))
124 err = self.assertRaises(exception.ValidationError, data.validate)
125 self.assertEqual(_('The value "xyz" of property "protocol" is not '
126 'valid. Expected a value from "[udp, tcp, igmp]".'
130 def test_built_in_datatype_with_short_name(self):
134 port_id: 2c0c7a37-691a-23a6-7709-2d10ad041467
135 network_id: 3e54214f-5c09-1bc9-9999-44100326da1b
136 mac_address: f1:18:3b:41:92:1e
137 addresses: [ 172.24.9.102 ]
139 value = yamlparser.simple_parse(value_snippet)
140 data = DataEntity('PortInfo', value.get('ethernet_port'))
141 self.assertIsNotNone(data.validate())
143 def test_built_in_datatype_without_properties(self):
147 value = yamlparser.simple_parse(value_snippet)
148 datatype = DataType('PortDef')
149 self.assertEqual('integer', datatype.value_type)
150 data = DataEntity('PortDef', value)
151 self.assertIsNotNone(data.validate())
153 @skip('The example in TOSCA spec may have some problem.')
154 def test_built_in_nested_datatype(self):
161 value = yamlparser.simple_parse(value_snippet)
162 data = DataEntity('PortSpec', value.get('user_port'))
163 self.assertIsNotNone(data.validate())
165 def test_built_in_nested_datatype_portdef(self):
170 description: Port for the MySQL database
172 inputs = yamlparser.simple_parse(tpl_snippet)['inputs']
173 name, attrs = list(inputs.items())[0]
174 input = Input(name, attrs)
175 self.assertIsNone(input.validate(3360))
176 err = self.assertRaises(exception.ValidationError, input.validate,
178 self.assertEqual(_('The value "336000" of property "None" is out of '
179 'range "(min:1, max:65535)".'),
182 def test_custom_datatype(self):
187 value = yamlparser.simple_parse(value_snippet)
188 data = DataEntity('tosca.my.datatypes.PeopleBase', value,
189 DataTypeTest.custom_type_def)
190 self.assertIsNotNone(data.validate())
192 def test_custom_datatype_with_parent(self):
197 - {contact_name: Tom,
198 contact_email: tom@email.com,
199 contact_phone: '123456789'}
200 - {contact_name: Jerry,
201 contact_email: jerry@email.com,
202 contact_phone: '321654987'}
204 value = yamlparser.simple_parse(value_snippet)
205 data = DataEntity('tosca.my.datatypes.People', value,
206 DataTypeTest.custom_type_def)
207 self.assertIsNotNone(data.validate())
209 # [Tom, Jerry] is not a dict, it can't be a value of datatype PeopleBase
210 def test_non_dict_value_for_datatype(self):
214 value = yamlparser.simple_parse(value_snippet)
215 data = DataEntity('tosca.my.datatypes.PeopleBase', value,
216 DataTypeTest.custom_type_def)
217 error = self.assertRaises(exception.TypeMismatchError, data.validate)
218 self.assertEqual(_('[\'Tom\', \'Jerry\'] must be of type '
219 '"tosca.my.datatypes.PeopleBase".'),
222 # 'nema' is an invalid field name
223 def test_field_error_in_dataentity(self):
228 value = yamlparser.simple_parse(value_snippet)
229 data = DataEntity('tosca.my.datatypes.PeopleBase', value,
230 DataTypeTest.custom_type_def)
231 error = self.assertRaises(exception.UnknownFieldError, data.validate)
232 self.assertEqual(_('Data value of type '
233 '"tosca.my.datatypes.PeopleBase" contains unknown '
234 'field "nema". Refer to the definition to verify '
238 def test_default_field_in_dataentity(self):
242 value = yamlparser.simple_parse(value_snippet)
243 data = DataEntity('tosca.my.datatypes.PeopleBase', value,
244 DataTypeTest.custom_type_def)
245 data = data.validate()
246 self.assertEqual('unknown', data.get('gender'))
248 # required field 'name' is missing
249 def test_missing_field_in_dataentity(self):
253 value = yamlparser.simple_parse(value_snippet)
254 data = DataEntity('tosca.my.datatypes.PeopleBase', value,
255 DataTypeTest.custom_type_def)
256 error = self.assertRaises(exception.MissingRequiredFieldError,
258 self.assertEqual(_('Data value of type '
259 '"tosca.my.datatypes.PeopleBase" is missing '
260 'required field "[\'name\']".'),
263 # the value of name field is not a string
264 def test_type_error_in_dataentity(self):
269 value = yamlparser.simple_parse(value_snippet)
270 data = DataEntity('tosca.my.datatypes.PeopleBase', value,
271 DataTypeTest.custom_type_def)
272 error = self.assertRaises(ValueError, data.validate)
273 self.assertEqual(_('"123" is not a string.'), error.__str__())
275 # the value of name doesn't meet the defined constraint
276 def test_value_error_in_dataentity(self):
281 value = yamlparser.simple_parse(value_snippet)
282 data = DataEntity('tosca.my.datatypes.PeopleBase', value,
283 DataTypeTest.custom_type_def)
284 error = self.assertRaises(exception.ValidationError, data.validate)
285 self.assertEqual(_('Length of value "M" of property "name" must be '
286 'at least "2".'), error.__str__())
288 # value of addresses doesn't fit the entry_schema
289 def test_validation_in_collection_entry(self):
293 addresses: {Home: 1, Office: 9 bar avenue}
295 value = yamlparser.simple_parse(value_snippet)
296 data = DataEntity('tosca.my.datatypes.People', value,
297 DataTypeTest.custom_type_def)
298 error = self.assertRaises(ValueError, data.validate)
299 self.assertEqual(_('"1" is not a string.'), error.__str__())
301 # 'contact_pone' is an invalid attribute name in nested datatype below
302 def test_validation_in_nested_datatype(self):
307 - {contact_name: Tom,
308 contact_email: tom@email.com,
309 contact_pone: '123456789'}
310 - {contact_name: Jerry,
311 contact_email: jerry@email.com,
312 contact_phone: '321654987'}
314 value = yamlparser.simple_parse(value_snippet)
315 data = DataEntity('tosca.my.datatypes.People', value,
316 DataTypeTest.custom_type_def)
317 error = self.assertRaises(exception.UnknownFieldError, data.validate)
318 self.assertEqual(_('Data value of type '
319 '"tosca.my.datatypes.ContactInfo" contains unknown '
320 'field "contact_pone". Refer to the definition to '
321 'verify valid values.'),
324 def test_datatype_in_current_template(self):
325 tpl_path = os.path.join(
326 os.path.dirname(os.path.abspath(__file__)),
327 "data/datatypes/test_custom_datatypes_in_current_template.yaml")
328 self.assertIsNotNone(ToscaTemplate(tpl_path))
330 def test_datatype_in_template_positive(self):
331 tpl_path = os.path.join(
332 os.path.dirname(os.path.abspath(__file__)),
333 "data/datatypes/test_custom_datatypes_positive.yaml")
334 self.assertIsNotNone(ToscaTemplate(tpl_path))
336 def test_datatype_in_template_invalid_value(self):
337 tpl_path = os.path.join(
338 os.path.dirname(os.path.abspath(__file__)),
339 "data/datatypes/test_custom_datatypes_value_error.yaml")
340 self.assertRaises(exception.ValidationError, ToscaTemplate, tpl_path)
341 exception.ExceptionCollector.assertExceptionMessage(
343 _('"[\'1 foo street\', \'9 bar avenue\']" is not a map.'))
345 def test_datatype_in_template_nested_datatype_error(self):
346 tpl_path = os.path.join(
347 os.path.dirname(os.path.abspath(__file__)),
348 "data/datatypes/test_custom_datatypes_nested_datatype_error.yaml")
349 self.assertRaises(exception.ValidationError, ToscaTemplate, tpl_path)
350 exception.ExceptionCollector.assertExceptionMessage(
351 ValueError, _('"123456789" is not a string.'))
353 def test_valid_range_type(self):
357 target_range: [20000, 60000]
358 source_range: [1000, 3000]
360 value = yamlparser.simple_parse(value_snippet)
361 data = DataEntity('PortSpec', value.get('user_port'))
362 self.assertIsNotNone(data.validate())
364 def test_invalid_range_datatype(self):
368 target_range: [20000]
370 value = yamlparser.simple_parse(value_snippet)
371 data = DataEntity('PortSpec', value.get('user_port'))
372 err = self.assertRaises(ValueError, data.validate)
373 self.assertEqual(_('"[20000]" is not a valid range.'
380 target_range: [20000, 3000]
382 value = yamlparser.simple_parse(value_snippet)
383 data = DataEntity('PortSpec', value.get('user_port'))
384 err = self.assertRaises(ValueError, data.validate)
385 self.assertEqual(_('"[20000, 3000]" is not a valid range.'
390 humidity: [-100, 100]
392 value = yamlparser.simple_parse(value_snippet)
393 data = DataEntity('tosca.my.datatypes.TestLab',
394 value, DataTypeTest.custom_type_def)
395 err = self.assertRaises(exception.InvalidSchemaError,
396 lambda: data.validate())
397 self.assertEqual(_('The property "in_range" expects comparable values.'
401 def test_range_unbounded(self):
403 temperature: [-100, 999999]
405 value = yamlparser.simple_parse(value_snippet)
406 data = DataEntity('tosca.my.datatypes.TestLab', value,
407 DataTypeTest.custom_type_def)
408 self.assertIsNotNone(data.validate())