mirror of
				https://github.com/django/django.git
				synced 2025-10-31 01:25:32 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			218 lines
		
	
	
		
			8.8 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			218 lines
		
	
	
		
			8.8 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| from django.test import TestCase
 | |
| from django.db.migrations.graph import CircularDependencyError, MigrationGraph, NodeNotFoundError
 | |
| 
 | |
| 
 | |
| class GraphTests(TestCase):
 | |
|     """
 | |
|     Tests the digraph structure.
 | |
|     """
 | |
| 
 | |
|     def test_simple_graph(self):
 | |
|         """
 | |
|         Tests a basic dependency graph:
 | |
| 
 | |
|         app_a:  0001 <-- 0002 <--- 0003 <-- 0004
 | |
|                                  /
 | |
|         app_b:  0001 <-- 0002 <-/
 | |
|         """
 | |
|         # Build graph
 | |
|         graph = MigrationGraph()
 | |
|         graph.add_node(("app_a", "0001"), None)
 | |
|         graph.add_node(("app_a", "0002"), None)
 | |
|         graph.add_node(("app_a", "0003"), None)
 | |
|         graph.add_node(("app_a", "0004"), None)
 | |
|         graph.add_node(("app_b", "0001"), None)
 | |
|         graph.add_node(("app_b", "0002"), None)
 | |
|         graph.add_dependency("app_a.0004", ("app_a", "0004"), ("app_a", "0003"))
 | |
|         graph.add_dependency("app_a.0003", ("app_a", "0003"), ("app_a", "0002"))
 | |
|         graph.add_dependency("app_a.0002", ("app_a", "0002"), ("app_a", "0001"))
 | |
|         graph.add_dependency("app_a.0003", ("app_a", "0003"), ("app_b", "0002"))
 | |
|         graph.add_dependency("app_b.0002", ("app_b", "0002"), ("app_b", "0001"))
 | |
|         # Test root migration case
 | |
|         self.assertEqual(
 | |
|             graph.forwards_plan(("app_a", "0001")),
 | |
|             [('app_a', '0001')],
 | |
|         )
 | |
|         # Test branch B only
 | |
|         self.assertEqual(
 | |
|             graph.forwards_plan(("app_b", "0002")),
 | |
|             [("app_b", "0001"), ("app_b", "0002")],
 | |
|         )
 | |
|         # Test whole graph
 | |
|         self.assertEqual(
 | |
|             graph.forwards_plan(("app_a", "0004")),
 | |
|             [('app_b', '0001'), ('app_b', '0002'), ('app_a', '0001'), ('app_a', '0002'), ('app_a', '0003'), ('app_a', '0004')],
 | |
|         )
 | |
|         # Test reverse to b:0002
 | |
|         self.assertEqual(
 | |
|             graph.backwards_plan(("app_b", "0002")),
 | |
|             [('app_a', '0004'), ('app_a', '0003'), ('app_b', '0002')],
 | |
|         )
 | |
|         # Test roots and leaves
 | |
|         self.assertEqual(
 | |
|             graph.root_nodes(),
 | |
|             [('app_a', '0001'), ('app_b', '0001')],
 | |
|         )
 | |
|         self.assertEqual(
 | |
|             graph.leaf_nodes(),
 | |
|             [('app_a', '0004'), ('app_b', '0002')],
 | |
|         )
 | |
| 
 | |
|     def test_complex_graph(self):
 | |
|         """
 | |
|         Tests a complex dependency graph:
 | |
| 
 | |
|         app_a:  0001 <-- 0002 <--- 0003 <-- 0004
 | |
|                       \        \ /         /
 | |
|         app_b:  0001 <-\ 0002 <-X         /
 | |
|                       \          \       /
 | |
|         app_c:         \ 0001 <-- 0002 <-
 | |
|         """
 | |
|         # Build graph
 | |
|         graph = MigrationGraph()
 | |
|         graph.add_node(("app_a", "0001"), None)
 | |
|         graph.add_node(("app_a", "0002"), None)
 | |
|         graph.add_node(("app_a", "0003"), None)
 | |
|         graph.add_node(("app_a", "0004"), None)
 | |
|         graph.add_node(("app_b", "0001"), None)
 | |
|         graph.add_node(("app_b", "0002"), None)
 | |
|         graph.add_node(("app_c", "0001"), None)
 | |
|         graph.add_node(("app_c", "0002"), None)
 | |
|         graph.add_dependency("app_a.0004", ("app_a", "0004"), ("app_a", "0003"))
 | |
|         graph.add_dependency("app_a.0003", ("app_a", "0003"), ("app_a", "0002"))
 | |
|         graph.add_dependency("app_a.0002", ("app_a", "0002"), ("app_a", "0001"))
 | |
|         graph.add_dependency("app_a.0003", ("app_a", "0003"), ("app_b", "0002"))
 | |
|         graph.add_dependency("app_b.0002", ("app_b", "0002"), ("app_b", "0001"))
 | |
|         graph.add_dependency("app_a.0004", ("app_a", "0004"), ("app_c", "0002"))
 | |
|         graph.add_dependency("app_c.0002", ("app_c", "0002"), ("app_c", "0001"))
 | |
|         graph.add_dependency("app_c.0001", ("app_c", "0001"), ("app_b", "0001"))
 | |
|         graph.add_dependency("app_c.0002", ("app_c", "0002"), ("app_a", "0002"))
 | |
|         # Test branch C only
 | |
|         self.assertEqual(
 | |
|             graph.forwards_plan(("app_c", "0002")),
 | |
|             [('app_b', '0001'), ('app_c', '0001'), ('app_a', '0001'), ('app_a', '0002'), ('app_c', '0002')],
 | |
|         )
 | |
|         # Test whole graph
 | |
|         self.assertEqual(
 | |
|             graph.forwards_plan(("app_a", "0004")),
 | |
|             [('app_b', '0001'), ('app_c', '0001'), ('app_a', '0001'), ('app_a', '0002'), ('app_c', '0002'), ('app_b', '0002'), ('app_a', '0003'), ('app_a', '0004')],
 | |
|         )
 | |
|         # Test reverse to b:0001
 | |
|         self.assertEqual(
 | |
|             graph.backwards_plan(("app_b", "0001")),
 | |
|             [('app_a', '0004'), ('app_c', '0002'), ('app_c', '0001'), ('app_a', '0003'), ('app_b', '0002'), ('app_b', '0001')],
 | |
|         )
 | |
|         # Test roots and leaves
 | |
|         self.assertEqual(
 | |
|             graph.root_nodes(),
 | |
|             [('app_a', '0001'), ('app_b', '0001'), ('app_c', '0001')],
 | |
|         )
 | |
|         self.assertEqual(
 | |
|             graph.leaf_nodes(),
 | |
|             [('app_a', '0004'), ('app_b', '0002'), ('app_c', '0002')],
 | |
|         )
 | |
| 
 | |
|     def test_circular_graph(self):
 | |
|         """
 | |
|         Tests a circular dependency graph.
 | |
|         """
 | |
|         # Build graph
 | |
|         graph = MigrationGraph()
 | |
|         graph.add_node(("app_a", "0001"), None)
 | |
|         graph.add_node(("app_a", "0002"), None)
 | |
|         graph.add_node(("app_a", "0003"), None)
 | |
|         graph.add_node(("app_b", "0001"), None)
 | |
|         graph.add_node(("app_b", "0002"), None)
 | |
|         graph.add_dependency("app_a.0003", ("app_a", "0003"), ("app_a", "0002"))
 | |
|         graph.add_dependency("app_a.0002", ("app_a", "0002"), ("app_a", "0001"))
 | |
|         graph.add_dependency("app_a.0001", ("app_a", "0001"), ("app_b", "0002"))
 | |
|         graph.add_dependency("app_b.0002", ("app_b", "0002"), ("app_b", "0001"))
 | |
|         graph.add_dependency("app_b.0001", ("app_b", "0001"), ("app_a", "0003"))
 | |
|         # Test whole graph
 | |
|         self.assertRaises(
 | |
|             CircularDependencyError,
 | |
|             graph.forwards_plan, ("app_a", "0003"),
 | |
|         )
 | |
| 
 | |
|     def test_dfs(self):
 | |
|         graph = MigrationGraph()
 | |
|         root = ("app_a", "1")
 | |
|         graph.add_node(root, None)
 | |
|         expected = [root]
 | |
|         for i in range(2, 1000):
 | |
|             parent = ("app_a", str(i - 1))
 | |
|             child = ("app_a", str(i))
 | |
|             graph.add_node(child, None)
 | |
|             graph.add_dependency(str(i), child, parent)
 | |
|             expected.append(child)
 | |
| 
 | |
|         actual = graph.dfs(root, lambda x: graph.dependents.get(x, set()))
 | |
|         self.assertEqual(expected[::-1], actual)
 | |
| 
 | |
|     def test_plan_invalid_node(self):
 | |
|         """
 | |
|         Tests for forwards/backwards_plan of nonexistent node.
 | |
|         """
 | |
|         graph = MigrationGraph()
 | |
|         message = "Node ('app_b', '0001') not a valid node"
 | |
| 
 | |
|         with self.assertRaisesMessage(NodeNotFoundError, message):
 | |
|             graph.forwards_plan(("app_b", "0001"))
 | |
| 
 | |
|         with self.assertRaisesMessage(NodeNotFoundError, message):
 | |
|             graph.backwards_plan(("app_b", "0001"))
 | |
| 
 | |
|     def test_missing_parent_nodes(self):
 | |
|         """
 | |
|         Tests for missing parent nodes.
 | |
|         """
 | |
|         # Build graph
 | |
|         graph = MigrationGraph()
 | |
|         graph.add_node(("app_a", "0001"), None)
 | |
|         graph.add_node(("app_a", "0002"), None)
 | |
|         graph.add_node(("app_a", "0003"), None)
 | |
|         graph.add_node(("app_b", "0001"), None)
 | |
|         graph.add_dependency("app_a.0003", ("app_a", "0003"), ("app_a", "0002"))
 | |
|         graph.add_dependency("app_a.0002", ("app_a", "0002"), ("app_a", "0001"))
 | |
|         msg = "Migration app_a.0001 dependencies reference nonexistent parent node ('app_b', '0002')"
 | |
|         with self.assertRaisesMessage(NodeNotFoundError, msg):
 | |
|             graph.add_dependency("app_a.0001", ("app_a", "0001"), ("app_b", "0002"))
 | |
| 
 | |
|     def test_missing_child_nodes(self):
 | |
|         """
 | |
|         Tests for missing child nodes.
 | |
|         """
 | |
|         # Build graph
 | |
|         graph = MigrationGraph()
 | |
|         graph.add_node(("app_a", "0001"), None)
 | |
|         msg = "Migration app_a.0002 dependencies reference nonexistent child node ('app_a', '0002')"
 | |
|         with self.assertRaisesMessage(NodeNotFoundError, msg):
 | |
|             graph.add_dependency("app_a.0002", ("app_a", "0002"), ("app_a", "0001"))
 | |
| 
 | |
|     def test_infinite_loop(self):
 | |
|         """
 | |
|         Tests a complex dependency graph:
 | |
| 
 | |
|         app_a:        0001 <-
 | |
|                              \
 | |
|         app_b:        0001 <- x 0002 <-
 | |
|                        /               \
 | |
|         app_c:   0001<-  <------------- x 0002
 | |
| 
 | |
|         And apply sqashing on app_c.
 | |
|         """
 | |
|         graph = MigrationGraph()
 | |
| 
 | |
|         graph.add_node(("app_a", "0001"), None)
 | |
|         graph.add_node(("app_b", "0001"), None)
 | |
|         graph.add_node(("app_b", "0002"), None)
 | |
|         graph.add_node(("app_c", "0001_squashed_0002"), None)
 | |
| 
 | |
|         graph.add_dependency("app_b.0001", ("app_b", "0001"), ("app_c", "0001_squashed_0002"))
 | |
|         graph.add_dependency("app_b.0002", ("app_b", "0002"), ("app_a", "0001"))
 | |
|         graph.add_dependency("app_b.0002", ("app_b", "0002"), ("app_b", "0001"))
 | |
|         graph.add_dependency("app_c.0001_squashed_0002", ("app_c", "0001_squashed_0002"), ("app_b", "0002"))
 | |
| 
 | |
|         with self.assertRaises(CircularDependencyError):
 | |
|             graph.forwards_plan(("app_c", "0001_squashed_0002"))
 |