6 from south import migration
8 # Add the tests directory so fakeapp is on sys.path
9 test_root = os.path.dirname(__file__)
10 sys.path.append(test_root)
13 class TestMigrationLogic(unittest.TestCase):
16 Tests if the various logic functions in migration actually work.
19 def create_fake_app(self, name):
29 def create_test_app(self):
35 fake.__name__ = "fakeapp.migrations"
36 fake.__file__ = os.path.join(test_root, "fakeapp", "migrations", "__init__.py")
40 def monkeypatch(self):
41 """Swaps out various Django calls for fake ones for our own nefarious purposes."""
46 from django.db import models
47 from django.conf import settings
48 models.get_apps_old, models.get_apps = models.get_apps, new_get_apps
49 settings.INSTALLED_APPS, settings.OLD_INSTALLED_APPS = (
51 settings.INSTALLED_APPS,
57 def unmonkeypatch(self):
58 """Undoes what monkeypatch did."""
60 from django.db import models
61 from django.conf import settings
62 models.get_apps = models.get_apps_old
63 settings.INSTALLED_APPS = settings.OLD_INSTALLED_APPS
65 tearDown = unmonkeypatch
68 def redo_app_cache(self):
69 from django.db.models.loading import AppCache
75 def test_get_app_name(self):
78 migration.get_app_name(self.create_fake_app("southtest.migrations")),
82 migration.get_app_name(self.create_fake_app("foo.bar.baz.migrations")),
86 def test_get_migrated_apps(self):
88 P1 = __import__("fakeapp.migrations", {}, {}, [''])
92 list(migration.get_migrated_apps()),
96 def test_get_app(self):
98 P1 = __import__("fakeapp.migrations", {}, {}, [''])
100 self.assertEqual(P1, migration.get_app("fakeapp"))
101 self.assertEqual(P1, migration.get_app(self.create_fake_app("fakeapp.models")))
104 def test_get_app_fullname(self):
107 migration.get_app_fullname(self.create_fake_app("southtest.migrations")),
111 migration.get_app_fullname(self.create_fake_app("foo.bar.baz.migrations")),
115 def test_get_migration_names(self):
117 app = self.create_test_app()
120 ["0001_spam", "0002_eggs", "0003_alter_spam"],
121 migration.get_migration_names(app),
125 def test_get_migration_classes(self):
127 app = self.create_test_app()
129 # Can't use vanilla import, modules beginning with numbers aren't in grammar
130 M1 = __import__("fakeapp.migrations.0001_spam", {}, {}, ['Migration']).Migration
131 M2 = __import__("fakeapp.migrations.0002_eggs", {}, {}, ['Migration']).Migration
132 M3 = __import__("fakeapp.migrations.0003_alter_spam", {}, {}, ['Migration']).Migration
136 list(migration.get_migration_classes(app)),
140 def test_get_migration(self):
142 app = self.create_test_app()
144 # Can't use vanilla import, modules beginning with numbers aren't in grammar
145 M1 = __import__("fakeapp.migrations.0001_spam", {}, {}, ['Migration']).Migration
146 M2 = __import__("fakeapp.migrations.0002_eggs", {}, {}, ['Migration']).Migration
148 self.assertEqual(M1, migration.get_migration(app, "0001_spam"))
149 self.assertEqual(M2, migration.get_migration(app, "0002_eggs"))
151 self.assertRaises((ImportError, ValueError), migration.get_migration, app, "0001_jam")
154 def test_all_migrations(self):
156 app = migration.get_app("fakeapp")
160 "0001_spam": migration.get_migration(app, "0001_spam"),
161 "0002_eggs": migration.get_migration(app, "0002_eggs"),
162 "0003_alter_spam": migration.get_migration(app, "0003_alter_spam"),
164 migration.all_migrations(),
168 def assertListEqual(self, list1, list2):
173 return self.assertEqual(list1, list2)
176 def test_apply_migrations(self):
178 app = migration.get_app("fakeapp")
180 # We should start with no migrations
181 self.assertEqual(list(migration.MigrationHistory.objects.all()), [])
183 # Apply them normally
184 migration.migrate_app(app, target_name=None, resolve_mode=None, fake=False, silent=True)
186 # We should finish with all migrations
187 self.assertListEqual(
189 (u"fakeapp", u"0001_spam"),
190 (u"fakeapp", u"0002_eggs"),
191 (u"fakeapp", u"0003_alter_spam"),
193 migration.MigrationHistory.objects.values_list("app_name", "migration"),
196 # Now roll them backwards
197 migration.migrate_app(app, target_name="zero", resolve_mode=None, fake=False, silent=True)
200 self.assertEqual(list(migration.MigrationHistory.objects.all()), [])
203 def test_migration_merge_forwards(self):
205 app = migration.get_app("fakeapp")
207 # We should start with no migrations
208 self.assertEqual(list(migration.MigrationHistory.objects.all()), [])
210 # Insert one in the wrong order
211 migration.MigrationHistory.objects.create(
212 app_name = "fakeapp",
213 migration = "0002_eggs",
214 applied = datetime.datetime.now(),
218 self.assertListEqual(
220 (u"fakeapp", u"0002_eggs"),
222 migration.MigrationHistory.objects.values_list("app_name", "migration"),
225 # Apply them normally
227 migration.migrate_app(app, target_name=None, resolve_mode=None, fake=False, silent=True)
231 # Nothing should have changed (no merge mode!)
232 self.assertListEqual(
234 (u"fakeapp", u"0002_eggs"),
236 migration.MigrationHistory.objects.values_list("app_name", "migration"),
240 migration.migrate_app(app, target_name=None, resolve_mode="merge", fake=False, silent=True)
242 # We should finish with all migrations
243 self.assertListEqual(
245 (u"fakeapp", u"0001_spam"),
246 (u"fakeapp", u"0002_eggs"),
247 (u"fakeapp", u"0003_alter_spam"),
249 migration.MigrationHistory.objects.values_list("app_name", "migration"),
252 # Now roll them backwards
253 migration.migrate_app(app, target_name="0002", resolve_mode=None, fake=False, silent=True)
254 migration.migrate_app(app, target_name="0001", resolve_mode=None, fake=True, silent=True)
255 migration.migrate_app(app, target_name="zero", resolve_mode=None, fake=False, silent=True)
258 self.assertEqual(list(migration.MigrationHistory.objects.all()), [])
260 def test_alter_column_null(self):
262 from django.db import connection, transaction
263 # the DBAPI introspection module fails on postgres NULLs.
264 cursor = connection.cursor()
266 cursor.execute("INSERT INTO southtest_spam (id, weight, expires, name) VALUES (100, 10.1, now(), NULL);")
268 transaction.rollback()
271 cursor.execute("DELETE FROM southtest_spam")
275 app = migration.get_app("fakeapp")
276 self.assertEqual(list(migration.MigrationHistory.objects.all()), [])
278 # by default name is NOT NULL
279 migration.migrate_app(app, target_name="0002", resolve_mode=None, fake=False, silent=True)
280 self.failIf(null_ok())
282 # after 0003, it should be NULL
283 migration.migrate_app(app, target_name="0003", resolve_mode=None, fake=False, silent=True)
284 self.assert_(null_ok())
286 # make sure it is NOT NULL again
287 migration.migrate_app(app, target_name="0002", resolve_mode=None, fake=False, silent=True)
288 self.failIf(null_ok(), 'name not null after migration')
290 # finish with no migrations, otherwise other tests fail...
291 migration.migrate_app(app, target_name="zero", resolve_mode=None, fake=False, silent=True)
292 self.assertEqual(list(migration.MigrationHistory.objects.all()), [])