From c35be1a5b650184ba04be6b0e340915421691c77 Mon Sep 17 00:00:00 2001 From: Saurav Sharma Date: Sun, 17 Mar 2024 17:07:14 +0545 Subject: [PATCH] test: add some test for generate migration plan Signed-off-by: Saurav Sharma --- src/migrator/mod.rs | 4 + src/migrator/tests.rs | 283 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 287 insertions(+) create mode 100644 src/migrator/tests.rs diff --git a/src/migrator/mod.rs b/src/migrator/mod.rs index 6fe9510e..dea9b68e 100644 --- a/src/migrator/mod.rs +++ b/src/migrator/mod.rs @@ -166,6 +166,10 @@ mod sqlite; #[cfg(feature = "postgres")] mod postgres; +/// Module for testing +#[cfg(all(test, feature = "sqlite"))] +mod tests; + type BoxMigration = Box>; type MigrationVec<'migration, DB, State> = Vec<&'migration BoxMigration>; type MigrationVecResult<'migration, DB, State> = Result, Error>; diff --git a/src/migrator/tests.rs b/src/migrator/tests.rs new file mode 100644 index 00000000..e5743ef7 --- /dev/null +++ b/src/migrator/tests.rs @@ -0,0 +1,283 @@ +use std::collections::HashSet; + +use sqlx::{Sqlite, SqlitePool}; + +use super::{DatabaseOperation, Info, Migrate}; +use crate::error::Error; +use crate::migration::{AppliedMigrationSqlRow, Migration}; +use crate::migrator::Plan; +use crate::vec_box; + +#[derive(Default)] +struct CustomMigrator { + migrations: HashSet>>, +} + +impl Info for CustomMigrator { + fn migrations(&self) -> &HashSet>> { + &self.migrations + } + + fn migrations_mut(&mut self) -> &mut HashSet>> { + &mut self.migrations + } + + fn state(&self) -> &() { + &() + } +} + +#[async_trait::async_trait] +impl DatabaseOperation for CustomMigrator { + async fn ensure_migration_table_exists( + &self, + _connection: &mut ::Connection, + ) -> Result<(), Error> { + Ok(()) + } + + async fn drop_migration_table_if_exists( + &self, + _connection: &mut ::Connection, + ) -> Result<(), Error> { + Ok(()) + } + + async fn add_migration_to_db_table( + &self, + _migration: &Box>, + _connection: &mut ::Connection, + ) -> Result<(), Error> { + Ok(()) + } + + async fn delete_migration_from_db_table( + &self, + _migration: &Box>, + _connection: &mut ::Connection, + ) -> Result<(), Error> { + Ok(()) + } + + async fn fetch_applied_migration_from_db( + &self, + _connection: &mut ::Connection, + ) -> Result, Error> { + Ok(vec![]) + } + + async fn lock( + &self, + _connection: &mut ::Connection, + ) -> Result<(), Error> { + Ok(()) + } + + async fn unlock( + &self, + _connection: &mut ::Connection, + ) -> Result<(), Error> { + Ok(()) + } +} + +impl Migrate for CustomMigrator {} + +macro_rules! migration { + ($op:ty, $name:expr, $parents:expr, $operations:expr, $replaces:expr, $run_before:expr) => { + #[async_trait::async_trait] + impl crate::migration::Migration for $op { + fn app(&self) -> &str { + "test" + } + + fn name(&self) -> &str { + $name + } + + fn parents(&self) -> Vec>> { + $parents + } + + fn operations(&self) -> Vec>> { + $operations + } + + fn replaces(&self) -> Vec>> { + $replaces + } + + fn run_before(&self) -> Vec>> { + $run_before + } + } + }; +} + +async fn generate_plan( + migrator: &mut CustomMigrator, + migration_list: Vec>>, +) -> Result>>, Error> { + migrator.add_migrations(migration_list); + let sqlite = SqlitePool::connect("sqlite::memory:").await.unwrap(); + let mut conn = sqlite.acquire().await.unwrap(); + migrator + .generate_migration_plan(Some(&Plan::apply_all()), &mut conn) + .await +} + +#[tokio::test] +async fn simple_test() { + struct A; + migration!(A, "a", vec_box!(), vec_box!(), vec_box!(), vec_box!()); + struct B; + migration!(B, "b", vec_box!(A), vec_box!(), vec_box!(), vec_box!()); + struct C; + migration!(C, "c", vec_box!(B), vec_box!(), vec_box!(), vec_box!()); + let mut migrator = CustomMigrator::default(); + let plan = generate_plan(&mut migrator, vec_box!(A, B, C)) + .await + .unwrap(); + assert!(plan.contains(&&(Box::new(A) as Box>))); + assert!(plan.contains(&&(Box::new(B) as Box>))); + assert!(plan.contains(&&(Box::new(C) as Box>))); +} + +#[tokio::test] +async fn replace_test() { + struct A; + migration!(A, "a", vec_box!(), vec_box!(), vec_box!(), vec_box!()); + struct B; + migration!(B, "b", vec_box!(A), vec_box!(), vec_box!(), vec_box!()); + struct C; + migration!(C, "c", vec_box!(), vec_box!(), vec_box!(B), vec_box!()); + struct D; + migration!(D, "d", vec_box!(), vec_box!(), vec_box!(C), vec_box!()); + let mut migrator = CustomMigrator::default(); + let plan = generate_plan(&mut migrator, vec_box!(A, B, C, D)) + .await + .unwrap(); + assert!(!plan.contains(&&(Box::new(B) as Box>))); + assert!(!plan.contains(&&(Box::new(C) as Box>))); + assert!(plan.contains(&&(Box::new(D) as Box>))); +} + +#[tokio::test] +async fn run_before_test() { + struct A; + migration!(A, "a", vec_box!(), vec_box!(), vec_box!(), vec_box!()); + struct B; + migration!(B, "b", vec_box!(A), vec_box!(), vec_box!(), vec_box!()); + struct C; + migration!(C, "c", vec_box!(), vec_box!(), vec_box!(), vec_box!(B)); + struct D; + migration!(D, "d", vec_box!(), vec_box!(), vec_box!(), vec_box!(C)); + let mut migrator = CustomMigrator::default(); + let plan = generate_plan(&mut migrator, vec_box!(A, B, C, D)) + .await + .unwrap(); + assert!( + plan.get(1).unwrap() == &&(Box::new(D) as Box>), + "1 index is not D" + ); + assert!( + plan.get(2).unwrap() == &&(Box::new(C) as Box>), + "2 index is not C" + ); + assert!( + plan.get(3).unwrap() == &&(Box::new(B) as Box>), + "3 index is not B" + ); +} + +#[tokio::test] +async fn replaces_multiple_times() { + struct A; + migration!(A, "a", vec_box!(), vec_box!(), vec_box!(), vec_box!()); + struct B; + migration!(B, "b", vec_box!(A), vec_box!(), vec_box!(), vec_box!()); + struct C; + migration!(C, "c", vec_box!(), vec_box!(), vec_box!(B), vec_box!()); + struct D; + migration!(D, "d", vec_box!(), vec_box!(), vec_box!(B), vec_box!()); + let mut migrator = CustomMigrator::default(); + let plan = generate_plan(&mut migrator, vec_box!(A, B, C, D)).await; + assert!(plan.is_err()); +} + +#[tokio::test] +async fn replaces_run_before_mismatch_1() { + struct A; + migration!(A, "a", vec_box!(), vec_box!(), vec_box!(), vec_box!()); + struct B; + migration!(B, "b", vec_box!(A), vec_box!(), vec_box!(), vec_box!()); + struct C; + migration!(C, "c", vec_box!(), vec_box!(), vec_box!(B), vec_box!()); + struct D; + migration!(D, "d", vec_box!(), vec_box!(), vec_box!(), vec_box!(B)); + let mut migrator = CustomMigrator::default(); + let plan = generate_plan(&mut migrator, vec_box!(A, B, C, D)).await; + assert!(plan.is_err()); +} + +#[tokio::test] +async fn replaces_run_before_mismatch_2() { + struct A; + migration!(A, "a", vec_box!(), vec_box!(), vec_box!(), vec_box!()); + struct B; + migration!(B, "b", vec_box!(A), vec_box!(), vec_box!(), vec_box!()); + struct C; + migration!(C, "c", vec_box!(), vec_box!(), vec_box!(B), vec_box!()); + struct D; + migration!(D, "d", vec_box!(), vec_box!(), vec_box!(C), vec_box!()); + struct E; + migration!(E, "e", vec_box!(), vec_box!(), vec_box!(), vec_box!(C)); + let mut migrator = CustomMigrator::default(); + let plan = generate_plan(&mut migrator, vec_box!(A, B, C, D, E)).await; + assert!(plan.is_err()); +} + +#[tokio::test] +async fn replaces_run_before_ok_1() { + struct A; + migration!(A, "a", vec_box!(), vec_box!(), vec_box!(), vec_box!()); + struct B; + migration!(B, "b", vec_box!(A), vec_box!(), vec_box!(), vec_box!()); + struct C; + migration!(C, "c", vec_box!(), vec_box!(), vec_box!(B), vec_box!()); + struct D; + migration!(D, "d", vec_box!(), vec_box!(), vec_box!(C), vec_box!()); + struct E; + migration!(E, "e", vec_box!(), vec_box!(), vec_box!(), vec_box!(D)); + let mut migrator = CustomMigrator::default(); + let plan = generate_plan(&mut migrator, vec_box!(A, B, C, D, E)).await; + assert!(plan.is_ok(), "{:?}", plan.err()); +} + +#[tokio::test] +async fn replaces_run_before_ok_2() { + struct A; + migration!(A, "a", vec_box!(), vec_box!(), vec_box!(), vec_box!()); + struct B; + migration!(B, "b", vec_box!(A), vec_box!(), vec_box!(), vec_box!()); + struct C; + migration!(C, "c", vec_box!(), vec_box!(), vec_box!(B), vec_box!()); + struct D; + migration!(D, "d", vec_box!(), vec_box!(), vec_box!(C, E), vec_box!()); + struct E; + migration!(E, "e", vec_box!(), vec_box!(), vec_box!(), vec_box!(C)); + let mut migrator = CustomMigrator::default(); + let plan = generate_plan(&mut migrator, vec_box!(A, B, C, D, E)).await; + assert!(plan.is_ok(), "{:?}", plan.err()); +} + +#[tokio::test] +async fn loop_error() { + struct A; + migration!(A, "a", vec_box!(), vec_box!(), vec_box!(), vec_box!()); + struct B; + migration!(B, "b", vec_box!(A), vec_box!(), vec_box!(), vec_box!(A)); + let mut migrator = CustomMigrator::default(); + let plan = generate_plan(&mut migrator, vec_box!(A, B)).await; + assert!(plan.is_err()); +}