"Fossies" - the Fresh Open Source Software Archive

Member "magnum-8.1.0/magnum/tests/unit/objects/test_objects.py" (1 Oct 2019, 17805 Bytes) of package /linux/misc/openstack/magnum-8.1.0.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. See also the last Fossies "Diffs" side-by-side code changes report for "test_objects.py": 9.0.0_vs_9.1.0.

    1 #    Copyright 2015 IBM Corp.
    2 #
    3 #    Licensed under the Apache License, Version 2.0 (the "License"); you may
    4 #    not use this file except in compliance with the License. You may obtain
    5 #    a copy of the License at
    6 #
    7 #         http://www.apache.org/licenses/LICENSE-2.0
    8 #
    9 #    Unless required by applicable law or agreed to in writing, software
   10 #    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
   11 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
   12 #    License for the specific language governing permissions and limitations
   13 #    under the License.
   14 
   15 import datetime
   16 import gettext
   17 
   18 import mock
   19 from oslo_versionedobjects import exception as object_exception
   20 from oslo_versionedobjects import fields
   21 from oslo_versionedobjects import fixture
   22 
   23 from magnum.common import context as magnum_context
   24 from magnum.objects import base
   25 from magnum.tests import base as test_base
   26 
   27 gettext.install('magnum')
   28 
   29 
   30 @base.MagnumObjectRegistry.register
   31 class MyObj(base.MagnumPersistentObject, base.MagnumObject):
   32     VERSION = '1.0'
   33 
   34     fields = {'foo': fields.IntegerField(),
   35               'bar': fields.StringField(),
   36               'missing': fields.StringField(),
   37               }
   38 
   39     def obj_load_attr(self, attrname):
   40         setattr(self, attrname, 'loaded!')
   41 
   42     @base.remotable_classmethod
   43     def query(cls, context):
   44         obj = cls(context)
   45         obj.foo = 1
   46         obj.bar = 'bar'
   47         obj.obj_reset_changes()
   48         return obj
   49 
   50     @base.remotable
   51     def marco(self, context):
   52         return 'polo'
   53 
   54     @base.remotable
   55     def update_test(self, context):
   56         if context.project_id == 'alternate':
   57             self.bar = 'alternate-context'
   58         else:
   59             self.bar = 'updated'
   60 
   61     @base.remotable
   62     def save(self, context):
   63         self.obj_reset_changes()
   64 
   65     @base.remotable
   66     def refresh(self, context):
   67         self.foo = 321
   68         self.bar = 'refreshed'
   69         self.obj_reset_changes()
   70 
   71     @base.remotable
   72     def modify_save_modify(self, context):
   73         self.bar = 'meow'
   74         self.save(context)
   75         self.foo = 42
   76 
   77 
   78 class MyObj2(object):
   79     @classmethod
   80     def obj_name(cls):
   81         return 'MyObj'
   82 
   83     @base.remotable_classmethod
   84     def get(cls, *args, **kwargs):
   85         pass
   86 
   87 
   88 @base.MagnumObjectRegistry.register_if(False)
   89 class TestSubclassedObject(MyObj):
   90     fields = {'new_field': fields.StringField()}
   91 
   92 
   93 class _TestObject(object):
   94     def test_hydration_type_error(self):
   95         primitive = {'magnum_object.name': 'MyObj',
   96                      'magnum_object.namespace': 'magnum',
   97                      'magnum_object.version': '1.0',
   98                      'magnum_object.data': {'foo': 'a'}}
   99         self.assertRaises(ValueError, MyObj.obj_from_primitive, primitive)
  100 
  101     def test_hydration(self):
  102         primitive = {'magnum_object.name': 'MyObj',
  103                      'magnum_object.namespace': 'magnum',
  104                      'magnum_object.version': '1.0',
  105                      'magnum_object.data': {'foo': 1}}
  106         obj = MyObj.obj_from_primitive(primitive)
  107         self.assertEqual(1, obj.foo)
  108 
  109     def test_hydration_bad_ns(self):
  110         primitive = {'magnum_object.name': 'MyObj',
  111                      'magnum_object.namespace': 'foo',
  112                      'magnum_object.version': '1.0',
  113                      'magnum_object.data': {'foo': 1}}
  114         self.assertRaises(object_exception.UnsupportedObjectError,
  115                           MyObj.obj_from_primitive, primitive)
  116 
  117     def test_dehydration(self):
  118         expected = {'magnum_object.name': 'MyObj',
  119                     'magnum_object.namespace': 'magnum',
  120                     'magnum_object.version': '1.0',
  121                     'magnum_object.data': {'foo': 1}}
  122         obj = MyObj(self.context)
  123         obj.foo = 1
  124         obj.obj_reset_changes()
  125         self.assertEqual(expected, obj.obj_to_primitive())
  126 
  127     def test_get_updates(self):
  128         obj = MyObj(self.context)
  129         self.assertEqual({}, obj.obj_get_changes())
  130         obj.foo = 123
  131         self.assertEqual({'foo': 123}, obj.obj_get_changes())
  132         obj.bar = 'test'
  133         self.assertEqual({'foo': 123, 'bar': 'test'}, obj.obj_get_changes())
  134         obj.obj_reset_changes()
  135         self.assertEqual({}, obj.obj_get_changes())
  136 
  137     def test_object_property(self):
  138         obj = MyObj(self.context, foo=1)
  139         self.assertEqual(1, obj.foo)
  140 
  141     def test_object_property_type_error(self):
  142         obj = MyObj(self.context)
  143 
  144         def fail():
  145             obj.foo = 'a'
  146         self.assertRaises(ValueError, fail)
  147 
  148     def test_load(self):
  149         obj = MyObj(self.context)
  150         self.assertEqual('loaded!', obj.bar)
  151 
  152     def test_load_in_base(self):
  153         @base.MagnumObjectRegistry.register_if(False)
  154         class Foo(base.MagnumPersistentObject, base.MagnumObject):
  155             fields = {'foobar': fields.IntegerField()}
  156         obj = Foo(self.context)
  157         # NOTE(danms): Can't use assertRaisesRegexp() because of py26
  158         raised = False
  159         ex = None
  160         try:
  161             obj.foobar
  162         except NotImplementedError as e:
  163             raised = True
  164             ex = e
  165         self.assertTrue(raised)
  166         self.assertIn('foobar', str(ex))
  167 
  168     def test_loaded_in_primitive(self):
  169         obj = MyObj(self.context)
  170         obj.foo = 1
  171         obj.obj_reset_changes()
  172         self.assertEqual('loaded!', obj.bar)
  173         expected = {'magnum_object.name': 'MyObj',
  174                     'magnum_object.namespace': 'magnum',
  175                     'magnum_object.version': '1.0',
  176                     'magnum_object.changes': ['bar'],
  177                     'magnum_object.data': {'foo': 1,
  178                                            'bar': 'loaded!'}}
  179         self.assertEqual(expected, obj.obj_to_primitive())
  180 
  181     def test_changes_in_primitive(self):
  182         obj = MyObj(self.context)
  183         obj.foo = 123
  184         self.assertEqual(set(['foo']), obj.obj_what_changed())
  185         primitive = obj.obj_to_primitive()
  186         self.assertIn('magnum_object.changes', primitive)
  187         obj2 = MyObj.obj_from_primitive(primitive)
  188         self.assertEqual(set(['foo']), obj2.obj_what_changed())
  189         obj2.obj_reset_changes()
  190         self.assertEqual(set(), obj2.obj_what_changed())
  191 
  192     def test_unknown_objtype(self):
  193         self.assertRaises(object_exception.UnsupportedObjectError,
  194                           base.MagnumObject.obj_class_from_name, 'foo', '1.0')
  195 
  196     def test_with_alternate_context(self):
  197         context1 = magnum_context.RequestContext('foo', 'foo')
  198         context2 = magnum_context.RequestContext('bar', project_id='alternate')
  199         obj = MyObj.query(context1)
  200         obj.update_test(context2)
  201         self.assertEqual('alternate-context', obj.bar)
  202 
  203     def test_orphaned_object(self):
  204         obj = MyObj.query(self.context)
  205         obj._context = None
  206         self.assertRaises(object_exception.OrphanedObjectError,
  207                           obj.update_test)
  208 
  209     def test_changed_1(self):
  210         obj = MyObj.query(self.context)
  211         obj.foo = 123
  212         self.assertEqual(set(['foo']), obj.obj_what_changed())
  213         obj.update_test(self.context)
  214         self.assertEqual(set(['foo', 'bar']), obj.obj_what_changed())
  215         self.assertEqual(123, obj.foo)
  216 
  217     def test_changed_2(self):
  218         obj = MyObj.query(self.context)
  219         obj.foo = 123
  220         self.assertEqual(set(['foo']), obj.obj_what_changed())
  221         obj.save(self.context)
  222         self.assertEqual(set([]), obj.obj_what_changed())
  223         self.assertEqual(123, obj.foo)
  224 
  225     def test_changed_3(self):
  226         obj = MyObj.query(self.context)
  227         obj.foo = 123
  228         self.assertEqual(set(['foo']), obj.obj_what_changed())
  229         obj.refresh(self.context)
  230         self.assertEqual(set([]), obj.obj_what_changed())
  231         self.assertEqual(321, obj.foo)
  232         self.assertEqual('refreshed', obj.bar)
  233 
  234     def test_changed_4(self):
  235         obj = MyObj.query(self.context)
  236         obj.bar = 'something'
  237         self.assertEqual(set(['bar']), obj.obj_what_changed())
  238         obj.modify_save_modify(self.context)
  239         self.assertEqual(set(['foo']), obj.obj_what_changed())
  240         self.assertEqual(42, obj.foo)
  241         self.assertEqual('meow', obj.bar)
  242 
  243     def test_static_result(self):
  244         obj = MyObj.query(self.context)
  245         self.assertEqual('bar', obj.bar)
  246         result = obj.marco(self.context)
  247         self.assertEqual('polo', result)
  248 
  249     def test_updates(self):
  250         obj = MyObj.query(self.context)
  251         self.assertEqual(1, obj.foo)
  252         obj.update_test(self.context)
  253         self.assertEqual('updated', obj.bar)
  254 
  255     def test_base_attributes(self):
  256         dt = datetime.datetime(1955, 11, 5)
  257         datatime = fields.DateTimeField()
  258         obj = MyObj(self.context)
  259         obj.created_at = dt
  260         obj.updated_at = dt
  261         expected = {'magnum_object.name': 'MyObj',
  262                     'magnum_object.namespace': 'magnum',
  263                     'magnum_object.version': '1.0',
  264                     'magnum_object.changes':
  265                         ['created_at', 'updated_at'],
  266                     'magnum_object.data':
  267                         {'created_at': datatime.stringify(dt),
  268                          'updated_at': datatime.stringify(dt)}
  269                     }
  270         actual = obj.obj_to_primitive()
  271         # magnum_object.changes is built from a set and order is undefined
  272         self.assertEqual(sorted(expected['magnum_object.changes']),
  273                          sorted(actual['magnum_object.changes']))
  274         del expected['magnum_object.changes'], actual['magnum_object.changes']
  275         self.assertEqual(expected, actual)
  276 
  277     def test_contains(self):
  278         obj = MyObj(self.context)
  279         self.assertNotIn('foo', obj)
  280         obj.foo = 1
  281         self.assertIn('foo', obj)
  282         self.assertNotIn('does_not_exist', obj)
  283 
  284     def test_obj_attr_is_set(self):
  285         obj = MyObj(self.context, foo=1)
  286         self.assertTrue(obj.obj_attr_is_set('foo'))
  287         self.assertFalse(obj.obj_attr_is_set('bar'))
  288         self.assertRaises(AttributeError, obj.obj_attr_is_set, 'bang')
  289 
  290     def test_get(self):
  291         obj = MyObj(self.context, foo=1)
  292         # Foo has value, should not get the default
  293         self.assertEqual(1, getattr(obj, 'foo', 2))
  294         # Foo has value, should return the value without error
  295         self.assertEqual(1, getattr(obj, 'foo'))
  296         # Bar without a default should lazy-load
  297         self.assertEqual('loaded!', getattr(obj, 'bar'))
  298         # Bar now has a default, but loaded value should be returned
  299         self.assertEqual('loaded!', getattr(obj, 'bar', 'not-loaded'))
  300         # Invalid attribute should raise AttributeError
  301         self.assertFalse(hasattr(obj, 'nothing'))
  302 
  303     def test_object_inheritance(self):
  304         base_fields = list(base.MagnumPersistentObject.fields.keys())
  305         myobj_fields = ['foo', 'bar', 'missing'] + base_fields
  306         myobj3_fields = ['new_field']
  307         self.assertTrue(issubclass(TestSubclassedObject, MyObj))
  308         self.assertEqual(len(MyObj.fields), len(myobj_fields))
  309         self.assertEqual(set(MyObj.fields.keys()), set(myobj_fields))
  310         self.assertEqual(len(TestSubclassedObject.fields),
  311                          len(myobj_fields) + len(myobj3_fields))
  312         self.assertEqual(set(TestSubclassedObject.fields.keys()),
  313                          set(myobj_fields) | set(myobj3_fields))
  314 
  315     def test_get_changes(self):
  316         obj = MyObj(self.context)
  317         self.assertEqual({}, obj.obj_get_changes())
  318         obj.foo = 123
  319         self.assertEqual({'foo': 123}, obj.obj_get_changes())
  320         obj.bar = 'test'
  321         self.assertEqual({'foo': 123, 'bar': 'test'}, obj.obj_get_changes())
  322         obj.obj_reset_changes()
  323         self.assertEqual({}, obj.obj_get_changes())
  324 
  325     def test_obj_fields(self):
  326         @base.MagnumObjectRegistry.register_if(False)
  327         class TestObj(base.MagnumPersistentObject, base.MagnumObject):
  328             fields = {'foo': fields.IntegerField()}
  329             obj_extra_fields = ['bar']
  330 
  331             @property
  332             def bar(self):
  333                 return 'this is bar'
  334 
  335         obj = TestObj(self.context)
  336         self.assertEqual(set(['created_at', 'updated_at', 'foo', 'bar']),
  337                          set(obj.obj_fields))
  338 
  339     def test_obj_constructor(self):
  340         obj = MyObj(self.context, foo=123, bar='abc')
  341         self.assertEqual(123, obj.foo)
  342         self.assertEqual('abc', obj.bar)
  343         self.assertEqual(set(['foo', 'bar']), obj.obj_what_changed())
  344 
  345 
  346 class TestObject(test_base.TestCase, _TestObject):
  347     pass
  348 
  349 
  350 # This is a static dictionary that holds all fingerprints of the versioned
  351 # objects registered with the MagnumRegistry. Each fingerprint contains
  352 # the version of the object and an md5 hash of RPC-critical parts of the
  353 # object (fields and remotable methods). If either the version or hash
  354 # change, the static tree needs to be updated.
  355 # For more information on object version testing, read
  356 # https://docs.openstack.org/magnum/latest/contributor/objects.html
  357 object_data = {
  358     'Cluster': '1.18-9f0dfcc3e898eef2b9a09647b612adb6',
  359     'ClusterTemplate': '1.19-3b0b2b3933d0955abf3ab40111744960',
  360     'Certificate': '1.1-1924dc077daa844f0f9076332ef96815',
  361     'MyObj': '1.0-34c4b1aadefd177b13f9a2f894cc23cd',
  362     'X509KeyPair': '1.2-d81950af36c59a71365e33ce539d24f9',
  363     'MagnumService': '1.0-2d397ec59b0046bd5ec35cd3e06efeca',
  364     'Stats': '1.0-73a1cd6e3c0294c932a66547faba216c',
  365     'Quota': '1.0-94e100aebfa88f7d8428e007f2049c18',
  366     'Federation': '1.0-166da281432b083f0e4b851336e12e20'
  367 }
  368 
  369 
  370 class TestObjectVersions(test_base.TestCase):
  371     def test_versions(self):
  372         # Test the versions of current objects with the static tree above.
  373         # This ensures that any incompatible object changes require a version
  374         # bump.
  375         classes = base.MagnumObjectRegistry.obj_classes()
  376         checker = fixture.ObjectVersionChecker(obj_classes=classes)
  377 
  378         expected, actual = checker.test_hashes(object_data)
  379         self.assertEqual(expected, actual,
  380                          "Fields or remotable methods in some objects have "
  381                          "changed. Make sure the versions of the objects has "
  382                          "been bumped, and update the hashes in the static "
  383                          "fingerprints tree (object_data). For more "
  384                          "information, read https://docs.openstack.org/"
  385                          "magnum/latest/contributor/objects.html")
  386 
  387 
  388 class TestObjectSerializer(test_base.TestCase):
  389 
  390     def test_object_serialization(self):
  391         ser = base.MagnumObjectSerializer()
  392         obj = MyObj(self.context)
  393         primitive = ser.serialize_entity(self.context, obj)
  394         self.assertIn('magnum_object.name', primitive)
  395         obj2 = ser.deserialize_entity(self.context, primitive)
  396         self.assertIsInstance(obj2, MyObj)
  397         self.assertEqual(self.context, obj2._context)
  398 
  399     def test_object_serialization_iterables(self):
  400         ser = base.MagnumObjectSerializer()
  401         obj = MyObj(self.context)
  402         for iterable in (list, tuple, set):
  403             thing = iterable([obj])
  404             primitive = ser.serialize_entity(self.context, thing)
  405             self.assertEqual(1, len(primitive))
  406             for item in primitive:
  407                 self.assertFalse(isinstance(item, base.MagnumObject))
  408             thing2 = ser.deserialize_entity(self.context, primitive)
  409             self.assertEqual(1, len(thing2))
  410             for item in thing2:
  411                 self.assertIsInstance(item, MyObj)
  412 
  413     @mock.patch('magnum.objects.base.MagnumObject.indirection_api')
  414     def _test_deserialize_entity_newer(self, obj_version, backported_to,
  415                                        mock_indirection_api,
  416                                        my_version='1.6'):
  417         ser = base.MagnumObjectSerializer()
  418         mock_indirection_api.object_backport_versions.side_effect \
  419             = NotImplementedError()
  420         mock_indirection_api.object_backport.return_value = 'backported'
  421 
  422         @base.MagnumObjectRegistry.register
  423         class MyTestObj(MyObj):
  424             VERSION = my_version
  425 
  426         obj = MyTestObj()
  427         obj.VERSION = obj_version
  428         primitive = obj.obj_to_primitive()
  429         result = ser.deserialize_entity(self.context, primitive)
  430         if backported_to is None:
  431             self.assertEqual(
  432                 False,
  433                 mock_indirection_api.object_backport.called)
  434         else:
  435             self.assertEqual('backported', result)
  436             mock_indirection_api.object_backport.assert_called_with(
  437                 self.context, primitive, backported_to)
  438 
  439     def test_deserialize_entity_newer_version_backports_level1(self):
  440         "Test object with unsupported (newer) version"
  441         self._test_deserialize_entity_newer('11.5', '1.6')
  442 
  443     def test_deserialize_entity_newer_version_backports_level2(self):
  444         "Test object with unsupported (newer) version"
  445         self._test_deserialize_entity_newer('1.25', '1.6')
  446 
  447     def test_deserialize_entity_same_revision_does_not_backport(self):
  448         "Test object with supported revision"
  449         self._test_deserialize_entity_newer('1.6', None)
  450 
  451     def test_deserialize_entity_newer_revision_does_not_backport_zero(self):
  452         "Test object with supported revision"
  453         self._test_deserialize_entity_newer('1.6.0', None)
  454 
  455     def test_deserialize_entity_newer_revision_does_not_backport(self):
  456         "Test object with supported (newer) revision"
  457         self._test_deserialize_entity_newer('1.6.1', None)
  458 
  459     def test_deserialize_entity_newer_version_passes_revision(self):
  460         "Test object with unsupported (newer) version and revision"
  461         self._test_deserialize_entity_newer('1.7', '1.6.1', my_version='1.6.1')