mirror of
				https://github.com/django/django.git
				synced 2025-10-31 09:41:08 +00:00 
			
		
		
		
	[1.7.x] Fixed #22602 -- Improved code coverage of makemigrations command tests.
Backport of f851a954ac from master
			
			
This commit is contained in:
		| @@ -7,6 +7,7 @@ import shutil | |||||||
|  |  | ||||||
| from django.apps import apps | from django.apps import apps | ||||||
| from django.core.management import call_command, CommandError | from django.core.management import call_command, CommandError | ||||||
|  | from django.db.migrations import questioner | ||||||
| from django.test import override_settings, override_system_checks | from django.test import override_settings, override_system_checks | ||||||
| from django.utils import six | from django.utils import six | ||||||
| from django.utils._os import upath | from django.utils._os import upath | ||||||
| @@ -211,3 +212,153 @@ class MakeMigrationsTests(MigrationTestBase): | |||||||
|             call_command("makemigrations", merge=True, verbosity=0) |             call_command("makemigrations", merge=True, verbosity=0) | ||||||
|         except CommandError: |         except CommandError: | ||||||
|             self.fail("Makemigrations errored in merge mode with conflicts") |             self.fail("Makemigrations errored in merge mode with conflicts") | ||||||
|  |  | ||||||
|  |     @override_system_checks([]) | ||||||
|  |     @override_settings(MIGRATION_MODULES={"migrations": "migrations.test_migrations"}) | ||||||
|  |     def test_makemigrations_merge_no_conflict(self): | ||||||
|  |         """ | ||||||
|  |         Makes sure that makemigrations exits if in merge mode with no conflicts. | ||||||
|  |         """ | ||||||
|  |         stdout = six.StringIO() | ||||||
|  |         try: | ||||||
|  |             call_command("makemigrations", merge=True, stdout=stdout) | ||||||
|  |         except CommandError: | ||||||
|  |             self.fail("Makemigrations errored in merge mode with no conflicts") | ||||||
|  |         self.assertIn("No conflicts detected to merge.", stdout.getvalue()) | ||||||
|  |  | ||||||
|  |     @override_system_checks([]) | ||||||
|  |     def test_makemigrations_no_app_sys_exit(self): | ||||||
|  |         """ | ||||||
|  |         Makes sure that makemigrations exits if a non-existent app is specified. | ||||||
|  |         """ | ||||||
|  |         stderr = six.StringIO() | ||||||
|  |         with self.assertRaises(SystemExit): | ||||||
|  |             call_command("makemigrations", "this_app_does_not_exist", stderr=stderr) | ||||||
|  |         self.assertIn("'this_app_does_not_exist' could not be found.", stderr.getvalue()) | ||||||
|  |  | ||||||
|  |     @override_system_checks([]) | ||||||
|  |     def test_makemigrations_empty_no_app_specified(self): | ||||||
|  |         """ | ||||||
|  |         Makes sure that makemigrations exits if no app is specified with 'empty' mode. | ||||||
|  |         """ | ||||||
|  |         with override_settings(MIGRATION_MODULES={"migrations": self.migration_pkg}): | ||||||
|  |             self.assertRaises(CommandError, call_command, "makemigrations", empty=True) | ||||||
|  |  | ||||||
|  |     @override_system_checks([]) | ||||||
|  |     def test_makemigrations_empty_migration(self): | ||||||
|  |         """ | ||||||
|  |         Makes sure that makemigrations properly constructs an empty migration. | ||||||
|  |         """ | ||||||
|  |         with override_settings(MIGRATION_MODULES={"migrations": self.migration_pkg}): | ||||||
|  |             try: | ||||||
|  |                 call_command("makemigrations", "migrations", empty=True, verbosity=0) | ||||||
|  |             except CommandError: | ||||||
|  |                 self.fail("Makemigrations errored in creating empty migration for a proper app.") | ||||||
|  |  | ||||||
|  |         initial_file = os.path.join(self.migration_dir, "0001_initial.py") | ||||||
|  |  | ||||||
|  |         # Check for existing 0001_initial.py file in migration folder | ||||||
|  |         self.assertTrue(os.path.exists(initial_file)) | ||||||
|  |  | ||||||
|  |         with codecs.open(initial_file, 'r', encoding='utf-8') as fp: | ||||||
|  |             content = fp.read() | ||||||
|  |             self.assertTrue('# -*- coding: utf-8 -*-' in content) | ||||||
|  |  | ||||||
|  |             # Remove all whitespace to check for empty dependencies and operations | ||||||
|  |             content = content.replace(' ', '') | ||||||
|  |             self.assertIn('dependencies=[\n]', content) | ||||||
|  |             self.assertIn('operations=[\n]', content) | ||||||
|  |  | ||||||
|  |     @override_system_checks([]) | ||||||
|  |     def test_makemigrations_no_changes_no_apps(self): | ||||||
|  |         """ | ||||||
|  |         Makes sure that makemigrations exits when there are no changes and no apps are specified. | ||||||
|  |         """ | ||||||
|  |         stdout = six.StringIO() | ||||||
|  |         call_command("makemigrations", stdout=stdout) | ||||||
|  |         self.assertIn("No changes detected", stdout.getvalue()) | ||||||
|  |  | ||||||
|  |     @override_system_checks([]) | ||||||
|  |     @override_settings(MIGRATION_MODULES={"migrations": "migrations.test_migrations_no_changes"}) | ||||||
|  |     def test_makemigrations_no_changes(self): | ||||||
|  |         """ | ||||||
|  |         Makes sure that makemigrations exits when there are no changes to an app. | ||||||
|  |         """ | ||||||
|  |         stdout = six.StringIO() | ||||||
|  |         call_command("makemigrations", "migrations", stdout=stdout) | ||||||
|  |         self.assertIn("No changes detected in app 'migrations'", stdout.getvalue()) | ||||||
|  |  | ||||||
|  |     @override_system_checks([]) | ||||||
|  |     def test_makemigrations_migrations_announce(self): | ||||||
|  |         """ | ||||||
|  |         Makes sure that makemigrations announces the migration at the default verbosity level. | ||||||
|  |         """ | ||||||
|  |         stdout = six.StringIO() | ||||||
|  |         with override_settings(MIGRATION_MODULES={"migrations": self.migration_pkg}): | ||||||
|  |             call_command("makemigrations", "migrations", stdout=stdout) | ||||||
|  |         self.assertIn("Migrations for 'migrations'", stdout.getvalue()) | ||||||
|  |  | ||||||
|  |     @override_system_checks([]) | ||||||
|  |     @override_settings(MIGRATION_MODULES={"migrations": "migrations.test_migrations_no_ancestor"}) | ||||||
|  |     def test_makemigrations_no_common_ancestor(self): | ||||||
|  |         """ | ||||||
|  |         Makes sure that makemigrations fails to merge migrations with no common ancestor. | ||||||
|  |         """ | ||||||
|  |         with self.assertRaises(ValueError) as context: | ||||||
|  |             call_command("makemigrations", "migrations", merge=True) | ||||||
|  |         exception_message = str(context.exception) | ||||||
|  |         self.assertIn("Could not find common ancestor of", exception_message) | ||||||
|  |         self.assertIn("0002_second", exception_message) | ||||||
|  |         self.assertIn("0002_conflicting_second", exception_message) | ||||||
|  |  | ||||||
|  |     @override_system_checks([]) | ||||||
|  |     @override_settings(MIGRATION_MODULES={"migrations": "migrations.test_migrations_conflict"}) | ||||||
|  |     def test_makemigrations_interactive_reject(self): | ||||||
|  |         """ | ||||||
|  |         Makes sure that makemigrations enters and exits interactive mode properly. | ||||||
|  |         """ | ||||||
|  |         # Monkeypatch interactive questioner to auto reject | ||||||
|  |         old_input = questioner.input | ||||||
|  |         questioner.input = lambda _: "N" | ||||||
|  |         try: | ||||||
|  |             call_command("makemigrations", "migrations", merge=True, interactive=True, verbosity=0) | ||||||
|  |             merge_file = os.path.join(self.test_dir, 'test_migrations_conflict', '0003_merge.py') | ||||||
|  |             self.assertFalse(os.path.exists(merge_file)) | ||||||
|  |         except CommandError: | ||||||
|  |             self.fail("Makemigrations failed while running interactive questioner") | ||||||
|  |         finally: | ||||||
|  |             questioner.input = old_input | ||||||
|  |  | ||||||
|  |     @override_system_checks([]) | ||||||
|  |     @override_settings(MIGRATION_MODULES={"migrations": "migrations.test_migrations_conflict"}) | ||||||
|  |     def test_makemigrations_interactive_accept(self): | ||||||
|  |         """ | ||||||
|  |         Makes sure that makemigrations enters interactive mode and merges properly. | ||||||
|  |         """ | ||||||
|  |         # Monkeypatch interactive questioner to auto accept | ||||||
|  |         old_input = questioner.input | ||||||
|  |         questioner.input = lambda _: "y" | ||||||
|  |         stdout = six.StringIO() | ||||||
|  |         try: | ||||||
|  |             call_command("makemigrations", "migrations", merge=True, interactive=True, stdout=stdout) | ||||||
|  |             merge_file = os.path.join(self.test_dir, 'test_migrations_conflict', '0003_merge.py') | ||||||
|  |             self.assertTrue(os.path.exists(merge_file)) | ||||||
|  |             os.remove(merge_file) | ||||||
|  |             self.assertFalse(os.path.exists(merge_file)) | ||||||
|  |         except CommandError: | ||||||
|  |             self.fail("Makemigrations failed while running interactive questioner") | ||||||
|  |         finally: | ||||||
|  |             questioner.input = old_input | ||||||
|  |         self.assertIn("Created new merge migration", stdout.getvalue()) | ||||||
|  |  | ||||||
|  |     @override_system_checks([]) | ||||||
|  |     @override_settings(MIGRATION_MODULES={"migrations": "migrations.test_migrations_conflict"}) | ||||||
|  |     def test_makemigrations_handle_merge(self): | ||||||
|  |         """ | ||||||
|  |         Makes sure that makemigrations properly merges the conflicting migrations. | ||||||
|  |         """ | ||||||
|  |         stdout = six.StringIO() | ||||||
|  |         call_command("makemigrations", "migrations", merge=True, stdout=stdout) | ||||||
|  |         self.assertIn("Merging migrations", stdout.getvalue()) | ||||||
|  |         self.assertIn("Branch 0002_second", stdout.getvalue()) | ||||||
|  |         self.assertIn("Branch 0002_conflicting_second", stdout.getvalue()) | ||||||
|   | |||||||
							
								
								
									
										30
									
								
								tests/migrations/test_migrations_no_ancestor/0001_initial.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								tests/migrations/test_migrations_no_ancestor/0001_initial.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,30 @@ | |||||||
|  | # -*- coding: utf-8 -*- | ||||||
|  | from __future__ import unicode_literals | ||||||
|  |  | ||||||
|  | from django.db import migrations, models | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class Migration(migrations.Migration): | ||||||
|  |  | ||||||
|  |     operations = [ | ||||||
|  |  | ||||||
|  |         migrations.CreateModel( | ||||||
|  |             "Author", | ||||||
|  |             [ | ||||||
|  |                 ("id", models.AutoField(primary_key=True)), | ||||||
|  |                 ("name", models.CharField(max_length=255)), | ||||||
|  |                 ("slug", models.SlugField(null=True)), | ||||||
|  |                 ("age", models.IntegerField(default=0)), | ||||||
|  |                 ("silly_field", models.BooleanField(default=False)), | ||||||
|  |             ], | ||||||
|  |         ), | ||||||
|  |  | ||||||
|  |         migrations.CreateModel( | ||||||
|  |             "Tribble", | ||||||
|  |             [ | ||||||
|  |                 ("id", models.AutoField(primary_key=True)), | ||||||
|  |                 ("fluffy", models.BooleanField(default=True)), | ||||||
|  |             ], | ||||||
|  |         ) | ||||||
|  |  | ||||||
|  |     ] | ||||||
| @@ -0,0 +1,28 @@ | |||||||
|  | # -*- coding: utf-8 -*- | ||||||
|  | from __future__ import unicode_literals | ||||||
|  |  | ||||||
|  | from django.db import migrations, models | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class Migration(migrations.Migration): | ||||||
|  |  | ||||||
|  |     dependencies = [ | ||||||
|  |     ] | ||||||
|  |  | ||||||
|  |     operations = [ | ||||||
|  |  | ||||||
|  |         migrations.DeleteModel("Tribble"), | ||||||
|  |  | ||||||
|  |         migrations.RemoveField("Author", "silly_field"), | ||||||
|  |  | ||||||
|  |         migrations.AddField("Author", "rating", models.IntegerField(default=0)), | ||||||
|  |  | ||||||
|  |         migrations.CreateModel( | ||||||
|  |             "Book", | ||||||
|  |             [ | ||||||
|  |                 ("id", models.AutoField(primary_key=True)), | ||||||
|  |                 ("author", models.ForeignKey("migrations.Author", null=True)), | ||||||
|  |             ], | ||||||
|  |         ) | ||||||
|  |  | ||||||
|  |     ] | ||||||
							
								
								
									
										29
									
								
								tests/migrations/test_migrations_no_ancestor/0002_second.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								tests/migrations/test_migrations_no_ancestor/0002_second.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,29 @@ | |||||||
|  | # -*- coding: utf-8 -*- | ||||||
|  | from __future__ import unicode_literals | ||||||
|  |  | ||||||
|  | from django.db import migrations, models | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class Migration(migrations.Migration): | ||||||
|  |  | ||||||
|  |     dependencies = [ | ||||||
|  |         ("migrations", "0001_initial"), | ||||||
|  |     ] | ||||||
|  |  | ||||||
|  |     operations = [ | ||||||
|  |  | ||||||
|  |         migrations.DeleteModel("Tribble"), | ||||||
|  |  | ||||||
|  |         migrations.RemoveField("Author", "silly_field"), | ||||||
|  |  | ||||||
|  |         migrations.AddField("Author", "rating", models.IntegerField(default=0)), | ||||||
|  |  | ||||||
|  |         migrations.CreateModel( | ||||||
|  |             "Book", | ||||||
|  |             [ | ||||||
|  |                 ("id", models.AutoField(primary_key=True)), | ||||||
|  |                 ("author", models.ForeignKey("migrations.Author", null=True)), | ||||||
|  |             ], | ||||||
|  |         ) | ||||||
|  |  | ||||||
|  |     ] | ||||||
							
								
								
									
										30
									
								
								tests/migrations/test_migrations_no_changes/0001_initial.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								tests/migrations/test_migrations_no_changes/0001_initial.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,30 @@ | |||||||
|  | # -*- coding: utf-8 -*- | ||||||
|  | from __future__ import unicode_literals | ||||||
|  |  | ||||||
|  | from django.db import migrations, models | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class Migration(migrations.Migration): | ||||||
|  |  | ||||||
|  |     operations = [ | ||||||
|  |  | ||||||
|  |         migrations.CreateModel( | ||||||
|  |             "Author", | ||||||
|  |             [ | ||||||
|  |                 ("id", models.AutoField(primary_key=True)), | ||||||
|  |                 ("name", models.CharField(max_length=255)), | ||||||
|  |                 ("slug", models.SlugField(null=True)), | ||||||
|  |                 ("age", models.IntegerField(default=0)), | ||||||
|  |                 ("silly_field", models.BooleanField(default=False)), | ||||||
|  |             ], | ||||||
|  |         ), | ||||||
|  |  | ||||||
|  |         migrations.CreateModel( | ||||||
|  |             "Tribble", | ||||||
|  |             [ | ||||||
|  |                 ("id", models.AutoField(primary_key=True)), | ||||||
|  |                 ("fluffy", models.BooleanField(default=True)), | ||||||
|  |             ], | ||||||
|  |         ) | ||||||
|  |  | ||||||
|  |     ] | ||||||
							
								
								
									
										29
									
								
								tests/migrations/test_migrations_no_changes/0002_second.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								tests/migrations/test_migrations_no_changes/0002_second.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,29 @@ | |||||||
|  | # -*- coding: utf-8 -*- | ||||||
|  | from __future__ import unicode_literals | ||||||
|  |  | ||||||
|  | from django.db import migrations, models | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class Migration(migrations.Migration): | ||||||
|  |  | ||||||
|  |     dependencies = [ | ||||||
|  |         ("migrations", "0001_initial"), | ||||||
|  |     ] | ||||||
|  |  | ||||||
|  |     operations = [ | ||||||
|  |  | ||||||
|  |         migrations.DeleteModel("Tribble"), | ||||||
|  |  | ||||||
|  |         migrations.RemoveField("Author", "silly_field"), | ||||||
|  |  | ||||||
|  |         migrations.AddField("Author", "rating", models.IntegerField(default=0)), | ||||||
|  |  | ||||||
|  |         migrations.CreateModel( | ||||||
|  |             "Book", | ||||||
|  |             [ | ||||||
|  |                 ("id", models.AutoField(primary_key=True)), | ||||||
|  |                 ("author", models.ForeignKey("migrations.Author", null=True)), | ||||||
|  |             ], | ||||||
|  |         ) | ||||||
|  |  | ||||||
|  |     ] | ||||||
							
								
								
									
										29
									
								
								tests/migrations/test_migrations_no_changes/0003_third.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								tests/migrations/test_migrations_no_changes/0003_third.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,29 @@ | |||||||
|  | # -*- coding: utf-8 -*- | ||||||
|  | from __future__ import unicode_literals | ||||||
|  |  | ||||||
|  | from django.db import models, migrations | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class Migration(migrations.Migration): | ||||||
|  |  | ||||||
|  |     dependencies = [ | ||||||
|  |         ('migrations', '0002_second'), | ||||||
|  |     ] | ||||||
|  |  | ||||||
|  |     operations = [ | ||||||
|  |         migrations.CreateModel( | ||||||
|  |             name='ModelWithCustomBase', | ||||||
|  |             fields=[ | ||||||
|  |                 ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), | ||||||
|  |             ], | ||||||
|  |             options={ | ||||||
|  |             }, | ||||||
|  |             bases=(models.Model,), | ||||||
|  |         ), | ||||||
|  |         migrations.DeleteModel( | ||||||
|  |             name='Author', | ||||||
|  |         ), | ||||||
|  |         migrations.DeleteModel( | ||||||
|  |             name='Book', | ||||||
|  |         ), | ||||||
|  |     ] | ||||||
		Reference in New Issue
	
	Block a user