Sync upstream code
[parser.git] / tosca2heat / tosca-parser / toscaparser / tests / test_properties.py
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
4 #
5 #         http://www.apache.org/licenses/LICENSE-2.0
6 #
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
11 #    under the License.
12
13 from testtools import matchers
14
15 from toscaparser.common import exception
16 from toscaparser.elements.property_definition import PropertyDef
17 from toscaparser.nodetemplate import NodeTemplate
18 from toscaparser.properties import Property
19 from toscaparser.tests.base import TestCase
20 from toscaparser.utils.gettextutils import _
21 from toscaparser.utils import yamlparser
22
23
24 class PropertyTest(TestCase):
25
26     def test_type(self):
27         test_property_schema = {'type': 'string'}
28         propertyInstance = Property('test_property', 'Hughes',
29                                     test_property_schema)
30         self.assertEqual('string', propertyInstance.type)
31
32     def test_type_invalid(self):
33         test_property_schema = {'type': 'Fish'}
34         propertyInstance = Property('test_property', 'Hughes',
35                                     test_property_schema)
36         error = self.assertRaises(exception.InvalidTypeError,
37                                   propertyInstance.validate)
38         self.assertEqual(_('Type "Fish" is not a valid type.'), str(error))
39
40     def test_list(self):
41         test_property_schema = {'type': 'list'}
42         propertyInstance = Property('test_property', ['a', 'b'],
43                                     test_property_schema)
44         self.assertIsNone(propertyInstance.validate())
45         self.assertEqual(['a', 'b'], propertyInstance.value)
46
47     def test_list_invalid(self):
48         test_property_schema = {'type': 'list'}
49         propertyInstance = Property('test_property', 'a',
50                                     test_property_schema)
51         error = self.assertRaises(ValueError, propertyInstance.validate)
52         self.assertEqual(_('"a" is not a list.'), str(error))
53
54     def test_list_entry_schema(self):
55         test_property_schema = {'type': 'list',
56                                 'entry_schema': {'type': 'string'}}
57         propertyInstance = Property('test_property', ['a', 'b'],
58                                     test_property_schema)
59         self.assertIsNone(propertyInstance.validate())
60         self.assertEqual(['a', 'b'], propertyInstance.value)
61
62         schema_snippet = '''
63         type: list
64         entry_schema:
65           type: string
66           constraints:
67             - min_length: 2
68         '''
69         test_property_schema = yamlparser.simple_parse(schema_snippet)
70         propertyInstance = Property('test_property', ['ab', 'cd'],
71                                     test_property_schema)
72         self.assertIsNone(propertyInstance.validate())
73         self.assertEqual(['ab', 'cd'], propertyInstance.value)
74
75     def test_list_entry_schema_invalid(self):
76         test_property_schema = {'type': 'list',
77                                 'entry_schema': {'type': 'integer'}}
78         propertyInstance = Property('test_property', [1, 'b'],
79                                     test_property_schema)
80         error = self.assertRaises(ValueError, propertyInstance.validate)
81         self.assertEqual(_('"b" is not an integer.'), str(error))
82
83     def test_map(self):
84         test_property_schema = {'type': 'map'}
85         propertyInstance = Property('test_property', {'a': 'b'},
86                                     test_property_schema)
87         self.assertIsNone(propertyInstance.validate())
88         self.assertEqual({'a': 'b'}, propertyInstance.value)
89
90     def test_map_invalid(self):
91         test_property_schema = {'type': 'map'}
92         propertyInstance = Property('test_property', 12,
93                                     test_property_schema)
94         error = self.assertRaises(ValueError, propertyInstance.validate)
95         self.assertEqual(_('"12" is not a map.'), str(error))
96
97     def test_map_entry_schema(self):
98         test_property_schema = {'type': 'map',
99                                 'entry_schema': {'type': 'boolean'}}
100         propertyInstance = Property('test_property',
101                                     {'valid': True, 'required': True},
102                                     test_property_schema)
103         self.assertIsNone(propertyInstance.validate())
104         self.assertEqual({'valid': True, 'required': True},
105                          propertyInstance.value)
106
107     def test_map_entry_schema_invalid(self):
108         test_property_schema = {'type': 'map',
109                                 'entry_schema': {'type': 'boolean'}}
110         propertyInstance = Property('test_property',
111                                     {'valid': True, 'contact_name': 123},
112                                     test_property_schema)
113         error = self.assertRaises(ValueError, propertyInstance.validate)
114         self.assertEqual(_('"123" is not a boolean.'), str(error))
115
116     def test_boolean(self):
117         test_property_schema = {'type': 'boolean'}
118         propertyInstance = Property('test_property', 'true',
119                                     test_property_schema)
120         self.assertIsNone(propertyInstance.validate())
121         propertyInstance = Property('test_property', True,
122                                     test_property_schema)
123         self.assertIsNone(propertyInstance.validate())
124         self.assertEqual(True, propertyInstance.value)
125
126     def test_boolean_invalid(self):
127         test_property_schema = {'type': 'boolean'}
128         propertyInstance = Property('test_property', 12,
129                                     test_property_schema)
130         error = self.assertRaises(ValueError, propertyInstance.validate)
131         self.assertEqual(_('"12" is not a boolean.'), str(error))
132
133     def test_float(self):
134         test_property_schema = {'type': 'float'}
135         propertyInstance = Property('test_property', 0.1,
136                                     test_property_schema)
137         self.assertIsNone(propertyInstance.validate())
138         self.assertEqual(0.1, propertyInstance.value)
139
140     def test_float_invalid(self):
141         test_property_schema = {'type': 'float'}
142         propertyInstance = Property('test_property', 12,
143                                     test_property_schema)
144         error = self.assertRaises(ValueError, propertyInstance.validate)
145         self.assertEqual(_('"12" is not a float.'), str(error))
146
147     def test_timestamp(self):
148         test_property_schema = {'type': 'timestamp'}
149         # canonical timestamp
150         propertyInstance = Property('test_property', '2015-04-01T02:59:43.1Z',
151                                     test_property_schema)
152         self.assertIsNone(propertyInstance.validate())
153         self.assertEqual("2015-04-01T02:59:43.1Z", propertyInstance.value)
154
155         # iso8601 timestamp
156         propertyInstance = Property('test_property',
157                                     '2015-04-01t21:59:43.10-05:00',
158                                     test_property_schema)
159         self.assertIsNone(propertyInstance.validate())
160         self.assertEqual("2015-04-01t21:59:43.10-05:00",
161                          propertyInstance.value)
162
163         # space separated timestamp
164         propertyInstance = Property('test_property',
165                                     '2015-04-01 21:59:43.10 -5',
166                                     test_property_schema)
167         self.assertIsNone(propertyInstance.validate())
168         self.assertEqual("2015-04-01 21:59:43.10 -5", propertyInstance.value)
169
170         # no time zone timestamp
171         propertyInstance = Property('test_property', '2015-04-01 21:59:43.10',
172                                     test_property_schema)
173         self.assertIsNone(propertyInstance.validate())
174         self.assertEqual("2015-04-01 21:59:43.10", propertyInstance.value)
175
176         # date (00:00:00Z)
177         propertyInstance = Property('test_property', '2015-04-01',
178                                     test_property_schema)
179         self.assertIsNone(propertyInstance.validate())
180         self.assertEqual("2015-04-01", propertyInstance.value)
181
182     def test_timestamp_invalid(self):
183         test_property_schema = {'type': 'timestamp'}
184         # invalid timestamp - day out of range
185         value = '2015-04-115T02:59:43.1Z'
186         propertyInstance = Property('test_property', value,
187                                     test_property_schema)
188         error = self.assertRaises(ValueError, propertyInstance.validate)
189         expected_message = (_('"%s" is not a valid timestamp.') % value)
190         self.assertThat(str(error), matchers.StartsWith(expected_message))
191
192     def test_required(self):
193         test_property_schema = {'type': 'string'}
194         propertyInstance = Property('test_property', 'Foo',
195                                     test_property_schema)
196         self.assertEqual(True, propertyInstance.required)
197
198     def test_proprety_inheritance(self):
199
200         tosca_custom_def = '''
201           tosca.nodes.SoftwareComponent.MySoftware:
202             derived_from: SoftwareComponent
203             properties:
204               install_path:
205                 required: false
206                 type: string
207                 default: /opt/mysoftware
208         '''
209
210         tosca_node_template = '''
211           node_templates:
212             mysoftware_instance:
213               type: tosca.nodes.SoftwareComponent.MySoftware
214               properties:
215                 component_version: 3.1
216         '''
217
218         expected_properties = ['component_version',
219                                'install_path']
220
221         tpl = self._get_nodetemplate(tosca_node_template, tosca_custom_def)
222         self.assertIsNone(tpl.validate())
223         self.assertEqual(expected_properties,
224                          sorted(tpl.get_properties().keys()))
225
226     def test_missing_property_type(self):
227         tpl_snippet = '''
228          properties:
229            prop:
230              typo: tosca.mytesttype.Test
231         '''
232         schema = yamlparser.simple_parse(tpl_snippet)
233         error = self.assertRaises(exception.InvalidSchemaError, PropertyDef,
234                                   'prop', None, schema['properties']['prop'])
235         self.assertEqual(_('Schema definition of "prop" must have a "type" '
236                            'attribute.'), str(error))
237
238     def test_invalid_required_value(self):
239         tpl_snippet = '''
240          properties:
241            prop:
242              type: tosca.mytesttype.Test
243              required: dunno
244         '''
245         schema = yamlparser.simple_parse(tpl_snippet)
246         error = self.assertRaises(exception.InvalidSchemaError, PropertyDef,
247                                   'prop', None, schema['properties']['prop'])
248
249         valid_values = ', '.join(PropertyDef.VALID_REQUIRED_VALUES)
250         expected_message = (_('Schema definition of "prop" has "required" '
251                               'attribute with invalid value "dunno". The '
252                               'value must be one of "%s".') % valid_values)
253         self.assertEqual(expected_message, str(error))
254
255     def test_invalid_property_status(self):
256         tpl_snippet = '''
257          properties:
258            prop:
259              type: string
260              status: unknown
261         '''
262         schema = yamlparser.simple_parse(tpl_snippet)
263         error = self.assertRaises(exception.InvalidSchemaError, PropertyDef,
264                                   'prop', None, schema['properties']['prop'])
265
266         valid_values = ', '.join(PropertyDef.VALID_STATUS_VALUES)
267         expected_message = (_('Schema definition of "prop" has "status" '
268                               'attribute with invalid value "unknown". The '
269                               'value must be one of "%s".') % valid_values)
270         self.assertEqual(expected_message, str(error))
271
272     def test_capability_proprety_inheritance(self):
273         tosca_custom_def_example1 = '''
274           tosca.capabilities.ScalableNew:
275             derived_from: tosca.capabilities.Scalable
276             properties:
277               max_instances:
278                 type: integer
279                 default: 0
280                 required: no
281
282           tosca.nodes.ComputeNew:
283             derived_from: tosca.nodes.Compute
284             capabilities:
285               scalable:
286                 type: tosca.capabilities.ScalableNew
287         '''
288
289         tosca_node_template_example1 = '''
290           node_templates:
291             compute_instance:
292               type: tosca.nodes.ComputeNew
293               capabilities:
294                 scalable:
295                   properties:
296                     min_instances: 1
297         '''
298
299         tosca_custom_def_example2 = '''
300           tosca.nodes.ComputeNew:
301             derived_from: tosca.nodes.Compute
302             capabilities:
303               new_cap:
304                 type: tosca.capabilities.Scalable
305         '''
306
307         tosca_node_template_example2 = '''
308           node_templates:
309             db_server:
310                 type: tosca.nodes.ComputeNew
311                 capabilities:
312                   host:
313                    properties:
314                      num_cpus: 1
315         '''
316
317         tpl1 = self._get_nodetemplate(tosca_node_template_example1,
318                                       tosca_custom_def_example1)
319         self.assertIsNone(tpl1.validate())
320
321         tpl2 = self._get_nodetemplate(tosca_node_template_example2,
322                                       tosca_custom_def_example2)
323         self.assertIsNone(tpl2.validate())
324
325     def _get_nodetemplate(self, tpl_snippet,
326                           custom_def_snippet=None):
327         nodetemplates = yamlparser.\
328             simple_parse(tpl_snippet)['node_templates']
329         custom_def = []
330         if custom_def_snippet:
331             custom_def = yamlparser.simple_parse(custom_def_snippet)
332         name = list(nodetemplates.keys())[0]
333         tpl = NodeTemplate(name, nodetemplates, custom_def)
334         return tpl
335
336     def test_explicit_relationship_proprety(self):
337
338         tosca_node_template = '''
339           node_templates:
340
341             client_node:
342               type: tosca.nodes.Compute
343               requirements:
344                 - local_storage:
345                     node: my_storage
346                     relationship:
347                       type: AttachesTo
348                       properties:
349                         location: /mnt/disk
350
351             my_storage:
352               type: tosca.nodes.BlockStorage
353               properties:
354                 size: 1 GB
355         '''
356
357         expected_properties = ['location']
358
359         nodetemplates = yamlparser.\
360             simple_parse(tosca_node_template)['node_templates']
361         tpl = NodeTemplate('client_node', nodetemplates, [])
362
363         self.assertIsNone(tpl.validate())
364         rel_tpls = []
365         for relationship, trgt in tpl.relationships.items():
366             rel_tpls.extend(trgt.get_relationship_template())
367         self.assertEqual(expected_properties,
368                          sorted(rel_tpls[0].get_properties().keys()))