/tests/regressiontests/fixtures_regress/tests.py
Python | 620 lines | 582 code | 10 blank | 28 comment | 5 complexity | 74dd7a0d5e5cb13262412e4353992b50 MD5 | raw file
Possible License(s): BSD-3-Clause
1# -*- coding: utf-8 -*- 2# Unittests for fixtures. 3import os 4import re 5import sys 6try: 7 from cStringIO import StringIO 8except ImportError: 9 from StringIO import StringIO 10 11from django.core import management 12from django.core.management.commands.dumpdata import sort_dependencies 13from django.core.management.base import CommandError 14from django.db.models import signals 15from django.db import transaction 16from django.test import TestCase, TransactionTestCase, skipIfDBFeature, \ 17 skipUnlessDBFeature 18 19from models import Animal, Stuff 20from models import Absolute, Parent, Child 21from models import Article, Widget 22from models import Store, Person, Book 23from models import NKChild, RefToNKChild 24from models import Circle1, Circle2, Circle3 25from models import ExternalDependency 26from models import Thingy 27 28 29pre_save_checks = [] 30def animal_pre_save_check(signal, sender, instance, **kwargs): 31 "A signal that is used to check the type of data loaded from fixtures" 32 pre_save_checks.append( 33 ( 34 'Count = %s (%s)' % (instance.count, type(instance.count)), 35 'Weight = %s (%s)' % (instance.weight, type(instance.weight)), 36 ) 37 ) 38 39 40class TestFixtures(TestCase): 41 def test_duplicate_pk(self): 42 """ 43 This is a regression test for ticket #3790. 44 """ 45 # Load a fixture that uses PK=1 46 management.call_command( 47 'loaddata', 48 'sequence', 49 verbosity=0, 50 commit=False 51 ) 52 53 # Create a new animal. Without a sequence reset, this new object 54 # will take a PK of 1 (on Postgres), and the save will fail. 55 56 animal = Animal( 57 name='Platypus', 58 latin_name='Ornithorhynchus anatinus', 59 count=2, 60 weight=2.2 61 ) 62 animal.save() 63 self.assertGreater(animal.id, 1) 64 65 @skipIfDBFeature('interprets_empty_strings_as_nulls') 66 def test_pretty_print_xml(self): 67 """ 68 Regression test for ticket #4558 -- pretty printing of XML fixtures 69 doesn't affect parsing of None values. 70 """ 71 # Load a pretty-printed XML fixture with Nulls. 72 management.call_command( 73 'loaddata', 74 'pretty.xml', 75 verbosity=0, 76 commit=False 77 ) 78 self.assertEqual(Stuff.objects.all()[0].name, None) 79 self.assertEqual(Stuff.objects.all()[0].owner, None) 80 81 @skipUnlessDBFeature('interprets_empty_strings_as_nulls') 82 def test_pretty_print_xml_empty_strings(self): 83 """ 84 Regression test for ticket #4558 -- pretty printing of XML fixtures 85 doesn't affect parsing of None values. 86 """ 87 # Load a pretty-printed XML fixture with Nulls. 88 management.call_command( 89 'loaddata', 90 'pretty.xml', 91 verbosity=0, 92 commit=False 93 ) 94 self.assertEqual(Stuff.objects.all()[0].name, u'') 95 self.assertEqual(Stuff.objects.all()[0].owner, None) 96 97 def test_absolute_path(self): 98 """ 99 Regression test for ticket #6436 -- 100 os.path.join will throw away the initial parts of a path if it 101 encounters an absolute path. 102 This means that if a fixture is specified as an absolute path, 103 we need to make sure we don't discover the absolute path in every 104 fixture directory. 105 """ 106 load_absolute_path = os.path.join( 107 os.path.dirname(__file__), 108 'fixtures', 109 'absolute.json' 110 ) 111 management.call_command( 112 'loaddata', 113 load_absolute_path, 114 verbosity=0, 115 commit=False 116 ) 117 self.assertEqual(Absolute.load_count, 1) 118 119 120 def test_unknown_format(self): 121 """ 122 Test for ticket #4371 -- Loading data of an unknown format should fail 123 Validate that error conditions are caught correctly 124 """ 125 stderr = StringIO() 126 management.call_command( 127 'loaddata', 128 'bad_fixture1.unkn', 129 verbosity=0, 130 commit=False, 131 stderr=stderr, 132 ) 133 self.assertEqual( 134 stderr.getvalue(), 135 "Problem installing fixture 'bad_fixture1': unkn is not a known serialization format.\n" 136 ) 137 138 def test_invalid_data(self): 139 """ 140 Test for ticket #4371 -- Loading a fixture file with invalid data 141 using explicit filename. 142 Validate that error conditions are caught correctly 143 """ 144 stderr = StringIO() 145 management.call_command( 146 'loaddata', 147 'bad_fixture2.xml', 148 verbosity=0, 149 commit=False, 150 stderr=stderr, 151 ) 152 self.assertEqual( 153 stderr.getvalue(), 154 "No fixture data found for 'bad_fixture2'. (File format may be invalid.)\n" 155 ) 156 157 def test_invalid_data_no_ext(self): 158 """ 159 Test for ticket #4371 -- Loading a fixture file with invalid data 160 without file extension. 161 Validate that error conditions are caught correctly 162 """ 163 stderr = StringIO() 164 management.call_command( 165 'loaddata', 166 'bad_fixture2', 167 verbosity=0, 168 commit=False, 169 stderr=stderr, 170 ) 171 self.assertEqual( 172 stderr.getvalue(), 173 "No fixture data found for 'bad_fixture2'. (File format may be invalid.)\n" 174 ) 175 176 def test_empty(self): 177 """ 178 Test for ticket #4371 -- Loading a fixture file with no data returns an error. 179 Validate that error conditions are caught correctly 180 """ 181 stderr = StringIO() 182 management.call_command( 183 'loaddata', 184 'empty', 185 verbosity=0, 186 commit=False, 187 stderr=stderr, 188 ) 189 self.assertEqual( 190 stderr.getvalue(), 191 "No fixture data found for 'empty'. (File format may be invalid.)\n" 192 ) 193 194 def test_abort_loaddata_on_error(self): 195 """ 196 Test for ticket #4371 -- If any of the fixtures contain an error, 197 loading is aborted. 198 Validate that error conditions are caught correctly 199 """ 200 stderr = StringIO() 201 management.call_command( 202 'loaddata', 203 'empty', 204 verbosity=0, 205 commit=False, 206 stderr=stderr, 207 ) 208 self.assertEqual( 209 stderr.getvalue(), 210 "No fixture data found for 'empty'. (File format may be invalid.)\n" 211 ) 212 213 def test_error_message(self): 214 """ 215 (Regression for #9011 - error message is correct) 216 """ 217 stderr = StringIO() 218 management.call_command( 219 'loaddata', 220 'bad_fixture2', 221 'animal', 222 verbosity=0, 223 commit=False, 224 stderr=stderr, 225 ) 226 self.assertEqual( 227 stderr.getvalue(), 228 "No fixture data found for 'bad_fixture2'. (File format may be invalid.)\n" 229 ) 230 231 def test_pg_sequence_resetting_checks(self): 232 """ 233 Test for ticket #7565 -- PostgreSQL sequence resetting checks shouldn't 234 ascend to parent models when inheritance is used 235 (since they are treated individually). 236 """ 237 management.call_command( 238 'loaddata', 239 'model-inheritance.json', 240 verbosity=0, 241 commit=False 242 ) 243 self.assertEqual(Parent.objects.all()[0].id, 1) 244 self.assertEqual(Child.objects.all()[0].id, 1) 245 246 def test_close_connection_after_loaddata(self): 247 """ 248 Test for ticket #7572 -- MySQL has a problem if the same connection is 249 used to create tables, load data, and then query over that data. 250 To compensate, we close the connection after running loaddata. 251 This ensures that a new connection is opened when test queries are 252 issued. 253 """ 254 management.call_command( 255 'loaddata', 256 'big-fixture.json', 257 verbosity=0, 258 commit=False 259 ) 260 articles = Article.objects.exclude(id=9) 261 self.assertEqual( 262 list(articles.values_list('id', flat=True)), 263 [1, 2, 3, 4, 5, 6, 7, 8] 264 ) 265 # Just for good measure, run the same query again. 266 # Under the influence of ticket #7572, this will 267 # give a different result to the previous call. 268 self.assertEqual( 269 list(articles.values_list('id', flat=True)), 270 [1, 2, 3, 4, 5, 6, 7, 8] 271 ) 272 273 def test_field_value_coerce(self): 274 """ 275 Test for tickets #8298, #9942 - Field values should be coerced into the 276 correct type by the deserializer, not as part of the database write. 277 """ 278 global pre_save_checks 279 pre_save_checks = [] 280 signals.pre_save.connect(animal_pre_save_check) 281 management.call_command( 282 'loaddata', 283 'animal.xml', 284 verbosity=0, 285 commit=False, 286 ) 287 self.assertEqual( 288 pre_save_checks, 289 [ 290 ("Count = 42 (<type 'int'>)", "Weight = 1.2 (<type 'float'>)") 291 ] 292 ) 293 signals.pre_save.disconnect(animal_pre_save_check) 294 295 def test_dumpdata_uses_default_manager(self): 296 """ 297 Regression for #11286 298 Ensure that dumpdata honors the default manager 299 Dump the current contents of the database as a JSON fixture 300 """ 301 management.call_command( 302 'loaddata', 303 'animal.xml', 304 verbosity=0, 305 commit=False, 306 ) 307 management.call_command( 308 'loaddata', 309 'sequence.json', 310 verbosity=0, 311 commit=False, 312 ) 313 animal = Animal( 314 name='Platypus', 315 latin_name='Ornithorhynchus anatinus', 316 count=2, 317 weight=2.2 318 ) 319 animal.save() 320 321 stdout = StringIO() 322 management.call_command( 323 'dumpdata', 324 'fixtures_regress.animal', 325 format='json', 326 stdout=stdout 327 ) 328 329 # Output order isn't guaranteed, so check for parts 330 data = stdout.getvalue() 331 332 # Get rid of artifacts like '000000002' to eliminate the differences 333 # between different Python versions. 334 data = re.sub('0{6,}\d', '', data) 335 336 lion_json = '{"pk": 1, "model": "fixtures_regress.animal", "fields": {"count": 3, "weight": 1.2, "name": "Lion", "latin_name": "Panthera leo"}}' 337 emu_json = '{"pk": 10, "model": "fixtures_regress.animal", "fields": {"count": 42, "weight": 1.2, "name": "Emu", "latin_name": "Dromaius novaehollandiae"}}' 338 platypus_json = '{"pk": %d, "model": "fixtures_regress.animal", "fields": {"count": 2, "weight": 2.2, "name": "Platypus", "latin_name": "Ornithorhynchus anatinus"}}' 339 platypus_json = platypus_json % animal.pk 340 341 self.assertEqual(len(data), len('[%s]' % ', '.join([lion_json, emu_json, platypus_json]))) 342 self.assertTrue(lion_json in data) 343 self.assertTrue(emu_json in data) 344 self.assertTrue(platypus_json in data) 345 346 def test_proxy_model_included(self): 347 """ 348 Regression for #11428 - Proxy models aren't included when you dumpdata 349 """ 350 stdout = StringIO() 351 # Create an instance of the concrete class 352 widget = Widget.objects.create(name='grommet') 353 management.call_command( 354 'dumpdata', 355 'fixtures_regress.widget', 356 'fixtures_regress.widgetproxy', 357 format='json', 358 stdout=stdout 359 ) 360 self.assertEqual( 361 stdout.getvalue(), 362 """[{"pk": %d, "model": "fixtures_regress.widget", "fields": {"name": "grommet"}}]""" 363 % widget.pk 364 ) 365 366 367class NaturalKeyFixtureTests(TestCase): 368 def assertRaisesMessage(self, exc, msg, func, *args, **kwargs): 369 try: 370 func(*args, **kwargs) 371 except Exception, e: 372 self.assertEqual(msg, str(e)) 373 self.assertTrue(isinstance(e, exc), "Expected %s, got %s" % (exc, type(e))) 374 375 def test_nk_deserialize(self): 376 """ 377 Test for ticket #13030 - Python based parser version 378 natural keys deserialize with fk to inheriting model 379 """ 380 management.call_command( 381 'loaddata', 382 'model-inheritance.json', 383 verbosity=0, 384 commit=False 385 ) 386 management.call_command( 387 'loaddata', 388 'nk-inheritance.json', 389 verbosity=0, 390 commit=False 391 ) 392 self.assertEqual( 393 NKChild.objects.get(pk=1).data, 394 'apple' 395 ) 396 397 self.assertEqual( 398 RefToNKChild.objects.get(pk=1).nk_fk.data, 399 'apple' 400 ) 401 402 def test_nk_deserialize_xml(self): 403 """ 404 Test for ticket #13030 - XML version 405 natural keys deserialize with fk to inheriting model 406 """ 407 management.call_command( 408 'loaddata', 409 'model-inheritance.json', 410 verbosity=0, 411 commit=False 412 ) 413 management.call_command( 414 'loaddata', 415 'nk-inheritance.json', 416 verbosity=0, 417 commit=False 418 ) 419 management.call_command( 420 'loaddata', 421 'nk-inheritance2.xml', 422 verbosity=0, 423 commit=False 424 ) 425 self.assertEqual( 426 NKChild.objects.get(pk=2).data, 427 'banana' 428 ) 429 self.assertEqual( 430 RefToNKChild.objects.get(pk=2).nk_fk.data, 431 'apple' 432 ) 433 434 def test_nk_on_serialize(self): 435 """ 436 Check that natural key requirements are taken into account 437 when serializing models 438 """ 439 management.call_command( 440 'loaddata', 441 'forward_ref_lookup.json', 442 verbosity=0, 443 commit=False 444 ) 445 446 stdout = StringIO() 447 management.call_command( 448 'dumpdata', 449 'fixtures_regress.book', 450 'fixtures_regress.person', 451 'fixtures_regress.store', 452 verbosity=0, 453 format='json', 454 use_natural_keys=True, 455 stdout=stdout, 456 ) 457 self.assertEqual( 458 stdout.getvalue(), 459 """[{"pk": 2, "model": "fixtures_regress.store", "fields": {"name": "Amazon"}}, {"pk": 3, "model": "fixtures_regress.store", "fields": {"name": "Borders"}}, {"pk": 4, "model": "fixtures_regress.person", "fields": {"name": "Neal Stephenson"}}, {"pk": 1, "model": "fixtures_regress.book", "fields": {"stores": [["Amazon"], ["Borders"]], "name": "Cryptonomicon", "author": ["Neal Stephenson"]}}]""" 460 ) 461 462 def test_dependency_sorting(self): 463 """ 464 Now lets check the dependency sorting explicitly 465 It doesn't matter what order you mention the models 466 Store *must* be serialized before then Person, and both 467 must be serialized before Book. 468 """ 469 sorted_deps = sort_dependencies( 470 [('fixtures_regress', [Book, Person, Store])] 471 ) 472 self.assertEqual( 473 sorted_deps, 474 [Store, Person, Book] 475 ) 476 477 def test_dependency_sorting_2(self): 478 sorted_deps = sort_dependencies( 479 [('fixtures_regress', [Book, Store, Person])] 480 ) 481 self.assertEqual( 482 sorted_deps, 483 [Store, Person, Book] 484 ) 485 486 def test_dependency_sorting_3(self): 487 sorted_deps = sort_dependencies( 488 [('fixtures_regress', [Store, Book, Person])] 489 ) 490 self.assertEqual( 491 sorted_deps, 492 [Store, Person, Book] 493 ) 494 495 def test_dependency_sorting_4(self): 496 sorted_deps = sort_dependencies( 497 [('fixtures_regress', [Store, Person, Book])] 498 ) 499 self.assertEqual( 500 sorted_deps, 501 [Store, Person, Book] 502 ) 503 504 def test_dependency_sorting_5(self): 505 sorted_deps = sort_dependencies( 506 [('fixtures_regress', [Person, Book, Store])] 507 ) 508 self.assertEqual( 509 sorted_deps, 510 [Store, Person, Book] 511 ) 512 513 def test_dependency_sorting_6(self): 514 sorted_deps = sort_dependencies( 515 [('fixtures_regress', [Person, Store, Book])] 516 ) 517 self.assertEqual( 518 sorted_deps, 519 [Store, Person, Book] 520 ) 521 522 def test_dependency_sorting_dangling(self): 523 sorted_deps = sort_dependencies( 524 [('fixtures_regress', [Person, Circle1, Store, Book])] 525 ) 526 self.assertEqual( 527 sorted_deps, 528 [Circle1, Store, Person, Book] 529 ) 530 531 def test_dependency_sorting_tight_circular(self): 532 self.assertRaisesMessage( 533 CommandError, 534 """Can't resolve dependencies for fixtures_regress.Circle1, fixtures_regress.Circle2 in serialized app list.""", 535 sort_dependencies, 536 [('fixtures_regress', [Person, Circle2, Circle1, Store, Book])], 537 ) 538 539 def test_dependency_sorting_tight_circular_2(self): 540 self.assertRaisesMessage( 541 CommandError, 542 """Can't resolve dependencies for fixtures_regress.Circle1, fixtures_regress.Circle2 in serialized app list.""", 543 sort_dependencies, 544 [('fixtures_regress', [Circle1, Book, Circle2])], 545 ) 546 547 def test_dependency_self_referential(self): 548 self.assertRaisesMessage( 549 CommandError, 550 """Can't resolve dependencies for fixtures_regress.Circle3 in serialized app list.""", 551 sort_dependencies, 552 [('fixtures_regress', [Book, Circle3])], 553 ) 554 555 def test_dependency_sorting_long(self): 556 self.assertRaisesMessage( 557 CommandError, 558 """Can't resolve dependencies for fixtures_regress.Circle1, fixtures_regress.Circle2, fixtures_regress.Circle3 in serialized app list.""", 559 sort_dependencies, 560 [('fixtures_regress', [Person, Circle2, Circle1, Circle3, Store, Book])], 561 ) 562 563 def test_dependency_sorting_normal(self): 564 sorted_deps = sort_dependencies( 565 [('fixtures_regress', [Person, ExternalDependency, Book])] 566 ) 567 self.assertEqual( 568 sorted_deps, 569 [Person, Book, ExternalDependency] 570 ) 571 572 def test_normal_pk(self): 573 """ 574 Check that normal primary keys still work 575 on a model with natural key capabilities 576 """ 577 management.call_command( 578 'loaddata', 579 'non_natural_1.json', 580 verbosity=0, 581 commit=False 582 ) 583 management.call_command( 584 'loaddata', 585 'forward_ref_lookup.json', 586 verbosity=0, 587 commit=False 588 ) 589 management.call_command( 590 'loaddata', 591 'non_natural_2.xml', 592 verbosity=0, 593 commit=False 594 ) 595 books = Book.objects.all() 596 self.assertEqual( 597 books.__repr__(), 598 """[<Book: Cryptonomicon by Neal Stephenson (available at Amazon, Borders)>, <Book: Ender's Game by Orson Scott Card (available at Collins Bookstore)>, <Book: Permutation City by Greg Egan (available at Angus and Robertson)>]""" 599 ) 600 601 602class TestTicket11101(TransactionTestCase): 603 604 def ticket_11101(self): 605 management.call_command( 606 'loaddata', 607 'thingy.json', 608 verbosity=0, 609 commit=False 610 ) 611 self.assertEqual(Thingy.objects.count(), 1) 612 transaction.rollback() 613 self.assertEqual(Thingy.objects.count(), 0) 614 transaction.commit() 615 616 @skipUnlessDBFeature('supports_transactions') 617 def test_ticket_11101(self): 618 """Test that fixtures can be rolled back (ticket #11101).""" 619 ticket_11101 = transaction.commit_manually(self.ticket_11101) 620 ticket_11101()