271 lines
9.7 KiB
Python
271 lines
9.7 KiB
Python
import unittest
|
|
import datetime
|
|
import sys
|
|
import os
|
|
import StringIO
|
|
|
|
from south import migration
|
|
from south.tests import Monkeypatcher
|
|
from south.utils import snd
|
|
|
|
class TestMigrationLogic(Monkeypatcher):
|
|
|
|
"""
|
|
Tests if the various logic functions in migration actually work.
|
|
"""
|
|
|
|
def test_get_app_name(self):
|
|
self.assertEqual(
|
|
"southtest",
|
|
migration.get_app_name(self.create_fake_app("southtest.migrations")),
|
|
)
|
|
self.assertEqual(
|
|
"baz",
|
|
migration.get_app_name(self.create_fake_app("foo.bar.baz.migrations")),
|
|
)
|
|
|
|
|
|
def test_get_migrated_apps(self):
|
|
|
|
P1 = __import__("fakeapp.migrations", {}, {}, [''])
|
|
P2 = __import__("otherfakeapp.migrations", {}, {}, [''])
|
|
|
|
self.assertEqual(
|
|
[P1,P2],
|
|
list(migration.get_migrated_apps()),
|
|
)
|
|
|
|
|
|
def test_get_app(self):
|
|
|
|
P1 = __import__("fakeapp.migrations", {}, {}, [''])
|
|
|
|
self.assertEqual(P1, migration.get_app("fakeapp"))
|
|
self.assertEqual(P1, migration.get_app(self.create_fake_app("fakeapp.models")))
|
|
|
|
|
|
def test_get_app_fullname(self):
|
|
self.assertEqual(
|
|
"southtest",
|
|
migration.get_app_fullname(self.create_fake_app("southtest.migrations")),
|
|
)
|
|
self.assertEqual(
|
|
"foo.bar.baz",
|
|
migration.get_app_fullname(self.create_fake_app("foo.bar.baz.migrations")),
|
|
)
|
|
|
|
|
|
def test_get_migration_names(self):
|
|
|
|
app = self.create_test_app()
|
|
|
|
self.assertEqual(
|
|
["0001_spam", "0002_eggs", "0003_alter_spam"],
|
|
migration.get_migration_names(app),
|
|
)
|
|
|
|
|
|
def test_get_migration_classes(self):
|
|
|
|
app = self.create_test_app()
|
|
|
|
# Can't use vanilla import, modules beginning with numbers aren't in grammar
|
|
M1 = __import__("fakeapp.migrations.0001_spam", {}, {}, ['Migration']).Migration
|
|
M2 = __import__("fakeapp.migrations.0002_eggs", {}, {}, ['Migration']).Migration
|
|
M3 = __import__("fakeapp.migrations.0003_alter_spam", {}, {}, ['Migration']).Migration
|
|
|
|
self.assertEqual(
|
|
[M1, M2, M3],
|
|
list(migration.get_migration_classes(app)),
|
|
)
|
|
|
|
|
|
def test_get_migration(self):
|
|
|
|
app = self.create_test_app()
|
|
|
|
# Can't use vanilla import, modules beginning with numbers aren't in grammar
|
|
M1 = __import__("fakeapp.migrations.0001_spam", {}, {}, ['Migration']).Migration
|
|
M2 = __import__("fakeapp.migrations.0002_eggs", {}, {}, ['Migration']).Migration
|
|
|
|
self.assertEqual(M1, migration.get_migration(app, "0001_spam"))
|
|
self.assertEqual(M2, migration.get_migration(app, "0002_eggs"))
|
|
|
|
# Temporarily redirect sys.stdout during this, it whinges.
|
|
stdout, sys.stdout = sys.stdout, StringIO.StringIO()
|
|
try:
|
|
self.assertRaises((ImportError, ValueError), migration.get_migration, app, "0001_jam")
|
|
finally:
|
|
sys.stdout = stdout
|
|
|
|
|
|
def test_all_migrations(self):
|
|
|
|
app = migration.get_app("fakeapp")
|
|
otherapp = migration.get_app("otherfakeapp")
|
|
|
|
self.assertEqual({
|
|
app: {
|
|
"0001_spam": migration.get_migration(app, "0001_spam"),
|
|
"0002_eggs": migration.get_migration(app, "0002_eggs"),
|
|
"0003_alter_spam": migration.get_migration(app, "0003_alter_spam"),
|
|
},
|
|
otherapp: {
|
|
"0001_first": migration.get_migration(otherapp, "0001_first"),
|
|
"0002_second": migration.get_migration(otherapp, "0002_second"),
|
|
"0003_third": migration.get_migration(otherapp, "0003_third"),
|
|
},
|
|
},
|
|
migration.all_migrations(),
|
|
)
|
|
|
|
|
|
def assertListEqual(self, list1, list2):
|
|
list1 = list(list1)
|
|
list2 = list(list2)
|
|
list1.sort()
|
|
list2.sort()
|
|
return self.assertEqual(list1, list2)
|
|
|
|
|
|
def test_apply_migrations(self):
|
|
migration.MigrationHistory.objects.all().delete()
|
|
app = migration.get_app("fakeapp")
|
|
|
|
# We should start with no migrations
|
|
self.assertEqual(list(migration.MigrationHistory.objects.all()), [])
|
|
|
|
# Apply them normally
|
|
tree = migration.dependency_tree()
|
|
migration.migrate_app(app, tree, target_name=None, resolve_mode=None, fake=False, verbosity=0)
|
|
|
|
# We should finish with all migrations
|
|
self.assertListEqual(
|
|
(
|
|
(u"fakeapp", u"0001_spam"),
|
|
(u"fakeapp", u"0002_eggs"),
|
|
(u"fakeapp", u"0003_alter_spam"),
|
|
),
|
|
migration.MigrationHistory.objects.values_list("app_name", "migration"),
|
|
)
|
|
|
|
# Now roll them backwards
|
|
migration.migrate_app(app, tree, target_name="zero", resolve_mode=None, fake=False, verbosity=0)
|
|
|
|
# Finish with none
|
|
self.assertEqual(list(migration.MigrationHistory.objects.all()), [])
|
|
|
|
|
|
def test_migration_merge_forwards(self):
|
|
migration.MigrationHistory.objects.all().delete()
|
|
app = migration.get_app("fakeapp")
|
|
|
|
# We should start with no migrations
|
|
self.assertEqual(list(migration.MigrationHistory.objects.all()), [])
|
|
|
|
# Insert one in the wrong order
|
|
migration.MigrationHistory.objects.create(
|
|
app_name = "fakeapp",
|
|
migration = "0002_eggs",
|
|
applied = datetime.datetime.now(),
|
|
)
|
|
|
|
# Did it go in?
|
|
self.assertListEqual(
|
|
(
|
|
(u"fakeapp", u"0002_eggs"),
|
|
),
|
|
migration.MigrationHistory.objects.values_list("app_name", "migration"),
|
|
)
|
|
|
|
# Apply them normally
|
|
tree = migration.dependency_tree()
|
|
try:
|
|
# Redirect the error it will print to nowhere
|
|
stdout, sys.stdout = sys.stdout, StringIO.StringIO()
|
|
migration.migrate_app(app, tree, target_name=None, resolve_mode=None, fake=False, verbosity=0)
|
|
sys.stdout = stdout
|
|
except SystemExit:
|
|
pass
|
|
|
|
# Nothing should have changed (no merge mode!)
|
|
self.assertListEqual(
|
|
(
|
|
(u"fakeapp", u"0002_eggs"),
|
|
),
|
|
migration.MigrationHistory.objects.values_list("app_name", "migration"),
|
|
)
|
|
|
|
# Apply with merge
|
|
migration.migrate_app(app, tree, target_name=None, resolve_mode="merge", fake=False, verbosity=0)
|
|
|
|
# We should finish with all migrations
|
|
self.assertListEqual(
|
|
(
|
|
(u"fakeapp", u"0001_spam"),
|
|
(u"fakeapp", u"0002_eggs"),
|
|
(u"fakeapp", u"0003_alter_spam"),
|
|
),
|
|
migration.MigrationHistory.objects.values_list("app_name", "migration"),
|
|
)
|
|
|
|
# Now roll them backwards
|
|
migration.migrate_app(app, tree, target_name="0002", resolve_mode=None, fake=False, verbosity=0)
|
|
migration.migrate_app(app, tree, target_name="0001", resolve_mode=None, fake=True, verbosity=0)
|
|
migration.migrate_app(app, tree, target_name="zero", resolve_mode=None, fake=False, verbosity=0)
|
|
|
|
# Finish with none
|
|
self.assertEqual(list(migration.MigrationHistory.objects.all()), [])
|
|
|
|
def test_alter_column_null(self):
|
|
def null_ok():
|
|
from django.db import connection, transaction
|
|
# the DBAPI introspection module fails on postgres NULLs.
|
|
cursor = connection.cursor()
|
|
try:
|
|
cursor.execute("INSERT INTO southtest_spam (id, weight, expires, name) VALUES (100, 10.1, now(), NULL);")
|
|
except:
|
|
transaction.rollback()
|
|
return False
|
|
else:
|
|
cursor.execute("DELETE FROM southtest_spam")
|
|
transaction.commit()
|
|
return True
|
|
|
|
app = migration.get_app("fakeapp")
|
|
tree = migration.dependency_tree()
|
|
self.assertEqual(list(migration.MigrationHistory.objects.all()), [])
|
|
|
|
# by default name is NOT NULL
|
|
migration.migrate_app(app, tree, target_name="0002", resolve_mode=None, fake=False, verbosity=0)
|
|
self.failIf(null_ok())
|
|
|
|
# after 0003, it should be NULL
|
|
migration.migrate_app(app, tree, target_name="0003", resolve_mode=None, fake=False, verbosity=0)
|
|
self.assert_(null_ok())
|
|
|
|
# make sure it is NOT NULL again
|
|
migration.migrate_app(app, tree, target_name="0002", resolve_mode=None, fake=False, verbosity=0)
|
|
self.failIf(null_ok(), 'name not null after migration')
|
|
|
|
# finish with no migrations, otherwise other tests fail...
|
|
migration.migrate_app(app, tree, target_name="zero", resolve_mode=None, fake=False, verbosity=0)
|
|
self.assertEqual(list(migration.MigrationHistory.objects.all()), [])
|
|
|
|
def test_dependencies(self):
|
|
|
|
fakeapp = migration.get_app("fakeapp")
|
|
otherfakeapp = migration.get_app("otherfakeapp")
|
|
|
|
# Test a simple path
|
|
tree = migration.dependency_tree()
|
|
self.assertEqual(
|
|
map(snd, migration.needed_before_forwards(tree, fakeapp, "0003_alter_spam")),
|
|
['0001_spam', '0002_eggs'],
|
|
)
|
|
|
|
# And a complex one, with both back and forwards deps
|
|
self.assertEqual(
|
|
map(snd, migration.needed_before_forwards(tree, otherfakeapp, "0003_third")),
|
|
['0001_spam', '0001_first', '0002_second', '0002_eggs', '0003_alter_spam'],
|
|
) |