369 lines
15 KiB
Python
369 lines
15 KiB
Python
# 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.
|
|
|
|
from testtools import matchers
|
|
|
|
from toscaparser.common import exception
|
|
from toscaparser.elements.property_definition import PropertyDef
|
|
from toscaparser.nodetemplate import NodeTemplate
|
|
from toscaparser.properties import Property
|
|
from toscaparser.tests.base import TestCase
|
|
from toscaparser.utils.gettextutils import _
|
|
from toscaparser.utils import yamlparser
|
|
|
|
|
|
class PropertyTest(TestCase):
|
|
|
|
def test_type(self):
|
|
test_property_schema = {'type': 'string'}
|
|
propertyInstance = Property('test_property', 'Hughes',
|
|
test_property_schema)
|
|
self.assertEqual('string', propertyInstance.type)
|
|
|
|
def test_type_invalid(self):
|
|
test_property_schema = {'type': 'Fish'}
|
|
propertyInstance = Property('test_property', 'Hughes',
|
|
test_property_schema)
|
|
error = self.assertRaises(exception.InvalidTypeError,
|
|
propertyInstance.validate)
|
|
self.assertEqual(_('Type "Fish" is not a valid type.'), str(error))
|
|
|
|
def test_list(self):
|
|
test_property_schema = {'type': 'list'}
|
|
propertyInstance = Property('test_property', ['a', 'b'],
|
|
test_property_schema)
|
|
self.assertIsNone(propertyInstance.validate())
|
|
self.assertEqual(['a', 'b'], propertyInstance.value)
|
|
|
|
def test_list_invalid(self):
|
|
test_property_schema = {'type': 'list'}
|
|
propertyInstance = Property('test_property', 'a',
|
|
test_property_schema)
|
|
error = self.assertRaises(ValueError, propertyInstance.validate)
|
|
self.assertEqual(_('"a" is not a list.'), str(error))
|
|
|
|
def test_list_entry_schema(self):
|
|
test_property_schema = {'type': 'list',
|
|
'entry_schema': {'type': 'string'}}
|
|
propertyInstance = Property('test_property', ['a', 'b'],
|
|
test_property_schema)
|
|
self.assertIsNone(propertyInstance.validate())
|
|
self.assertEqual(['a', 'b'], propertyInstance.value)
|
|
|
|
schema_snippet = '''
|
|
type: list
|
|
entry_schema:
|
|
type: string
|
|
constraints:
|
|
- min_length: 2
|
|
'''
|
|
test_property_schema = yamlparser.simple_parse(schema_snippet)
|
|
propertyInstance = Property('test_property', ['ab', 'cd'],
|
|
test_property_schema)
|
|
self.assertIsNone(propertyInstance.validate())
|
|
self.assertEqual(['ab', 'cd'], propertyInstance.value)
|
|
|
|
def test_list_entry_schema_invalid(self):
|
|
test_property_schema = {'type': 'list',
|
|
'entry_schema': {'type': 'integer'}}
|
|
propertyInstance = Property('test_property', [1, 'b'],
|
|
test_property_schema)
|
|
error = self.assertRaises(ValueError, propertyInstance.validate)
|
|
self.assertEqual(_('"b" is not an integer.'), str(error))
|
|
|
|
def test_map(self):
|
|
test_property_schema = {'type': 'map'}
|
|
propertyInstance = Property('test_property', {'a': 'b'},
|
|
test_property_schema)
|
|
self.assertIsNone(propertyInstance.validate())
|
|
self.assertEqual({'a': 'b'}, propertyInstance.value)
|
|
|
|
def test_map_invalid(self):
|
|
test_property_schema = {'type': 'map'}
|
|
propertyInstance = Property('test_property', 12,
|
|
test_property_schema)
|
|
error = self.assertRaises(ValueError, propertyInstance.validate)
|
|
self.assertEqual(_('"12" is not a map.'), str(error))
|
|
|
|
def test_map_entry_schema(self):
|
|
test_property_schema = {'type': 'map',
|
|
'entry_schema': {'type': 'boolean'}}
|
|
propertyInstance = Property('test_property',
|
|
{'valid': True, 'required': True},
|
|
test_property_schema)
|
|
self.assertIsNone(propertyInstance.validate())
|
|
self.assertEqual({'valid': True, 'required': True},
|
|
propertyInstance.value)
|
|
|
|
def test_map_entry_schema_invalid(self):
|
|
test_property_schema = {'type': 'map',
|
|
'entry_schema': {'type': 'boolean'}}
|
|
propertyInstance = Property('test_property',
|
|
{'valid': True, 'contact_name': 123},
|
|
test_property_schema)
|
|
error = self.assertRaises(ValueError, propertyInstance.validate)
|
|
self.assertEqual(_('"123" is not a boolean.'), str(error))
|
|
|
|
def test_boolean(self):
|
|
test_property_schema = {'type': 'boolean'}
|
|
propertyInstance = Property('test_property', 'true',
|
|
test_property_schema)
|
|
self.assertIsNone(propertyInstance.validate())
|
|
propertyInstance = Property('test_property', True,
|
|
test_property_schema)
|
|
self.assertIsNone(propertyInstance.validate())
|
|
self.assertEqual(True, propertyInstance.value)
|
|
|
|
def test_boolean_invalid(self):
|
|
test_property_schema = {'type': 'boolean'}
|
|
propertyInstance = Property('test_property', 12,
|
|
test_property_schema)
|
|
error = self.assertRaises(ValueError, propertyInstance.validate)
|
|
self.assertEqual(_('"12" is not a boolean.'), str(error))
|
|
|
|
def test_float(self):
|
|
test_property_schema = {'type': 'float'}
|
|
propertyInstance = Property('test_property', 0.1,
|
|
test_property_schema)
|
|
self.assertIsNone(propertyInstance.validate())
|
|
self.assertEqual(0.1, propertyInstance.value)
|
|
|
|
def test_float_invalid(self):
|
|
test_property_schema = {'type': 'float'}
|
|
propertyInstance = Property('test_property', 12,
|
|
test_property_schema)
|
|
error = self.assertRaises(ValueError, propertyInstance.validate)
|
|
self.assertEqual(_('"12" is not a float.'), str(error))
|
|
|
|
def test_timestamp(self):
|
|
test_property_schema = {'type': 'timestamp'}
|
|
# canonical timestamp
|
|
propertyInstance = Property('test_property', '2015-04-01T02:59:43.1Z',
|
|
test_property_schema)
|
|
self.assertIsNone(propertyInstance.validate())
|
|
self.assertEqual("2015-04-01T02:59:43.1Z", propertyInstance.value)
|
|
|
|
# iso8601 timestamp
|
|
propertyInstance = Property('test_property',
|
|
'2015-04-01t21:59:43.10-05:00',
|
|
test_property_schema)
|
|
self.assertIsNone(propertyInstance.validate())
|
|
self.assertEqual("2015-04-01t21:59:43.10-05:00",
|
|
propertyInstance.value)
|
|
|
|
# space separated timestamp
|
|
propertyInstance = Property('test_property',
|
|
'2015-04-01 21:59:43.10 -5',
|
|
test_property_schema)
|
|
self.assertIsNone(propertyInstance.validate())
|
|
self.assertEqual("2015-04-01 21:59:43.10 -5", propertyInstance.value)
|
|
|
|
# no time zone timestamp
|
|
propertyInstance = Property('test_property', '2015-04-01 21:59:43.10',
|
|
test_property_schema)
|
|
self.assertIsNone(propertyInstance.validate())
|
|
self.assertEqual("2015-04-01 21:59:43.10", propertyInstance.value)
|
|
|
|
# date (00:00:00Z)
|
|
propertyInstance = Property('test_property', '2015-04-01',
|
|
test_property_schema)
|
|
self.assertIsNone(propertyInstance.validate())
|
|
self.assertEqual("2015-04-01", propertyInstance.value)
|
|
|
|
def test_timestamp_invalid(self):
|
|
test_property_schema = {'type': 'timestamp'}
|
|
# invalid timestamp - day out of range
|
|
value = '2015-04-115T02:59:43.1Z'
|
|
propertyInstance = Property('test_property', value,
|
|
test_property_schema)
|
|
error = self.assertRaises(ValueError, propertyInstance.validate)
|
|
expected_message = (_('"%s" is not a valid timestamp.') % value)
|
|
self.assertThat(str(error), matchers.StartsWith(expected_message))
|
|
|
|
def test_required(self):
|
|
test_property_schema = {'type': 'string'}
|
|
propertyInstance = Property('test_property', 'Foo',
|
|
test_property_schema)
|
|
self.assertEqual(True, propertyInstance.required)
|
|
|
|
def test_proprety_inheritance(self):
|
|
|
|
tosca_custom_def = '''
|
|
tosca.nodes.SoftwareComponent.MySoftware:
|
|
derived_from: SoftwareComponent
|
|
properties:
|
|
install_path:
|
|
required: false
|
|
type: string
|
|
default: /opt/mysoftware
|
|
'''
|
|
|
|
tosca_node_template = '''
|
|
node_templates:
|
|
mysoftware_instance:
|
|
type: tosca.nodes.SoftwareComponent.MySoftware
|
|
properties:
|
|
component_version: 3.1
|
|
'''
|
|
|
|
expected_properties = ['component_version',
|
|
'install_path']
|
|
|
|
tpl = self._get_nodetemplate(tosca_node_template, tosca_custom_def)
|
|
self.assertIsNone(tpl.validate())
|
|
self.assertEqual(expected_properties,
|
|
sorted(tpl.get_properties().keys()))
|
|
|
|
def test_missing_property_type(self):
|
|
tpl_snippet = '''
|
|
properties:
|
|
prop:
|
|
typo: tosca.mytesttype.Test
|
|
'''
|
|
schema = yamlparser.simple_parse(tpl_snippet)
|
|
error = self.assertRaises(exception.InvalidSchemaError, PropertyDef,
|
|
'prop', None, schema['properties']['prop'])
|
|
self.assertEqual(_('Schema definition of "prop" must have a "type" '
|
|
'attribute.'), str(error))
|
|
|
|
def test_invalid_required_value(self):
|
|
tpl_snippet = '''
|
|
properties:
|
|
prop:
|
|
type: tosca.mytesttype.Test
|
|
required: dunno
|
|
'''
|
|
schema = yamlparser.simple_parse(tpl_snippet)
|
|
error = self.assertRaises(exception.InvalidSchemaError, PropertyDef,
|
|
'prop', None, schema['properties']['prop'])
|
|
|
|
valid_values = ', '.join(PropertyDef.VALID_REQUIRED_VALUES)
|
|
expected_message = (_('Schema definition of "prop" has "required" '
|
|
'attribute with invalid value "dunno". The '
|
|
'value must be one of "%s".') % valid_values)
|
|
self.assertEqual(expected_message, str(error))
|
|
|
|
def test_invalid_property_status(self):
|
|
tpl_snippet = '''
|
|
properties:
|
|
prop:
|
|
type: string
|
|
status: unknown
|
|
'''
|
|
schema = yamlparser.simple_parse(tpl_snippet)
|
|
error = self.assertRaises(exception.InvalidSchemaError, PropertyDef,
|
|
'prop', None, schema['properties']['prop'])
|
|
|
|
valid_values = ', '.join(PropertyDef.VALID_STATUS_VALUES)
|
|
expected_message = (_('Schema definition of "prop" has "status" '
|
|
'attribute with invalid value "unknown". The '
|
|
'value must be one of "%s".') % valid_values)
|
|
self.assertEqual(expected_message, str(error))
|
|
|
|
def test_capability_proprety_inheritance(self):
|
|
tosca_custom_def_example1 = '''
|
|
tosca.capabilities.ScalableNew:
|
|
derived_from: tosca.capabilities.Scalable
|
|
properties:
|
|
max_instances:
|
|
type: integer
|
|
default: 0
|
|
required: no
|
|
|
|
tosca.nodes.ComputeNew:
|
|
derived_from: tosca.nodes.Compute
|
|
capabilities:
|
|
scalable:
|
|
type: tosca.capabilities.ScalableNew
|
|
'''
|
|
|
|
tosca_node_template_example1 = '''
|
|
node_templates:
|
|
compute_instance:
|
|
type: tosca.nodes.ComputeNew
|
|
capabilities:
|
|
scalable:
|
|
properties:
|
|
min_instances: 1
|
|
'''
|
|
|
|
tosca_custom_def_example2 = '''
|
|
tosca.nodes.ComputeNew:
|
|
derived_from: tosca.nodes.Compute
|
|
capabilities:
|
|
new_cap:
|
|
type: tosca.capabilities.Scalable
|
|
'''
|
|
|
|
tosca_node_template_example2 = '''
|
|
node_templates:
|
|
db_server:
|
|
type: tosca.nodes.ComputeNew
|
|
capabilities:
|
|
host:
|
|
properties:
|
|
num_cpus: 1
|
|
'''
|
|
|
|
tpl1 = self._get_nodetemplate(tosca_node_template_example1,
|
|
tosca_custom_def_example1)
|
|
self.assertIsNone(tpl1.validate())
|
|
|
|
tpl2 = self._get_nodetemplate(tosca_node_template_example2,
|
|
tosca_custom_def_example2)
|
|
self.assertIsNone(tpl2.validate())
|
|
|
|
def _get_nodetemplate(self, tpl_snippet,
|
|
custom_def_snippet=None):
|
|
nodetemplates = yamlparser.\
|
|
simple_parse(tpl_snippet)['node_templates']
|
|
custom_def = []
|
|
if custom_def_snippet:
|
|
custom_def = yamlparser.simple_parse(custom_def_snippet)
|
|
name = list(nodetemplates.keys())[0]
|
|
tpl = NodeTemplate(name, nodetemplates, custom_def)
|
|
return tpl
|
|
|
|
def test_explicit_relationship_proprety(self):
|
|
|
|
tosca_node_template = '''
|
|
node_templates:
|
|
|
|
client_node:
|
|
type: tosca.nodes.Compute
|
|
requirements:
|
|
- local_storage:
|
|
node: my_storage
|
|
relationship:
|
|
type: AttachesTo
|
|
properties:
|
|
location: /mnt/disk
|
|
|
|
my_storage:
|
|
type: tosca.nodes.BlockStorage
|
|
properties:
|
|
size: 1 GB
|
|
'''
|
|
|
|
expected_properties = ['location']
|
|
|
|
nodetemplates = yamlparser.\
|
|
simple_parse(tosca_node_template)['node_templates']
|
|
tpl = NodeTemplate('client_node', nodetemplates, [])
|
|
|
|
self.assertIsNone(tpl.validate())
|
|
rel_tpls = []
|
|
for relationship, trgt in tpl.relationships.items():
|
|
rel_tpls.extend(trgt.get_relationship_template())
|
|
self.assertEqual(expected_properties,
|
|
sorted(rel_tpls[0].get_properties().keys()))
|