Project

General

Profile

Statistics
| Branch: | Revision:

root / env / lib / python2.7 / site-packages / south / tests / logic.py @ d1a4905f

History | View | Annotate | Download (32.7 KB)

1
from south.tests import unittest
2

    
3
import datetime
4
import sys
5

    
6
from south import exceptions
7
from south.migration import migrate_app
8
from south.migration.base import all_migrations, Migrations
9
from south.creator.changes import ManualChanges
10
from south.migration.utils import depends, flatten, get_app_label
11
from south.models import MigrationHistory
12
from south.tests import Monkeypatcher
13
from south.db import db
14

    
15

    
16

    
17
class TestBrokenMigration(Monkeypatcher):
18
    installed_apps = ["fakeapp", "otherfakeapp", "brokenapp"]
19

    
20
    def test_broken_dependencies(self):
21
        self.assertRaises(
22
            exceptions.DependsOnUnmigratedApplication,
23
            Migrations.calculate_dependencies,
24
            force=True,
25
        )
26
        #depends_on_unknown = self.brokenapp['0002_depends_on_unknown']
27
        #self.assertRaises(exceptions.DependsOnUnknownMigration,
28
        #                  depends_on_unknown.dependencies)
29
        #depends_on_higher = self.brokenapp['0003_depends_on_higher']
30
        #self.assertRaises(exceptions.DependsOnHigherMigration,
31
        #                  depends_on_higher.dependencies)
32

    
33

    
34
class TestMigration(Monkeypatcher):
35
    installed_apps = ["fakeapp", "otherfakeapp"]
36

    
37
    def setUp(self):
38
        super(TestMigration, self).setUp()
39
        self.fakeapp = Migrations('fakeapp')
40
        self.otherfakeapp = Migrations('otherfakeapp')
41
        Migrations.calculate_dependencies(force=True)
42

    
43
    def test_str(self):
44
        migrations = [str(m) for m in self.fakeapp]
45
        self.assertEqual(['fakeapp:0001_spam',
46
                          'fakeapp:0002_eggs',
47
                          'fakeapp:0003_alter_spam'],
48
                         migrations)
49
    
50
    def test_repr(self):
51
        migrations = [repr(m) for m in self.fakeapp]
52
        self.assertEqual(['<Migration: fakeapp:0001_spam>',
53
                          '<Migration: fakeapp:0002_eggs>',
54
                          '<Migration: fakeapp:0003_alter_spam>'],
55
                         migrations)
56

    
57
    def test_app_label(self):
58
        self.assertEqual(['fakeapp', 'fakeapp', 'fakeapp'],
59
                         [m.app_label() for m in self.fakeapp])
60
                         
61
    def test_name(self):
62
        self.assertEqual(['0001_spam', '0002_eggs', '0003_alter_spam'],
63
                         [m.name() for m in self.fakeapp])
64

    
65
    def test_full_name(self):
66
        self.assertEqual(['fakeapp.migrations.0001_spam',
67
                          'fakeapp.migrations.0002_eggs',
68
                          'fakeapp.migrations.0003_alter_spam'],
69
                         [m.full_name() for m in self.fakeapp])
70
    
71
    def test_migration(self):
72
        # Can't use vanilla import, modules beginning with numbers aren't in grammar
73
        M1 = __import__("fakeapp.migrations.0001_spam", {}, {}, ['Migration']).Migration
74
        M2 = __import__("fakeapp.migrations.0002_eggs", {}, {}, ['Migration']).Migration
75
        M3 = __import__("fakeapp.migrations.0003_alter_spam", {}, {}, ['Migration']).Migration
76
        self.assertEqual([M1, M2, M3],
77
                         [m.migration().Migration for m in self.fakeapp])
78
        self.assertRaises(exceptions.UnknownMigration,
79
                          self.fakeapp['9999_unknown'].migration)
80

    
81
    def test_previous(self):
82
        self.assertEqual([None,
83
                          self.fakeapp['0001_spam'],
84
                          self.fakeapp['0002_eggs']],
85
                         [m.previous() for m in self.fakeapp])
86

    
87
    def test_dependencies(self):
88
        "Test that the dependency detection works."
89
        self.assertEqual([
90
                set([]),
91
                set([self.fakeapp['0001_spam']]),
92
                set([self.fakeapp['0002_eggs']])
93
            ],
94
            [m.dependencies for m in self.fakeapp],
95
        )
96
        self.assertEqual([
97
                set([self.fakeapp['0001_spam']]),
98
                set([self.otherfakeapp['0001_first']]),
99
                set([
100
                    self.otherfakeapp['0002_second'],
101
                    self.fakeapp['0003_alter_spam'],
102
                ])
103
            ],
104
            [m.dependencies for m in self.otherfakeapp],
105
        )
106

    
107
    def test_forwards_plan(self):
108
        self.assertEqual([
109
                [self.fakeapp['0001_spam']],
110
                [
111
                    self.fakeapp['0001_spam'],
112
                    self.fakeapp['0002_eggs']
113
                ],
114
                [
115
                    self.fakeapp['0001_spam'],
116
                    self.fakeapp['0002_eggs'],
117
                    self.fakeapp['0003_alter_spam'],
118
                ]
119
            ],
120
            [m.forwards_plan() for m in self.fakeapp],
121
        )
122
        self.assertEqual([
123
                [
124
                    self.fakeapp['0001_spam'],
125
                    self.otherfakeapp['0001_first']
126
                ],
127
                [
128
                    self.fakeapp['0001_spam'],
129
                    self.otherfakeapp['0001_first'],
130
                    self.otherfakeapp['0002_second']
131
                ],
132
                [
133
                    self.fakeapp['0001_spam'],
134
                    self.otherfakeapp['0001_first'],
135
                    self.otherfakeapp['0002_second'],
136
                    self.fakeapp['0002_eggs'],
137
                    self.fakeapp['0003_alter_spam'],
138
                    self.otherfakeapp['0003_third'],
139
                ]
140
            ],
141
            [m.forwards_plan() for m in self.otherfakeapp],
142
        )
143

    
144
    def test_is_before(self):
145
        F1 = self.fakeapp['0001_spam']
146
        F2 = self.fakeapp['0002_eggs']
147
        F3 = self.fakeapp['0003_alter_spam']
148
        O1 = self.otherfakeapp['0001_first']
149
        O2 = self.otherfakeapp['0002_second']
150
        O3 = self.otherfakeapp['0003_third']
151
        self.assertTrue(F1.is_before(F2))
152
        self.assertTrue(F1.is_before(F3))
153
        self.assertTrue(F2.is_before(F3))
154
        self.assertEqual(O3.is_before(O1), False)
155
        self.assertEqual(O3.is_before(O2), False)
156
        self.assertEqual(O2.is_before(O2), False)
157
        self.assertEqual(O2.is_before(O1), False)
158
        self.assertEqual(F2.is_before(O1), None)
159
        self.assertEqual(F2.is_before(O2), None)
160
        self.assertEqual(F2.is_before(O3), None)
161

    
162

    
163
class TestMigrationDependencies(Monkeypatcher):
164
    installed_apps = ['deps_a', 'deps_b', 'deps_c']
165

    
166
    def setUp(self):
167
        super(TestMigrationDependencies, self).setUp()
168
        self.deps_a = Migrations('deps_a')
169
        self.deps_b = Migrations('deps_b')
170
        self.deps_c = Migrations('deps_c')
171
        Migrations.calculate_dependencies(force=True)
172

    
173
    def test_dependencies(self):
174
        self.assertEqual(
175
            [
176
                set([]),
177
                set([self.deps_a['0001_a']]),
178
                set([self.deps_a['0002_a']]),
179
                set([
180
                    self.deps_a['0003_a'],
181
                    self.deps_b['0003_b'],
182
                ]),
183
                set([self.deps_a['0004_a']]),
184
            ],
185
            [m.dependencies for m in self.deps_a],
186
        )
187
        self.assertEqual(
188
            [
189
                set([]),
190
                set([
191
                    self.deps_b['0001_b'],
192
                    self.deps_a['0002_a']
193
                ]),
194
                set([
195
                    self.deps_b['0002_b'],
196
                    self.deps_a['0003_a']
197
                ]),
198
                set([self.deps_b['0003_b']]),
199
                set([self.deps_b['0004_b']]),
200
            ],
201
            [m.dependencies for m in self.deps_b],
202
        )
203
        self.assertEqual(
204
            [
205
                set([]),
206
                set([self.deps_c['0001_c']]),
207
                set([self.deps_c['0002_c']]),
208
                set([self.deps_c['0003_c']]),
209
                set([
210
                    self.deps_c['0004_c'],
211
                    self.deps_a['0002_a']
212
                ]),
213
            ],
214
            [m.dependencies for m in self.deps_c],
215
        )
216

    
217
    def test_dependents(self):
218
        self.assertEqual([set([self.deps_a['0002_a']]),
219
                          set([self.deps_c['0005_c'],
220
                                 self.deps_b['0002_b'],
221
                                 self.deps_a['0003_a']]),
222
                          set([self.deps_b['0003_b'],
223
                                 self.deps_a['0004_a']]),
224
                          set([self.deps_a['0005_a']]),
225
                          set([])],
226
                         [m.dependents for m in self.deps_a])
227
        self.assertEqual([set([self.deps_b['0002_b']]),
228
                          set([self.deps_b['0003_b']]),
229
                          set([self.deps_b['0004_b'],
230
                                 self.deps_a['0004_a']]),
231
                          set([self.deps_b['0005_b']]),
232
                          set([])],
233
                         [m.dependents for m in self.deps_b])
234
        self.assertEqual([set([self.deps_c['0002_c']]),
235
                          set([self.deps_c['0003_c']]),
236
                          set([self.deps_c['0004_c']]),
237
                          set([self.deps_c['0005_c']]),
238
                          set([])],
239
                         [m.dependents for m in self.deps_c])
240

    
241
    def test_forwards_plan(self):
242
        self.assertEqual([[self.deps_a['0001_a']],
243
                          [self.deps_a['0001_a'],
244
                           self.deps_a['0002_a']],
245
                          [self.deps_a['0001_a'],
246
                           self.deps_a['0002_a'],
247
                           self.deps_a['0003_a']],
248
                          [self.deps_b['0001_b'],
249
                           self.deps_a['0001_a'],
250
                           self.deps_a['0002_a'],
251
                           self.deps_b['0002_b'],
252
                           self.deps_a['0003_a'],
253
                           self.deps_b['0003_b'],
254
                           self.deps_a['0004_a']],
255
                          [self.deps_b['0001_b'],
256
                           self.deps_a['0001_a'],
257
                           self.deps_a['0002_a'],
258
                           self.deps_b['0002_b'],
259
                           self.deps_a['0003_a'],
260
                           self.deps_b['0003_b'],
261
                           self.deps_a['0004_a'],
262
                           self.deps_a['0005_a']]],
263
                         [m.forwards_plan() for m in self.deps_a])
264
        self.assertEqual([[self.deps_b['0001_b']],
265
                          [self.deps_b['0001_b'],
266
                           self.deps_a['0001_a'],
267
                           self.deps_a['0002_a'],
268
                           self.deps_b['0002_b']],
269
                          [self.deps_b['0001_b'],
270
                           self.deps_a['0001_a'],
271
                           self.deps_a['0002_a'],
272
                           self.deps_b['0002_b'],
273
                           self.deps_a['0003_a'],
274
                           self.deps_b['0003_b']],
275
                          [self.deps_b['0001_b'],
276
                           self.deps_a['0001_a'],
277
                           self.deps_a['0002_a'],
278
                           self.deps_b['0002_b'],
279
                           self.deps_a['0003_a'],
280
                           self.deps_b['0003_b'],
281
                           self.deps_b['0004_b']],
282
                          [self.deps_b['0001_b'],
283
                           self.deps_a['0001_a'],
284
                           self.deps_a['0002_a'],
285
                           self.deps_b['0002_b'],
286
                           self.deps_a['0003_a'],
287
                           self.deps_b['0003_b'],
288
                           self.deps_b['0004_b'],
289
                           self.deps_b['0005_b']]],
290
                         [m.forwards_plan() for m in self.deps_b])
291
        self.assertEqual([[self.deps_c['0001_c']],
292
                          [self.deps_c['0001_c'],
293
                           self.deps_c['0002_c']],
294
                          [self.deps_c['0001_c'],
295
                           self.deps_c['0002_c'],
296
                           self.deps_c['0003_c']],
297
                          [self.deps_c['0001_c'],
298
                           self.deps_c['0002_c'],
299
                           self.deps_c['0003_c'],
300
                           self.deps_c['0004_c']],
301
                          [self.deps_c['0001_c'],
302
                           self.deps_c['0002_c'],
303
                           self.deps_c['0003_c'],
304
                           self.deps_c['0004_c'],
305
                           self.deps_a['0001_a'],
306
                           self.deps_a['0002_a'],
307
                           self.deps_c['0005_c']]],
308
                         [m.forwards_plan() for m in self.deps_c])
309

    
310
    def test_backwards_plan(self):
311
        self.assertEqual([
312
            [
313
                self.deps_c['0005_c'],
314
                self.deps_b['0005_b'],
315
                self.deps_b['0004_b'],
316
                self.deps_a['0005_a'],
317
                self.deps_a['0004_a'],
318
                self.deps_b['0003_b'],
319
                self.deps_b['0002_b'],
320
                self.deps_a['0003_a'],
321
                self.deps_a['0002_a'],
322
                self.deps_a['0001_a'],
323
            ],
324
            [
325
                self.deps_c['0005_c'],
326
                self.deps_b['0005_b'],
327
                self.deps_b['0004_b'],
328
                self.deps_a['0005_a'],
329
                self.deps_a['0004_a'],
330
                self.deps_b['0003_b'],
331
                self.deps_b['0002_b'],
332
                self.deps_a['0003_a'],
333
                self.deps_a['0002_a'],
334
            ],
335
            [
336
                self.deps_b['0005_b'],
337
                self.deps_b['0004_b'],
338
                self.deps_a['0005_a'],
339
                self.deps_a['0004_a'],
340
                self.deps_b['0003_b'],
341
                self.deps_a['0003_a'],
342
            ],
343
            [
344
                self.deps_a['0005_a'],
345
                self.deps_a['0004_a'],
346
            ],
347
            [
348
                self.deps_a['0005_a'],
349
            ]
350
        ], [m.backwards_plan() for m in self.deps_a])
351
        self.assertEqual([
352
            [
353
                self.deps_b['0005_b'],
354
                self.deps_b['0004_b'],
355
                self.deps_a['0005_a'],
356
                self.deps_a['0004_a'],
357
                self.deps_b['0003_b'],
358
                self.deps_b['0002_b'],
359
                self.deps_b['0001_b'],
360
            ],
361
            [
362
                self.deps_b['0005_b'],
363
                self.deps_b['0004_b'],
364
                self.deps_a['0005_a'],
365
                self.deps_a['0004_a'],
366
                self.deps_b['0003_b'],
367
                self.deps_b['0002_b'],
368
            ],
369
            [
370
                self.deps_b['0005_b'],
371
                self.deps_b['0004_b'],
372
                self.deps_a['0005_a'],
373
                self.deps_a['0004_a'],
374
                self.deps_b['0003_b'],
375
            ],
376
            [
377
                self.deps_b['0005_b'],
378
                self.deps_b['0004_b'],
379
            ],
380
            [
381
                self.deps_b['0005_b'],
382
            ],
383
        ], [m.backwards_plan() for m in self.deps_b])
384
        self.assertEqual([
385
            [
386
                self.deps_c['0005_c'],
387
                self.deps_c['0004_c'],
388
                self.deps_c['0003_c'],
389
                self.deps_c['0002_c'],
390
                self.deps_c['0001_c'],
391
            ],
392
            [
393
                self.deps_c['0005_c'],
394
                self.deps_c['0004_c'],
395
                self.deps_c['0003_c'],
396
                self.deps_c['0002_c'],
397
            ],
398
            [
399
                self.deps_c['0005_c'],
400
                self.deps_c['0004_c'],
401
                self.deps_c['0003_c'],
402
            ],
403
            [
404
                self.deps_c['0005_c'],
405
                self.deps_c['0004_c'],
406
            ],
407
            [self.deps_c['0005_c']]
408
        ],  [m.backwards_plan() for m in self.deps_c])
409

    
410

    
411
class TestCircularDependencies(Monkeypatcher):
412
    installed_apps = ["circular_a", "circular_b"]
413

    
414
    def test_plans(self):
415
        Migrations.calculate_dependencies(force=True)
416
        circular_a = Migrations('circular_a')
417
        circular_b = Migrations('circular_b')
418
        self.assertRaises(
419
            exceptions.CircularDependency,
420
            circular_a[-1].forwards_plan,
421
        )
422
        self.assertRaises(
423
            exceptions.CircularDependency,
424
            circular_b[-1].forwards_plan,
425
        )
426
        self.assertRaises(
427
            exceptions.CircularDependency,
428
            circular_a[-1].backwards_plan,
429
        )
430
        self.assertRaises(
431
            exceptions.CircularDependency,
432
            circular_b[-1].backwards_plan,
433
        )
434

    
435

    
436
class TestMigrations(Monkeypatcher):
437
    installed_apps = ["fakeapp", "otherfakeapp"]
438

    
439
    def test_all(self):
440
        
441
        M1 = Migrations(__import__("fakeapp", {}, {}, ['']))
442
        M2 = Migrations(__import__("otherfakeapp", {}, {}, ['']))
443
        
444
        self.assertEqual(
445
            [M1, M2],
446
            list(all_migrations()),
447
        )
448

    
449
    def test(self):
450
        
451
        M1 = Migrations(__import__("fakeapp", {}, {}, ['']))
452
        
453
        self.assertEqual(M1, Migrations("fakeapp"))
454
        self.assertEqual(M1, Migrations(self.create_fake_app("fakeapp")))
455

    
456
    def test_application(self):
457
        fakeapp = Migrations("fakeapp")
458
        application = __import__("fakeapp", {}, {}, [''])
459
        self.assertEqual(application, fakeapp.application)
460

    
461
    def test_migration(self):
462
        # Can't use vanilla import, modules beginning with numbers aren't in grammar
463
        M1 = __import__("fakeapp.migrations.0001_spam", {}, {}, ['Migration']).Migration
464
        M2 = __import__("fakeapp.migrations.0002_eggs", {}, {}, ['Migration']).Migration
465
        migration = Migrations('fakeapp')
466
        self.assertEqual(M1, migration['0001_spam'].migration().Migration)
467
        self.assertEqual(M2, migration['0002_eggs'].migration().Migration)
468
        self.assertRaises(exceptions.UnknownMigration,
469
                          migration['0001_jam'].migration)
470

    
471
    def test_guess_migration(self):
472
        # Can't use vanilla import, modules beginning with numbers aren't in grammar
473
        M1 = __import__("fakeapp.migrations.0001_spam", {}, {}, ['Migration']).Migration
474
        migration = Migrations('fakeapp')
475
        self.assertEqual(M1, migration.guess_migration("0001_spam").migration().Migration)
476
        self.assertEqual(M1, migration.guess_migration("0001_spa").migration().Migration)
477
        self.assertEqual(M1, migration.guess_migration("0001_sp").migration().Migration)
478
        self.assertEqual(M1, migration.guess_migration("0001_s").migration().Migration)
479
        self.assertEqual(M1, migration.guess_migration("0001_").migration().Migration)
480
        self.assertEqual(M1, migration.guess_migration("0001").migration().Migration)
481
        self.assertRaises(exceptions.UnknownMigration,
482
                          migration.guess_migration, "0001-spam")
483
        self.assertRaises(exceptions.MultiplePrefixMatches,
484
                          migration.guess_migration, "000")
485
        self.assertRaises(exceptions.MultiplePrefixMatches,
486
                          migration.guess_migration, "")
487
        self.assertRaises(exceptions.UnknownMigration,
488
                          migration.guess_migration, "0001_spams")
489
        self.assertRaises(exceptions.UnknownMigration,
490
                          migration.guess_migration, "0001_jam")
491

    
492
    def test_app_label(self):
493
        names = ['fakeapp', 'otherfakeapp']
494
        self.assertEqual(names,
495
                         [Migrations(n).app_label() for n in names])
496
    
497
    def test_full_name(self):
498
        names = ['fakeapp', 'otherfakeapp']
499
        self.assertEqual([n + '.migrations' for n in names],
500
                         [Migrations(n).full_name() for n in names])
501

    
502

    
503
class TestMigrationLogic(Monkeypatcher):
504

    
505
    """
506
    Tests if the various logic functions in migration actually work.
507
    """
508
    
509
    installed_apps = ["fakeapp", "otherfakeapp"]
510

    
511
    def assertListEqual(self, list1, list2, msg=None):
512
        list1 = list(list1)
513
        list2 = list(list2)
514
        list1.sort()
515
        list2.sort()
516
        return self.assert_(list1 == list2, "%s is not equal to %s" % (list1, list2))
517

    
518
    def test_find_ghost_migrations(self):
519
        pass
520
    
521
    def test_apply_migrations(self):
522
        MigrationHistory.objects.all().delete()
523
        migrations = Migrations("fakeapp")
524
        
525
        # We should start with no migrations
526
        self.assertEqual(list(MigrationHistory.objects.all()), [])
527
        
528
        # Apply them normally
529
        migrate_app(migrations, target_name=None, fake=False,
530
                    load_initial_data=True)
531
        
532
        # We should finish with all migrations
533
        self.assertListEqual(
534
            ((u"fakeapp", u"0001_spam"),
535
             (u"fakeapp", u"0002_eggs"),
536
             (u"fakeapp", u"0003_alter_spam"),),
537
            MigrationHistory.objects.values_list("app_name", "migration"),
538
        )
539
        
540
        # Now roll them backwards
541
        migrate_app(migrations, target_name="zero", fake=False)
542
        
543
        # Finish with none
544
        self.assertEqual(list(MigrationHistory.objects.all()), [])
545
    
546
    
547
    def test_migration_merge_forwards(self):
548
        MigrationHistory.objects.all().delete()
549
        migrations = Migrations("fakeapp")
550
        
551
        # We should start with no migrations
552
        self.assertEqual(list(MigrationHistory.objects.all()), [])
553
        
554
        # Insert one in the wrong order
555
        MigrationHistory.objects.create(app_name = "fakeapp",
556
                                        migration = "0002_eggs",
557
                                        applied = datetime.datetime.now())
558
        
559
        # Did it go in?
560
        self.assertListEqual(
561
            ((u"fakeapp", u"0002_eggs"),),
562
            MigrationHistory.objects.values_list("app_name", "migration"),
563
        )
564
        
565
        # Apply them normally
566
        self.assertRaises(exceptions.InconsistentMigrationHistory,
567
                          migrate_app,
568
                          migrations, target_name=None, fake=False)
569
        self.assertRaises(exceptions.InconsistentMigrationHistory,
570
                          migrate_app,
571
                          migrations, target_name='zero', fake=False)
572
        try:
573
            migrate_app(migrations, target_name=None, fake=False)
574
        except exceptions.InconsistentMigrationHistory, e:
575
            self.assertEqual(
576
                [
577
                    (
578
                        migrations['0002_eggs'],
579
                        migrations['0001_spam'],
580
                    )
581
                ],
582
                e.problems,
583
            )
584
        try:
585
            migrate_app(migrations, target_name="zero", fake=False)
586
        except exceptions.InconsistentMigrationHistory, e:
587
            self.assertEqual(
588
                [
589
                    (
590
                        migrations['0002_eggs'],
591
                        migrations['0001_spam'],
592
                    )
593
                ],
594
                e.problems,
595
            )
596
        
597
        # Nothing should have changed (no merge mode!)
598
        self.assertListEqual(
599
            ((u"fakeapp", u"0002_eggs"),),
600
            MigrationHistory.objects.values_list("app_name", "migration"),
601
        )
602
        
603
        # Apply with merge
604
        migrate_app(migrations, target_name=None, merge=True, fake=False)
605
        
606
        # We should finish with all migrations
607
        self.assertListEqual(
608
            ((u"fakeapp", u"0001_spam"),
609
             (u"fakeapp", u"0002_eggs"),
610
             (u"fakeapp", u"0003_alter_spam"),),
611
            MigrationHistory.objects.values_list("app_name", "migration"),
612
        )
613
        
614
        # Now roll them backwards
615
        migrate_app(migrations, target_name="0002", fake=False)
616
        migrate_app(migrations, target_name="0001", fake=True)
617
        migrate_app(migrations, target_name="zero", fake=False)
618
        
619
        # Finish with none
620
        self.assertEqual(list(MigrationHistory.objects.all()), [])
621
    
622
    def test_alter_column_null(self):
623
        
624
        def null_ok(eat_exception=True):
625
            from django.db import connection, transaction
626
            # the DBAPI introspection module fails on postgres NULLs.
627
            cursor = connection.cursor()
628
        
629
            # SQLite has weird now()
630
            if db.backend_name == "sqlite3":
631
                now_func = "DATETIME('NOW')"
632
            # So does SQLServer... should we be using a backend attribute?
633
            elif db.backend_name == "pyodbc":
634
                now_func = "GETDATE()"
635
            elif db.backend_name == "oracle":
636
                now_func = "SYSDATE"
637
            else:
638
                now_func = "NOW()"
639
            
640
            try:
641
                if db.backend_name == "pyodbc":
642
                    cursor.execute("SET IDENTITY_INSERT southtest_spam ON;")
643
                cursor.execute("INSERT INTO southtest_spam (id, weight, expires, name) VALUES (100, NULL, %s, 'whatever');" % now_func)
644
            except:
645
                if eat_exception:
646
                    transaction.rollback()
647
                    return False
648
                else:
649
                    raise
650
            else:
651
                cursor.execute("DELETE FROM southtest_spam")
652
                transaction.commit()
653
                return True
654

    
655
        MigrationHistory.objects.all().delete()
656
        migrations = Migrations("fakeapp")
657
        
658
        # by default name is NOT NULL
659
        migrate_app(migrations, target_name="0002", fake=False)
660
        self.failIf(null_ok())
661
        self.assertListEqual(
662
            ((u"fakeapp", u"0001_spam"),
663
             (u"fakeapp", u"0002_eggs"),),
664
            MigrationHistory.objects.values_list("app_name", "migration"),
665
        )
666
        
667
        # after 0003, it should be NULL
668
        migrate_app(migrations, target_name="0003", fake=False)
669
        self.assert_(null_ok(False))
670
        self.assertListEqual(
671
            ((u"fakeapp", u"0001_spam"),
672
             (u"fakeapp", u"0002_eggs"),
673
             (u"fakeapp", u"0003_alter_spam"),),
674
            MigrationHistory.objects.values_list("app_name", "migration"),
675
        )
676

    
677
        # make sure it is NOT NULL again
678
        migrate_app(migrations, target_name="0002", fake=False)
679
        self.failIf(null_ok(), 'weight not null after migration')
680
        self.assertListEqual(
681
            ((u"fakeapp", u"0001_spam"),
682
             (u"fakeapp", u"0002_eggs"),),
683
            MigrationHistory.objects.values_list("app_name", "migration"),
684
        )
685
        
686
        # finish with no migrations, otherwise other tests fail...
687
        migrate_app(migrations, target_name="zero", fake=False)
688
        self.assertEqual(list(MigrationHistory.objects.all()), [])
689
    
690
    def test_dependencies(self):
691
        
692
        fakeapp = Migrations("fakeapp")
693
        otherfakeapp = Migrations("otherfakeapp")
694
        
695
        # Test a simple path
696
        self.assertEqual([fakeapp['0001_spam'],
697
                          fakeapp['0002_eggs'],
698
                          fakeapp['0003_alter_spam']],
699
                         fakeapp['0003_alter_spam'].forwards_plan())
700
        
701
        # And a complex one.
702
        self.assertEqual(
703
            [
704
                fakeapp['0001_spam'],
705
                otherfakeapp['0001_first'],
706
                otherfakeapp['0002_second'],
707
                fakeapp['0002_eggs'],
708
                fakeapp['0003_alter_spam'],
709
                otherfakeapp['0003_third']
710
            ],
711
            otherfakeapp['0003_third'].forwards_plan(),
712
        )
713

    
714

    
715
class TestMigrationUtils(Monkeypatcher):
716
    installed_apps = ["fakeapp", "otherfakeapp"]
717

    
718
    def test_get_app_label(self):
719
        self.assertEqual(
720
            "southtest",
721
            get_app_label(self.create_fake_app("southtest.models")),
722
        )
723
        self.assertEqual(
724
            "baz",
725
            get_app_label(self.create_fake_app("foo.bar.baz.models")),
726
        )
727

    
728
class TestUtils(unittest.TestCase):
729

    
730
    def test_flatten(self):
731
        self.assertEqual([], list(flatten(iter([]))))
732
        self.assertEqual([], list(flatten(iter([iter([]), ]))))
733
        self.assertEqual([1], list(flatten(iter([1]))))
734
        self.assertEqual([1, 2], list(flatten(iter([1, 2]))))
735
        self.assertEqual([1, 2], list(flatten(iter([iter([1]), 2]))))
736
        self.assertEqual([1, 2], list(flatten(iter([iter([1, 2])]))))
737
        self.assertEqual([1, 2, 3], list(flatten(iter([iter([1, 2]), 3]))))
738
        self.assertEqual([1, 2, 3],
739
                         list(flatten(iter([iter([1]), iter([2]), 3]))))
740
        self.assertEqual([1, 2, 3],
741
                         list(flatten([[1], [2], 3])))
742

    
743
    def test_depends(self):
744
        graph = {'A1': []}
745
        self.assertEqual(['A1'],
746
                         depends('A1', lambda n: graph[n]))
747
        graph = {'A1': [],
748
                 'A2': ['A1'],
749
                 'A3': ['A2']}
750
        self.assertEqual(['A1', 'A2', 'A3'],
751
                         depends('A3', lambda n: graph[n]))
752
        graph = {'A1': [],
753
                 'A2': ['A1'],
754
                 'A3': ['A2', 'A1']}
755
        self.assertEqual(['A1', 'A2', 'A3'],
756
                         depends('A3', lambda n: graph[n]))
757
        graph = {'A1': [],
758
                 'A2': ['A1'],
759
                 'A3': ['A2', 'A1', 'B1'],
760
                 'B1': []}
761
        self.assertEqual(
762
            ['B1', 'A1', 'A2', 'A3'],
763
            depends('A3', lambda n: graph[n]),
764
        )
765
        graph = {'A1': [],
766
                 'A2': ['A1'],
767
                 'A3': ['A2', 'A1', 'B2'],
768
                 'B1': [],
769
                 'B2': ['B1']}
770
        self.assertEqual(
771
            ['B1', 'B2', 'A1', 'A2', 'A3'],
772
            depends('A3', lambda n: graph[n]),
773
        )
774
        graph = {'A1': [],
775
                 'A2': ['A1', 'B1'],
776
                 'A3': ['A2'],
777
                 'B1': ['A1']}
778
        self.assertEqual(['A1', 'B1', 'A2', 'A3'],
779
                         depends('A3', lambda n: graph[n]))
780
        graph = {'A1': [],
781
                 'A2': ['A1'],
782
                 'A3': ['A2', 'A1', 'B2'],
783
                 'B1': [],
784
                 'B2': ['B1', 'C1'],
785
                 'C1': ['B1']}
786
        self.assertEqual(
787
            ['B1', 'C1', 'B2', 'A1', 'A2', 'A3'],
788
            depends('A3', lambda n: graph[n]),
789
        )
790
        graph = {'A1': [],
791
                 'A2': ['A1'],
792
                 'A3': ['A2', 'B2', 'A1', 'C1'],
793
                 'B1': ['A1'],
794
                 'B2': ['B1', 'C2', 'A1'],
795
                 'C1': ['B1'],
796
                 'C2': ['C1', 'A1'],
797
                 'C3': ['C2']}
798
        self.assertEqual(
799
            ['A1', 'B1', 'C1', 'C2', 'B2', 'A2', 'A3'],
800
            depends('A3', lambda n: graph[n]),
801
        )
802

    
803
    def assertCircularDependency(self, trace, target, graph):
804
        "Custom assertion that checks a circular dependency is detected correctly."
805
        self.assertRaises(
806
            exceptions.CircularDependency,
807
            depends,
808
            target,
809
            lambda n: graph[n],
810
        )
811
        try:
812
            depends(target, lambda n: graph[n])
813
        except exceptions.CircularDependency, e:
814
            self.assertEqual(trace, e.trace)
815

    
816
    def test_depends_cycle(self):
817
        graph = {'A1': ['A1']}
818
        self.assertCircularDependency(
819
            ['A1', 'A1'],
820
            'A1',
821
            graph,
822
        )
823
        graph = {'A1': [],
824
                 'A2': ['A1', 'A2'],
825
                 'A3': ['A2']}
826
        self.assertCircularDependency(
827
            ['A2', 'A2'],
828
            'A3',
829
            graph,
830
        )
831
        graph = {'A1': [],
832
                 'A2': ['A1'],
833
                 'A3': ['A2', 'A3'],
834
                 'A4': ['A3']}
835
        self.assertCircularDependency(
836
            ['A3', 'A3'],
837
            'A4',
838
            graph,
839
        )
840
        graph = {'A1': ['B1'],
841
                 'B1': ['A1']}
842
        self.assertCircularDependency(
843
            ['A1', 'B1', 'A1'],
844
            'A1',
845
            graph,
846
        )
847
        graph = {'A1': [],
848
                 'A2': ['A1', 'B2'],
849
                 'A3': ['A2'],
850
                 'B1': [],
851
                 'B2': ['B1', 'A2'],
852
                 'B3': ['B2']}
853
        self.assertCircularDependency(
854
            ['A2', 'B2', 'A2'],
855
            'A3',
856
            graph,
857
        )
858
        graph = {'A1': [],
859
                 'A2': ['A1', 'B3'],
860
                 'A3': ['A2'],
861
                 'B1': [],
862
                 'B2': ['B1', 'A2'],
863
                 'B3': ['B2']}
864
        self.assertCircularDependency(
865
            ['A2', 'B3', 'B2', 'A2'],
866
            'A3',
867
            graph,
868
        )
869
        graph = {'A1': [],
870
                 'A2': ['A1'],
871
                 'A3': ['A2', 'B2'],
872
                 'A4': ['A3'],
873
                 'B1': ['A3'],
874
                 'B2': ['B1']}
875
        self.assertCircularDependency(
876
            ['A3', 'B2', 'B1', 'A3'],
877
            'A4',
878
            graph,
879
        )
880

    
881
class TestManualChanges(Monkeypatcher):
882
    installed_apps = ["fakeapp", "otherfakeapp"]
883

    
884
    def test_suggest_name(self):
885
        migrations = Migrations('fakeapp')
886
        change = ManualChanges(migrations,
887
                               [],
888
                               ['fakeapp.slug'],
889
                               [])
890
        self.assertEquals(change.suggest_name(), 
891
                          'add_field_fakeapp_slug')
892

    
893
        change = ManualChanges(migrations,
894
                               [],
895
                               [],
896
                               ['fakeapp.slug'])
897
        self.assertEquals(change.suggest_name(), 
898
                          'add_index_fakeapp_slug')