Uaktualnienie django-south.
[wolnelektury.git] / apps / south / tests / logic.py
1 import unittest
2 import datetime
3 import sys
4 import os
5
6 from south import migration
7
8 # Add the tests directory so fakeapp is on sys.path
9 test_root = os.path.dirname(__file__)
10 sys.path.append(test_root)
11
12
13 class TestMigrationLogic(unittest.TestCase):
14
15     """
16     Tests if the various logic functions in migration actually work.
17     """
18
19     def create_fake_app(self, name):
20         
21         class Fake:
22             pass
23         
24         fake = Fake()
25         fake.__name__ = name
26         return fake
27
28
29     def create_test_app(self):
30         
31         class Fake:
32             pass
33         
34         fake = Fake()
35         fake.__name__ = "fakeapp.migrations"
36         fake.__file__ = os.path.join(test_root, "fakeapp", "migrations", "__init__.py")
37         return fake
38     
39     
40     def monkeypatch(self):
41         """Swaps out various Django calls for fake ones for our own nefarious purposes."""
42         
43         def new_get_apps():
44             return ['fakeapp']
45         
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 = (
50             ["fakeapp"],
51             settings.INSTALLED_APPS,
52         )
53         self.redo_app_cache()
54     setUp = monkeypatch
55     
56     
57     def unmonkeypatch(self):
58         """Undoes what monkeypatch did."""
59         
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
64         self.redo_app_cache()
65     tearDown = unmonkeypatch
66     
67     
68     def redo_app_cache(self):
69         from django.db.models.loading import AppCache
70         a = AppCache()
71         a.loaded = False
72         a._populate()
73     
74
75     def test_get_app_name(self):
76         self.assertEqual(
77             "southtest",
78             migration.get_app_name(self.create_fake_app("southtest.migrations")),
79         )
80         self.assertEqual(
81             "baz",
82             migration.get_app_name(self.create_fake_app("foo.bar.baz.migrations")),
83         )
84     
85     
86     def test_get_migrated_apps(self):
87         
88         P1 = __import__("fakeapp.migrations", {}, {}, [''])
89         
90         self.assertEqual(
91             [P1],
92             list(migration.get_migrated_apps()),
93         )
94     
95     
96     def test_get_app(self):
97         
98         P1 = __import__("fakeapp.migrations", {}, {}, [''])
99         
100         self.assertEqual(P1, migration.get_app("fakeapp"))
101         self.assertEqual(P1, migration.get_app(self.create_fake_app("fakeapp.models")))
102     
103     
104     def test_get_app_fullname(self):
105         self.assertEqual(
106             "southtest",
107             migration.get_app_fullname(self.create_fake_app("southtest.migrations")),
108         )
109         self.assertEqual(
110             "foo.bar.baz",
111             migration.get_app_fullname(self.create_fake_app("foo.bar.baz.migrations")),
112         )
113     
114     
115     def test_get_migration_names(self):
116         
117         app = self.create_test_app()
118         
119         self.assertEqual(
120             ["0001_spam", "0002_eggs", "0003_alter_spam"],
121             migration.get_migration_names(app),
122         )
123     
124     
125     def test_get_migration_classes(self):
126         
127         app = self.create_test_app()
128         
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
133         
134         self.assertEqual(
135             [M1, M2, M3],
136             list(migration.get_migration_classes(app)),
137         )
138     
139     
140     def test_get_migration(self):
141         
142         app = self.create_test_app()
143         
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
147         
148         self.assertEqual(M1, migration.get_migration(app, "0001_spam"))
149         self.assertEqual(M2, migration.get_migration(app, "0002_eggs"))
150         
151         self.assertRaises((ImportError, ValueError), migration.get_migration, app, "0001_jam")
152     
153     
154     def test_all_migrations(self):
155         
156         app = migration.get_app("fakeapp")
157         
158         self.assertEqual(
159             {app: {
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"),
163             }},
164             migration.all_migrations(),
165         )
166     
167     
168     def assertListEqual(self, list1, list2):
169         list1 = list(list1)
170         list2 = list(list2)
171         list1.sort()
172         list2.sort()
173         return self.assertEqual(list1, list2)
174     
175     
176     def test_apply_migrations(self):
177         
178         app = migration.get_app("fakeapp")
179         
180         # We should start with no migrations
181         self.assertEqual(list(migration.MigrationHistory.objects.all()), [])
182         
183         # Apply them normally
184         migration.migrate_app(app, target_name=None, resolve_mode=None, fake=False, silent=True)
185         
186         # We should finish with all migrations
187         self.assertListEqual(
188             (
189                 (u"fakeapp", u"0001_spam"),
190                 (u"fakeapp", u"0002_eggs"),
191                 (u"fakeapp", u"0003_alter_spam"),
192             ),
193             migration.MigrationHistory.objects.values_list("app_name", "migration"),
194         )
195         
196         # Now roll them backwards
197         migration.migrate_app(app, target_name="zero", resolve_mode=None, fake=False, silent=True)
198         
199         # Finish with none
200         self.assertEqual(list(migration.MigrationHistory.objects.all()), [])
201     
202     
203     def test_migration_merge_forwards(self):
204         
205         app = migration.get_app("fakeapp")
206         
207         # We should start with no migrations
208         self.assertEqual(list(migration.MigrationHistory.objects.all()), [])
209         
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(),
215         )
216         
217         # Did it go in?
218         self.assertListEqual(
219             (
220                 (u"fakeapp", u"0002_eggs"),
221             ),
222             migration.MigrationHistory.objects.values_list("app_name", "migration"),
223         )
224         
225         # Apply them normally
226         try:
227             migration.migrate_app(app, target_name=None, resolve_mode=None, fake=False, silent=True)
228         except SystemExit:
229             pass
230         
231         # Nothing should have changed (no merge mode!)
232         self.assertListEqual(
233             (
234                 (u"fakeapp", u"0002_eggs"),
235             ),
236             migration.MigrationHistory.objects.values_list("app_name", "migration"),
237         )
238         
239         # Apply with merge
240         migration.migrate_app(app, target_name=None, resolve_mode="merge", fake=False, silent=True)
241         
242         # We should finish with all migrations
243         self.assertListEqual(
244             (
245                 (u"fakeapp", u"0001_spam"),
246                 (u"fakeapp", u"0002_eggs"),
247                 (u"fakeapp", u"0003_alter_spam"),
248             ),
249             migration.MigrationHistory.objects.values_list("app_name", "migration"),
250         )
251         
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)
256         
257         # Finish with none
258         self.assertEqual(list(migration.MigrationHistory.objects.all()), [])
259     
260     def test_alter_column_null(self):
261         def null_ok():
262             from django.db import connection, transaction
263             # the DBAPI introspection module fails on postgres NULLs.
264             cursor = connection.cursor()
265             try:
266                 cursor.execute("INSERT INTO southtest_spam (id, weight, expires, name) VALUES (100, 10.1, now(), NULL);")
267             except:
268                 transaction.rollback()
269                 return False
270             else:
271                 cursor.execute("DELETE FROM southtest_spam")
272                 transaction.commit()
273                 return True
274         
275         app = migration.get_app("fakeapp")
276         self.assertEqual(list(migration.MigrationHistory.objects.all()), [])
277         
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())
281         
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())
285
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')
289         
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()), [])