1
from django.test import TestCase
2
from django.db.migrations.graph import MigrationGraph, CircularDependencyError
5
class GraphTests(TestCase):
7
Tests the digraph structure.
10
def test_simple_graph(self):
12
Tests a basic dependency graph:
14
app_a: 0001 <-- 0002 <--- 0003 <-- 0004
16
app_b: 0001 <-- 0002 <-/
19
graph = MigrationGraph()
20
graph.add_node(("app_a", "0001"), None)
21
graph.add_node(("app_a", "0002"), None)
22
graph.add_node(("app_a", "0003"), None)
23
graph.add_node(("app_a", "0004"), None)
24
graph.add_node(("app_b", "0001"), None)
25
graph.add_node(("app_b", "0002"), None)
26
graph.add_dependency("app_a.0004", ("app_a", "0004"), ("app_a", "0003"))
27
graph.add_dependency("app_a.0003", ("app_a", "0003"), ("app_a", "0002"))
28
graph.add_dependency("app_a.0002", ("app_a", "0002"), ("app_a", "0001"))
29
graph.add_dependency("app_a.0003", ("app_a", "0003"), ("app_b", "0002"))
30
graph.add_dependency("app_b.0002", ("app_b", "0002"), ("app_b", "0001"))
31
# Test root migration case
33
graph.forwards_plan(("app_a", "0001")),
38
graph.forwards_plan(("app_b", "0002")),
39
[("app_b", "0001"), ("app_b", "0002")],
43
graph.forwards_plan(("app_a", "0004")),
44
[('app_b', '0001'), ('app_b', '0002'), ('app_a', '0001'), ('app_a', '0002'), ('app_a', '0003'), ('app_a', '0004')],
46
# Test reverse to b:0002
48
graph.backwards_plan(("app_b", "0002")),
49
[('app_a', '0004'), ('app_a', '0003'), ('app_b', '0002')],
51
# Test roots and leaves
54
[('app_a', '0001'), ('app_b', '0001')],
58
[('app_a', '0004'), ('app_b', '0002')],
61
def test_complex_graph(self):
63
Tests a complex dependency graph:
65
app_a: 0001 <-- 0002 <--- 0003 <-- 0004
67
app_b: 0001 <-\ 0002 <-X /
69
app_c: \ 0001 <-- 0002 <-
72
graph = MigrationGraph()
73
graph.add_node(("app_a", "0001"), None)
74
graph.add_node(("app_a", "0002"), None)
75
graph.add_node(("app_a", "0003"), None)
76
graph.add_node(("app_a", "0004"), None)
77
graph.add_node(("app_b", "0001"), None)
78
graph.add_node(("app_b", "0002"), None)
79
graph.add_node(("app_c", "0001"), None)
80
graph.add_node(("app_c", "0002"), None)
81
graph.add_dependency("app_a.0004", ("app_a", "0004"), ("app_a", "0003"))
82
graph.add_dependency("app_a.0003", ("app_a", "0003"), ("app_a", "0002"))
83
graph.add_dependency("app_a.0002", ("app_a", "0002"), ("app_a", "0001"))
84
graph.add_dependency("app_a.0003", ("app_a", "0003"), ("app_b", "0002"))
85
graph.add_dependency("app_b.0002", ("app_b", "0002"), ("app_b", "0001"))
86
graph.add_dependency("app_a.0004", ("app_a", "0004"), ("app_c", "0002"))
87
graph.add_dependency("app_c.0002", ("app_c", "0002"), ("app_c", "0001"))
88
graph.add_dependency("app_c.0001", ("app_c", "0001"), ("app_b", "0001"))
89
graph.add_dependency("app_c.0002", ("app_c", "0002"), ("app_a", "0002"))
92
graph.forwards_plan(("app_c", "0002")),
93
[('app_b', '0001'), ('app_c', '0001'), ('app_a', '0001'), ('app_a', '0002'), ('app_c', '0002')],
97
graph.forwards_plan(("app_a", "0004")),
98
[('app_b', '0001'), ('app_c', '0001'), ('app_a', '0001'), ('app_a', '0002'), ('app_c', '0002'), ('app_b', '0002'), ('app_a', '0003'), ('app_a', '0004')],
100
# Test reverse to b:0001
102
graph.backwards_plan(("app_b", "0001")),
103
[('app_a', '0004'), ('app_c', '0002'), ('app_c', '0001'), ('app_a', '0003'), ('app_b', '0002'), ('app_b', '0001')],
105
# Test roots and leaves
108
[('app_a', '0001'), ('app_b', '0001'), ('app_c', '0001')],
112
[('app_a', '0004'), ('app_b', '0002'), ('app_c', '0002')],
115
def test_circular_graph(self):
117
Tests a circular dependency graph.
120
graph = MigrationGraph()
121
graph.add_node(("app_a", "0001"), None)
122
graph.add_node(("app_a", "0002"), None)
123
graph.add_node(("app_a", "0003"), None)
124
graph.add_node(("app_b", "0001"), None)
125
graph.add_node(("app_b", "0002"), None)
126
graph.add_dependency("app_a.0003", ("app_a", "0003"), ("app_a", "0002"))
127
graph.add_dependency("app_a.0002", ("app_a", "0002"), ("app_a", "0001"))
128
graph.add_dependency("app_a.0001", ("app_a", "0001"), ("app_b", "0002"))
129
graph.add_dependency("app_b.0002", ("app_b", "0002"), ("app_b", "0001"))
130
graph.add_dependency("app_b.0001", ("app_b", "0001"), ("app_a", "0003"))
133
CircularDependencyError,
134
graph.forwards_plan, ("app_a", "0003"),
137
def test_plan_invalid_node(self):
139
Tests for forwards/backwards_plan of nonexistent node.
141
graph = MigrationGraph()
142
message = "Node ('app_b', '0001') not a valid node"
144
with self.assertRaisesMessage(ValueError, message):
145
graph.forwards_plan(("app_b", "0001"))
147
with self.assertRaisesMessage(ValueError, message):
148
graph.backwards_plan(("app_b", "0001"))
150
def test_missing_parent_nodes(self):
152
Tests for missing parent nodes.
155
graph = MigrationGraph()
156
graph.add_node(("app_a", "0001"), None)
157
graph.add_node(("app_a", "0002"), None)
158
graph.add_node(("app_a", "0003"), None)
159
graph.add_node(("app_b", "0001"), None)
160
graph.add_dependency("app_a.0003", ("app_a", "0003"), ("app_a", "0002"))
161
graph.add_dependency("app_a.0002", ("app_a", "0002"), ("app_a", "0001"))
162
with self.assertRaisesMessage(KeyError, "Migration app_a.0001 dependencies references nonexistent parent node ('app_b', '0002')"):
163
graph.add_dependency("app_a.0001", ("app_a", "0001"), ("app_b", "0002"))
165
def test_missing_child_nodes(self):
167
Tests for missing child nodes.
170
graph = MigrationGraph()
171
graph.add_node(("app_a", "0001"), None)
172
with self.assertRaisesMessage(KeyError, "Migration app_a.0002 dependencies references nonexistent child node ('app_a', '0002')"):
173
graph.add_dependency("app_a.0002", ("app_a", "0002"), ("app_a", "0001"))