vFW and vDNS support added to azure-plugin
[multicloud/azure.git] / azure / aria / aria-extension-cloudify / src / aria / tests / modeling / test_models.py
diff --git a/azure/aria/aria-extension-cloudify/src/aria/tests/modeling/test_models.py b/azure/aria/aria-extension-cloudify/src/aria/tests/modeling/test_models.py
new file mode 100644 (file)
index 0000000..25b4080
--- /dev/null
@@ -0,0 +1,872 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You 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 datetime import datetime
+from contextlib import contextmanager
+
+import pytest
+
+from aria import application_model_storage
+from aria.storage import (
+    sql_mapi,
+)
+from aria.storage.exceptions import StorageError
+from aria.modeling.exceptions import ValueFormatException
+from aria.modeling.models import (
+    ServiceTemplate,
+    Service,
+    ServiceUpdate,
+    ServiceUpdateStep,
+    ServiceModification,
+    Execution,
+    Task,
+    Plugin,
+    Relationship,
+    NodeTemplate,
+    Node,
+    Input,
+    Output,
+    Property,
+    Attribute,
+    Configuration,
+    Argument,
+    Type
+)
+
+from tests import mock
+from tests.storage import release_sqlite_storage, init_inmemory_model_storage
+
+
+@contextmanager
+def sql_storage(storage_func):
+    storage = None
+    try:
+        storage = storage_func()
+        yield storage
+    finally:
+        if storage:
+            release_sqlite_storage(storage)
+
+
+def _empty_storage():
+    return application_model_storage(sql_mapi.SQLAlchemyModelAPI,
+                                     initiator=init_inmemory_model_storage)
+
+
+def _service_template_storage():
+    storage = _empty_storage()
+    service_template = mock.models.create_service_template()
+    storage.service_template.put(service_template)
+    return storage
+
+
+def _service_storage():
+    storage = _service_template_storage()
+    service = mock.models.create_service(
+        storage.service_template.get_by_name(mock.models.SERVICE_TEMPLATE_NAME))
+    storage.service.put(service)
+    return storage
+
+
+def _service_update_storage():
+    storage = _service_storage()
+    service_update = ServiceUpdate(
+        service=storage.service.list()[0],
+        created_at=now,
+        service_plan={},
+    )
+    storage.service_update.put(service_update)
+    return storage
+
+
+def _node_template_storage():
+    storage = _service_storage()
+    service_template = storage.service_template.list()[0]
+    dependency_node_template = mock.models.create_dependency_node_template(service_template)
+    mock.models.create_dependent_node_template(service_template, dependency_node_template)
+    storage.service_template.update(service_template)
+    return storage
+
+
+def _nodes_storage():
+    storage = _node_template_storage()
+    service = storage.service.get_by_name(mock.models.SERVICE_NAME)
+    dependency_node_template = storage.node_template.get_by_name(
+        mock.models.DEPENDENCY_NODE_TEMPLATE_NAME)
+    mock.models.create_node(dependency_node_template, service,
+                            name=mock.models.DEPENDENCY_NODE_NAME)
+
+    dependent_node_template = mock.models.create_dependent_node_template(service.service_template,
+                                                                         dependency_node_template)
+
+    mock.models.create_node(dependent_node_template, service, name=mock.models.DEPENDENT_NODE_NAME)
+    storage.service.update(service)
+    return storage
+
+
+def _execution_storage():
+    storage = _service_storage()
+    execution = mock.models.create_execution(storage.service.list()[0])
+    plugin = mock.models.create_plugin()
+    storage.execution.put(execution)
+    storage.plugin.put(plugin)
+    return storage
+
+
+@pytest.fixture
+def empty_storage():
+    with sql_storage(_empty_storage) as storage:
+        yield storage
+
+
+@pytest.fixture
+def service_template_storage():
+    with sql_storage(_service_template_storage) as storage:
+        yield storage
+
+
+@pytest.fixture
+def service_storage():
+    with sql_storage(_service_storage) as storage:
+        yield storage
+
+
+@pytest.fixture
+def service_update_storage():
+    with sql_storage(_service_update_storage) as storage:
+        yield storage
+
+
+@pytest.fixture
+def node_template_storage():
+    with sql_storage(_node_template_storage) as storage:
+        yield storage
+
+
+@pytest.fixture
+def nodes_storage():
+    with sql_storage(_nodes_storage) as storage:
+        yield storage
+
+
+@pytest.fixture
+def execution_storage():
+    with sql_storage(_execution_storage) as storage:
+        yield storage
+
+
+m_cls = type('MockClass')
+now = datetime.utcnow()
+
+
+def _test_model(is_valid, storage, model_cls, model_kwargs):
+    if is_valid:
+        model = model_cls(**model_kwargs)
+        getattr(storage, model_cls.__modelname__).put(model)
+        return model
+    else:
+        with pytest.raises((ValueFormatException, StorageError, TypeError),):
+            getattr(storage, model_cls.__modelname__).put(model_cls(**model_kwargs))
+
+
+class TestServiceTemplate(object):
+
+    @pytest.mark.parametrize(
+        'is_valid, description, created_at, updated_at, main_file_name',
+        [
+            (False, [], now, now, '/path'),
+            (False, 'description', 'error', now, '/path'),
+            (False, 'description', now, 'error', '/path'),
+            (False, 'description', now, now, {}),
+
+            (True, 'description', now, now, '/path'),
+        ]
+    )
+
+    def test_service_template_model_creation(self, empty_storage, is_valid, description, created_at,
+                                             updated_at, main_file_name):
+        _test_model(is_valid=is_valid,
+                    storage=empty_storage,
+                    model_cls=ServiceTemplate,
+                    model_kwargs=dict(
+                        description=description,
+                        created_at=created_at,
+                        updated_at=updated_at,
+                        main_file_name=main_file_name)
+                   )
+
+
+class TestService(object):
+
+    @pytest.mark.parametrize(
+        'is_valid, name, created_at, description, inputs, '
+        'outputs, updated_at',
+        [
+            (False, m_cls, now, 'desc', {}, {}, now),
+            (False, 'name', m_cls, 'desc', {}, {}, now),
+            (False, 'name', now, m_cls, {}, {}, now),
+            (False, 'name', now, 'desc', m_cls, {}, now),
+            (False, 'name', now, 'desc', {}, m_cls, now),
+            (False, 'name', now, 'desc', {}, {}, m_cls),
+
+            (True, 'name', now, 'desc', {}, {}, now),
+            (True, None, now, 'desc', {}, {}, now),
+            (True, 'name', now, None, {}, {}, now),
+            (True, 'name', now, 'desc', {}, {}, None),
+            (True, 'name', now, 'desc', {}, {}, now),
+        ]
+    )
+    def test_service_model_creation(self, service_storage, is_valid, name, created_at, description,
+                                    inputs, outputs, updated_at):
+        service = _test_model(
+            is_valid=is_valid,
+            storage=service_storage,
+            model_cls=Service,
+            model_kwargs=dict(
+                name=name,
+                service_template=service_storage.service_template.list()[0],
+                created_at=created_at,
+                description=description,
+                inputs=inputs,
+                outputs=outputs,
+                updated_at=updated_at
+            ))
+        if is_valid:
+            assert service.service_template == \
+                   service_storage.service_template.list()[0]
+
+
+class TestExecution(object):
+
+    @pytest.mark.parametrize(
+        'is_valid, created_at, started_at, ended_at, error, inputs, '
+        'status, workflow_name',
+        [
+            (False, m_cls, now, now, 'error', {}, Execution.STARTED, 'wf_name'),
+            (False, now, m_cls, now, 'error', {}, Execution.STARTED, 'wf_name'),
+            (False, now, now, m_cls, 'error', {}, Execution.STARTED, 'wf_name'),
+            (False, now, now, now, m_cls, {}, Execution.STARTED, 'wf_name'),
+            (False, now, now, now, 'error', m_cls, Execution.STARTED, 'wf_name'),
+            (False, now, now, now, 'error', {}, m_cls, 'wf_name'),
+            (False, now, now, now, 'error', {}, Execution.STARTED, m_cls),
+
+            (True, now, now, now, 'error', {}, Execution.STARTED, 'wf_name'),
+            (True, now, None, now, 'error', {}, Execution.STARTED, 'wf_name'),
+            (True, now, now, None, 'error', {}, Execution.STARTED, 'wf_name'),
+            (True, now, now, now, None, {}, Execution.STARTED, 'wf_name'),
+        ]
+    )
+    def test_execution_model_creation(self, service_storage, is_valid, created_at, started_at,
+                                      ended_at, error, inputs, status, workflow_name):
+        execution = _test_model(
+            is_valid=is_valid,
+            storage=service_storage,
+            model_cls=Execution,
+            model_kwargs=dict(
+                service=service_storage.service.list()[0],
+                created_at=created_at,
+                started_at=started_at,
+                ended_at=ended_at,
+                error=error,
+                inputs=inputs,
+                status=status,
+                workflow_name=workflow_name,
+            ))
+        if is_valid:
+            assert execution.service == service_storage.service.list()[0]
+            assert execution.service_template == service_storage.service_template.list()[0]
+
+    def test_execution_status_transition(self):
+        def create_execution(status):
+            execution = Execution(
+                id='e_id',
+                workflow_name='w_name',
+                status=status,
+                inputs={},
+                created_at=now,
+            )
+            return execution
+
+        valid_transitions = {
+            Execution.PENDING: [Execution.STARTED,
+                                Execution.CANCELLED,
+                                Execution.PENDING],
+            Execution.STARTED: [Execution.FAILED,
+                                Execution.SUCCEEDED,
+                                Execution.CANCELLED,
+                                Execution.CANCELLING,
+                                Execution.STARTED],
+            Execution.CANCELLING: [Execution.FAILED,
+                                   Execution.SUCCEEDED,
+                                   Execution.CANCELLED,
+                                   Execution.CANCELLING],
+            Execution.FAILED: [Execution.FAILED],
+            Execution.SUCCEEDED: [Execution.SUCCEEDED],
+            Execution.CANCELLED: [Execution.CANCELLED, Execution.PENDING]
+        }
+
+        invalid_transitions = {
+            Execution.PENDING: [Execution.FAILED,
+                                Execution.SUCCEEDED,
+                                Execution.CANCELLING],
+            Execution.STARTED: [Execution.PENDING],
+            Execution.CANCELLING: [Execution.PENDING,
+                                   Execution.STARTED],
+            Execution.FAILED: [Execution.STARTED,
+                               Execution.SUCCEEDED,
+                               Execution.CANCELLED,
+                               Execution.CANCELLING],
+            Execution.SUCCEEDED: [Execution.PENDING,
+                                  Execution.STARTED,
+                                  Execution.FAILED,
+                                  Execution.CANCELLED,
+                                  Execution.CANCELLING],
+            Execution.CANCELLED: [Execution.STARTED,
+                                  Execution.FAILED,
+                                  Execution.SUCCEEDED,
+                                  Execution.CANCELLING],
+        }
+
+        for current_status, valid_transitioned_statues in valid_transitions.items():
+            for transitioned_status in valid_transitioned_statues:
+                execution = create_execution(current_status)
+                execution.status = transitioned_status
+
+        for current_status, invalid_transitioned_statues in invalid_transitions.items():
+            for transitioned_status in invalid_transitioned_statues:
+                execution = create_execution(current_status)
+                with pytest.raises(ValueError):
+                    execution.status = transitioned_status
+
+
+class TestServiceUpdate(object):
+    @pytest.mark.parametrize(
+        'is_valid, created_at, service_plan, service_update_nodes, '
+        'service_update_service, service_update_node_templates, '
+        'modified_entity_ids, state',
+        [
+            (False, m_cls, {}, {}, {}, [], {}, 'state'),
+            (False, now, m_cls, {}, {}, [], {}, 'state'),
+            (False, now, {}, m_cls, {}, [], {}, 'state'),
+            (False, now, {}, {}, m_cls, [], {}, 'state'),
+            (False, now, {}, {}, {}, m_cls, {}, 'state'),
+            (False, now, {}, {}, {}, [], m_cls, 'state'),
+            (False, now, {}, {}, {}, [], {}, m_cls),
+
+            (True, now, {}, {}, {}, [], {}, 'state'),
+            (True, now, {}, None, {}, [], {}, 'state'),
+            (True, now, {}, {}, None, [], {}, 'state'),
+            (True, now, {}, {}, {}, None, {}, 'state'),
+            (True, now, {}, {}, {}, [], None, 'state'),
+            (True, now, {}, {}, {}, [], {}, None),
+        ]
+    )
+    def test_service_update_model_creation(self, service_storage, is_valid, created_at,
+                                           service_plan, service_update_nodes,
+                                           service_update_service, service_update_node_templates,
+                                           modified_entity_ids, state):
+        service_update = _test_model(
+            is_valid=is_valid,
+            storage=service_storage,
+            model_cls=ServiceUpdate,
+            model_kwargs=dict(
+                service=service_storage.service.list()[0],
+                created_at=created_at,
+                service_plan=service_plan,
+                service_update_nodes=service_update_nodes,
+                service_update_service=service_update_service,
+                service_update_node_templates=service_update_node_templates,
+                modified_entity_ids=modified_entity_ids,
+                state=state
+            ))
+        if is_valid:
+            assert service_update.service == \
+                   service_storage.service.list()[0]
+
+
+class TestServiceUpdateStep(object):
+
+    @pytest.mark.parametrize(
+        'is_valid, action, entity_id, entity_type',
+        [
+            (False, m_cls, 'id', ServiceUpdateStep.ENTITY_TYPES.NODE),
+            (False, ServiceUpdateStep.ACTION_TYPES.ADD, m_cls,
+             ServiceUpdateStep.ENTITY_TYPES.NODE),
+            (False, ServiceUpdateStep.ACTION_TYPES.ADD, 'id', m_cls),
+
+            (True, ServiceUpdateStep.ACTION_TYPES.ADD, 'id',
+             ServiceUpdateStep.ENTITY_TYPES.NODE)
+        ]
+    )
+    def test_service_update_step_model_creation(self, service_update_storage, is_valid, action,
+                                                entity_id, entity_type):
+        service_update_step = _test_model(
+            is_valid=is_valid,
+            storage=service_update_storage,
+            model_cls=ServiceUpdateStep,
+            model_kwargs=dict(
+                service_update=
+                service_update_storage.service_update.list()[0],
+                action=action,
+                entity_id=entity_id,
+                entity_type=entity_type
+            ))
+        if is_valid:
+            assert service_update_step.service_update == \
+                   service_update_storage.service_update.list()[0]
+
+    def test_service_update_step_order(self):
+        add_node = ServiceUpdateStep(
+            id='add_step',
+            action='add',
+            entity_type='node',
+            entity_id='node_id')
+
+        modify_node = ServiceUpdateStep(
+            id='modify_step',
+            action='modify',
+            entity_type='node',
+            entity_id='node_id')
+
+        remove_node = ServiceUpdateStep(
+            id='remove_step',
+            action='remove',
+            entity_type='node',
+            entity_id='node_id')
+
+        for step in (add_node, modify_node, remove_node):
+            assert hash((step.id, step.entity_id)) == hash(step)
+
+        assert remove_node < modify_node < add_node
+        assert not remove_node > modify_node > add_node
+
+        add_rel = ServiceUpdateStep(
+            id='add_step',
+            action='add',
+            entity_type='relationship',
+            entity_id='relationship_id')
+
+        remove_rel = ServiceUpdateStep(
+            id='remove_step',
+            action='remove',
+            entity_type='relationship',
+            entity_id='relationship_id')
+
+        assert remove_rel < remove_node < add_node < add_rel
+        assert not add_node < None
+
+
+class TestServiceModification(object):
+    @pytest.mark.parametrize(
+        'is_valid, context, created_at, ended_at, modified_node_templates, nodes, status',
+        [
+            (False, m_cls, now, now, {}, {}, ServiceModification.STARTED),
+            (False, {}, m_cls, now, {}, {}, ServiceModification.STARTED),
+            (False, {}, now, m_cls, {}, {}, ServiceModification.STARTED),
+            (False, {}, now, now, m_cls, {}, ServiceModification.STARTED),
+            (False, {}, now, now, {}, m_cls, ServiceModification.STARTED),
+            (False, {}, now, now, {}, {}, m_cls),
+
+            (True, {}, now, now, {}, {}, ServiceModification.STARTED),
+            (True, {}, now, None, {}, {}, ServiceModification.STARTED),
+            (True, {}, now, now, None, {}, ServiceModification.STARTED),
+            (True, {}, now, now, {}, None, ServiceModification.STARTED),
+        ]
+    )
+    def test_service_modification_model_creation(self, service_storage, is_valid, context,
+                                                 created_at, ended_at, modified_node_templates,
+                                                 nodes, status):
+        service_modification = _test_model(
+            is_valid=is_valid,
+            storage=service_storage,
+            model_cls=ServiceModification,
+            model_kwargs=dict(
+                service=service_storage.service.list()[0],
+                context=context,
+                created_at=created_at,
+                ended_at=ended_at,
+                modified_node_templates=modified_node_templates,
+                nodes=nodes,
+                status=status,
+            ))
+        if is_valid:
+            assert service_modification.service == \
+                   service_storage.service.list()[0]
+
+
+class TestNodeTemplate(object):
+    @pytest.mark.parametrize(
+        'is_valid, name, properties',
+        [
+            (False, m_cls, {}),
+            (False, 'name', m_cls),
+
+            (True, 'name', {}),
+        ]
+    )
+    def test_node_template_model_creation(self, service_storage, is_valid, name, properties):
+        node_template = _test_model(
+            is_valid=is_valid,
+            storage=service_storage,
+            model_cls=NodeTemplate,
+            model_kwargs=dict(
+                name=name,
+                type=service_storage.type.list()[0],
+                properties=properties,
+                service_template=service_storage.service_template.list()[0]
+            ))
+        if is_valid:
+            assert node_template.service_template == \
+                   service_storage.service_template.list()[0]
+
+
+class TestNode(object):
+    @pytest.mark.parametrize(
+        'is_valid, name, state, version',
+        [
+            (False, m_cls, 'state', 1),
+            (False, 'name', 'state', 1),
+            (False, 'name', m_cls, 1),
+            (False, m_cls, 'state', m_cls),
+
+            (True, 'name', 'initial', 1),
+            (True, None, 'initial', 1),
+            (True, 'name', 'initial', 1),
+            (True, 'name', 'initial', None),
+        ]
+    )
+    def test_node_model_creation(self, node_template_storage, is_valid, name, state, version):
+        node = _test_model(
+            is_valid=is_valid,
+            storage=node_template_storage,
+            model_cls=Node,
+            model_kwargs=dict(
+                node_template=node_template_storage.node_template.list()[0],
+                type=node_template_storage.type.list()[0],
+                name=name,
+                state=state,
+                version=version,
+                service=node_template_storage.service.list()[0]
+            ))
+        if is_valid:
+            assert node.node_template == node_template_storage.node_template.list()[0]
+            assert node.service == \
+                   node_template_storage.service.list()[0]
+
+
+class TestNodeHostAddress(object):
+
+    host_address = '1.1.1.1'
+
+    def test_host_address_on_none_hosted_node(self, service_storage):
+        node_template = self._node_template(service_storage, host_address='not considered')
+        node = self._node(service_storage,
+                          node_template,
+                          is_host=False,
+                          host_address='not considered')
+        assert node.host_address is None
+
+    def test_property_host_address_on_host_node(self, service_storage):
+        node_template = self._node_template(service_storage, host_address=self.host_address)
+        node = self._node(service_storage, node_template, is_host=True, host_address=None)
+        assert node.host_address == self.host_address
+
+    def test_runtime_property_host_address_on_host_node(self, service_storage):
+        node_template = self._node_template(service_storage, host_address='not considered')
+        node = self._node(service_storage, node_template, is_host=True,
+                          host_address=self.host_address)
+        assert node.host_address == self.host_address
+
+    def test_no_host_address_configured_on_host_node(self, service_storage):
+        node_template = self._node_template(service_storage, host_address=None)
+        node = self._node(service_storage, node_template, is_host=True, host_address=None)
+        assert node.host_address is None
+
+    def test_runtime_property_on_hosted_node(self, service_storage):
+        host_node_template = self._node_template(service_storage, host_address=None)
+        host_node = self._node(service_storage,
+                               host_node_template,
+                               is_host=True,
+                               host_address=self.host_address)
+        node_template = self._node_template(service_storage, host_address=None)
+        node = self._node(service_storage,
+                          node_template,
+                          is_host=False,
+                          host_address=None,
+                          host_fk=host_node.id)
+        assert node.host_address == self.host_address
+
+    def _node_template(self, storage, host_address):
+        kwargs = dict(
+            name='node_template',
+            type=storage.type.list()[0],
+            service_template=storage.service_template.list()[0]
+        )
+        if host_address:
+            kwargs['properties'] = {'host_address': Property.wrap('host_address', host_address)}
+        node = NodeTemplate(**kwargs)
+        storage.node_template.put(node)
+        return node
+
+    def _node(self, storage, node_template, is_host, host_address, host_fk=None):
+        kwargs = dict(
+            name='node',
+            node_template=node_template,
+            type=storage.type.list()[0],
+            state='initial',
+            service=storage.service.list()[0]
+        )
+        if is_host and (host_address is None):
+            host_address = node_template.properties.get('host_address')
+            if host_address is not None:
+                host_address = host_address.value
+        if host_address:
+            kwargs.setdefault('attributes', {})['ip'] = Attribute.wrap('ip', host_address)
+        if is_host:
+            kwargs['host_fk'] = 1
+        elif host_fk:
+            kwargs['host_fk'] = host_fk
+        node = Node(**kwargs)
+        storage.node.put(node)
+        return node
+
+
+class TestRelationship(object):
+    @pytest.mark.parametrize(
+        'is_valid, source_position, target_position',
+        [
+            (False, m_cls, 0),
+            (False, 0, m_cls),
+
+            (True, 0, 0),
+            (True, None, 0),
+            (True, 0, None),
+        ]
+    )
+    def test_relationship_model_creation(self, nodes_storage, is_valid, source_position,
+                                         target_position):
+        nodes = nodes_storage.node
+        source_node = nodes.get_by_name(mock.models.DEPENDENT_NODE_NAME)
+        target_node = nodes.get_by_name(mock.models.DEPENDENCY_NODE_NAME)
+        _test_model(is_valid=is_valid,
+                    storage=nodes_storage,
+                    model_cls=Relationship,
+                    model_kwargs=dict(
+                        source_node=source_node,
+                        target_node=target_node,
+                        source_position=source_position,
+                        target_position=target_position
+                    ))
+
+
+class TestPlugin(object):
+    @pytest.mark.parametrize(
+        'is_valid, archive_name, distribution, distribution_release, '
+        'distribution_version, package_name, package_source, '
+        'package_version, supported_platform, supported_py_versions, uploaded_at, wheels',
+        [
+            (False, m_cls, 'dis_name', 'dis_rel', 'dis_ver', 'pak_name', 'pak_src', 'pak_ver',
+             'sup_plat', [], now, []),
+            (False, 'arc_name', m_cls, 'dis_rel', 'dis_ver', 'pak_name', 'pak_src', 'pak_ver',
+             'sup_plat', [], now, []),
+            (False, 'arc_name', 'dis_name', m_cls, 'dis_ver', 'pak_name', 'pak_src', 'pak_ver',
+             'sup_plat', [], now, []),
+            (False, 'arc_name', 'dis_name', 'dis_rel', m_cls, 'pak_name', 'pak_src', 'pak_ver',
+             'sup_plat', [], now, []),
+            (False, 'arc_name', 'dis_name', 'dis_rel', 'dis_ver', m_cls, 'pak_src', 'pak_ver',
+             'sup_plat', [], now, []),
+            (False, 'arc_name', 'dis_name', 'dis_rel', 'dis_ver', 'pak_name', m_cls, 'pak_ver',
+             'sup_plat', [], now, []),
+            (False, 'arc_name', 'dis_name', 'dis_rel', 'dis_ver', 'pak_name', 'pak_src', m_cls,
+             'sup_plat', [], now, []),
+            (False, 'arc_name', 'dis_name', 'dis_rel', 'dis_ver', 'pak_name', 'pak_src',
+             'pak_ver', m_cls, [], now, []),
+            (False, 'arc_name', 'dis_name', 'dis_rel', 'dis_ver', 'pak_name', 'pak_src',
+             'pak_ver', 'sup_plat', m_cls, now, []),
+            (False, 'arc_name', 'dis_name', 'dis_rel', 'dis_ver', 'pak_name', 'pak_src',
+             'pak_ver', 'sup_plat', [], m_cls, []),
+            (False, 'arc_name', 'dis_name', 'dis_rel', 'dis_ver', 'pak_name', 'pak_src',
+             'pak_ver', 'sup_plat', [], now, m_cls),
+
+            (True, 'arc_name', 'dis_name', 'dis_rel', 'dis_ver', 'pak_name', 'pak_src', 'pak_ver',
+             'sup_plat', [], now, []),
+            (True, 'arc_name', None, 'dis_rel', 'dis_ver', 'pak_name', 'pak_src', 'pak_ver',
+             'sup_plat', [], now, []),
+            (True, 'arc_name', 'dis_name', None, 'dis_ver', 'pak_name', 'pak_src', 'pak_ver',
+             'sup_plat', [], now, []),
+            (True, 'arc_name', 'dis_name', 'dis_rel', None, 'pak_name', 'pak_src', 'pak_ver',
+             'sup_plat', [], now, []),
+            (True, 'arc_name', 'dis_name', 'dis_rel', 'dis_ver', 'pak_name', 'pak_src',
+             'pak_ver', 'sup_plat', [], now, []),
+            (True, 'arc_name', 'dis_name', 'dis_rel', 'dis_ver', 'pak_name', None, 'pak_ver',
+             'sup_plat', [], now, []),
+            (True, 'arc_name', 'dis_name', 'dis_rel', 'dis_ver', 'pak_name', 'pak_src', None,
+             'sup_plat', [], now, []),
+            (True, 'arc_name', 'dis_name', 'dis_rel', 'dis_ver', 'pak_name', 'pak_src',
+             'pak_ver', None, [], now, []),
+            (True, 'arc_name', 'dis_name', 'dis_rel', 'dis_ver', 'pak_name', 'pak_src',
+             'pak_ver', 'sup_plat', None, now, []),
+            (True, 'arc_name', 'dis_name', 'dis_rel', 'dis_ver', 'pak_name', 'pak_src',
+             'pak_ver', 'sup_plat', [], now, []),
+        ]
+    )
+    def test_plugin_model_creation(self, empty_storage, is_valid, archive_name, distribution,
+                                   distribution_release, distribution_version, package_name,
+                                   package_source, package_version, supported_platform,
+                                   supported_py_versions, uploaded_at, wheels):
+        _test_model(is_valid=is_valid,
+                    storage=empty_storage,
+                    model_cls=Plugin,
+                    model_kwargs=dict(
+                        archive_name=archive_name,
+                        distribution=distribution,
+                        distribution_release=distribution_release,
+                        distribution_version=distribution_version,
+                        package_name=package_name,
+                        package_source=package_source,
+                        package_version=package_version,
+                        supported_platform=supported_platform,
+                        supported_py_versions=supported_py_versions,
+                        uploaded_at=uploaded_at,
+                        wheels=wheels,
+                    ))
+
+
+class TestTask(object):
+
+    @pytest.mark.parametrize(
+        'is_valid, status, due_at, started_at, ended_at, max_attempts, attempts_count, '
+        'retry_interval, ignore_failure, name, operation_mapping, arguments, plugin_id',
+        [
+            (False, m_cls, now, now, now, 1, 1, 1, True, 'name', 'map', {}, '1'),
+            (False, Task.STARTED, m_cls, now, now, 1, 1, 1, True, 'name', 'map', {}, '1'),
+            (False, Task.STARTED, now, m_cls, now, 1, 1, 1, True, 'name', 'map', {}, '1'),
+            (False, Task.STARTED, now, now, m_cls, 1, 1, 1, True, 'name', 'map', {}, '1'),
+            (False, Task.STARTED, now, now, now, m_cls, 1, 1, True, 'name', 'map', {}, '1'),
+            (False, Task.STARTED, now, now, now, 1, m_cls, 1, True, 'name', 'map', {}, '1'),
+            (False, Task.STARTED, now, now, now, 1, 1, m_cls, True, 'name', 'map', {}, '1'),
+            (False, Task.STARTED, now, now, now, 1, 1, 1, True, m_cls, 'map', {}, '1'),
+            (False, Task.STARTED, now, now, now, 1, 1, 1, True, 'name', m_cls, {}, '1'),
+            (False, Task.STARTED, now, now, now, 1, 1, 1, True, 'name', 'map', m_cls, '1'),
+            (False, Task.STARTED, now, now, now, 1, 1, 1, True, 'name', 'map', {}, m_cls),
+            (False, Task.STARTED, now, now, now, 1, 1, 1, True, 'name', 'map', None, '1'),
+
+            (True, Task.STARTED, now, now, now, 1, 1, 1, True, 'name', 'map', {}, '1'),
+            (True, Task.STARTED, None, now, now, 1, 1, 1, True, 'name', 'map', {}, '1'),
+            (True, Task.STARTED, now, None, now, 1, 1, 1, True, 'name', 'map', {}, '1'),
+            (True, Task.STARTED, now, now, None, 1, 1, 1, True, 'name', 'map', {}, '1'),
+            (True, Task.STARTED, now, now, now, 1, None, 1, True, 'name', 'map', {}, '1'),
+            (True, Task.STARTED, now, now, now, 1, 1, None, True, 'name', 'map', {}, '1'),
+            (True, Task.STARTED, now, now, now, 1, 1, 1, None, 'name', 'map', {}, '1'),
+            (True, Task.STARTED, now, now, now, 1, 1, 1, True, None, 'map', {}, '1'),
+            (True, Task.STARTED, now, now, now, 1, 1, 1, True, 'name', None, {}, '1'),
+            (True, Task.STARTED, now, now, now, 1, 1, 1, True, 'name', 'map', {}, None),
+        ]
+    )
+    def test_task_model_creation(self, execution_storage, is_valid, status, due_at, started_at,
+                                 ended_at, max_attempts, attempts_count, retry_interval,
+                                 ignore_failure, name, operation_mapping, arguments, plugin_id):
+        task = _test_model(
+            is_valid=is_valid,
+            storage=execution_storage,
+            model_cls=Task,
+            model_kwargs=dict(
+                status=status,
+                execution=execution_storage.execution.list()[0],
+                due_at=due_at,
+                started_at=started_at,
+                ended_at=ended_at,
+                max_attempts=max_attempts,
+                attempts_count=attempts_count,
+                retry_interval=retry_interval,
+                ignore_failure=ignore_failure,
+                name=name,
+                function=operation_mapping,
+                arguments=arguments,
+                plugin_fk=plugin_id,
+            ))
+        if is_valid:
+            assert task.execution == execution_storage.execution.list()[0]
+            if task.plugin:
+                assert task.plugin == execution_storage.plugin.list()[0]
+
+    def test_task_max_attempts_validation(self):
+        def create_task(max_attempts):
+            Task(execution_fk='eid',
+                 name='name',
+                 function='',
+                 arguments={},
+                 max_attempts=max_attempts)
+        create_task(max_attempts=1)
+        create_task(max_attempts=2)
+        create_task(max_attempts=Task.INFINITE_RETRIES)
+        with pytest.raises(ValueError):
+            create_task(max_attempts=0)
+        with pytest.raises(ValueError):
+            create_task(max_attempts=-2)
+
+
+class TestType(object):
+    def test_type_hierarchy(self):
+        super_type = Type(variant='variant', name='super')
+        sub_type = Type(variant='variant', parent=super_type, name='sub')
+        additional_type = Type(variant='variant', name='non_related')
+
+        assert super_type.hierarchy == [super_type]
+        assert sub_type.hierarchy == [sub_type, super_type]
+        assert additional_type.hierarchy == [additional_type]
+
+        super_type.parent = additional_type
+
+        assert super_type.hierarchy == [super_type, additional_type]
+        assert sub_type.hierarchy == [sub_type, super_type, additional_type]
+
+
+class TestParameter(object):
+
+    MODELS_DERIVED_FROM_PARAMETER = (Input, Output, Property, Attribute, Configuration, Argument)
+
+    @pytest.mark.parametrize(
+        'is_valid, name, type_name, description',
+        [
+            (False, 'name', 'int', []),
+            (False, 'name', [], 'desc'),
+            (False, [], 'type_name', 'desc'),
+            (True, 'name', 'type_name', 'desc'),
+        ]
+    )
+    def test_derived_from_parameter_model_creation(self, empty_storage, is_valid, name, type_name,
+                                                   description):
+
+        for model_cls in self.MODELS_DERIVED_FROM_PARAMETER:
+            _test_model(is_valid=is_valid,
+                        storage=empty_storage,
+                        model_cls=model_cls,
+                        model_kwargs=dict(
+                            name=name,
+                            type_name=type_name,
+                            description=description,
+                            _value={})
+                       )
+
+    def test_as_argument(self):
+
+        for model_cls in self.MODELS_DERIVED_FROM_PARAMETER:
+            model = model_cls(name='name',
+                              type_name='type_name',
+                              description='description',
+                              _value={})
+            argument = model.as_argument()
+            assert isinstance(argument, Argument)