summaryrefslogtreecommitdiff
path: root/tests/migrations/test_executor.py
diff options
context:
space:
mode:
Diffstat (limited to 'tests/migrations/test_executor.py')
-rw-r--r--tests/migrations/test_executor.py96
1 files changed, 95 insertions, 1 deletions
diff --git a/tests/migrations/test_executor.py b/tests/migrations/test_executor.py
index b8eddc6f97..61da17b341 100644
--- a/tests/migrations/test_executor.py
+++ b/tests/migrations/test_executor.py
@@ -1,6 +1,7 @@
from django.db import connection
from django.db.migrations.executor import MigrationExecutor
-from django.test import modify_settings, override_settings
+from django.db.migrations.graph import MigrationGraph
+from django.test import modify_settings, override_settings, TestCase
from django.apps.registry import apps as global_apps
from .test_base import MigrationTestBase
@@ -231,3 +232,96 @@ class ExecutorTests(MigrationTestBase):
executor.migrate([("migrations", None)])
self.assertTableNotExists("migrations_author")
self.assertTableNotExists("migrations_tribble")
+
+
+class FakeLoader(object):
+ def __init__(self, graph, applied):
+ self.graph = graph
+ self.applied_migrations = applied
+
+
+class FakeMigration(object):
+ """Really all we need is any object with a debug-useful repr."""
+ def __init__(self, name):
+ self.name = name
+
+ def __repr__(self):
+ return 'M<%s>' % self.name
+
+
+class ExecutorUnitTests(TestCase):
+ """(More) isolated unit tests for executor methods."""
+ def test_minimize_rollbacks(self):
+ """
+ Minimize unnecessary rollbacks in connected apps.
+
+ When you say "./manage.py migrate appA 0001", rather than migrating to
+ just after appA-0001 in the linearized migration plan (which could roll
+ back migrations in other apps that depend on appA 0001, but don't need
+ to be rolled back since we're not rolling back appA 0001), we migrate
+ to just before appA-0002.
+ """
+ a1_impl = FakeMigration('a1')
+ a1 = ('a', '1')
+ a2_impl = FakeMigration('a2')
+ a2 = ('a', '2')
+ b1_impl = FakeMigration('b1')
+ b1 = ('b', '1')
+ graph = MigrationGraph()
+ graph.add_node(a1, a1_impl)
+ graph.add_node(a2, a2_impl)
+ graph.add_node(b1, b1_impl)
+ graph.add_dependency(None, b1, a1)
+ graph.add_dependency(None, a2, a1)
+
+ executor = MigrationExecutor(None)
+ executor.loader = FakeLoader(graph, {a1, b1, a2})
+
+ plan = executor.migration_plan({a1})
+
+ self.assertEqual(plan, [(a2_impl, True)])
+
+ def test_minimize_rollbacks_branchy(self):
+ """
+ Minimize rollbacks when target has multiple in-app children.
+
+ a: 1 <---- 3 <--\
+ \ \- 2 <--- 4
+ \ \
+ b: \- 1 <--- 2
+ """
+ a1_impl = FakeMigration('a1')
+ a1 = ('a', '1')
+ a2_impl = FakeMigration('a2')
+ a2 = ('a', '2')
+ a3_impl = FakeMigration('a3')
+ a3 = ('a', '3')
+ a4_impl = FakeMigration('a4')
+ a4 = ('a', '4')
+ b1_impl = FakeMigration('b1')
+ b1 = ('b', '1')
+ b2_impl = FakeMigration('b2')
+ b2 = ('b', '2')
+ graph = MigrationGraph()
+ graph.add_node(a1, a1_impl)
+ graph.add_node(a2, a2_impl)
+ graph.add_node(a3, a3_impl)
+ graph.add_node(a4, a4_impl)
+ graph.add_node(b1, b1_impl)
+ graph.add_node(b2, b2_impl)
+ graph.add_dependency(None, a2, a1)
+ graph.add_dependency(None, a3, a1)
+ graph.add_dependency(None, a4, a2)
+ graph.add_dependency(None, a4, a3)
+ graph.add_dependency(None, b2, b1)
+ graph.add_dependency(None, b1, a1)
+ graph.add_dependency(None, b2, a2)
+
+ executor = MigrationExecutor(None)
+ executor.loader = FakeLoader(graph, {a1, b1, a2, b2, a3, a4})
+
+ plan = executor.migration_plan({a1})
+
+ should_be_rolled_back = [b2_impl, a4_impl, a2_impl, a3_impl]
+ exp = [(m, True) for m in should_be_rolled_back]
+ self.assertEqual(plan, exp)