You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@ariatosca.apache.org by mx...@apache.org on 2017/01/30 16:32:54 UTC

incubator-ariatosca git commit: ARIA-66-Convert-custom-parser-fields-into-sqla-based-fields [Forced Update!]

Repository: incubator-ariatosca
Updated Branches:
  refs/heads/ARIA-66-Convert-custom-parser-fields-into-sqla-based-fields 49f21eaf8 -> 2165d7582 (forced update)


ARIA-66-Convert-custom-parser-fields-into-sqla-based-fields


Project: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/commit/2165d758
Tree: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/tree/2165d758
Diff: http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/diff/2165d758

Branch: refs/heads/ARIA-66-Convert-custom-parser-fields-into-sqla-based-fields
Commit: 2165d75821d6353243a3f4cdb520d6a7b7183aab
Parents: 4447829
Author: mxmrlv <mx...@gmail.com>
Authored: Sun Jan 15 19:07:29 2017 +0200
Committer: mxmrlv <mx...@gmail.com>
Committed: Mon Jan 30 18:32:39 2017 +0200

----------------------------------------------------------------------
 aria/storage/type.py                | 156 ++++++++++++++++-
 tests/storage/__init__.py           |  30 +++-
 tests/storage/test_model_storage.py | 195 +---------------------
 tests/storage/test_structures.py    | 277 +++++++++++++++++++++++++++++++
 4 files changed, 455 insertions(+), 203 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/2165d758/aria/storage/type.py
----------------------------------------------------------------------
diff --git a/aria/storage/type.py b/aria/storage/type.py
index dd2a5ce..8fb02c6 100644
--- a/aria/storage/type.py
+++ b/aria/storage/type.py
@@ -12,17 +12,18 @@
 # 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 json
+from collections import namedtuple
 
 from sqlalchemy import (
     TypeDecorator,
     VARCHAR,
     event
 )
-
 from sqlalchemy.ext import mutable
 
-from . import exceptions
+from aria.storage import exceptions
 
 
 class _MutableType(TypeDecorator):
@@ -61,10 +62,60 @@ class List(_MutableType):
         return list
 
 
+class _StrictDictMixin(object):
+
+    @classmethod
+    def coerce(cls, key, value):
+        "Convert plain dictionaries to MutableDict."
+        try:
+            if not isinstance(value, cls):
+                if isinstance(value, dict):
+                    for k, v in value.items():
+                        cls._assert_strict_key(k)
+                        cls._assert_strict_value(v)
+                    return cls(value)
+                return mutable.MutableDict.coerce(key, value)
+            else:
+                return value
+        except ValueError as e:
+            raise exceptions.StorageError('SQL Storage error: {0}'.format(str(e)))
+
+    def __setitem__(self, key, value):
+        self._assert_strict_key(key)
+        self._assert_strict_value(value)
+        super(_StrictDictMixin, self).__setitem__(key, value)
+
+    def setdefault(self, key, value):
+        self._assert_strict_key(key)
+        self._assert_strict_value(value)
+        super(_StrictDictMixin, self).setdefault(key, value)
+
+    def update(self, *args, **kwargs):
+        for k, v in kwargs.items():
+            self._assert_strict_key(k)
+            self._assert_strict_value(v)
+        super(_StrictDictMixin, self).update(*args, **kwargs)
+
+    @classmethod
+    def _assert_strict_key(cls, key):
+        if cls._key_cls is not None and not isinstance(key, cls._key_cls):
+            raise exceptions.StorageError("Key type was set strictly to {0}, but was {1}".format(
+                cls._key_cls, type(key)
+            ))
+
+    @classmethod
+    def _assert_strict_value(cls, value):
+        if cls._value_cls is not None and not isinstance(value, cls._value_cls):
+            raise exceptions.StorageError("Value type was set strictly to {0}, but was {1}".format(
+                cls._value_cls, type(value)
+            ))
+
+
 class _MutableDict(mutable.MutableDict):
     """
     Enables tracking for dict values.
     """
+
     @classmethod
     def coerce(cls, key, value):
         "Convert plain dictionaries to MutableDict."
@@ -74,16 +125,115 @@ class _MutableDict(mutable.MutableDict):
             raise exceptions.StorageError('SQL Storage error: {0}'.format(str(e)))
 
 
+class _StrictListMixin(object):
+    ANY_TYPE = 'any_type'
+
+    _item_cls = ANY_TYPE
+
+    @classmethod
+    def coerce(cls, key, value):
+        "Convert plain dictionaries to MutableDict."
+        try:
+            if not isinstance(value, cls):
+                if isinstance(value, list):
+                    for item in value:
+                        cls._assert_item(item)
+                    return cls(value)
+                return mutable.MutableList.coerce(key, value)
+            else:
+                return value
+        except ValueError as e:
+            raise exceptions.StorageError('SQL Storage error: {0}'.format(str(e)))
+
+    def __setitem__(self, index, value):
+        """Detect list set events and emit change events."""
+        self._assert_item(value)
+        super(_StrictListMixin, self).__setitem__(index, value)
+
+    def append(self, item):
+        self._assert_item(item)
+        super(_StrictListMixin, self).append(item)
+
+    def extend(self, item):
+        self._assert_item(item)
+        super(_StrictListMixin, self).extend(item)
+
+    def insert(self, index, item):
+        self._assert_item(item)
+        super(_StrictListMixin, self).insert(index, item)
+
+    @classmethod
+    def _assert_item(cls, item):
+        if cls._item_cls is not None and not isinstance(item, cls._item_cls):
+            raise exceptions.StorageError("Key type was set strictly to {0}, but was {1}".format(
+                cls._item_cls, type(item)
+            ))
+
+
 class _MutableList(mutable.MutableList):
 
     @classmethod
     def coerce(cls, key, value):
         "Convert plain dictionaries to MutableDict."
+
         try:
-            return mutable.MutableList.coerce(key, value)
+            if not isinstance(value, cls):
+                if isinstance(value, list):
+                    return cls(value)
+
+                return mutable.Mutable.coerce(key, value)
+            else:
+                return value
+
         except ValueError as e:
             raise exceptions.StorageError('SQL Storage error: {0}'.format(str(e)))
 
+StrictDictID = namedtuple('strict_dict_id', 'key_cls, value_cls')
+
+
+class _StrictDict(object):
+    _strict_map = {}
+
+    def __call__(self, key_cls=None, value_cls=None, *args, **kwargs):
+        strict_dict_map_key = StrictDictID(key_cls=key_cls, value_cls=value_cls)
+        if strict_dict_map_key not in self._strict_map:
+            strict_dict_cls = type(
+                'StrictDict_{0}_{1}'.format(key_cls.__name__, value_cls.__name__),
+                (Dict, ),
+                {}
+            )
+            type(
+                'StrictMutableDict_{0}_{1}'.format(key_cls.__name__, value_cls.__name__),
+                (_StrictDictMixin, _MutableDict),
+                {'_key_cls': key_cls, '_value_cls': value_cls}
+            ).associate_with(strict_dict_cls)
+            self._strict_map[strict_dict_map_key] = strict_dict_cls
+
+        return self._strict_map[strict_dict_map_key]
+
+StrictDict = _StrictDict()
+
+
+class _StrictList(object):
+    _strict_map = {}
+
+    def __call__(self, item_cls=None):
+        if item_cls not in self._strict_map:
+            strict_list_cls = type(
+                'StrictList_{0}'.format(item_cls.__name__),
+                (List, ),
+                {}
+            )
+            type(
+                'StrictMutableList_{0}'.format(item_cls.__name__),
+                (_StrictListMixin, _MutableList),
+                {'_item_cls': item_cls}
+            ).associate_with(strict_list_cls)
+            self._strict_map[item_cls] = strict_list_cls
+
+        return self._strict_map[item_cls]
+
+StrictList = _StrictList()
 
 def _mutable_association_listener(mapper, cls):
     for prop in mapper.column_attrs:

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/2165d758/tests/storage/__init__.py
----------------------------------------------------------------------
diff --git a/tests/storage/__init__.py b/tests/storage/__init__.py
index 9101fd0..3b3715e 100644
--- a/tests/storage/__init__.py
+++ b/tests/storage/__init__.py
@@ -17,12 +17,30 @@ import platform
 from tempfile import mkdtemp
 from shutil import rmtree
 
-from aria.storage import model
 from sqlalchemy import (
     create_engine,
-    orm)
-from sqlalchemy.orm import scoped_session
-from sqlalchemy.pool import StaticPool
+    orm,
+    Column,
+    Text,
+    Integer,
+    pool
+)
+
+
+from aria.storage import (
+    model,
+    structure,
+    type as aria_type,
+)
+
+
+class MockModel(model.DeclarativeBase, structure.ModelMixin): #pylint: disable=abstract-method
+    __tablename__ = 'mock_models'
+    model_dict = Column(aria_type.Dict)
+    model_list = Column(aria_type.List)
+    value = Column(Integer)
+    name = Column(Text)
+
 
 
 class TestFileSystem(object):
@@ -53,11 +71,11 @@ def get_sqlite_api_kwargs(base_dir=None, filename='db.sqlite'):
     else:
         uri = 'sqlite:///:memory:'
         engine_kwargs = dict(connect_args={'check_same_thread': False},
-                             poolclass=StaticPool)
+                             poolclass=pool.StaticPool)
 
     engine = create_engine(uri, **engine_kwargs)
     session_factory = orm.sessionmaker(bind=engine)
-    session = scoped_session(session_factory=session_factory) if base_dir else session_factory()
+    session = orm.scoped_session(session_factory=session_factory) if base_dir else session_factory()
 
     model.DeclarativeBase.metadata.create_all(bind=engine)
     return dict(engine=engine, session=session)

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/2165d758/tests/storage/test_model_storage.py
----------------------------------------------------------------------
diff --git a/tests/storage/test_model_storage.py b/tests/storage/test_model_storage.py
index 0e8d1a0..d1596e3 100644
--- a/tests/storage/test_model_storage.py
+++ b/tests/storage/test_model_storage.py
@@ -15,31 +15,16 @@
 
 import pytest
 
-from sqlalchemy import Column, Text, Integer
-
 from aria.storage import (
     ModelStorage,
     model,
     exceptions,
     sql_mapi,
-    structure,
-    type as aria_type,
 )
 from aria import application_model_storage
 from ..storage import get_sqlite_api_kwargs, release_sqlite_storage
-from ..mock import (
-    context as mock_context,
-    models,
-    operations
-)
-
 
-class MockModel(model.DeclarativeBase, structure.ModelMixin): #pylint: disable=abstract-method
-    __tablename__ = 'mock_models'
-    model_dict = Column(aria_type.Dict)
-    model_list = Column(aria_type.List)
-    value = Column(Integer)
-    name = Column(Text)
+from . import MockModel
 
 
 @pytest.fixture
@@ -50,11 +35,6 @@ def storage():
     release_sqlite_storage(base_storage)
 
 
-@pytest.fixture
-def context():
-    return mock_context.simple(get_sqlite_api_kwargs())
-
-
 @pytest.fixture(scope='module', autouse=True)
 def module_cleanup():
     model.DeclarativeBase.metadata.remove(MockModel.__table__)  #pylint: disable=no-member
@@ -79,65 +59,6 @@ def test_model_storage(storage):
         storage.mock_model.get(mock_model.id)
 
 
-def test_inner_dict_update(storage):
-    inner_dict = {'inner_value': 1}
-
-    mock_model = MockModel(model_dict={'inner_dict': inner_dict, 'value': 0})
-    storage.mock_model.put(mock_model)
-
-    storage_mm = storage.mock_model.get(mock_model.id)
-    assert storage_mm == mock_model
-
-    storage_mm.model_dict['inner_dict']['inner_value'] = 2
-    storage_mm.model_dict['value'] = -1
-    storage.mock_model.update(storage_mm)
-    storage_mm = storage.mock_model.get(storage_mm.id)
-
-    assert storage_mm.model_dict['inner_dict']['inner_value'] == 2
-    assert storage_mm.model_dict['value'] == -1
-
-
-def test_inner_list_update(storage):
-    mock_model = MockModel(model_list=[0, [1]])
-    storage.mock_model.put(mock_model)
-
-    storage_mm = storage.mock_model.get(mock_model.id)
-    assert storage_mm == mock_model
-
-    storage_mm.model_list[1][0] = 'new_inner_value'
-    storage_mm.model_list[0] = 'new_value'
-    storage.mock_model.update(storage_mm)
-    storage_mm = storage.mock_model.get(storage_mm.id)
-
-    assert storage_mm.model_list[1][0] == 'new_inner_value'
-    assert storage_mm.model_list[0] == 'new_value'
-
-
-def test_model_to_dict(context):
-    deployment = context.deployment
-    deployment_dict = deployment.to_dict()
-
-    expected_keys = [
-        'created_at',
-        'description',
-        'inputs',
-        'groups',
-        'permalink',
-        'policy_triggers',
-        'policy_types',
-        'outputs',
-        'scaling_groups',
-        'updated_at',
-        'workflows',
-        'blueprint_name',
-    ]
-
-    for expected_key in expected_keys:
-        assert expected_key in deployment_dict
-
-    assert 'blueprint_fk' not in deployment_dict
-
-
 def test_application_storage_factory():
     storage = application_model_storage(sql_mapi.SQLAlchemyModelAPI,
                                         api_kwargs=get_sqlite_api_kwargs())
@@ -152,117 +73,3 @@ def test_application_storage_factory():
     assert storage.execution
 
     release_sqlite_storage(storage)
-
-
-def test_relationship_model_ordering(context):
-    deployment = context.model.deployment.get_by_name(models.DEPLOYMENT_NAME)
-    source_node = context.model.node.get_by_name(models.DEPENDENT_NODE_NAME)
-    source_node_instance = context.model.node_instance.get_by_name(
-        models.DEPENDENT_NODE_INSTANCE_NAME)
-    target_node = context.model.node.get_by_name(models.DEPENDENCY_NODE_NAME)
-    target_node_instance = context.model.node_instance.get_by_name(
-        models.DEPENDENCY_NODE_INSTANCE_NAME)
-    new_node = model.Node(
-        name='new_node',
-        type='test_node_type',
-        type_hierarchy=[],
-        number_of_instances=1,
-        planned_number_of_instances=1,
-        deploy_number_of_instances=1,
-        properties={},
-        operations=dict((key, {}) for key in operations.NODE_OPERATIONS),
-        min_number_of_instances=1,
-        max_number_of_instances=1,
-        deployment=deployment
-    )
-    source_to_new_relationship = model.Relationship(
-        source_node=source_node,
-        target_node=new_node,
-        source_interfaces={},
-        source_operations=dict((key, {}) for key in operations.RELATIONSHIP_OPERATIONS),
-        target_interfaces={},
-        target_operations=dict((key, {}) for key in operations.RELATIONSHIP_OPERATIONS),
-        type='rel_type',
-        type_hierarchy=[],
-        properties={},
-    )
-    new_node_instance = model.NodeInstance(
-        name='new_node_instance',
-        runtime_properties={},
-        version=None,
-        node=new_node,
-        state='',
-        scaling_groups=[]
-    )
-    source_to_new_relationship_instance = model.RelationshipInstance(
-        relationship=source_to_new_relationship,
-        source_node_instance=source_node_instance,
-        target_node_instance=new_node_instance,
-    )
-
-    new_to_target_relationship = model.Relationship(
-        source_node=new_node,
-        target_node=target_node,
-        source_interfaces={},
-        source_operations=dict((key, {}) for key in operations.RELATIONSHIP_OPERATIONS),
-        target_interfaces={},
-        target_operations=dict((key, {}) for key in operations.RELATIONSHIP_OPERATIONS),
-        type='rel_type',
-        type_hierarchy=[],
-        properties={},
-    )
-    new_to_target_relationship_instance = model.RelationshipInstance(
-        relationship=new_to_target_relationship,
-        source_node_instance=new_node_instance,
-        target_node_instance=target_node_instance,
-    )
-
-
-    context.model.node.put(new_node)
-    context.model.node_instance.put(new_node_instance)
-    context.model.relationship.put(source_to_new_relationship)
-    context.model.relationship.put(new_to_target_relationship)
-    context.model.relationship_instance.put(source_to_new_relationship_instance)
-    context.model.relationship_instance.put(new_to_target_relationship_instance)
-
-    def flip_and_assert(node_instance, direction):
-        """
-        Reversed the order of relationships and assert effects took place.
-        :param node_instance: the node instance to operatate on
-        :param direction: the type of relationships to flip (inbound/outbount)
-        :return:
-        """
-        assert direction in ('inbound', 'outbound')
-
-        relationships = getattr(node_instance.node, direction + '_relationships')
-        relationship_instances = getattr(node_instance, direction + '_relationship_instances')
-        assert len(relationships) == 2
-        assert len(relationship_instances) == 2
-
-        first_rel, second_rel = relationships
-        first_rel_instance, second_rel_instance = relationship_instances
-        assert getattr(first_rel, relationships.ordering_attr) == 0
-        assert getattr(second_rel, relationships.ordering_attr) == 1
-        assert getattr(first_rel_instance, relationship_instances.ordering_attr) == 0
-        assert getattr(second_rel_instance, relationship_instances.ordering_attr) == 1
-
-        reversed_relationships = list(reversed(relationships))
-        reversed_relationship_instances = list(reversed(relationship_instances))
-
-        assert relationships != reversed_relationships
-        assert relationship_instances != reversed_relationship_instances
-
-        relationships[:] = reversed_relationships
-        relationship_instances[:] = reversed_relationship_instances
-        context.model.node_instance.update(node_instance)
-
-        assert relationships == reversed_relationships
-        assert relationship_instances == reversed_relationship_instances
-
-        assert getattr(first_rel, relationships.ordering_attr) == 1
-        assert getattr(second_rel, relationships.ordering_attr) == 0
-        assert getattr(first_rel_instance, relationship_instances.ordering_attr) == 1
-        assert getattr(second_rel_instance, relationship_instances.ordering_attr) == 0
-
-    flip_and_assert(source_node_instance, 'outbound')
-    flip_and_assert(target_node_instance, 'inbound')

http://git-wip-us.apache.org/repos/asf/incubator-ariatosca/blob/2165d758/tests/storage/test_structures.py
----------------------------------------------------------------------
diff --git a/tests/storage/test_structures.py b/tests/storage/test_structures.py
new file mode 100644
index 0000000..7e34b17
--- /dev/null
+++ b/tests/storage/test_structures.py
@@ -0,0 +1,277 @@
+# 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.
+
+import pytest
+
+import sqlalchemy
+
+from aria.storage import (
+    ModelStorage,
+    sql_mapi,
+    model,
+    type,
+    exceptions
+)
+
+from ..storage import get_sqlite_api_kwargs, release_sqlite_storage, structure
+from . import MockModel
+from ..mock import (
+    models,
+    operations,
+    context as mock_context
+)
+
+
+@pytest.fixture
+def storage():
+    base_storage = ModelStorage(sql_mapi.SQLAlchemyModelAPI, api_kwargs=get_sqlite_api_kwargs())
+    base_storage.register(MockModel)
+    yield base_storage
+    release_sqlite_storage(base_storage)
+
+
+@pytest.fixture(scope='module', autouse=True)
+def module_cleanup():
+    model.DeclarativeBase.metadata.remove(MockModel.__table__)  #pylint: disable=no-member
+
+
+@pytest.fixture
+def context():
+    return mock_context.simple(get_sqlite_api_kwargs())
+
+
+def test_inner_dict_update(storage):
+    inner_dict = {'inner_value': 1}
+
+    mock_model = MockModel(model_dict={'inner_dict': inner_dict, 'value': 0})
+    storage.mock_model.put(mock_model)
+
+    storage_mm = storage.mock_model.get(mock_model.id)
+    assert storage_mm == mock_model
+
+    storage_mm.model_dict['inner_dict']['inner_value'] = 2
+    storage_mm.model_dict['value'] = -1
+    storage.mock_model.update(storage_mm)
+    storage_mm = storage.mock_model.get(storage_mm.id)
+
+    assert storage_mm.model_dict['inner_dict']['inner_value'] == 2
+    assert storage_mm.model_dict['value'] == -1
+
+
+def test_inner_list_update(storage):
+    mock_model = MockModel(model_list=[0, [1]])
+    storage.mock_model.put(mock_model)
+
+    storage_mm = storage.mock_model.get(mock_model.id)
+    assert storage_mm == mock_model
+
+    storage_mm.model_list[1][0] = 'new_inner_value'
+    storage_mm.model_list[0] = 'new_value'
+    storage.mock_model.update(storage_mm)
+    storage_mm = storage.mock_model.get(storage_mm.id)
+
+    assert storage_mm.model_list[1][0] == 'new_inner_value'
+    assert storage_mm.model_list[0] == 'new_value'
+
+
+def test_model_to_dict(context):
+    deployment = context.deployment
+    deployment_dict = deployment.to_dict()
+
+    expected_keys = [
+        'created_at',
+        'description',
+        'inputs',
+        'groups',
+        'permalink',
+        'policy_triggers',
+        'policy_types',
+        'outputs',
+        'scaling_groups',
+        'updated_at',
+        'workflows',
+        'blueprint_name',
+    ]
+
+    for expected_key in expected_keys:
+        assert expected_key in deployment_dict
+
+    assert 'blueprint_fk' not in deployment_dict
+
+
+def test_relationship_model_ordering(context):
+    deployment = context.model.deployment.get_by_name(models.DEPLOYMENT_NAME)
+    source_node = context.model.node.get_by_name(models.DEPENDENT_NODE_NAME)
+    source_node_instance = context.model.node_instance.get_by_name(
+        models.DEPENDENT_NODE_INSTANCE_NAME)
+    target_node = context.model.node.get_by_name(models.DEPENDENCY_NODE_NAME)
+    target_node_instance = context.model.node_instance.get_by_name(
+        models.DEPENDENCY_NODE_INSTANCE_NAME)
+    new_node = model.Node(
+        name='new_node',
+        type='test_node_type',
+        type_hierarchy=[],
+        number_of_instances=1,
+        planned_number_of_instances=1,
+        deploy_number_of_instances=1,
+        properties={},
+        operations=dict((key, {}) for key in operations.NODE_OPERATIONS),
+        min_number_of_instances=1,
+        max_number_of_instances=1,
+        deployment=deployment
+    )
+    source_to_new_relationship = model.Relationship(
+        source_node=source_node,
+        target_node=new_node,
+        source_interfaces={},
+        source_operations=dict((key, {}) for key in operations.RELATIONSHIP_OPERATIONS),
+        target_interfaces={},
+        target_operations=dict((key, {}) for key in operations.RELATIONSHIP_OPERATIONS),
+        type='rel_type',
+        type_hierarchy=[],
+        properties={},
+    )
+    new_node_instance = model.NodeInstance(
+        name='new_node_instance',
+        runtime_properties={},
+        version=None,
+        node=new_node,
+        state='',
+        scaling_groups=[]
+    )
+    source_to_new_relationship_instance = model.RelationshipInstance(
+        relationship=source_to_new_relationship,
+        source_node_instance=source_node_instance,
+        target_node_instance=new_node_instance,
+    )
+
+    new_to_target_relationship = model.Relationship(
+        source_node=new_node,
+        target_node=target_node,
+        source_interfaces={},
+        source_operations=dict((key, {}) for key in operations.RELATIONSHIP_OPERATIONS),
+        target_interfaces={},
+        target_operations=dict((key, {}) for key in operations.RELATIONSHIP_OPERATIONS),
+        type='rel_type',
+        type_hierarchy=[],
+        properties={},
+    )
+    new_to_target_relationship_instance = model.RelationshipInstance(
+        relationship=new_to_target_relationship,
+        source_node_instance=new_node_instance,
+        target_node_instance=target_node_instance,
+    )
+
+
+    context.model.node.put(new_node)
+    context.model.node_instance.put(new_node_instance)
+    context.model.relationship.put(source_to_new_relationship)
+    context.model.relationship.put(new_to_target_relationship)
+    context.model.relationship_instance.put(source_to_new_relationship_instance)
+    context.model.relationship_instance.put(new_to_target_relationship_instance)
+
+    def flip_and_assert(node_instance, direction):
+        """
+        Reversed the order of relationships and assert effects took place.
+        :param node_instance: the node instance to operatate on
+        :param direction: the type of relationships to flip (inbound/outbount)
+        :return:
+        """
+        assert direction in ('inbound', 'outbound')
+
+        relationships = getattr(node_instance.node, direction + '_relationships')
+        relationship_instances = getattr(node_instance, direction + '_relationship_instances')
+        assert len(relationships) == 2
+        assert len(relationship_instances) == 2
+
+        first_rel, second_rel = relationships
+        first_rel_instance, second_rel_instance = relationship_instances
+        assert getattr(first_rel, relationships.ordering_attr) == 0
+        assert getattr(second_rel, relationships.ordering_attr) == 1
+        assert getattr(first_rel_instance, relationship_instances.ordering_attr) == 0
+        assert getattr(second_rel_instance, relationship_instances.ordering_attr) == 1
+
+        reversed_relationships = list(reversed(relationships))
+        reversed_relationship_instances = list(reversed(relationship_instances))
+
+        assert relationships != reversed_relationships
+        assert relationship_instances != reversed_relationship_instances
+
+        relationships[:] = reversed_relationships
+        relationship_instances[:] = reversed_relationship_instances
+        context.model.node_instance.update(node_instance)
+
+        assert relationships == reversed_relationships
+        assert relationship_instances == reversed_relationship_instances
+
+        assert getattr(first_rel, relationships.ordering_attr) == 1
+        assert getattr(second_rel, relationships.ordering_attr) == 0
+        assert getattr(first_rel_instance, relationship_instances.ordering_attr) == 1
+        assert getattr(second_rel_instance, relationship_instances.ordering_attr) == 0
+
+    flip_and_assert(source_node_instance, 'outbound')
+    flip_and_assert(target_node_instance, 'inbound')
+
+
+class StrictClass(model.DeclarativeBase, structure.ModelMixin):
+    __tablename__ = 'strict_class'
+
+    strict_dict = sqlalchemy.Column(type.StrictDict(basestring, basestring))
+    strict_list = sqlalchemy.Column(type.StrictList(basestring))
+
+
+def test_strict_dict():
+
+    strict_class = StrictClass()
+
+    def assert_strict(sd, after_assignment=False):
+        if after_assignment:
+            with pytest.raises(exceptions.StorageError):
+                sd.strict_dict['key'] = None
+            with pytest.raises(exceptions.StorageError):
+                sd.strict_dict[None] = 'value'
+            with pytest.raises(exceptions.StorageError):
+                sd.strict_dict[None] = None
+
+        with pytest.raises(exceptions.StorageError):
+            sd.strict_dict = {'key': None}
+
+        with pytest.raises(exceptions.StorageError):
+            sd.strict_dict = {None: 'value'}
+
+        with pytest.raises(exceptions.StorageError):
+            sd.strict_dict = {None: None}
+
+    assert_strict(strict_class)
+    strict_class.strict_dict = {'key': 'value'}
+    assert strict_class.strict_dict == {'key': 'value'}
+    assert_strict(strict_class, after_assignment=True)
+
+
+def test_strict_list():
+    strict_class = StrictClass()
+
+    def assert_strict(sd, after_assignment=False):
+        if after_assignment:
+            with pytest.raises(exceptions.StorageError):
+                sd.strict_list[0] = None
+
+        with pytest.raises(exceptions.StorageError):
+            sd.strict_list = [None]
+
+    assert_strict(strict_class)
+    strict_class.strict_list = ['item']
+    assert strict_class.strict_list == ['item']
+    assert_strict(strict_class, after_assignment=True)