1# -*- coding: utf-8 -*- 2# Copyright 2013 Red Hat, Inc. 3# 4# Licensed under the Apache License, Version 2.0 (the "License"); you may 5# not use this file except in compliance with the License. You may obtain 6# a copy of the License at 7# 8# http://www.apache.org/licenses/LICENSE-2.0 9# 10# Unless required by applicable law or agreed to in writing, software 11# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13# License for the specific language governing permissions and limitations 14# under the License. 15 16import datetime 17from unittest import mock 18import warnings 19 20import iso8601 21import netaddr 22import testtools 23 24from oslo_versionedobjects import _utils 25from oslo_versionedobjects import base as obj_base 26from oslo_versionedobjects import exception 27from oslo_versionedobjects import fields 28from oslo_versionedobjects import test 29 30 31class FakeFieldType(fields.FieldType): 32 def coerce(self, obj, attr, value): 33 return '*%s*' % value 34 35 def to_primitive(self, obj, attr, value): 36 return '!%s!' % value 37 38 def from_primitive(self, obj, attr, value): 39 return value[1:-1] 40 41 def get_schema(self): 42 return {'type': ['foo']} 43 44 45class FakeEnum(fields.Enum): 46 FROG = "frog" 47 PLATYPUS = "platypus" 48 ALLIGATOR = "alligator" 49 50 ALL = (FROG, PLATYPUS, ALLIGATOR) 51 52 def __init__(self, **kwargs): 53 super(FakeEnum, self).__init__(valid_values=FakeEnum.ALL, 54 **kwargs) 55 56 57class FakeEnumAlt(fields.Enum): 58 FROG = "frog" 59 PLATYPUS = "platypus" 60 AARDVARK = "aardvark" 61 62 ALL = set([FROG, PLATYPUS, AARDVARK]) 63 64 def __init__(self, **kwargs): 65 super(FakeEnumAlt, self).__init__(valid_values=FakeEnumAlt.ALL, 66 **kwargs) 67 68 69class FakeEnumField(fields.BaseEnumField): 70 AUTO_TYPE = FakeEnum() 71 72 73class FakeStateMachineField(fields.StateMachine): 74 75 ACTIVE = 'ACTIVE' 76 PENDING = 'PENDING' 77 ERROR = 'ERROR' 78 79 ALLOWED_TRANSITIONS = { 80 ACTIVE: { 81 PENDING, 82 ERROR 83 }, 84 PENDING: { 85 ACTIVE, 86 ERROR 87 }, 88 ERROR: { 89 PENDING, 90 }, 91 } 92 93 _TYPES = (ACTIVE, PENDING, ERROR) 94 95 def __init__(self, **kwargs): 96 super(FakeStateMachineField, self).__init__(self._TYPES, **kwargs) 97 98 99class FakeEnumAltField(fields.BaseEnumField): 100 AUTO_TYPE = FakeEnumAlt() 101 102 103class TestFieldType(test.TestCase): 104 def test_get_schema(self): 105 self.assertRaises(NotImplementedError, fields.FieldType().get_schema) 106 107 108class TestField(test.TestCase): 109 def setUp(self): 110 super(TestField, self).setUp() 111 self.field = fields.Field(FakeFieldType()) 112 self.coerce_good_values = [('foo', '*foo*')] 113 self.coerce_bad_values = [] 114 self.to_primitive_values = [('foo', '!foo!')] 115 self.from_primitive_values = [('!foo!', 'foo')] 116 117 def test_coerce_good_values(self): 118 for in_val, out_val in self.coerce_good_values: 119 self.assertEqual(out_val, self.field.coerce('obj', 'attr', in_val)) 120 121 def test_coerce_bad_values(self): 122 for in_val in self.coerce_bad_values: 123 self.assertRaises((TypeError, ValueError), 124 self.field.coerce, 'obj', 'attr', in_val) 125 126 def test_to_primitive(self): 127 for in_val, prim_val in self.to_primitive_values: 128 self.assertEqual(prim_val, self.field.to_primitive('obj', 'attr', 129 in_val)) 130 131 def test_from_primitive(self): 132 class ObjectLikeThing(object): 133 _context = 'context' 134 135 for prim_val, out_val in self.from_primitive_values: 136 self.assertEqual(out_val, self.field.from_primitive( 137 ObjectLikeThing, 'attr', prim_val)) 138 139 def test_stringify(self): 140 self.assertEqual('123', self.field.stringify(123)) 141 142 143class TestSchema(test.TestCase): 144 def setUp(self): 145 super(TestSchema, self).setUp() 146 self.field = fields.Field(FakeFieldType(), nullable=True, 147 default='', read_only=False) 148 149 def test_get_schema(self): 150 self.assertEqual({'type': ['foo', 'null'], 'default': '', 151 'readonly': False}, 152 self.field.get_schema()) 153 154 155class TestString(TestField): 156 def setUp(self): 157 super(TestString, self).setUp() 158 self.field = fields.StringField() 159 self.coerce_good_values = [ 160 ('foo', 'foo'), (1, '1'), (1.0, '1.0'), (True, 'True')] 161 self.coerce_bad_values = [None] 162 self.to_primitive_values = self.coerce_good_values[0:1] 163 self.from_primitive_values = self.coerce_good_values[0:1] 164 165 def test_stringify(self): 166 self.assertEqual("'123'", self.field.stringify(123)) 167 168 def test_fieldtype_get_schema(self): 169 self.assertEqual({'type': ['string']}, self.field._type.get_schema()) 170 171 def test_get_schema(self): 172 self.assertEqual({'type': ['string'], 'readonly': False}, 173 self.field.get_schema()) 174 175 176class TestSensitiveString(TestString): 177 def setUp(self): 178 super(TestSensitiveString, self).setUp() 179 self.field = fields.SensitiveStringField() 180 181 def test_stringify(self): 182 payload = """{'admin_password':'mypassword'}""" 183 expected = """'{'admin_password':'***'}'""" 184 self.assertEqual(expected, self.field.stringify(payload)) 185 186 187class TestVersionPredicate(TestString): 188 def setUp(self): 189 super(TestVersionPredicate, self).setUp() 190 self.field = fields.VersionPredicateField() 191 self.coerce_good_values = [('>=1.0', '>=1.0'), 192 ('==1.1', '==1.1'), 193 ('<1.1.0', '<1.1.0')] 194 self.coerce_bad_values = ['1', 'foo', '>1', 1.0, '1.0', '=1.0'] 195 self.to_primitive_values = self.coerce_good_values[0:1] 196 self.from_primitive_values = self.coerce_good_values[0:1] 197 198 199class TestMACAddress(TestField): 200 def setUp(self): 201 super(TestMACAddress, self).setUp() 202 self.field = fields.MACAddressField() 203 self.coerce_good_values = [ 204 ('c6:df:11:a5:c8:5d', 'c6:df:11:a5:c8:5d'), 205 ('C6:DF:11:A5:C8:5D', 'c6:df:11:a5:c8:5d'), 206 ('c6:df:11:a5:c8:5d', 'c6:df:11:a5:c8:5d'), 207 ('C6:DF:11:A5:C8:5D', 'c6:df:11:a5:c8:5d'), 208 ] 209 self.coerce_bad_values = [ 210 'C6:DF:11:A5:C8', # Too short 211 'C6:DF:11:A5:C8:5D:D7', # Too long 212 'C6:DF:11:A5:C8:KD', # Bad octal 213 1123123, # Number 214 {}, # dict 215 ] 216 self.to_primitive_values = self.coerce_good_values[0:1] 217 self.from_primitive_values = self.coerce_good_values[0:1] 218 219 def test_get_schema(self): 220 schema = self.field.get_schema() 221 self.assertEqual(['string'], schema['type']) 222 self.assertEqual(False, schema['readonly']) 223 pattern = schema['pattern'] 224 for _, valid_val in self.coerce_good_values: 225 self.assertRegex(valid_val, pattern) 226 invalid_vals = [x for x in self.coerce_bad_values if type(x) == 'str'] 227 for invalid_val in invalid_vals: 228 self.assertNotRegex(invalid_val, pattern) 229 230 231class TestPCIAddress(TestField): 232 def setUp(self): 233 super(TestPCIAddress, self).setUp() 234 self.field = fields.PCIAddressField() 235 self.coerce_good_values = [ 236 ('0000:02:00.0', '0000:02:00.0'), 237 ('FFFF:FF:1F.7', 'ffff:ff:1f.7'), 238 ('fFfF:fF:1F.7', 'ffff:ff:1f.7'), 239 ] 240 self.coerce_bad_values = [ 241 '000:02:00.0', # Too short 242 '00000:02:00.0', # Too long 243 'FFFF:FF:2F.7', # Bad slot 244 'FFFF:GF:1F.7', # Bad octal 245 1123123, # Number 246 {}, # dict 247 ] 248 self.to_primitive_values = self.coerce_good_values[0:1] 249 self.from_primitive_values = self.coerce_good_values[0:1] 250 251 def test_get_schema(self): 252 schema = self.field.get_schema() 253 self.assertEqual(['string'], schema['type']) 254 self.assertEqual(False, schema['readonly']) 255 pattern = schema['pattern'] 256 for _, valid_val in self.coerce_good_values: 257 self.assertRegex(valid_val, pattern) 258 invalid_vals = [x for x in self.coerce_bad_values if type(x) == 'str'] 259 for invalid_val in invalid_vals: 260 self.assertNotRegex(invalid_val, pattern) 261 262 263class TestUUID(TestField): 264 def setUp(self): 265 super(TestUUID, self).setUp() 266 self.field = fields.UUIDField() 267 self.coerce_good_values = [ 268 ('da66a411-af0e-4829-9b67-475017ddd152', 269 'da66a411-af0e-4829-9b67-475017ddd152'), 270 ('da66a411af0e48299b67475017ddd152', 271 'da66a411af0e48299b67475017ddd152'), 272 ('DA66A411-AF0E-4829-9B67-475017DDD152', 273 'DA66A411-AF0E-4829-9B67-475017DDD152'), 274 ('DA66A411AF0E48299b67475017DDD152', 275 'DA66A411AF0E48299b67475017DDD152'), 276 # These values are included to ensure there is not change in 277 # behaviour - only when we can remove the old UUID behaviour can 278 # we add these to the "self.coerce_bad_values" list 279 ('da66a411-af0e-4829-9b67', 280 'da66a411-af0e-4829-9b67'), 281 ('da66a411-af0e-4829-9b67-475017ddd152548999', 282 'da66a411-af0e-4829-9b67-475017ddd152548999'), 283 ('da66a411-af0e-4829-9b67-475017ddz152', 284 'da66a411-af0e-4829-9b67-475017ddz152'), 285 ('fake_uuid', 'fake_uuid'), 286 (u'fake_uāid', u'fake_uāid'), 287 (b'fake_u\xe1id'.decode('latin_1'), 288 b'fake_u\xe1id'.decode('latin_1')), 289 ('1', '1'), 290 (1, '1') 291 ] 292 self.to_primitive_values = self.coerce_good_values[0:1] 293 self.from_primitive_values = self.coerce_good_values[0:1] 294 295 @mock.patch('warnings.warn') 296 def test_coerce_good_values(self, mock_warn): 297 super().test_coerce_good_values() 298 mock_warn.assert_has_calls( 299 [mock.call(mock.ANY, FutureWarning)] * 8, 300 ) 301 302 def test_validation_warning_can_be_escalated_to_exception(self): 303 warnings.filterwarnings(action='error') 304 self.assertRaises(FutureWarning, self.field.coerce, 'obj', 'attr', 305 'not a uuid') 306 307 def test_get_schema(self): 308 field = fields.UUIDField() 309 schema = field.get_schema() 310 self.assertEqual(['string'], schema['type']) 311 self.assertEqual(False, schema['readonly']) 312 pattern = schema['pattern'] 313 for _, valid_val in self.coerce_good_values[:4]: 314 self.assertRegex(valid_val, pattern) 315 invalid_vals = [x for x in self.coerce_bad_values if type(x) == 'str'] 316 for invalid_val in invalid_vals: 317 self.assertNotRegex(invalid_val, pattern) 318 319 320class TestBaseEnum(TestField): 321 def setUp(self): 322 super(TestBaseEnum, self).setUp() 323 self.field = FakeEnumField() 324 self.coerce_good_values = [('frog', 'frog'), 325 ('platypus', 'platypus'), 326 ('alligator', 'alligator')] 327 self.coerce_bad_values = ['aardvark', 'wookie'] 328 self.to_primitive_values = self.coerce_good_values[0:1] 329 self.from_primitive_values = self.coerce_good_values[0:1] 330 331 def test_stringify(self): 332 self.assertEqual("'platypus'", self.field.stringify('platypus')) 333 334 def test_stringify_invalid(self): 335 self.assertRaises(ValueError, self.field.stringify, 'aardvark') 336 337 def test_fingerprint(self): 338 # Notes(yjiang5): make sure changing valid_value will be detected 339 # in test_objects.test_versions 340 field1 = FakeEnumField() 341 field2 = FakeEnumAltField() 342 self.assertNotEqual(str(field1), str(field2)) 343 344 def test_valid_values(self): 345 self.assertEqual(self.field.valid_values, 346 FakeEnum.ALL) 347 348 def test_valid_values_keeps_type(self): 349 self.assertIsInstance(self.field.valid_values, tuple) 350 self.assertIsInstance(FakeEnumAltField().valid_values, set) 351 352 353class TestEnum(TestField): 354 def setUp(self): 355 super(TestEnum, self).setUp() 356 self.field = fields.EnumField( 357 valid_values=['foo', 'bar', 1, True]) 358 self.coerce_good_values = [('foo', 'foo'), (1, '1'), (True, 'True')] 359 self.coerce_bad_values = ['boo', 2, False] 360 self.to_primitive_values = self.coerce_good_values[0:1] 361 self.from_primitive_values = self.coerce_good_values[0:1] 362 363 def test_stringify(self): 364 self.assertEqual("'foo'", self.field.stringify('foo')) 365 366 def test_stringify_invalid(self): 367 self.assertRaises(ValueError, self.field.stringify, '123') 368 369 def test_fieldtype_get_schema(self): 370 self.assertEqual({'type': ['string'], 'enum': ["foo", "bar", 1, True]}, 371 self.field._type.get_schema()) 372 373 def test_get_schema(self): 374 self.assertEqual({'type': ['string'], 'enum': ["foo", "bar", 1, True], 375 'readonly': False}, self.field.get_schema()) 376 377 def test_fingerprint(self): 378 # Notes(yjiang5): make sure changing valid_value will be detected 379 # in test_objects.test_versions 380 field1 = fields.EnumField(valid_values=['foo', 'bar']) 381 field2 = fields.EnumField(valid_values=['foo', 'bar1']) 382 self.assertNotEqual(str(field1), str(field2)) 383 384 def test_missing_valid_values(self): 385 self.assertRaises(exception.EnumRequiresValidValuesError, 386 fields.EnumField, None) 387 388 def test_empty_valid_values(self): 389 self.assertRaises(exception.EnumRequiresValidValuesError, 390 fields.EnumField, []) 391 392 def test_non_iterable_valid_values(self): 393 self.assertRaises(exception.EnumValidValuesInvalidError, 394 fields.EnumField, True) 395 396 def test_enum_subclass_check(self): 397 def _test(): 398 class BrokenEnumField(fields.BaseEnumField): 399 AUTO_TYPE = int 400 401 BrokenEnumField() 402 403 self.assertRaises(exception.EnumFieldInvalid, _test) 404 405 406class TestStateMachine(TestField): 407 408 def test_good_transitions(self): 409 @obj_base.VersionedObjectRegistry.register 410 class AnObject(obj_base.VersionedObject): 411 fields = { 412 'status': FakeStateMachineField(), 413 } 414 415 obj = AnObject() 416 417 obj.status = FakeStateMachineField.ACTIVE 418 obj.status = FakeStateMachineField.PENDING 419 obj.status = FakeStateMachineField.ERROR 420 obj.status = FakeStateMachineField.PENDING 421 obj.status = FakeStateMachineField.ACTIVE 422 423 def test_bad_transitions(self): 424 @obj_base.VersionedObjectRegistry.register 425 class AnObject(obj_base.VersionedObject): 426 fields = { 427 'status': FakeStateMachineField(), 428 } 429 430 obj = AnObject(status='ERROR') 431 432 try: 433 obj.status = FakeStateMachineField.ACTIVE 434 except ValueError as e: 435 ex = e 436 else: 437 ex = None 438 439 self.assertIsNotNone(ex, 'Invalid transition failed to raise error') 440 self.assertEqual('AnObject.status is not allowed to transition out ' 441 'of \'ERROR\' state to \'ACTIVE\' state, choose from ' 442 '[\'PENDING\']', 443 str(ex)) 444 445 def test_bad_initial_value(self): 446 @obj_base.VersionedObjectRegistry.register 447 class AnObject(obj_base.VersionedObject): 448 fields = { 449 'status': FakeStateMachineField(), 450 } 451 452 obj = AnObject() 453 454 with testtools.ExpectedException(ValueError): 455 obj.status = "FOO" 456 457 def test_bad_updated_value(self): 458 @obj_base.VersionedObjectRegistry.register 459 class AnObject(obj_base.VersionedObject): 460 fields = { 461 'status': FakeStateMachineField(), 462 } 463 464 obj = AnObject() 465 466 with testtools.ExpectedException(ValueError): 467 obj.status = FakeStateMachineField.ACTIVE 468 obj.status = "FOO" 469 470 471class TestInteger(TestField): 472 def setUp(self): 473 super(TestField, self).setUp() 474 self.field = fields.IntegerField() 475 self.coerce_good_values = [(1, 1), ('1', 1)] 476 self.coerce_bad_values = ['foo', None] 477 self.to_primitive_values = self.coerce_good_values[0:1] 478 self.from_primitive_values = self.coerce_good_values[0:1] 479 480 def test_fieldtype_get_schema(self): 481 self.assertEqual({'type': ['integer']}, self.field._type.get_schema()) 482 483 def test_get_schema(self): 484 self.assertEqual({'type': ['integer'], 'readonly': False}, 485 self.field.get_schema()) 486 487 488class TestNonNegativeInteger(TestField): 489 def setUp(self): 490 super(TestNonNegativeInteger, self).setUp() 491 self.field = fields.NonNegativeIntegerField() 492 self.coerce_good_values = [(1, 1), ('1', 1)] 493 self.coerce_bad_values = ['-2', '4.2', 'foo', None] 494 self.to_primitive_values = self.coerce_good_values[0:1] 495 self.from_primitive_values = self.coerce_good_values[0:1] 496 497 def test_get_schema(self): 498 self.assertEqual({'type': ['integer'], 'readonly': False, 499 'minimum': 0}, self.field.get_schema()) 500 501 502class TestFloat(TestField): 503 def setUp(self): 504 super(TestFloat, self).setUp() 505 self.field = fields.FloatField() 506 self.coerce_good_values = [(1.1, 1.1), ('1.1', 1.1)] 507 self.coerce_bad_values = ['foo', None] 508 self.to_primitive_values = self.coerce_good_values[0:1] 509 self.from_primitive_values = self.coerce_good_values[0:1] 510 511 def test_fieldtype_get_schema(self): 512 self.assertEqual({'type': ['number']}, self.field._type.get_schema()) 513 514 def test_get_schema(self): 515 self.assertEqual({'type': ['number'], 'readonly': False}, 516 self.field.get_schema()) 517 518 519class TestNonNegativeFloat(TestField): 520 def setUp(self): 521 super(TestNonNegativeFloat, self).setUp() 522 self.field = fields.NonNegativeFloatField() 523 self.coerce_good_values = [(1.1, 1.1), ('1.1', 1.1)] 524 self.coerce_bad_values = ['-4.2', 'foo', None] 525 self.to_primitive_values = self.coerce_good_values[0:1] 526 self.from_primitive_values = self.coerce_good_values[0:1] 527 528 def test_get_schema(self): 529 self.assertEqual({'type': ['number'], 'readonly': False, 530 'minimum': 0}, self.field.get_schema()) 531 532 533class TestBoolean(TestField): 534 def setUp(self): 535 super(TestField, self).setUp() 536 self.field = fields.BooleanField() 537 self.coerce_good_values = [(True, True), (False, False), (1, True), 538 ('foo', True), (0, False), ('', False)] 539 self.coerce_bad_values = [] 540 self.to_primitive_values = self.coerce_good_values[0:2] 541 self.from_primitive_values = self.coerce_good_values[0:2] 542 543 def test_fieldtype_get_schema(self): 544 self.assertEqual({'type': ['boolean']}, self.field._type.get_schema()) 545 546 def test_get_schema(self): 547 self.assertEqual({'type': ['boolean'], 'readonly': False}, 548 self.field.get_schema()) 549 550 551class TestFlexibleBoolean(TestField): 552 def setUp(self): 553 super(TestFlexibleBoolean, self).setUp() 554 self.field = fields.FlexibleBooleanField() 555 self.coerce_good_values = [(True, True), (False, False), 556 ("true", True), ("false", False), 557 ("t", True), ("f", False), 558 ("yes", True), ("no", False), 559 ("y", True), ("n", False), 560 ("on", True), ("off", False), 561 (1, True), (0, False), 562 ('frog', False), ('', False)] 563 self.coerce_bad_values = [] 564 self.to_primitive_values = self.coerce_good_values[0:2] 565 self.from_primitive_values = self.coerce_good_values[0:2] 566 567 568class TestDateTime(TestField): 569 def setUp(self): 570 super(TestDateTime, self).setUp() 571 self.dt = datetime.datetime(1955, 11, 5, tzinfo=iso8601.iso8601.UTC) 572 self.field = fields.DateTimeField() 573 self.coerce_good_values = [(self.dt, self.dt), 574 (_utils.isotime(self.dt), self.dt)] 575 self.coerce_bad_values = [1, 'foo'] 576 self.to_primitive_values = [(self.dt, _utils.isotime(self.dt))] 577 self.from_primitive_values = [(_utils.isotime(self.dt), self.dt)] 578 579 def test_stringify(self): 580 self.assertEqual( 581 '1955-11-05T18:00:00Z', 582 self.field.stringify( 583 datetime.datetime(1955, 11, 5, 18, 0, 0, 584 tzinfo=iso8601.iso8601.UTC))) 585 586 def test_get_schema(self): 587 self.assertEqual({'type': ['string'], 'format': 'date-time', 588 'readonly': False}, 589 self.field.get_schema()) 590 591 592class TestDateTimeNoTzinfo(TestField): 593 def setUp(self): 594 super(TestDateTimeNoTzinfo, self).setUp() 595 self.dt = datetime.datetime(1955, 11, 5) 596 self.field = fields.DateTimeField(tzinfo_aware=False) 597 self.coerce_good_values = [(self.dt, self.dt), 598 (_utils.isotime(self.dt), self.dt)] 599 self.coerce_bad_values = [1, 'foo'] 600 self.to_primitive_values = [(self.dt, _utils.isotime(self.dt))] 601 self.from_primitive_values = [ 602 ( 603 _utils.isotime(self.dt), 604 self.dt, 605 ) 606 ] 607 608 def test_stringify(self): 609 self.assertEqual( 610 '1955-11-05T18:00:00Z', 611 self.field.stringify( 612 datetime.datetime(1955, 11, 5, 18, 0, 0))) 613 614 615class TestDict(TestField): 616 def setUp(self): 617 super(TestDict, self).setUp() 618 self.field = fields.Field(fields.Dict(FakeFieldType())) 619 self.coerce_good_values = [({'foo': 'bar'}, {'foo': '*bar*'}), 620 ({'foo': 1}, {'foo': '*1*'})] 621 self.coerce_bad_values = [{1: 'bar'}, 'foo'] 622 self.to_primitive_values = [({'foo': 'bar'}, {'foo': '!bar!'})] 623 self.from_primitive_values = [({'foo': '!bar!'}, {'foo': 'bar'})] 624 625 def test_stringify(self): 626 self.assertEqual("{key=val}", self.field.stringify({'key': 'val'})) 627 628 def test_get_schema(self): 629 self.assertEqual({'type': ['object'], 630 'additionalProperties': {'readonly': False, 631 'type': ['foo']}, 632 'readonly': False}, 633 self.field.get_schema()) 634 635 636class TestDictOfStrings(TestField): 637 def setUp(self): 638 super(TestDictOfStrings, self).setUp() 639 self.field = fields.DictOfStringsField() 640 self.coerce_good_values = [({'foo': 'bar'}, {'foo': 'bar'}), 641 ({'foo': 1}, {'foo': '1'})] 642 self.coerce_bad_values = [{1: 'bar'}, {'foo': None}, 'foo'] 643 self.to_primitive_values = [({'foo': 'bar'}, {'foo': 'bar'})] 644 self.from_primitive_values = [({'foo': 'bar'}, {'foo': 'bar'})] 645 646 def test_stringify(self): 647 self.assertEqual("{key='val'}", self.field.stringify({'key': 'val'})) 648 649 650class TestDictOfIntegers(TestField): 651 def setUp(self): 652 super(TestDictOfIntegers, self).setUp() 653 self.field = fields.DictOfIntegersField() 654 self.coerce_good_values = [({'foo': '42'}, {'foo': 42}), 655 ({'foo': 4.2}, {'foo': 4})] 656 self.coerce_bad_values = [{1: 'bar'}, {'foo': 'boo'}, 657 'foo', {'foo': None}] 658 self.to_primitive_values = [({'foo': 42}, {'foo': 42})] 659 self.from_primitive_values = [({'foo': 42}, {'foo': 42})] 660 661 def test_stringify(self): 662 self.assertEqual("{key=42}", self.field.stringify({'key': 42})) 663 664 665class TestDictOfStringsNone(TestField): 666 def setUp(self): 667 super(TestDictOfStringsNone, self).setUp() 668 self.field = fields.DictOfNullableStringsField() 669 self.coerce_good_values = [({'foo': 'bar'}, {'foo': 'bar'}), 670 ({'foo': 1}, {'foo': '1'}), 671 ({'foo': None}, {'foo': None})] 672 self.coerce_bad_values = [{1: 'bar'}, 'foo'] 673 self.to_primitive_values = [({'foo': 'bar'}, {'foo': 'bar'})] 674 self.from_primitive_values = [({'foo': 'bar'}, {'foo': 'bar'})] 675 676 def test_stringify(self): 677 self.assertEqual("{k2=None,key='val'}", 678 self.field.stringify({'k2': None, 679 'key': 'val'})) 680 681 682class TestListOfDictOfNullableStringsField(TestField): 683 def setUp(self): 684 super(TestListOfDictOfNullableStringsField, self).setUp() 685 self.field = fields.ListOfDictOfNullableStringsField() 686 self.coerce_good_values = [([{'f': 'b', 'f1': 'b1'}, {'f2': 'b2'}], 687 [{'f': 'b', 'f1': 'b1'}, {'f2': 'b2'}]), 688 ([{'f': 1}, {'f1': 'b1'}], 689 [{'f': '1'}, {'f1': 'b1'}]), 690 ([{'foo': None}], [{'foo': None}])] 691 self.coerce_bad_values = [[{1: 'a'}], ['ham', 1], ['eggs']] 692 self.to_primitive_values = [([{'f': 'b'}, {'f1': 'b1'}, {'f2': None}], 693 [{'f': 'b'}, {'f1': 'b1'}, {'f2': None}])] 694 self.from_primitive_values = [([{'f': 'b'}, {'f1': 'b1'}, 695 {'f2': None}], 696 [{'f': 'b'}, {'f1': 'b1'}, 697 {'f2': None}])] 698 699 def test_stringify(self): 700 self.assertEqual("[{f=None,f1='b1'},{f2='b2'}]", 701 self.field.stringify( 702 [{'f': None, 'f1': 'b1'}, {'f2': 'b2'}])) 703 704 705class TestList(TestField): 706 def setUp(self): 707 super(TestList, self).setUp() 708 self.field = fields.Field(fields.List(FakeFieldType())) 709 self.coerce_good_values = [(['foo', 'bar'], ['*foo*', '*bar*'])] 710 self.coerce_bad_values = ['foo'] 711 self.to_primitive_values = [(['foo'], ['!foo!'])] 712 self.from_primitive_values = [(['!foo!'], ['foo'])] 713 714 def test_stringify(self): 715 self.assertEqual('[123]', self.field.stringify([123])) 716 717 def test_fieldtype_get_schema(self): 718 self.assertEqual({'type': ['array'], 719 'items': {'type': ['foo'], 'readonly': False}}, 720 self.field._type.get_schema()) 721 722 def test_get_schema(self): 723 self.assertEqual({'type': ['array'], 724 'items': {'type': ['foo'], 'readonly': False}, 725 'readonly': False}, 726 self.field.get_schema()) 727 728 729class TestListOfStrings(TestField): 730 def setUp(self): 731 super(TestListOfStrings, self).setUp() 732 self.field = fields.ListOfStringsField() 733 self.coerce_good_values = [(['foo', 'bar'], ['foo', 'bar'])] 734 self.coerce_bad_values = ['foo'] 735 self.to_primitive_values = [(['foo'], ['foo'])] 736 self.from_primitive_values = [(['foo'], ['foo'])] 737 738 def test_stringify(self): 739 self.assertEqual("['abc']", self.field.stringify(['abc'])) 740 741 742class TestDictOfListOfStrings(TestField): 743 def setUp(self): 744 super(TestDictOfListOfStrings, self).setUp() 745 self.field = fields.DictOfListOfStringsField() 746 self.coerce_good_values = [({'foo': ['1', '2']}, {'foo': ['1', '2']}), 747 ({'foo': [1]}, {'foo': ['1']})] 748 self.coerce_bad_values = [{'foo': [None, None]}, 'foo'] 749 self.to_primitive_values = [({'foo': ['1', '2']}, {'foo': ['1', '2']})] 750 self.from_primitive_values = [({'foo': ['1', '2']}, 751 {'foo': ['1', '2']})] 752 753 def test_stringify(self): 754 self.assertEqual("{foo=['1','2']}", 755 self.field.stringify({'foo': ['1', '2']})) 756 757 758class TestListOfEnum(TestField): 759 def setUp(self): 760 super(TestListOfEnum, self).setUp() 761 self.field = fields.ListOfEnumField(valid_values=['foo', 'bar']) 762 self.coerce_good_values = [(['foo', 'bar'], ['foo', 'bar'])] 763 self.coerce_bad_values = ['foo', ['foo', 'bar1']] 764 self.to_primitive_values = [(['foo'], ['foo'])] 765 self.from_primitive_values = [(['foo'], ['foo'])] 766 767 def test_stringify(self): 768 self.assertEqual("['foo']", self.field.stringify(['foo'])) 769 770 def test_stringify_invalid(self): 771 self.assertRaises(ValueError, self.field.stringify, '[abc]') 772 773 def test_fingerprint(self): 774 # Notes(yjiang5): make sure changing valid_value will be detected 775 # in test_objects.test_versions 776 field1 = fields.ListOfEnumField(valid_values=['foo', 'bar']) 777 field2 = fields.ListOfEnumField(valid_values=['foo', 'bar1']) 778 self.assertNotEqual(str(field1), str(field2)) 779 780 781class TestSet(TestField): 782 def setUp(self): 783 super(TestSet, self).setUp() 784 self.field = fields.Field(fields.Set(FakeFieldType())) 785 self.coerce_good_values = [(set(['foo', 'bar']), 786 set(['*foo*', '*bar*']))] 787 self.coerce_bad_values = [['foo'], {'foo': 'bar'}] 788 self.to_primitive_values = [(set(['foo']), tuple(['!foo!']))] 789 self.from_primitive_values = [(tuple(['!foo!']), set(['foo']))] 790 791 def test_stringify(self): 792 self.assertEqual('set([123])', self.field.stringify(set([123]))) 793 794 def test_get_schema(self): 795 self.assertEqual({'type': ['array'], 'uniqueItems': True, 796 'items': {'type': ['foo'], 'readonly': False}, 797 'readonly': False}, 798 self.field.get_schema()) 799 800 801class TestSetOfIntegers(TestField): 802 def setUp(self): 803 super(TestSetOfIntegers, self).setUp() 804 self.field = fields.SetOfIntegersField() 805 self.coerce_good_values = [(set(['1', 2]), 806 set([1, 2]))] 807 self.coerce_bad_values = [set(['foo'])] 808 self.to_primitive_values = [(set([1]), tuple([1]))] 809 self.from_primitive_values = [(tuple([1]), set([1]))] 810 811 def test_stringify(self): 812 self.assertEqual('set([1,2])', self.field.stringify(set([1, 2]))) 813 814 def test_repr(self): 815 self.assertEqual("Set(default=<class 'oslo_versionedobjects.fields." 816 "UnspecifiedDefault'>,nullable=False)", 817 repr(self.field)) 818 self.assertEqual("Set(default=set([]),nullable=False)", 819 repr(fields.SetOfIntegersField(default=set()))) 820 self.assertEqual("Set(default=set([1,a]),nullable=False)", 821 repr(fields.SetOfIntegersField(default={1, 'a'}))) 822 823 824class TestListOfSetsOfIntegers(TestField): 825 def setUp(self): 826 super(TestListOfSetsOfIntegers, self).setUp() 827 self.field = fields.ListOfSetsOfIntegersField() 828 self.coerce_good_values = [([set(['1', 2]), set([3, '4'])], 829 [set([1, 2]), set([3, 4])])] 830 self.coerce_bad_values = [[set(['foo'])]] 831 self.to_primitive_values = [([set([1])], [tuple([1])])] 832 self.from_primitive_values = [([tuple([1])], [set([1])])] 833 834 def test_stringify(self): 835 self.assertEqual('[set([1,2])]', self.field.stringify([set([1, 2])])) 836 837 838class TestListOfIntegers(TestField): 839 def setUp(self): 840 super(TestListOfIntegers, self).setUp() 841 self.field = fields.ListOfIntegersField() 842 self.coerce_good_values = [(['1', 2], [1, 2]), 843 ([1, 2], [1, 2])] 844 self.coerce_bad_values = [['foo']] 845 self.to_primitive_values = [([1], [1])] 846 self.from_primitive_values = [([1], [1])] 847 848 def test_stringify(self): 849 self.assertEqual('[[1, 2]]', self.field.stringify([[1, 2]])) 850 851 852class TestListOfUUIDField(TestField): 853 def setUp(self): 854 super(TestListOfUUIDField, self).setUp() 855 self.field = fields.ListOfUUIDField() 856 self.uuid1 = '6b2097ea-d0e3-44dd-b131-95472b3ea8fd' 857 self.uuid2 = '478c193d-2533-4e71-ab2b-c7683f67d7f9' 858 self.coerce_good_values = [([self.uuid1, self.uuid2], 859 [self.uuid1, self.uuid2])] 860 # coerce_bad_values is intentionally ignored since the UUID field 861 # allows non-UUID values for now. See TestUUIDField for examples. 862 self.to_primitive_values = [([self.uuid1], [self.uuid1])] 863 self.from_primitive_values = [([self.uuid1], [self.uuid1])] 864 865 def test_stringify(self): 866 self.assertEqual('[%s,%s]' % (self.uuid1, self.uuid2), 867 self.field.stringify([self.uuid1, self.uuid2])) 868 869 870class TestLocalMethods(test.TestCase): 871 @mock.patch.object(obj_base.LOG, 'exception') 872 def test__make_class_properties_setter_value_error(self, mock_log): 873 @obj_base.VersionedObjectRegistry.register 874 class AnObject(obj_base.VersionedObject): 875 fields = { 876 'intfield': fields.IntegerField(), 877 } 878 879 self.assertRaises(ValueError, AnObject, intfield='badvalue') 880 self.assertFalse(mock_log.called) 881 882 @mock.patch.object(obj_base.LOG, 'exception') 883 def test__make_class_properties_setter_setattr_fails(self, mock_log): 884 @obj_base.VersionedObjectRegistry.register 885 class AnObject(obj_base.VersionedObject): 886 fields = { 887 'intfield': fields.IntegerField(), 888 } 889 890 # We want the setattr() call in _make_class_properties.setter() to 891 # raise an exception 892 with mock.patch.object(obj_base, '_get_attrname') as mock_attr: 893 mock_attr.return_value = '__class__' 894 self.assertRaises(TypeError, AnObject, intfield=2) 895 mock_attr.assert_called_once_with('intfield') 896 mock_log.assert_called_once_with(mock.ANY, 897 {'attr': 'AnObject.intfield'}) 898 899 900class TestObject(TestField): 901 def setUp(self): 902 super(TestObject, self).setUp() 903 904 @obj_base.VersionedObjectRegistry.register 905 class TestableObject(obj_base.VersionedObject): 906 fields = { 907 'uuid': fields.StringField(), 908 } 909 910 def __eq__(self, value): 911 # NOTE(danms): Be rather lax about this equality thing to 912 # satisfy the assertEqual() in test_from_primitive(). We 913 # just want to make sure the right type of object is re-created 914 return value.__class__.__name__ == TestableObject.__name__ 915 916 class OtherTestableObject(obj_base.VersionedObject): 917 pass 918 919 test_inst = TestableObject() 920 self._test_cls = TestableObject 921 self.field = fields.Field(fields.Object('TestableObject')) 922 self.coerce_good_values = [(test_inst, test_inst)] 923 self.coerce_bad_values = [OtherTestableObject(), 1, 'foo'] 924 self.to_primitive_values = [(test_inst, test_inst.obj_to_primitive())] 925 self.from_primitive_values = [(test_inst.obj_to_primitive(), 926 test_inst), 927 (test_inst, test_inst)] 928 929 def test_stringify(self): 930 obj = self._test_cls(uuid='fake-uuid') 931 self.assertEqual('TestableObject(fake-uuid)', 932 self.field.stringify(obj)) 933 934 def test_from_primitive(self): 935 @obj_base.VersionedObjectRegistry.register 936 class TestFakeObject(obj_base.VersionedObject): 937 OBJ_PROJECT_NAMESPACE = 'fake-project' 938 939 @obj_base.VersionedObjectRegistry.register 940 class TestBar(TestFakeObject, obj_base.ComparableVersionedObject): 941 fields = { 942 'name': fields.StringField(), 943 } 944 945 @obj_base.VersionedObjectRegistry.register 946 class TestFoo(TestFakeObject, obj_base.ComparableVersionedObject): 947 fields = { 948 'name': fields.StringField(), 949 'bar': fields.ObjectField('TestBar', nullable=True) 950 } 951 952 bar = TestBar(name='bar') 953 foo = TestFoo(name='foo', bar=bar) 954 from_primitive_values = [(foo.obj_to_primitive(), foo), (foo, foo)] 955 956 for prim_val, out_val in from_primitive_values: 957 self.assertEqual(out_val, self.field.from_primitive( 958 foo, 'attr', prim_val)) 959 960 def test_inheritance(self): 961 # We need a whole lot of classes in a hierarchy to 962 # test subclass recognition for the Object field 963 class TestAnimal(obj_base.VersionedObject): 964 pass 965 966 class TestMammal(TestAnimal): 967 pass 968 969 class TestReptile(TestAnimal): 970 pass 971 972 # We'll use this to create a diamond in the 973 # class hierarchy 974 class TestPet(TestAnimal): 975 pass 976 977 # Non-versioned object mixin 978 class TestScary(object): 979 pass 980 981 class TestCrocodile(TestReptile, TestPet, TestScary): 982 pass 983 984 class TestPig(TestMammal): 985 pass 986 987 class TestDog(TestMammal, TestPet): 988 pass 989 990 # Some fictional animals 991 wolfy = TestDog() # Terminator-2 992 ticktock = TestCrocodile() # Peter Pan 993 babe = TestPig() # Babe 994 995 # The various classes 996 animals = fields.Object('TestAnimal', subclasses=True) 997 mammals = fields.Object('TestMammal', subclasses=True) 998 reptiles = fields.Object('TestReptile', subclasses=True) 999 pets = fields.Object('TestPet', subclasses=True) 1000 pigs = fields.Object('TestPig', subclasses=True) 1001 dogs = fields.Object('TestDog', subclasses=True) 1002 crocs = fields.Object('TestCrocodile', subclasses=True) 1003 1004 self.assertEqual(["TestDog", "TestMammal", "TestPet", 1005 "TestAnimal", "VersionedObject"], 1006 fields.Object._get_all_obj_names(wolfy)) 1007 1008 self.assertEqual(["TestCrocodile", "TestReptile", "TestPet", 1009 "TestAnimal", "VersionedObject"], 1010 fields.Object._get_all_obj_names(ticktock)) 1011 1012 self.assertEqual(["TestPig", "TestMammal", 1013 "TestAnimal", "VersionedObject"], 1014 fields.Object._get_all_obj_names(babe)) 1015 1016 # When stringifying we should see the subclass object name 1017 # not the base class object name 1018 self.assertEqual("TestDog", animals.stringify(wolfy)) 1019 self.assertEqual("TestCrocodile", animals.stringify(ticktock)) 1020 self.assertEqual("TestPig", animals.stringify(babe)) 1021 1022 # Everything is an animal 1023 self.assertEqual(wolfy, animals.coerce(None, "animal", wolfy)) 1024 self.assertEqual(ticktock, animals.coerce(None, "animal", ticktock)) 1025 self.assertEqual(babe, animals.coerce(None, "animal", babe)) 1026 1027 # crocodiles are not mammals 1028 self.assertEqual(wolfy, mammals.coerce(None, "animal", wolfy)) 1029 self.assertRaises(ValueError, mammals.coerce, None, "animal", ticktock) 1030 self.assertEqual(babe, mammals.coerce(None, "animal", babe)) 1031 1032 # dogs and pigs are not reptiles 1033 self.assertRaises(ValueError, reptiles.coerce, None, "animal", wolfy) 1034 self.assertEqual(ticktock, reptiles.coerce(None, "animal", ticktock)) 1035 self.assertRaises(ValueError, reptiles.coerce, None, "animal", babe) 1036 1037 # pigs are not pets, but crocodiles (!) & dogs are 1038 self.assertEqual(wolfy, pets.coerce(None, "animal", wolfy)) 1039 self.assertEqual(ticktock, pets.coerce(None, "animal", ticktock)) 1040 self.assertRaises(ValueError, pets.coerce, None, "animal", babe) 1041 1042 # Only dogs are dogs 1043 self.assertEqual(wolfy, dogs.coerce(None, "animal", wolfy)) 1044 self.assertRaises(ValueError, dogs.coerce, None, "animal", ticktock) 1045 self.assertRaises(ValueError, dogs.coerce, None, "animal", babe) 1046 1047 # Only crocs are crocs 1048 self.assertRaises(ValueError, crocs.coerce, None, "animal", wolfy) 1049 self.assertEqual(ticktock, crocs.coerce(None, "animal", ticktock)) 1050 self.assertRaises(ValueError, crocs.coerce, None, "animal", babe) 1051 1052 # Only pigs are pigs 1053 self.assertRaises(ValueError, pigs.coerce, None, "animal", ticktock) 1054 self.assertRaises(ValueError, pigs.coerce, None, "animal", wolfy) 1055 self.assertEqual(babe, pigs.coerce(None, "animal", babe)) 1056 1057 def test_coerce_bad_value_primitive_type(self): 1058 # Tests that the ValueError has the primitive type in it's message. 1059 ex = self.assertRaises(ValueError, self.field.coerce, 1060 'obj', 'attr', [{}]) 1061 self.assertEqual('An object of type TestableObject is required ' 1062 'in field attr, not a list', str(ex)) 1063 1064 def test_get_schema(self): 1065 self.assertEqual( 1066 { 1067 'properties': { 1068 'versioned_object.changes': 1069 {'items': {'type': 'string'}, 'type': 'array'}, 1070 'versioned_object.data': { 1071 'description': 'fields of TestableObject', 1072 'properties': 1073 {'uuid': {'readonly': False, 'type': ['string']}}, 1074 'required': ['uuid'], 1075 'type': 'object'}, 1076 'versioned_object.name': {'type': 'string'}, 1077 'versioned_object.namespace': {'type': 'string'}, 1078 'versioned_object.version': {'type': 'string'} 1079 }, 1080 'readonly': False, 1081 'required': ['versioned_object.namespace', 1082 'versioned_object.name', 1083 'versioned_object.version', 1084 'versioned_object.data'], 1085 'type': ['object'] 1086 }, 1087 self.field.get_schema()) 1088 1089 1090class TestIPAddress(TestField): 1091 def setUp(self): 1092 super(TestIPAddress, self).setUp() 1093 self.field = fields.IPAddressField() 1094 self.coerce_good_values = [('1.2.3.4', netaddr.IPAddress('1.2.3.4')), 1095 ('::1', netaddr.IPAddress('::1')), 1096 (netaddr.IPAddress('::1'), 1097 netaddr.IPAddress('::1'))] 1098 self.coerce_bad_values = ['1-2', 'foo'] 1099 self.to_primitive_values = [(netaddr.IPAddress('1.2.3.4'), '1.2.3.4'), 1100 (netaddr.IPAddress('::1'), '::1')] 1101 self.from_primitive_values = [('1.2.3.4', 1102 netaddr.IPAddress('1.2.3.4')), 1103 ('::1', 1104 netaddr.IPAddress('::1'))] 1105 1106 1107class TestIPAddressV4(TestField): 1108 def setUp(self): 1109 super(TestIPAddressV4, self).setUp() 1110 self.field = fields.IPV4AddressField() 1111 self.coerce_good_values = [('1.2.3.4', netaddr.IPAddress('1.2.3.4')), 1112 (netaddr.IPAddress('1.2.3.4'), 1113 netaddr.IPAddress('1.2.3.4'))] 1114 self.coerce_bad_values = ['1-2', 'foo', '::1'] 1115 self.to_primitive_values = [(netaddr.IPAddress('1.2.3.4'), '1.2.3.4')] 1116 self.from_primitive_values = [('1.2.3.4', 1117 netaddr.IPAddress('1.2.3.4'))] 1118 1119 def test_get_schema(self): 1120 self.assertEqual({'type': ['string'], 'readonly': False, 1121 'format': 'ipv4'}, 1122 self.field.get_schema()) 1123 1124 1125class TestIPAddressV6(TestField): 1126 def setUp(self): 1127 super(TestIPAddressV6, self).setUp() 1128 self.field = fields.IPV6AddressField() 1129 self.coerce_good_values = [('::1', netaddr.IPAddress('::1')), 1130 (netaddr.IPAddress('::1'), 1131 netaddr.IPAddress('::1'))] 1132 self.coerce_bad_values = ['1.2', 'foo', '1.2.3.4'] 1133 self.to_primitive_values = [(netaddr.IPAddress('::1'), '::1')] 1134 self.from_primitive_values = [('::1', netaddr.IPAddress('::1'))] 1135 1136 def test_get_schema(self): 1137 self.assertEqual({'type': ['string'], 'readonly': False, 1138 'format': 'ipv6'}, 1139 self.field.get_schema()) 1140 1141 1142class TestIPV4AndV6Address(TestField): 1143 def setUp(self): 1144 super(TestIPV4AndV6Address, self).setUp() 1145 self.field = fields.IPV4AndV6Address() 1146 self.coerce_good_values = [('::1', netaddr.IPAddress('::1')), 1147 (netaddr.IPAddress('::1'), 1148 netaddr.IPAddress('::1')), 1149 ('1.2.3.4', 1150 netaddr.IPAddress('1.2.3.4')), 1151 (netaddr.IPAddress('1.2.3.4'), 1152 netaddr.IPAddress('1.2.3.4'))] 1153 self.coerce_bad_values = ['1-2', 'foo'] 1154 self.to_primitive_values = [(netaddr.IPAddress('::1'), 1155 '::1'), 1156 (netaddr.IPAddress('1.2.3.4'), 1157 '1.2.3.4')] 1158 self.from_primitive_values = [('::1', 1159 netaddr.IPAddress('::1')), 1160 ('1.2.3.4', 1161 netaddr.IPAddress('1.2.3.4'))] 1162 1163 def test_get_schema(self): 1164 self.assertEqual({'oneOf': [{'format': 'ipv4', 'type': ['string']}, 1165 {'format': 'ipv6', 'type': ['string']}]}, 1166 self.field.get_schema()) 1167 1168 1169class TestIPNetwork(TestField): 1170 def setUp(self): 1171 super(TestIPNetwork, self).setUp() 1172 self.field = fields.IPNetworkField() 1173 self.coerce_good_values = [('::1/0', netaddr.IPNetwork('::1/0')), 1174 ('1.2.3.4/24', 1175 netaddr.IPNetwork('1.2.3.4/24')), 1176 (netaddr.IPNetwork('::1/32'), 1177 netaddr.IPNetwork('::1/32'))] 1178 self.coerce_bad_values = ['foo'] 1179 self.to_primitive_values = [(netaddr.IPNetwork('::1/0'), '::1/0')] 1180 self.from_primitive_values = [('::1/0', 1181 netaddr.IPNetwork('::1/0'))] 1182 1183 1184class TestIPV4Network(TestField): 1185 def setUp(self): 1186 super(TestIPV4Network, self).setUp() 1187 self.field = fields.IPV4NetworkField() 1188 self.coerce_good_values = [('1.2.3.4/24', 1189 netaddr.IPNetwork('1.2.3.4/24'))] 1190 self.coerce_bad_values = ['foo', '::1/32'] 1191 self.to_primitive_values = [(netaddr.IPNetwork('1.2.3.4/24'), 1192 '1.2.3.4/24')] 1193 self.from_primitive_values = [('1.2.3.4/24', 1194 netaddr.IPNetwork('1.2.3.4/24'))] 1195 1196 def test_get_schema(self): 1197 schema = self.field.get_schema() 1198 self.assertEqual(['string'], schema['type']) 1199 self.assertEqual(False, schema['readonly']) 1200 pattern = schema['pattern'] 1201 for _, valid_val in self.coerce_good_values: 1202 self.assertRegex(str(valid_val), pattern) 1203 invalid_vals = [x for x in self.coerce_bad_values] 1204 for invalid_val in invalid_vals: 1205 self.assertNotRegex(str(invalid_val), pattern) 1206 1207 1208class TestIPV6Network(TestField): 1209 def setUp(self): 1210 super(TestIPV6Network, self).setUp() 1211 self.field = fields.IPV6NetworkField() 1212 self.coerce_good_values = [('::1/0', netaddr.IPNetwork('::1/0')), 1213 (netaddr.IPNetwork('::1/32'), 1214 netaddr.IPNetwork('::1/32'))] 1215 self.coerce_bad_values = ['foo', '1.2.3.4/24'] 1216 self.to_primitive_values = [(netaddr.IPNetwork('::1/0'), '::1/0')] 1217 self.from_primitive_values = [('::1/0', 1218 netaddr.IPNetwork('::1/0'))] 1219 1220 def test_get_schema(self): 1221 schema = self.field.get_schema() 1222 self.assertEqual(['string'], schema['type']) 1223 self.assertEqual(False, schema['readonly']) 1224 pattern = schema['pattern'] 1225 for _, valid_val in self.coerce_good_values: 1226 self.assertRegex(str(valid_val), pattern) 1227 invalid_vals = [x for x in self.coerce_bad_values] 1228 for invalid_val in invalid_vals: 1229 self.assertNotRegex(str(invalid_val), pattern) 1230 1231 1232class FakeCounter(object): 1233 def __init__(self): 1234 self.n = 0 1235 1236 def __iter__(self): 1237 return self 1238 1239 def __next__(self): 1240 if self.n <= 4: 1241 self.n += 1 1242 return self.n 1243 else: 1244 raise StopIteration 1245 1246 1247class TestListTypes(test.TestCase): 1248 1249 def test_regular_list(self): 1250 fields.List(fields.Integer).coerce(None, None, [1, 2]) 1251 1252 def test_non_iterable(self): 1253 self.assertRaises(ValueError, 1254 fields.List(fields.Integer).coerce, None, None, 2) 1255 1256 def test_string_iterable(self): 1257 self.assertRaises(ValueError, 1258 fields.List(fields.Integer).coerce, None, None, 1259 'hello') 1260 1261 def test_mapping_iterable(self): 1262 self.assertRaises(ValueError, 1263 fields.List(fields.Integer).coerce, None, None, 1264 {'a': 1, 'b': 2}) 1265 1266 def test_iter_class(self): 1267 fields.List(fields.Integer).coerce(None, None, FakeCounter()) 1268