1use super::dependency::{DependencyResolutionContext, DependencyResolver, MigrationDependency};
28use super::{Migration, MigrationError, Result};
29use serde::{Deserialize, Serialize};
30use std::collections::{HashMap, HashSet, VecDeque};
31
32#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
44pub struct MigrationKey {
45 pub app_label: String,
47 pub name: String,
49}
50
51impl MigrationKey {
52 pub fn new(app_label: impl Into<String>, name: impl Into<String>) -> Self {
64 Self {
65 app_label: app_label.into(),
66 name: name.into(),
67 }
68 }
69
70 pub fn id(&self) -> String {
81 format!("{}.{}", self.app_label, self.name)
82 }
83}
84
85impl std::fmt::Display for MigrationKey {
86 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
87 write!(f, "{}", self.id())
88 }
89}
90
91#[derive(Debug, Clone)]
93pub struct MigrationNode {
94 pub key: MigrationKey,
96 pub dependencies: Vec<MigrationKey>,
98 pub replaces: Vec<MigrationKey>,
100}
101
102impl MigrationNode {
103 pub fn new(key: MigrationKey, dependencies: Vec<MigrationKey>) -> Self {
105 Self {
106 key,
107 dependencies,
108 replaces: Vec::new(),
109 }
110 }
111
112 pub fn with_replaces(
114 key: MigrationKey,
115 dependencies: Vec<MigrationKey>,
116 replaces: Vec<MigrationKey>,
117 ) -> Self {
118 Self {
119 key,
120 dependencies,
121 replaces,
122 }
123 }
124}
125
126pub struct MigrationGraph {
147 nodes: HashMap<MigrationKey, MigrationNode>,
148}
149
150impl MigrationGraph {
151 pub fn new() -> Self {
162 Self {
163 nodes: HashMap::new(),
164 }
165 }
166
167 pub fn add_migration(&mut self, key: MigrationKey, dependencies: Vec<MigrationKey>) {
181 let node = MigrationNode::new(key.clone(), dependencies);
182 self.nodes.insert(key, node);
183 }
184
185 pub fn has_migration(&self, key: &MigrationKey) -> bool {
200 self.nodes.contains_key(key)
201 }
202
203 pub fn get_node(&self, key: &MigrationKey) -> Option<&MigrationNode> {
205 self.nodes.get(key)
206 }
207
208 pub fn is_empty(&self) -> bool {
222 self.nodes.is_empty()
223 }
224
225 pub fn len(&self) -> usize {
227 self.nodes.len()
228 }
229
230 pub fn all_migrations(&self) -> Vec<&MigrationKey> {
232 self.nodes.keys().collect()
233 }
234
235 pub fn get_dependencies(&self, key: &MigrationKey) -> Option<&[MigrationKey]> {
254 self.nodes.get(key).map(|node| node.dependencies.as_slice())
255 }
256
257 pub fn get_dependents(&self, key: &MigrationKey) -> Vec<&MigrationKey> {
276 self.nodes
277 .iter()
278 .filter(|(_, node)| node.dependencies.contains(key))
279 .map(|(k, _)| k)
280 .collect()
281 }
282
283 pub fn topological_sort(&self) -> Result<Vec<MigrationKey>> {
309 let mut in_degree: HashMap<MigrationKey, usize> = HashMap::new();
311 let mut dependents: HashMap<MigrationKey, Vec<MigrationKey>> = HashMap::new();
312
313 for key in self.nodes.keys() {
314 in_degree.insert(key.clone(), 0);
315 dependents.insert(key.clone(), Vec::new());
316 }
317
318 for node in self.nodes.values() {
319 for dep in &node.dependencies {
320 if self.nodes.contains_key(dep) {
323 *in_degree
324 .get_mut(&node.key)
325 .expect("node key must be initialized") += 1;
326 dependents
327 .get_mut(dep)
328 .expect("internal dependency key must be initialized")
329 .push(node.key.clone());
330 }
331 }
332 }
333
334 for dependent_keys in dependents.values_mut() {
335 dependent_keys.sort_by(|a, b| {
336 a.app_label
337 .cmp(&b.app_label)
338 .then_with(|| a.name.cmp(&b.name))
339 });
340 }
341
342 let mut zero_degree: Vec<MigrationKey> = in_degree
345 .iter()
346 .filter(|&(_, °ree)| degree == 0)
347 .map(|(key, _)| key.clone())
348 .collect();
349 zero_degree.sort_by(|a, b| {
350 a.app_label
351 .cmp(&b.app_label)
352 .then_with(|| a.name.cmp(&b.name))
353 });
354 let mut queue: VecDeque<MigrationKey> = zero_degree.into_iter().collect();
355
356 let mut result = Vec::with_capacity(self.nodes.len());
357
358 while let Some(key) = queue.pop_front() {
359 result.push(key.clone());
360
361 let mut newly_freed = Vec::new();
363 if let Some(dependent_keys) = dependents.get(&key) {
364 for other_key in dependent_keys {
365 if let Some(degree) = in_degree.get_mut(other_key) {
366 *degree -= 1;
367 if *degree == 0 {
368 newly_freed.push(other_key.clone());
369 }
370 }
371 }
372 }
373 queue.extend(newly_freed);
374 }
375
376 if result.len() != self.nodes.len() {
378 let remaining: Vec<String> = self
379 .nodes
380 .keys()
381 .filter(|k| !result.contains(k))
382 .map(|k| k.id())
383 .collect();
384
385 return Err(MigrationError::CircularDependency {
386 cycle: format!("Circular dependency detected: {}", remaining.join(", ")),
387 });
388 }
389
390 Ok(result)
391 }
392
393 pub fn get_leaf_nodes(&self) -> Vec<&MigrationKey> {
413 self.nodes
414 .keys()
415 .filter(|key| self.get_dependents(key).is_empty())
416 .collect()
417 }
418
419 pub fn get_leaf_nodes_for_app(&self, app_label: &str) -> Vec<&MigrationKey> {
444 let mut leaves: Vec<&MigrationKey> = self
445 .get_leaf_nodes()
446 .into_iter()
447 .filter(|key| key.app_label == app_label)
448 .collect();
449 leaves.sort_by(|a, b| a.name.cmp(&b.name));
450 leaves
451 }
452
453 pub fn detect_conflicts(&self) -> HashMap<String, Vec<&MigrationKey>> {
480 let leaves = self.get_leaf_nodes();
481 let mut app_leaves: HashMap<String, Vec<&MigrationKey>> = HashMap::new();
482
483 for leaf in leaves {
484 app_leaves
485 .entry(leaf.app_label.clone())
486 .or_default()
487 .push(leaf);
488 }
489
490 let mut conflicts: HashMap<String, Vec<&MigrationKey>> = HashMap::new();
492 for (app, mut leaves) in app_leaves {
493 if leaves.len() >= 2 {
494 leaves.sort_by(|a, b| a.name.cmp(&b.name));
495 conflicts.insert(app, leaves);
496 }
497 }
498
499 conflicts
500 }
501
502 pub fn get_root_nodes(&self) -> Vec<&MigrationKey> {
522 self.nodes
523 .values()
524 .filter(|node| node.dependencies.is_empty())
525 .map(|node| &node.key)
526 .collect()
527 }
528
529 pub fn remove_migration(&mut self, key: &MigrationKey) {
531 self.nodes.remove(key);
532 }
533
534 pub fn clear(&mut self) {
536 self.nodes.clear();
537 }
538
539 pub fn add_migration_with_replaces(
558 &mut self,
559 key: MigrationKey,
560 dependencies: Vec<MigrationKey>,
561 replaces: Vec<MigrationKey>,
562 ) {
563 let node = MigrationNode::with_replaces(key.clone(), dependencies, replaces);
564 self.nodes.insert(key, node);
565 }
566
567 pub fn find_migration_path(
592 &self,
593 from: &MigrationKey,
594 to: &MigrationKey,
595 ) -> Result<Vec<MigrationKey>> {
596 use std::collections::VecDeque;
597
598 if from == to {
599 return Ok(vec![from.clone()]);
600 }
601
602 if !self.has_migration(from) {
603 return Err(MigrationError::NodeNotFound {
604 message: "Source migration not found".to_string(),
605 node: from.id(),
606 });
607 }
608
609 if !self.has_migration(to) {
610 return Err(MigrationError::NodeNotFound {
611 message: "Target migration not found".to_string(),
612 node: to.id(),
613 });
614 }
615
616 let mut queue = VecDeque::new();
618 let mut visited = HashMap::new();
619 let mut parent: HashMap<MigrationKey, MigrationKey> = HashMap::new();
620
621 queue.push_back(from.clone());
622 visited.insert(from.clone(), true);
623
624 while let Some(current) = queue.pop_front() {
625 if ¤t == to {
626 let mut path = vec![to.clone()];
628 let mut node = to.clone();
629
630 while let Some(p) = parent.get(&node) {
631 path.push(p.clone());
632 node = p.clone();
633 }
634
635 path.reverse();
636 return Ok(path);
637 }
638
639 for dependent in self.get_dependents(¤t) {
641 if !visited.contains_key(dependent) {
642 visited.insert(dependent.clone(), true);
643 parent.insert(dependent.clone(), current.clone());
644 queue.push_back(dependent.clone());
645 }
646 }
647 }
648
649 Err(MigrationError::DependencyError(format!(
650 "No path found from {} to {}",
651 from.id(),
652 to.id()
653 )))
654 }
655
656 pub fn find_backward_path(
679 &self,
680 from: &MigrationKey,
681 to: &MigrationKey,
682 ) -> Result<Vec<MigrationKey>> {
683 use std::collections::VecDeque;
684
685 if from == to {
686 return Ok(vec![from.clone()]);
687 }
688
689 if !self.has_migration(from) {
690 return Err(MigrationError::NodeNotFound {
691 message: "Source migration not found".to_string(),
692 node: from.id(),
693 });
694 }
695
696 if !self.has_migration(to) {
697 return Err(MigrationError::NodeNotFound {
698 message: "Target migration not found".to_string(),
699 node: to.id(),
700 });
701 }
702
703 let mut queue = VecDeque::new();
705 let mut visited = HashMap::new();
706 let mut parent: HashMap<MigrationKey, MigrationKey> = HashMap::new();
707
708 queue.push_back(from.clone());
709 visited.insert(from.clone(), true);
710
711 while let Some(current) = queue.pop_front() {
712 if ¤t == to {
713 let mut path = vec![to.clone()];
715 let mut node = to.clone();
716
717 while let Some(p) = parent.get(&node) {
718 path.push(p.clone());
719 node = p.clone();
720 }
721
722 path.reverse();
723 return Ok(path);
724 }
725
726 if let Some(deps) = self.get_dependencies(¤t) {
728 for dep in deps {
729 if !visited.contains_key(dep) {
730 visited.insert(dep.clone(), true);
731 parent.insert(dep.clone(), current.clone());
732 queue.push_back(dep.clone());
733 }
734 }
735 }
736 }
737
738 Err(MigrationError::DependencyError(format!(
739 "No backward path found from {} to {}",
740 from.id(),
741 to.id()
742 )))
743 }
744
745 pub fn resolve_execution_order_with_replaces(&self) -> Result<Vec<MigrationKey>> {
775 let mut replaced: HashSet<MigrationKey> = HashSet::new();
777 let mut replacement_map: HashMap<MigrationKey, MigrationKey> = HashMap::new();
778
779 for (key, node) in &self.nodes {
780 for replaced_key in &node.replaces {
781 replaced.insert(replaced_key.clone());
782 replacement_map.insert(replaced_key.clone(), key.clone());
783 }
784 }
785
786 let mut filtered_graph = MigrationGraph::new();
788 for (key, node) in &self.nodes {
789 if !replaced.contains(key) {
790 let mut filtered_deps: HashSet<MigrationKey> = HashSet::new();
792
793 for dep in &node.dependencies {
794 if let Some(replacement) = replacement_map.get(dep) {
795 filtered_deps.insert(replacement.clone());
797 } else if !replaced.contains(dep) {
798 filtered_deps.insert(dep.clone());
800 }
801 }
803
804 filtered_graph.add_migration(key.clone(), filtered_deps.into_iter().collect());
805 }
806 }
807
808 filtered_graph.topological_sort()
810 }
811
812 pub fn is_replaced(&self, key: &MigrationKey) -> bool {
833 self.nodes.values().any(|node| node.replaces.contains(key))
834 }
835
836 pub fn get_replacement(&self, key: &MigrationKey) -> Option<&MigrationKey> {
857 self.nodes
858 .iter()
859 .find(|(_, node)| node.replaces.contains(key))
860 .map(|(k, _)| k)
861 }
862
863 pub fn detect_all_cycles(&self) -> Vec<Vec<MigrationKey>> {
884 let mut cycles = Vec::new();
885 let mut visited = HashSet::new();
886 let mut rec_stack = HashSet::new();
887 let mut path = Vec::new();
888
889 for key in self.nodes.keys() {
890 if !visited.contains(key) {
891 self.detect_cycle_dfs(key, &mut visited, &mut rec_stack, &mut path, &mut cycles);
892 }
893 }
894
895 cycles
896 }
897
898 fn detect_cycle_dfs(
899 &self,
900 key: &MigrationKey,
901 visited: &mut HashSet<MigrationKey>,
902 rec_stack: &mut HashSet<MigrationKey>,
903 path: &mut Vec<MigrationKey>,
904 cycles: &mut Vec<Vec<MigrationKey>>,
905 ) {
906 visited.insert(key.clone());
907 rec_stack.insert(key.clone());
908 path.push(key.clone());
909
910 if let Some(node) = self.nodes.get(key) {
911 for dep in &node.dependencies {
912 if !visited.contains(dep) {
913 self.detect_cycle_dfs(dep, visited, rec_stack, path, cycles);
914 } else if rec_stack.contains(dep) {
915 let cycle_start = path.iter().position(|k| k == dep).unwrap();
917 let cycle: Vec<MigrationKey> = path[cycle_start..].to_vec();
918 cycles.push(cycle);
919 }
920 }
921 }
922
923 path.pop();
924 rec_stack.remove(key);
925 }
926
927 pub fn add_migration_with_context(
969 &mut self,
970 migration: &Migration,
971 context: &DependencyResolutionContext,
972 ) {
973 let key = MigrationKey::new(migration.app_label.clone(), migration.name.clone());
974 let resolver = DependencyResolver::new(context);
975
976 let mut all_dependencies: Vec<MigrationKey> = Vec::new();
978
979 for (app_label, migration_name) in &migration.dependencies {
981 all_dependencies.push(MigrationKey::new(app_label.clone(), migration_name.clone()));
982 }
983
984 for swappable in &migration.swappable_dependencies {
986 let dep = MigrationDependency::Swappable(swappable.clone());
987 if let Some((app_label, migration_name)) = resolver.resolve(&dep) {
988 all_dependencies.push(MigrationKey::new(app_label, migration_name));
989 }
990 }
991
992 for optional in &migration.optional_dependencies {
994 let dep = MigrationDependency::Optional(optional.clone());
995 if let Some((app_label, migration_name)) = resolver.resolve(&dep) {
996 all_dependencies.push(MigrationKey::new(app_label, migration_name));
997 }
998 }
999
1000 let replaces: Vec<MigrationKey> = migration
1002 .replaces
1003 .iter()
1004 .map(|(app, name)| MigrationKey::new(app.clone(), name.clone()))
1005 .collect();
1006
1007 if replaces.is_empty() {
1008 self.add_migration(key, all_dependencies);
1009 } else {
1010 self.add_migration_with_replaces(key, all_dependencies, replaces);
1011 }
1012 }
1013
1014 pub fn from_migrations(
1039 migrations: &[Migration],
1040 context: &DependencyResolutionContext,
1041 ) -> Self {
1042 let mut graph = Self::new();
1043 for migration in migrations {
1044 graph.add_migration_with_context(migration, context);
1045 }
1046 graph
1047 }
1048
1049 pub fn resolve_execution_order(
1075 migrations: &[Migration],
1076 context: &DependencyResolutionContext,
1077 ) -> Result<Vec<MigrationKey>> {
1078 let graph = Self::from_migrations(migrations, context);
1079
1080 let has_replaces = migrations.iter().any(|m| !m.replaces.is_empty());
1082
1083 if has_replaces {
1084 graph.resolve_execution_order_with_replaces()
1085 } else {
1086 graph.topological_sort()
1087 }
1088 }
1089}
1090
1091impl Default for MigrationGraph {
1092 fn default() -> Self {
1093 Self::new()
1094 }
1095}
1096
1097#[cfg(test)]
1098mod tests {
1099 use super::*;
1100 use rstest::rstest;
1101
1102 #[test]
1103 fn test_migration_key_creation() {
1104 let key = MigrationKey::new("auth", "0001_initial");
1105 assert_eq!(key.app_label, "auth");
1106 assert_eq!(key.name, "0001_initial");
1107 assert_eq!(key.id(), "auth.0001_initial");
1108 }
1109
1110 #[test]
1111 fn test_migration_key_display() {
1112 let key = MigrationKey::new("users", "0002_add_email");
1113 assert_eq!(format!("{}", key), "users.0002_add_email");
1114 }
1115
1116 #[test]
1117 fn test_graph_creation() {
1118 let graph = MigrationGraph::new();
1119 assert!(graph.is_empty());
1120 assert_eq!(graph.len(), 0);
1121 }
1122
1123 #[test]
1124 fn test_add_migration() {
1125 let mut graph = MigrationGraph::new();
1126 let key = MigrationKey::new("auth", "0001_initial");
1127
1128 graph.add_migration(key.clone(), vec![]);
1129 assert!(!graph.is_empty());
1130 assert_eq!(graph.len(), 1);
1131 assert!(graph.has_migration(&key));
1132 }
1133
1134 #[test]
1135 fn test_get_dependencies() {
1136 let mut graph = MigrationGraph::new();
1137 let key1 = MigrationKey::new("auth", "0001_initial");
1138 let key2 = MigrationKey::new("auth", "0002_add_field");
1139
1140 graph.add_migration(key1.clone(), vec![]);
1141 graph.add_migration(key2.clone(), vec![key1.clone()]);
1142
1143 let deps = graph.get_dependencies(&key2).unwrap();
1144 assert_eq!(deps.len(), 1);
1145 assert_eq!(deps[0], key1);
1146 }
1147
1148 #[test]
1149 fn test_get_dependents() {
1150 let mut graph = MigrationGraph::new();
1151 let key1 = MigrationKey::new("auth", "0001_initial");
1152 let key2 = MigrationKey::new("auth", "0002_add_field");
1153 let key3 = MigrationKey::new("auth", "0003_alter_field");
1154
1155 graph.add_migration(key1.clone(), vec![]);
1156 graph.add_migration(key2.clone(), vec![key1.clone()]);
1157 graph.add_migration(key3.clone(), vec![key2.clone()]);
1158
1159 let dependents = graph.get_dependents(&key1);
1160 assert_eq!(dependents.len(), 1);
1161 assert_eq!(dependents[0], &key2);
1162
1163 let dependents2 = graph.get_dependents(&key2);
1164 assert_eq!(dependents2.len(), 1);
1165 assert_eq!(dependents2[0], &key3);
1166 }
1167
1168 #[test]
1169 fn test_topological_sort_simple() {
1170 let mut graph = MigrationGraph::new();
1171 let key1 = MigrationKey::new("auth", "0001_initial");
1172 let key2 = MigrationKey::new("auth", "0002_add_field");
1173
1174 graph.add_migration(key1.clone(), vec![]);
1175 graph.add_migration(key2.clone(), vec![key1.clone()]);
1176
1177 let order = graph.topological_sort().unwrap();
1178 assert_eq!(order.len(), 2);
1179 assert_eq!(order[0], key1);
1180 assert_eq!(order[1], key2);
1181 }
1182
1183 #[test]
1184 fn test_topological_sort_complex() {
1185 let mut graph = MigrationGraph::new();
1186
1187 let key1 = MigrationKey::new("auth", "0001_initial");
1188 let key2 = MigrationKey::new("auth", "0002_add_field");
1189 let key3 = MigrationKey::new("users", "0001_initial");
1190 let key4 = MigrationKey::new("users", "0002_add_auth");
1191
1192 graph.add_migration(key1.clone(), vec![]);
1193 graph.add_migration(key2.clone(), vec![key1.clone()]);
1194 graph.add_migration(key3.clone(), vec![]);
1195 graph.add_migration(key4.clone(), vec![key2.clone(), key3.clone()]);
1196
1197 let order = graph.topological_sort().unwrap();
1198 assert_eq!(order.len(), 4);
1199
1200 let pos1 = order.iter().position(|k| k == &key1).unwrap();
1202 let pos2 = order.iter().position(|k| k == &key2).unwrap();
1203 assert!(pos1 < pos2);
1204
1205 let pos4 = order.iter().position(|k| k == &key4).unwrap();
1207 assert!(pos2 < pos4);
1208 let pos3 = order.iter().position(|k| k == &key3).unwrap();
1209 assert!(pos3 < pos4);
1210 }
1211
1212 #[test]
1213 fn test_circular_dependency_detection() {
1214 let mut graph = MigrationGraph::new();
1215
1216 let key1 = MigrationKey::new("auth", "0001_initial");
1217 let key2 = MigrationKey::new("auth", "0002_add_field");
1218
1219 graph.add_migration(key1.clone(), vec![key2.clone()]);
1221 graph.add_migration(key2.clone(), vec![key1.clone()]);
1222
1223 let result = graph.topological_sort();
1224 assert!(result.is_err());
1225
1226 if let Err(MigrationError::CircularDependency { cycle }) = result {
1227 assert!(cycle.contains("Circular dependency"));
1228 } else {
1229 panic!("Expected CircularDependency error");
1230 }
1231 }
1232
1233 #[test]
1234 fn test_get_leaf_nodes() {
1235 let mut graph = MigrationGraph::new();
1236
1237 let key1 = MigrationKey::new("auth", "0001_initial");
1238 let key2 = MigrationKey::new("auth", "0002_add_field");
1239 let key3 = MigrationKey::new("users", "0001_initial");
1240
1241 graph.add_migration(key1.clone(), vec![]);
1242 graph.add_migration(key2.clone(), vec![key1.clone()]);
1243 graph.add_migration(key3.clone(), vec![]);
1244
1245 let leaves = graph.get_leaf_nodes();
1246 assert_eq!(leaves.len(), 2);
1247 assert!(leaves.contains(&&key2));
1248 assert!(leaves.contains(&&key3));
1249 }
1250
1251 #[test]
1252 fn test_get_root_nodes() {
1253 let mut graph = MigrationGraph::new();
1254
1255 let key1 = MigrationKey::new("auth", "0001_initial");
1256 let key2 = MigrationKey::new("auth", "0002_add_field");
1257 let key3 = MigrationKey::new("users", "0001_initial");
1258
1259 graph.add_migration(key1.clone(), vec![]);
1260 graph.add_migration(key2.clone(), vec![key1.clone()]);
1261 graph.add_migration(key3.clone(), vec![]);
1262
1263 let roots = graph.get_root_nodes();
1264 assert_eq!(roots.len(), 2);
1265 assert!(roots.contains(&&key1));
1266 assert!(roots.contains(&&key3));
1267 }
1268
1269 #[test]
1270 fn test_remove_migration() {
1271 let mut graph = MigrationGraph::new();
1272 let key = MigrationKey::new("auth", "0001_initial");
1273
1274 graph.add_migration(key.clone(), vec![]);
1275 assert!(graph.has_migration(&key));
1276
1277 graph.remove_migration(&key);
1278 assert!(!graph.has_migration(&key));
1279 assert!(graph.is_empty());
1280 }
1281
1282 #[test]
1283 fn test_clear() {
1284 let mut graph = MigrationGraph::new();
1285
1286 graph.add_migration(MigrationKey::new("auth", "0001_initial"), vec![]);
1287 graph.add_migration(MigrationKey::new("users", "0001_initial"), vec![]);
1288
1289 assert_eq!(graph.len(), 2);
1290
1291 graph.clear();
1292 assert!(graph.is_empty());
1293 assert_eq!(graph.len(), 0);
1294 }
1295
1296 #[test]
1297 fn test_multiple_root_nodes_sort() {
1298 let mut graph = MigrationGraph::new();
1299
1300 let auth_0001 = MigrationKey::new("auth", "0001_initial");
1301 let users_0001 = MigrationKey::new("users", "0001_initial");
1302 let posts_0001 = MigrationKey::new("posts", "0001_initial");
1303
1304 graph.add_migration(auth_0001.clone(), vec![]);
1305 graph.add_migration(users_0001.clone(), vec![]);
1306 graph.add_migration(posts_0001.clone(), vec![]);
1307
1308 let order = graph.topological_sort().unwrap();
1309 assert_eq!(order.len(), 3);
1310 assert!(order.contains(&auth_0001));
1312 assert!(order.contains(&users_0001));
1313 assert!(order.contains(&posts_0001));
1314 }
1315
1316 #[test]
1317 fn test_complex_dependency_chain() {
1318 let mut graph = MigrationGraph::new();
1319
1320 let a = MigrationKey::new("app", "0001_a");
1327 let b = MigrationKey::new("app", "0002_b");
1328 let c = MigrationKey::new("app", "0003_c");
1329 let d = MigrationKey::new("app", "0004_d");
1330
1331 graph.add_migration(a.clone(), vec![]);
1332 graph.add_migration(b.clone(), vec![a.clone()]);
1333 graph.add_migration(c.clone(), vec![a.clone()]);
1334 graph.add_migration(d.clone(), vec![b.clone(), c.clone()]);
1335
1336 let order = graph.topological_sort().unwrap();
1337 assert_eq!(order.len(), 4);
1338
1339 let pos_a = order.iter().position(|k| k == &a).unwrap();
1340 let pos_b = order.iter().position(|k| k == &b).unwrap();
1341 let pos_c = order.iter().position(|k| k == &c).unwrap();
1342 let pos_d = order.iter().position(|k| k == &d).unwrap();
1343
1344 assert!(pos_a < pos_b);
1346 assert!(pos_a < pos_c);
1347
1348 assert!(pos_b < pos_d);
1350 assert!(pos_c < pos_d);
1351 }
1352
1353 #[test]
1354 fn test_migration_replaces() {
1355 let mut graph = MigrationGraph::new();
1356
1357 let old1 = MigrationKey::new("auth", "0001_initial");
1358 let old2 = MigrationKey::new("auth", "0002_add_field");
1359 let squashed = MigrationKey::new("auth", "0001_squashed_0002");
1360
1361 graph.add_migration(old1.clone(), vec![]);
1362 graph.add_migration(old2.clone(), vec![old1.clone()]);
1363 graph.add_migration_with_replaces(
1364 squashed.clone(),
1365 vec![],
1366 vec![old1.clone(), old2.clone()],
1367 );
1368
1369 assert!(graph.is_replaced(&old1));
1371 assert!(graph.is_replaced(&old2));
1372 assert!(!graph.is_replaced(&squashed));
1373
1374 assert_eq!(graph.get_replacement(&old1), Some(&squashed));
1376 assert_eq!(graph.get_replacement(&old2), Some(&squashed));
1377 assert_eq!(graph.get_replacement(&squashed), None);
1378 }
1379
1380 #[test]
1381 fn test_resolve_execution_order_with_replaces() {
1382 let mut graph = MigrationGraph::new();
1383
1384 let old1 = MigrationKey::new("auth", "0001_initial");
1385 let old2 = MigrationKey::new("auth", "0002_add_field");
1386 let old3 = MigrationKey::new("auth", "0003_alter_field");
1387 let squashed = MigrationKey::new("auth", "0001_squashed_0002");
1388
1389 graph.add_migration(old1.clone(), vec![]);
1390 graph.add_migration(old2.clone(), vec![old1.clone()]);
1391 graph.add_migration(old3.clone(), vec![old2.clone()]);
1392 graph.add_migration_with_replaces(
1393 squashed.clone(),
1394 vec![],
1395 vec![old1.clone(), old2.clone()],
1396 );
1397
1398 let order = graph.resolve_execution_order_with_replaces().unwrap();
1399
1400 assert_eq!(order.len(), 2);
1402 assert!(order.contains(&squashed));
1403 assert!(order.contains(&old3));
1404 assert!(!order.contains(&old1));
1405 assert!(!order.contains(&old2));
1406
1407 let pos_squashed = order.iter().position(|k| k == &squashed).unwrap();
1409 let pos_old3 = order.iter().position(|k| k == &old3).unwrap();
1410 assert!(pos_squashed < pos_old3);
1411 }
1412
1413 #[test]
1414 fn test_find_migration_path_simple() {
1415 let mut graph = MigrationGraph::new();
1416
1417 let key1 = MigrationKey::new("auth", "0001_initial");
1418 let key2 = MigrationKey::new("auth", "0002_add_field");
1419 let key3 = MigrationKey::new("auth", "0003_alter_field");
1420
1421 graph.add_migration(key1.clone(), vec![]);
1422 graph.add_migration(key2.clone(), vec![key1.clone()]);
1423 graph.add_migration(key3.clone(), vec![key2.clone()]);
1424
1425 let path = graph.find_migration_path(&key1, &key3).unwrap();
1426 assert_eq!(path.len(), 3);
1427 assert_eq!(path[0], key1);
1428 assert_eq!(path[1], key2);
1429 assert_eq!(path[2], key3);
1430 }
1431
1432 #[test]
1433 fn test_find_migration_path_same_node() {
1434 let mut graph = MigrationGraph::new();
1435
1436 let key = MigrationKey::new("auth", "0001_initial");
1437 graph.add_migration(key.clone(), vec![]);
1438
1439 let path = graph.find_migration_path(&key, &key).unwrap();
1440 assert_eq!(path.len(), 1);
1441 assert_eq!(path[0], key);
1442 }
1443
1444 #[test]
1445 fn test_find_migration_path_no_path() {
1446 let mut graph = MigrationGraph::new();
1447
1448 let key1 = MigrationKey::new("auth", "0001_initial");
1449 let key2 = MigrationKey::new("users", "0001_initial");
1450
1451 graph.add_migration(key1.clone(), vec![]);
1452 graph.add_migration(key2.clone(), vec![]);
1453
1454 let result = graph.find_migration_path(&key1, &key2);
1455 assert!(result.is_err());
1456 }
1457
1458 #[test]
1459 fn test_find_backward_path() {
1460 let mut graph = MigrationGraph::new();
1461
1462 let key1 = MigrationKey::new("auth", "0001_initial");
1463 let key2 = MigrationKey::new("auth", "0002_add_field");
1464 let key3 = MigrationKey::new("auth", "0003_alter_field");
1465
1466 graph.add_migration(key1.clone(), vec![]);
1467 graph.add_migration(key2.clone(), vec![key1.clone()]);
1468 graph.add_migration(key3.clone(), vec![key2.clone()]);
1469
1470 let path = graph.find_backward_path(&key3, &key1).unwrap();
1471 assert_eq!(path.len(), 3);
1472 assert_eq!(path[0], key3);
1473 assert_eq!(path[1], key2);
1474 assert_eq!(path[2], key1);
1475 }
1476
1477 #[test]
1478 fn test_detect_all_cycles_no_cycle() {
1479 let mut graph = MigrationGraph::new();
1480
1481 let key1 = MigrationKey::new("auth", "0001_initial");
1482 let key2 = MigrationKey::new("auth", "0002_add_field");
1483 let key3 = MigrationKey::new("auth", "0003_alter_field");
1484
1485 graph.add_migration(key1.clone(), vec![]);
1486 graph.add_migration(key2.clone(), vec![key1.clone()]);
1487 graph.add_migration(key3.clone(), vec![key2.clone()]);
1488
1489 let cycles = graph.detect_all_cycles();
1490 assert!(cycles.is_empty());
1491 }
1492
1493 #[test]
1494 fn test_detect_all_cycles_with_cycle() {
1495 let mut graph = MigrationGraph::new();
1496
1497 let key1 = MigrationKey::new("auth", "0001_initial");
1498 let key2 = MigrationKey::new("auth", "0002_add_field");
1499
1500 graph.add_migration(key1.clone(), vec![key2.clone()]);
1502 graph.add_migration(key2.clone(), vec![key1.clone()]);
1503
1504 let cycles = graph.detect_all_cycles();
1505 assert!(!cycles.is_empty());
1506 assert_eq!(cycles.len(), 1);
1507 }
1508
1509 #[test]
1510 fn test_complex_replaces_scenario() {
1511 let mut graph = MigrationGraph::new();
1512
1513 let app1_0001 = MigrationKey::new("app1", "0001_initial");
1515 let app1_0002 = MigrationKey::new("app1", "0002_add_field");
1516 let app1_squashed = MigrationKey::new("app1", "0001_squashed_0002");
1517
1518 let app2_0001 = MigrationKey::new("app2", "0001_initial");
1520 let app2_0002 = MigrationKey::new("app2", "0002_add_relation");
1521
1522 graph.add_migration(app1_0001.clone(), vec![]);
1523 graph.add_migration(app1_0002.clone(), vec![app1_0001.clone()]);
1524 graph.add_migration_with_replaces(
1525 app1_squashed.clone(),
1526 vec![],
1527 vec![app1_0001.clone(), app1_0002.clone()],
1528 );
1529 graph.add_migration(app2_0001.clone(), vec![app1_0002.clone()]);
1530 graph.add_migration(app2_0002.clone(), vec![app2_0001.clone()]);
1531
1532 let order = graph.resolve_execution_order_with_replaces().unwrap();
1533
1534 assert!(!order.contains(&app1_0001));
1536 assert!(!order.contains(&app1_0002));
1537
1538 assert!(order.contains(&app1_squashed));
1540 assert!(order.contains(&app2_0001));
1541 assert!(order.contains(&app2_0002));
1542
1543 let pos_squashed = order.iter().position(|k| k == &app1_squashed).unwrap();
1545 let pos_app2_0001 = order.iter().position(|k| k == &app2_0001).unwrap();
1546 let pos_app2_0002 = order.iter().position(|k| k == &app2_0002).unwrap();
1547
1548 assert!(pos_squashed < pos_app2_0001);
1549 assert!(pos_app2_0001 < pos_app2_0002);
1550 }
1551
1552 #[test]
1553 fn test_multi_app_diamond_dependency() {
1554 let mut graph = MigrationGraph::new();
1555
1556 let auth_0001 = MigrationKey::new("auth", "0001_initial");
1564 let users_0001 = MigrationKey::new("users", "0001_initial");
1565 let posts_0001 = MigrationKey::new("posts", "0001_initial");
1566 let comments_0001 = MigrationKey::new("comments", "0001_initial");
1567
1568 graph.add_migration(auth_0001.clone(), vec![]);
1569 graph.add_migration(users_0001.clone(), vec![auth_0001.clone()]);
1570 graph.add_migration(posts_0001.clone(), vec![auth_0001.clone()]);
1571 graph.add_migration(
1572 comments_0001.clone(),
1573 vec![users_0001.clone(), posts_0001.clone()],
1574 );
1575
1576 let order = graph.topological_sort().unwrap();
1577 assert_eq!(order.len(), 4);
1578
1579 let pos_auth = order.iter().position(|k| k == &auth_0001).unwrap();
1580 let pos_users = order.iter().position(|k| k == &users_0001).unwrap();
1581 let pos_posts = order.iter().position(|k| k == &posts_0001).unwrap();
1582 let pos_comments = order.iter().position(|k| k == &comments_0001).unwrap();
1583
1584 assert!(pos_auth < pos_users);
1586 assert!(pos_auth < pos_posts);
1587
1588 assert!(pos_users < pos_comments);
1590 assert!(pos_posts < pos_comments);
1591 }
1592
1593 #[test]
1594 fn test_path_with_multiple_routes() {
1595 let mut graph = MigrationGraph::new();
1596
1597 let a = MigrationKey::new("app", "0001_a");
1607 let b = MigrationKey::new("app", "0002_b");
1608 let c = MigrationKey::new("app", "0003_c");
1609 let d = MigrationKey::new("app", "0004_d");
1610 let e = MigrationKey::new("app", "0005_e");
1611 let f = MigrationKey::new("app", "0006_f");
1612
1613 graph.add_migration(a.clone(), vec![]);
1614 graph.add_migration(b.clone(), vec![a.clone()]);
1615 graph.add_migration(c.clone(), vec![a.clone()]);
1616 graph.add_migration(d.clone(), vec![b.clone()]);
1617 graph.add_migration(e.clone(), vec![c.clone()]);
1618 graph.add_migration(f.clone(), vec![d.clone(), e.clone()]);
1619
1620 let path = graph.find_migration_path(&a, &f).unwrap();
1622 assert!(path.len() >= 3);
1623 assert_eq!(path[0], a);
1624 assert_eq!(path[path.len() - 1], f);
1625 }
1626
1627 #[rstest]
1628 fn test_get_leaf_nodes_for_app_single_leaf() {
1629 let mut graph = MigrationGraph::new();
1631 let key1 = MigrationKey::new("auth", "0001_initial");
1632 let key2 = MigrationKey::new("auth", "0002_add_field");
1633 graph.add_migration(key1.clone(), vec![]);
1634 graph.add_migration(key2.clone(), vec![key1]);
1635
1636 let leaves = graph.get_leaf_nodes_for_app("auth");
1638
1639 assert_eq!(leaves.len(), 1);
1641 assert_eq!(leaves[0], &key2);
1642 }
1643
1644 #[rstest]
1645 fn test_get_leaf_nodes_for_app_two_branches() {
1646 let mut graph = MigrationGraph::new();
1648 let key1 = MigrationKey::new("auth", "0001_initial");
1649 let key2a = MigrationKey::new("auth", "0002_add_field");
1650 let key2b = MigrationKey::new("auth", "0002_add_index");
1651 graph.add_migration(key1.clone(), vec![]);
1652 graph.add_migration(key2a.clone(), vec![key1.clone()]);
1653 graph.add_migration(key2b.clone(), vec![key1]);
1654
1655 let leaves = graph.get_leaf_nodes_for_app("auth");
1657
1658 assert_eq!(leaves.len(), 2);
1660 assert_eq!(leaves[0].name, "0002_add_field");
1661 assert_eq!(leaves[1].name, "0002_add_index");
1662 }
1663
1664 #[rstest]
1665 fn test_get_leaf_nodes_for_app_three_branches() {
1666 let mut graph = MigrationGraph::new();
1668 let key1 = MigrationKey::new("auth", "0001_initial");
1669 let key2a = MigrationKey::new("auth", "0002_a");
1670 let key2b = MigrationKey::new("auth", "0002_b");
1671 let key2c = MigrationKey::new("auth", "0002_c");
1672 graph.add_migration(key1.clone(), vec![]);
1673 graph.add_migration(key2a.clone(), vec![key1.clone()]);
1674 graph.add_migration(key2b.clone(), vec![key1.clone()]);
1675 graph.add_migration(key2c.clone(), vec![key1]);
1676
1677 let leaves = graph.get_leaf_nodes_for_app("auth");
1679
1680 assert_eq!(leaves.len(), 3);
1682 }
1683
1684 #[rstest]
1685 fn test_get_leaf_nodes_for_app_nonexistent() {
1686 let mut graph = MigrationGraph::new();
1688 graph.add_migration(MigrationKey::new("auth", "0001_initial"), vec![]);
1689
1690 let leaves = graph.get_leaf_nodes_for_app("nonexistent");
1692
1693 assert!(leaves.is_empty());
1695 }
1696
1697 #[rstest]
1698 fn test_get_leaf_nodes_for_app_empty_graph() {
1699 let graph = MigrationGraph::new();
1701
1702 let leaves = graph.get_leaf_nodes_for_app("auth");
1704
1705 assert!(leaves.is_empty());
1707 }
1708
1709 #[rstest]
1710 fn test_detect_conflicts_no_conflicts() {
1711 let mut graph = MigrationGraph::new();
1713 let key1 = MigrationKey::new("auth", "0001_initial");
1714 let key2 = MigrationKey::new("auth", "0002_add_field");
1715 graph.add_migration(key1.clone(), vec![]);
1716 graph.add_migration(key2.clone(), vec![key1]);
1717
1718 let conflicts = graph.detect_conflicts();
1720
1721 assert!(conflicts.is_empty());
1723 }
1724
1725 #[rstest]
1726 fn test_detect_conflicts_single_app() {
1727 let mut graph = MigrationGraph::new();
1729 let key1 = MigrationKey::new("auth", "0001_initial");
1730 let key2a = MigrationKey::new("auth", "0002_add_field");
1731 let key2b = MigrationKey::new("auth", "0002_add_index");
1732 graph.add_migration(key1.clone(), vec![]);
1733 graph.add_migration(key2a.clone(), vec![key1.clone()]);
1734 graph.add_migration(key2b.clone(), vec![key1]);
1735
1736 let conflicts = graph.detect_conflicts();
1738
1739 assert_eq!(conflicts.len(), 1);
1741 assert!(conflicts.contains_key("auth"));
1742 assert_eq!(conflicts["auth"].len(), 2);
1743 }
1744
1745 #[rstest]
1746 fn test_detect_conflicts_multiple_apps() {
1747 let mut graph = MigrationGraph::new();
1749 let auth1 = MigrationKey::new("auth", "0001_initial");
1750 let auth2a = MigrationKey::new("auth", "0002_a");
1751 let auth2b = MigrationKey::new("auth", "0002_b");
1752 let users1 = MigrationKey::new("users", "0001_initial");
1753 let users2a = MigrationKey::new("users", "0002_a");
1754 let users2b = MigrationKey::new("users", "0002_b");
1755 graph.add_migration(auth1.clone(), vec![]);
1756 graph.add_migration(auth2a.clone(), vec![auth1.clone()]);
1757 graph.add_migration(auth2b.clone(), vec![auth1]);
1758 graph.add_migration(users1.clone(), vec![]);
1759 graph.add_migration(users2a.clone(), vec![users1.clone()]);
1760 graph.add_migration(users2b.clone(), vec![users1]);
1761
1762 let conflicts = graph.detect_conflicts();
1764
1765 assert_eq!(conflicts.len(), 2);
1767 assert!(conflicts.contains_key("auth"));
1768 assert!(conflicts.contains_key("users"));
1769 }
1770
1771 #[rstest]
1772 fn test_detect_conflicts_mixed() {
1773 let mut graph = MigrationGraph::new();
1775 let auth1 = MigrationKey::new("auth", "0001_initial");
1776 let auth2a = MigrationKey::new("auth", "0002_a");
1777 let auth2b = MigrationKey::new("auth", "0002_b");
1778 let users1 = MigrationKey::new("users", "0001_initial");
1779 let users2 = MigrationKey::new("users", "0002_add_field");
1780 graph.add_migration(auth1.clone(), vec![]);
1781 graph.add_migration(auth2a.clone(), vec![auth1.clone()]);
1782 graph.add_migration(auth2b.clone(), vec![auth1]);
1783 graph.add_migration(users1.clone(), vec![]);
1784 graph.add_migration(users2.clone(), vec![users1]);
1785
1786 let conflicts = graph.detect_conflicts();
1788
1789 assert_eq!(conflicts.len(), 1);
1791 assert!(conflicts.contains_key("auth"));
1792 assert!(!conflicts.contains_key("users"));
1793 }
1794
1795 #[rstest]
1796 fn test_detect_conflicts_empty_graph() {
1797 let graph = MigrationGraph::new();
1799
1800 let conflicts = graph.detect_conflicts();
1802
1803 assert!(conflicts.is_empty());
1805 }
1806
1807 #[rstest]
1808 fn test_detect_conflicts_cross_app_deps() {
1809 let mut graph = MigrationGraph::new();
1811 let users1 = MigrationKey::new("users", "0001_initial");
1812 let auth1 = MigrationKey::new("auth", "0001_initial");
1813 let auth2a = MigrationKey::new("auth", "0002_add_field");
1814 let auth2b = MigrationKey::new("auth", "0002_add_fk");
1815 graph.add_migration(users1.clone(), vec![]);
1816 graph.add_migration(auth1.clone(), vec![]);
1817 graph.add_migration(auth2a.clone(), vec![auth1.clone()]);
1818 graph.add_migration(auth2b.clone(), vec![auth1, users1]);
1819
1820 let conflicts = graph.detect_conflicts();
1822
1823 assert_eq!(conflicts.len(), 1);
1825 assert!(conflicts.contains_key("auth"));
1826 assert_eq!(conflicts["auth"].len(), 2);
1827 }
1828
1829 #[rstest]
1830 fn test_detect_conflicts_sorted_output() {
1831 let mut graph = MigrationGraph::new();
1833 let key1 = MigrationKey::new("auth", "0001_initial");
1834 let key2c = MigrationKey::new("auth", "0002_c_zebra");
1835 let key2a = MigrationKey::new("auth", "0002_a_alpha");
1836 let key2b = MigrationKey::new("auth", "0002_b_beta");
1837 graph.add_migration(key1.clone(), vec![]);
1838 graph.add_migration(key2c.clone(), vec![key1.clone()]);
1839 graph.add_migration(key2a.clone(), vec![key1.clone()]);
1840 graph.add_migration(key2b.clone(), vec![key1]);
1841
1842 let conflicts = graph.detect_conflicts();
1844
1845 let auth_conflicts = &conflicts["auth"];
1847 assert_eq!(auth_conflicts[0].name, "0002_a_alpha");
1848 assert_eq!(auth_conflicts[1].name, "0002_b_beta");
1849 assert_eq!(auth_conflicts[2].name, "0002_c_zebra");
1850 }
1851}