def test_linter_creation(self): with self.assertRaises(ValueError): MigrationLinter('foo/') with self.assertRaises(ValueError): MigrationLinter('/dev/null') with self.assertRaises(ValueError): MigrationLinter(fixtures.NOT_DJANGO_GIT_PROJECT)
class RunSQLMigrationTestCase(unittest.TestCase): def setUp(self): test_project_path = os.path.dirname(settings.BASE_DIR) self.linter = MigrationLinter( test_project_path, include_apps=fixtures.DATA_MIGRATIONS, ) def test_missing_reserve_migration(self): runsql = migrations.RunSQL("sql;") error, ignored, warning = self.linter.lint_runsql(runsql) self.assertEqual("RUNSQL_REVERSIBLE", warning[0]["code"]) def test_sql_linting_error(self): runsql = migrations.RunSQL("ALTER TABLE t DROP COLUMN t;") error, ignored, warning = self.linter.lint_runsql(runsql) self.assertEqual("DROP_COLUMN", error[0]["code"]) def test_sql_linting_error_array(self): runsql = migrations.RunSQL( ["ALTER TABLE t DROP COLUMN c;", "ALTER TABLE t RENAME COLUMN c;"]) error, ignored, warning = self.linter.lint_runsql(runsql) self.assertEqual("DROP_COLUMN", error[0]["code"]) self.assertEqual("RENAME_COLUMN", error[1]["code"]) def test_sql_linting_error_args(self): runsql = migrations.RunSQL([("ALTER TABLE %s DROP COLUMN %s;", ("t", "c"))]) error, ignored, warning = self.linter.lint_runsql(runsql) self.assertEqual("DROP_COLUMN", error[0]["code"])
def test_gather_migrations_with_list(self): linter = MigrationLinter() migrations = linter._gather_all_migrations(migrations_list=[ ("app_add_not_null_column", "0001_create_table"), ("app_add_not_null_column", "0002_add_new_not_null_field"), ]) self.assertEqual(2, len(list(migrations)))
def test_get_sql(self): project_path = fixtures.ADD_NOT_NULL_COLUMN_PROJECT linter = MigrationLinter(project_path) sql_statements = linter.get_sql('test_app', '0001') self.assertEqual(len(sql_statements), 6) self.assertEqual(sql_statements[0], 'BEGIN;') self.assertEqual(sql_statements[-1], 'COMMIT;')
def _launch_linter(self, app=None, commit_id=None): linter = MigrationLinter( self.test_project_path, database=next(iter(self.databases)), no_cache=True, ) linter.lint_all_migrations(app_label=app, git_commit_id=commit_id) return linter
def test_include_migration_multiple_names(self): linter = MigrationLinter(include_name=("0002_foo", "0003_bar")) self.assertTrue( linter.should_ignore_migration("app_correct", "0001_initial")) self.assertFalse( linter.should_ignore_migration("app_correct", "0002_foo")) self.assertFalse( linter.should_ignore_migration("app_correct", "0003_bar"))
def test_ignore_applied_migrations(self): linter = MigrationLinter(only_unapplied_migrations=True) linter.migration_loader.applied_migrations = {("app_correct", "0002_foo")} self.assertFalse( linter.should_ignore_migration("app_correct", "0001_initial")) self.assertTrue( linter.should_ignore_migration("app_correct", "0002_foo"))
def test_gather_all_migrations(self): linter = MigrationLinter(fixtures.CORRECT_PROJECT) migrations = linter._gather_all_migrations() self.assertEqual(len(migrations), 3) self.assertEqual(migrations[0][0], 'test_app1') self.assertEqual(migrations[0][1], '0001_initial') self.assertEqual(migrations[1][0], 'test_app1') self.assertEqual(migrations[1][1], '0002_a_new_null_field') self.assertEqual(migrations[2][0], 'test_app2') self.assertEqual(migrations[2][1], '0001_foo')
def _launch_linter(self, app=None, commit_id=None): if app is not None: app = [app] linter = MigrationLinter( self.test_project_path, include_apps=app, database=next(iter(self.databases)), no_cache=True, ) linter.lint_all_migrations(git_commit_id=commit_id) return linter
def test_gather_all_migrations(self): linter = MigrationLinter(fixtures.CORRECT_PROJECT) migrations = linter._gather_all_migrations() self.assertEqual(len(migrations), 4) self.assertEqual( sorted([(m.app_name, m.name) for m in migrations]), sorted([ ("test_app1", "0001_initial"), ("test_app1", "0002_a_new_null_field"), ("test_app2", "0001_foo"), ("test_app3", "0001_initial"), ]) )
def test_cache_ignored(self): cache_file = os.path.join(DEFAULT_CACHE_PATH, 'test_project_ignore_migration.pickle') if os.path.exists(cache_file): os.remove(cache_file) linter = MigrationLinter(fixtures.IGNORE_MIGRATION_PROJECT) with mock.patch.object(MigrationLinter, 'get_sql', wraps=linter.get_sql) as sql_mock: linter.lint_all_migrations() self.assertEqual(sql_mock.call_count, 2) cache = Cache(fixtures.IGNORE_MIGRATION_PROJECT, DEFAULT_CACHE_PATH) cache.load() self.assertEqual(cache['63230606af0eccaef7f1f78c537c624c']['result'], 'OK') self.assertEqual(cache['5c5ca1780a9f28439c1defc1f32af894']['result'], 'IGNORE') # Start the Linter again -> should use cache now. linter = MigrationLinter(fixtures.IGNORE_MIGRATION_PROJECT) with mock.patch.object(MigrationLinter, 'get_sql', wraps=linter.get_sql) as sql_mock: linter.lint_all_migrations() self.assertEqual(sql_mock.call_count, 0)
def test_exclude_warning_from_test(self): self.linter = MigrationLinter( self.test_project_path, include_apps=fixtures.DATA_MIGRATIONS, exclude_migration_tests=("RUNPYTHON_REVERSIBLE", ), ) reverse_migration = self.linter.migration_loader.disk_migrations[( "app_data_migrations", "0002_missing_reverse")] self.linter.lint_migration(reverse_migration) self.assertEqual(0, self.linter.nb_warnings) self.assertEqual(1, self.linter.nb_valid) self.assertFalse(self.linter.has_errors)
def test_warnings_as_errors(self): self.linter = MigrationLinter( self.test_project_path, include_apps=fixtures.DATA_MIGRATIONS, warnings_as_errors=True, ) reverse_migration = self.linter.migration_loader.disk_migrations[( "app_data_migrations", "0003_incorrect_arguments")] self.linter.lint_migration(reverse_migration) self.assertEqual(0, self.linter.nb_warnings) self.assertEqual(1, self.linter.nb_erroneous) self.assertTrue(self.linter.has_errors)
def test_cache_ignored(self, *args): linter = MigrationLinter(self.test_project_path, ignore_name_contains="0001") linter.old_cache.clear() linter.old_cache.save() with mock.patch( "django_migration_linter.migration_linter.analyse_sql_statements", wraps=analyse_sql_statements, ) as analyse_sql_statements_mock: linter.lint_all_migrations() self.assertEqual(1, analyse_sql_statements_mock.call_count) cache = linter.new_cache cache.load() self.assertEqual("IGNORE", cache["0fab48322ba76570da1a3c193abb77b5"]["result"]) # Start the Linter again -> should use cache now. linter = MigrationLinter(self.test_project_path) with mock.patch( "django_migration_linter.migration_linter.analyse_sql_statements", wraps=analyse_sql_statements, ) as analyse_sql_statements_mock: linter.lint_all_migrations() self.assertEqual(1, analyse_sql_statements_mock.call_count)
def test_cache_ignored(self, *args): linter = MigrationLinter(self.test_project_path, ignore_name_contains="0001") linter.old_cache.clear() linter.old_cache.save() with mock.patch( "django_migration_linter.migration_linter.analyse_sql_statements", wraps=analyse_sql_statements, ) as analyse_sql_statements_mock: linter.lint_all_migrations() analyse_sql_statements_mock.assert_not_called() cache = linter.new_cache cache.load() self.assertFalse(cache)
def test_correct_one_param_get_model_import(self): def forward_method(apps, schema_editor): User = apps.get_model("auth.User") User.objects.filter(id=1).first() issues = MigrationLinter.get_runpython_model_import_issues( forward_method) self.assertEqual(0, len(issues))
def test_different_variable_name_one_param(self): def forward_op(apps, schema_editor): mymodel = apps.get_model("app.MyModel") mymodel.objects.filter(id=1).first() issues = MigrationLinter.get_runpython_model_variable_naming_issues( forward_op) self.assertEqual(1, len(issues))
def test_same_variable_name(self): def forward_op(apps, schema_editor): MyModel = apps.get_model("app", "MyModel") MyModel.objects.filter(id=1).first() issues = MigrationLinter.get_runpython_model_variable_naming_issues( forward_op) self.assertEqual(0, len(issues))
def test_correct_get_model_import(self): def correct_importing_model_forward(apps, schema_editor): MyModel = apps.get_model("app_data_migrations", "MyModel") MyModel.objects.filter(id=1).first() issues = MigrationLinter.get_data_migration_model_import_issues( correct_importing_model_forward) self.assertEqual(0, len(issues))
def test_missing_get_model_import(self): def incorrect_importing_model_forward(apps, schema_editor): from tests.test_project.app_data_migrations.models import MyModel MyModel.objects.filter(id=1).first() issues = MigrationLinter.get_runpython_model_import_issues( incorrect_importing_model_forward) self.assertEqual(1, len(issues))
def test_exclude_migration_tests(self): m = Migration("0002_add_new_not_null_field", "app_add_not_null_column") linter = MigrationLinter(exclude_migration_tests=[], database="mysql") linter.lint_migration(m) self.assertTrue(linter.has_errors) linter = MigrationLinter(exclude_migration_tests=["NOT_NULL"], database="mysql") linter.lint_migration(m) self.assertFalse(linter.has_errors)
class DataMigrationDetectionTestCase(unittest.TestCase): def setUp(self, *args, **kwargs): self.test_project_path = os.path.dirname(settings.BASE_DIR) self.linter = MigrationLinter( self.test_project_path, include_apps=fixtures.DATA_MIGRATIONS, ) def test_reverse_data_migration(self): self.assertEqual(0, self.linter.nb_warnings) reverse_migration = self.linter.migration_loader.disk_migrations[( "app_data_migrations", "0002_missing_reverse")] self.linter.lint_migration(reverse_migration) self.assertEqual(1, self.linter.nb_warnings) self.assertFalse(self.linter.has_errors) def test_reverse_data_migration_ignore(self): reverse_migration = self.linter.migration_loader.disk_migrations[( "app_data_migrations", "0003_incorrect_arguments")] self.linter.lint_migration(reverse_migration) self.assertEqual(1, self.linter.nb_warnings) self.assertFalse(self.linter.has_errors) def test_exclude_warning_from_test(self): self.linter = MigrationLinter( self.test_project_path, include_apps=fixtures.DATA_MIGRATIONS, exclude_migration_tests=("RUNPYTHON_REVERSIBLE", ), ) reverse_migration = self.linter.migration_loader.disk_migrations[( "app_data_migrations", "0002_missing_reverse")] self.linter.lint_migration(reverse_migration) self.assertEqual(0, self.linter.nb_warnings) self.assertEqual(1, self.linter.nb_valid) self.assertFalse(self.linter.has_errors) def test_warnings_as_errors(self): self.linter = MigrationLinter( self.test_project_path, include_apps=fixtures.DATA_MIGRATIONS, warnings_as_errors=True, ) reverse_migration = self.linter.migration_loader.disk_migrations[( "app_data_migrations", "0003_incorrect_arguments")] self.linter.lint_migration(reverse_migration) self.assertEqual(0, self.linter.nb_warnings) self.assertEqual(1, self.linter.nb_erroneous) self.assertTrue(self.linter.has_errors)
def test_diff_variable_name_multiline(self): def forward_op(apps, schema_editor): MyModelVeryLongLongLongLongLongNot = apps.get_model( "app", "MyModelVeryLongLongLongLongLong") MyModelVeryLongLongLongLongLongNot.objects.filter(id=1).first() issues = MigrationLinter.get_runpython_model_variable_naming_issues( forward_op) self.assertEqual(1, len(issues))
def test_different_variable_name_one_param_multiline(self): def forward_op(apps, schema_editor): m = apps.get_model( "quite_long_app_name_name_name.AVeryLongModelNameNameName") m.objects.filter(id=1).first() issues = MigrationLinter.get_runpython_model_variable_naming_issues( forward_op) self.assertEqual(1, len(issues))
def test_cache_ignored_command_line(self): cache_file = os.path.join(DEFAULT_CACHE_PATH, 'test_project_ignore_migration.pickle') if os.path.exists(cache_file): os.remove(cache_file) linter = MigrationLinter(fixtures.IGNORE_MIGRATION_PROJECT, ignore_name_contains='0001') with mock.patch.object(MigrationLinter, 'get_sql', wraps=linter.get_sql) as sql_mock: linter.lint_all_migrations() self.assertEqual(sql_mock.call_count, 1) cache = Cache(fixtures.IGNORE_MIGRATION_PROJECT, DEFAULT_CACHE_PATH) cache.load() self.assertNotIn('63230606af0eccaef7f1f78c537c624c', cache) self.assertEqual(cache['5c5ca1780a9f28439c1defc1f32af894']['result'], 'IGNORE')
def test_has_errors(self): project_path = fixtures.MULTI_COMMIT_PROJECT linter = MigrationLinter(project_path) self.assertFalse(linter.has_errors) linter.lint_migration('test_app', '0001') self.assertFalse(linter.has_errors) linter.lint_migration('test_app', '0002') self.assertTrue(linter.has_errors) linter.lint_migration('test_app', '0001') self.assertTrue(linter.has_errors)
def test_not_overlapping_model_name(self): """ Correct for the import error, but should raise a warning """ def forward_method(apps, schema_editor): User = apps.get_model("auth", "CustomUserModel") User.objects.filter(id=1).first() issues = MigrationLinter.get_runpython_model_import_issues( forward_method) self.assertEqual(0, len(issues))
def test_has_errors(self): linter = MigrationLinter() self.assertFalse(linter.has_errors) m = Migration("0001_create_table", "app_add_not_null_column") linter.lint_migration(m) self.assertFalse(linter.has_errors) m = Migration("0002_add_new_not_null_field", "app_add_not_null_column") linter.lint_migration(m) self.assertTrue(linter.has_errors) m = Migration("0001_create_table", "app_add_not_null_column") linter.lint_migration(m) self.assertTrue(linter.has_errors)
def test_ignore_cached_migration(self, *args): linter = MigrationLinter(self.test_project_path) linter.old_cache.clear() linter.old_cache.save() with mock.patch( "django_migration_linter.migration_linter.analyse_sql_statements", wraps=analyse_sql_statements, ) as analyse_sql_statements_mock: linter.lint_all_migrations() self.assertEqual(2, analyse_sql_statements_mock.call_count) cache = linter.new_cache cache.load() self.assertEqual("OK", cache["4a3770a405738d457e2d23e17fb1f3aa"]["result"]) self.assertEqual("ERR", cache["19fd3ea688fc05e2cc2a6e67c0b7aa17"]["result"]) self.assertListEqual( cache["19fd3ea688fc05e2cc2a6e67c0b7aa17"]["errors"], [{ "err_msg": "RENAMING tables", "code": "RENAME_TABLE", "table": None, "column": None, }], ) # Start the Linter again -> should use cache now but ignore the erroneous linter = MigrationLinter( self.test_project_path, ignore_name_contains="0002_add_new_not_null_field") with mock.patch( "django_migration_linter.migration_linter.analyse_sql_statements", wraps=analyse_sql_statements, ) as analyse_sql_statements_mock: linter.lint_all_migrations() analyse_sql_statements_mock.assert_not_called() self.assertFalse(linter.has_errors) cache = linter.new_cache cache.load() self.assertEqual(1, len(cache)) self.assertEqual("OK", cache["4a3770a405738d457e2d23e17fb1f3aa"]["result"])
def test_has_errors(self): project_path = fixtures.MULTI_COMMIT_PROJECT linter = MigrationLinter(project_path) self.assertFalse(linter.has_errors) m = Migration(os.path.join(project_path, 'test_app/migrations/0001_create_table.py')) linter.lint_migration(m) self.assertFalse(linter.has_errors) m = Migration(os.path.join(project_path, 'test_app/migrations/0002_add_new_not_null_field.py')) linter.lint_migration(m) self.assertTrue(linter.has_errors) m = Migration(os.path.join(project_path, 'test_app/migrations/0001_create_table.py')) linter.lint_migration(m) self.assertTrue(linter.has_errors)