Skip to content

Commit

Permalink
fix: two migration depends upon each other are not handled properly
Browse files Browse the repository at this point in the history
Signed-off-by: Saurav Sharma <[email protected]>
  • Loading branch information
iamsauravsharma committed Mar 24, 2024
1 parent 3507320 commit c6e4da5
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 3 deletions.
11 changes: 8 additions & 3 deletions src/migrator/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -346,16 +346,21 @@ fn populate_recursive<'populate, DB, State>(
>,
key: &'populate BoxMigration<DB, State>,
value: &'populate BoxMigration<DB, State>,
) {
) -> Result<(), Error> {
// protect against a case where two migration depends upon each other
if key == value {
return Err(Error::FailedToCreateMigrationPlan);
}
let populate_hash_map_vec = populate_hash_map.entry(key).or_default();
if !populate_hash_map_vec.contains(&value) {
populate_hash_map_vec.push(value);
}
if let Some(grand_values) = populate_hash_map.clone().get(value) {
for grand_value in grand_values {
populate_recursive(populate_hash_map, key, grand_value);
populate_recursive(populate_hash_map, key, grand_value)?;
}
}
Ok(())
}

fn get_parent_recursive<DB, State>(with: &BoxMigration<DB, State>) -> Vec<BoxMigration<DB, State>> {
Expand Down Expand Up @@ -573,7 +578,7 @@ where
}
// in second loop through recursive add all descendants
for (child, &parent) in &parent_due_to_replaces {
populate_recursive(&mut replace_children, parent, child);
populate_recursive(&mut replace_children, parent, child)?;
}

// Hashmap which contains key as migration name and value as list of migration
Expand Down
66 changes: 66 additions & 0 deletions src/migrator/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,72 @@ async fn simple_test() {
assert!(plan.contains(&&(Box::new(C) as Box<dyn Migration<Sqlite>>)));
}

#[tokio::test]
async fn interrelated_test() {
struct A;
migration!(A, "a", vec_box!(B), vec_box!(), vec_box!());
struct B;
migration!(B, "b", vec_box!(A), vec_box!(), vec_box!());
let mut migrator = CustomMigrator::default();
let plan = generate_apply_all_plan(&mut migrator, vec_box!(A, B)).await;
assert!(plan.is_err());
}

#[tokio::test]
async fn run_before_interrelated_test() {
struct A;
migration!(A, "a", vec_box!(), vec_box!(B), vec_box!());
struct B;
migration!(B, "b", vec_box!(), vec_box!(A), vec_box!());
let mut migrator = CustomMigrator::default();
let plan = generate_apply_all_plan(&mut migrator, vec_box!(A, B)).await;
assert!(plan.is_err());
}

#[tokio::test]
async fn replace_interrelated_test() {
struct A;
migration!(A, "a", vec_box!(), vec_box!(), vec_box!(B));
struct B;
migration!(B, "b", vec_box!(), vec_box!(), vec_box!(A));
let mut migrator = CustomMigrator::default();
let plan = generate_apply_all_plan(&mut migrator, vec_box!(A, B)).await;
assert!(plan.is_err());
}

#[tokio::test]
async fn depend_on_itself() {
struct A;
migration!(A, "a", vec_box!(A), vec_box!(), vec_box!());
struct B;
migration!(B, "b", vec_box!(B), vec_box!(), vec_box!());
let mut migrator = CustomMigrator::default();
let plan = generate_apply_all_plan(&mut migrator, vec_box!(A, B)).await;
assert!(plan.is_err());
}

#[tokio::test]
async fn run_before_depend_on_itself() {
struct A;
migration!(A, "a", vec_box!(), vec_box!(A), vec_box!());
struct B;
migration!(B, "b", vec_box!(), vec_box!(B), vec_box!());
let mut migrator = CustomMigrator::default();
let plan = generate_apply_all_plan(&mut migrator, vec_box!(A, B)).await;
assert!(plan.is_err());
}

#[tokio::test]
async fn replace_depend_on_itself() {
struct A;
migration!(A, "a", vec_box!(), vec_box!(), vec_box!(A));
struct B;
migration!(B, "b", vec_box!(), vec_box!(), vec_box!(B));
let mut migrator = CustomMigrator::default();
let plan = generate_apply_all_plan(&mut migrator, vec_box!(A, B)).await;
assert!(plan.is_err());
}

#[tokio::test]
async fn replace_test() {
struct A;
Expand Down

0 comments on commit c6e4da5

Please sign in to comment.