234 lines
9.5 KiB
Python
234 lines
9.5 KiB
Python
from __future__ import annotations
|
|
|
|
import tempfile
|
|
import unittest
|
|
from unittest.mock import patch
|
|
|
|
from django.db import ProgrammingError
|
|
from django.db import migrations
|
|
from django.db.migrations import Migration
|
|
|
|
from django_migration_linter import MigrationLinter
|
|
from django_migration_linter.sql_analyser import get_sql_analyser_class
|
|
|
|
|
|
class LinterFunctionsTestCase(unittest.TestCase):
|
|
def test_get_sql(self):
|
|
linter = MigrationLinter()
|
|
sql_statements = linter.get_sql("app_add_not_null_column", "0001")
|
|
self.assertEqual(len(sql_statements), 6)
|
|
self.assertEqual(sql_statements[0], "BEGIN;")
|
|
self.assertEqual(sql_statements[-1], "COMMIT;")
|
|
|
|
def test_has_errors(self):
|
|
linter = MigrationLinter(database="mysql")
|
|
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_migration_include_apps(self):
|
|
linter = MigrationLinter(include_apps=("app_add_not_null_column",))
|
|
self.assertTrue(linter.should_ignore_migration("app_correct", "0001"))
|
|
self.assertTrue(linter.should_ignore_migration("app_correct", "0002"))
|
|
self.assertFalse(
|
|
linter.should_ignore_migration("app_add_not_null_column", "0001")
|
|
)
|
|
|
|
def test_ignore_migration_exclude_apps(self):
|
|
linter = MigrationLinter(exclude_apps=("app_add_not_null_column",))
|
|
self.assertFalse(linter.should_ignore_migration("app_correct", "0001"))
|
|
self.assertFalse(linter.should_ignore_migration("app_correct", "0002"))
|
|
self.assertTrue(
|
|
linter.should_ignore_migration("app_add_not_null_column", "0001")
|
|
)
|
|
|
|
def test_ignore_migration_name_contains(self):
|
|
linter = MigrationLinter(ignore_name_contains="foo")
|
|
self.assertFalse(linter.should_ignore_migration("app_correct", "0001_initial"))
|
|
self.assertTrue(linter.should_ignore_migration("app_correct", "0002_foo"))
|
|
|
|
def test_ignore_migration_full_name(self):
|
|
linter = MigrationLinter(ignore_name=("0002_foo",))
|
|
self.assertFalse(linter.should_ignore_migration("app_correct", "0001_initial"))
|
|
self.assertTrue(linter.should_ignore_migration("app_correct", "0002_foo"))
|
|
|
|
def test_include_migration_name_contains(self):
|
|
linter = MigrationLinter(include_name_contains="foo")
|
|
self.assertTrue(linter.should_ignore_migration("app_correct", "0001_initial"))
|
|
self.assertFalse(linter.should_ignore_migration("app_correct", "0002_foo"))
|
|
|
|
def test_include_migration_full_name(self):
|
|
linter = MigrationLinter(include_name=("0002_foo",))
|
|
self.assertTrue(linter.should_ignore_migration("app_correct", "0001_initial"))
|
|
self.assertFalse(linter.should_ignore_migration("app_correct", "0002_foo"))
|
|
|
|
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_gather_all_migrations(self):
|
|
linter = MigrationLinter.__new__(MigrationLinter)
|
|
linter.exclude_migration_tests = []
|
|
linter.sql_analyser_class = get_sql_analyser_class("sqlite")
|
|
linter.include_apps = None
|
|
linter.exclude_apps = None
|
|
linter.ignore_name_contains = None
|
|
linter.include_name_contains = None
|
|
linter.ignore_name = []
|
|
linter.include_name = []
|
|
linter.only_applied_migrations = False
|
|
linter.only_unapplied_migrations = False
|
|
linter.ignore_initial_migrations = False
|
|
migrations = linter._gather_all_migrations()
|
|
self.assertGreater(len(list(migrations)), 1)
|
|
|
|
def test_ignore_unapplied_migrations(self):
|
|
linter = MigrationLinter(only_applied_migrations=True)
|
|
linter.migration_loader.applied_migrations = {("app_correct", "0002_foo")}
|
|
|
|
self.assertTrue(linter.should_ignore_migration("app_correct", "0001_initial"))
|
|
self.assertFalse(linter.should_ignore_migration("app_correct", "0002_foo"))
|
|
|
|
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_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)
|
|
|
|
def test_read_migrations_unknown_file(self):
|
|
file_path = "unknown_file"
|
|
with self.assertRaises(Exception):
|
|
MigrationLinter.read_migrations_list(file_path)
|
|
|
|
@patch(
|
|
"django_migration_linter.migration_linter.call_command",
|
|
side_effect=ProgrammingError,
|
|
)
|
|
def test_raise_exception_on_sqlmigrate_error(self, call_command_mock):
|
|
linter = MigrationLinter(
|
|
exclude_migration_tests=[], database="mysql", ignore_sqlmigrate_errors=False
|
|
)
|
|
with self.assertRaises(ProgrammingError):
|
|
linter.get_sql("app_correct", "0002_foo")
|
|
|
|
@patch(
|
|
"django_migration_linter.migration_linter.call_command",
|
|
side_effect=ProgrammingError,
|
|
)
|
|
def test_ignore_exception_on_sqlmigrate_error(self, call_command_mock):
|
|
linter = MigrationLinter(
|
|
exclude_migration_tests=[], database="mysql", ignore_sqlmigrate_errors=True
|
|
)
|
|
sql_result = linter.get_sql("app_correct", "0002_foo")
|
|
self.assertEqual([], sql_result)
|
|
|
|
def test_read_migrations_no_file(self):
|
|
migration_list = MigrationLinter.read_migrations_list(None)
|
|
self.assertIsNone(migration_list)
|
|
|
|
def test_read_migrations_empty_file(self):
|
|
with tempfile.NamedTemporaryFile() as tmp:
|
|
migration_list = MigrationLinter.read_migrations_list(tmp.name)
|
|
self.assertEqual([], migration_list)
|
|
|
|
def test_read_migrations_from_file(self):
|
|
tmp = tempfile.NamedTemporaryFile(mode="w", delete=False)
|
|
tmp.write(
|
|
"test_project/app_add_not_null_column/migrations/0001_create_table.py\n"
|
|
)
|
|
tmp.write("unknown\n")
|
|
tmp.write(
|
|
"test_project/app_add_not_null_column/migrations/0002_add_new_not_null_field.py\n"
|
|
)
|
|
tmp.close()
|
|
migration_list = MigrationLinter.read_migrations_list(tmp.name)
|
|
self.assertEqual(
|
|
[
|
|
("app_add_not_null_column", "0001_create_table"),
|
|
("app_add_not_null_column", "0002_add_new_not_null_field"),
|
|
],
|
|
migration_list,
|
|
)
|
|
|
|
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_ignore_initial_migrations(self):
|
|
linter = MigrationLinter(ignore_initial_migrations=True)
|
|
|
|
self.assertTrue(
|
|
linter.should_ignore_migration(
|
|
"app_correct", "0001_initial", is_initial=True
|
|
)
|
|
)
|
|
self.assertFalse(
|
|
linter.should_ignore_migration("app_correct", "0002_foo", is_initial=False)
|
|
)
|
|
|
|
def test_initial_migration_still_lints_runsql_and_runpython(self):
|
|
def forward(apps, schema_editor):
|
|
return None
|
|
|
|
migration = Migration("0001_initial", "app_initial_operations")
|
|
migration.initial = True
|
|
migration.operations = [
|
|
migrations.RunSQL("DROP TABLE legacy_data;"),
|
|
migrations.RunPython(forward),
|
|
]
|
|
linter = MigrationLinter.__new__(MigrationLinter)
|
|
linter.exclude_migration_tests = []
|
|
linter.sql_analyser_class = get_sql_analyser_class("sqlite")
|
|
linter.include_apps = None
|
|
linter.exclude_apps = None
|
|
linter.ignore_name_contains = None
|
|
linter.include_name_contains = None
|
|
linter.ignore_name = []
|
|
linter.include_name = []
|
|
linter.only_applied_migrations = False
|
|
linter.only_unapplied_migrations = False
|
|
linter.ignore_initial_migrations = False
|
|
|
|
errors, _, warnings = linter.analyse_data_migration(migration)
|
|
|
|
self.assertFalse(
|
|
linter.should_ignore_migration(
|
|
migration.app_label, migration.name, is_initial=migration.initial
|
|
)
|
|
)
|
|
self.assertEqual(["DROP_TABLE"], [issue.code for issue in errors])
|
|
self.assertEqual(
|
|
["RUNSQL_REVERSIBLE", "RUNPYTHON_REVERSIBLE"],
|
|
[issue.code for issue in warnings],
|
|
)
|