use sqlx::migrate::Migrator; use sqlx::sqlite::SqlitePoolOptions; use sqlx::SqlitePool; static MIGRATOR: Migrator = sqlx::migrate!("./migrations"); async fn mem_pool() -> SqlitePool { // NOTE: max_connections(1) is required because each :memory: connection // creates a separate database SqlitePoolOptions::new() .max_connections(1) .connect("sqlite::memory:") .await .expect("cannot open in-memory db") } async fn user_tables(pool: &SqlitePool) -> Vec { sqlx::query_scalar::<_, String>( "SELECT name FROM sqlite_master WHERE type = 'table' AND name NOT LIKE '_sqlx%' AND name NOT LIKE 'sqlite_%' ORDER BY name", ) .fetch_all(pool) .await .expect("cannot query sqlite_master") } #[tokio::test] async fn migrations_up() { let pool = mem_pool().await; MIGRATOR.run(&pool).await.expect("migrations failed"); let tables = user_tables(&pool).await; assert!(tables.contains(&"todos".to_string())); assert!(tables.contains(&"todo_tags".to_string())); } #[tokio::test] async fn migrations_down() { let pool = mem_pool().await; MIGRATOR.run(&pool).await.expect("run failed"); MIGRATOR.undo(&pool, 0).await.expect("undo failed"); let tables = user_tables(&pool).await; assert!(tables.is_empty(), "tables should be gone after revert: {tables:?}"); } #[tokio::test] async fn migrations_round_trip() { let pool = mem_pool().await; MIGRATOR.run(&pool).await.expect("first run failed"); let tables_before = user_tables(&pool).await; MIGRATOR.undo(&pool, 0).await.expect("undo failed"); MIGRATOR.run(&pool).await.expect("second run failed"); let tables_after = user_tables(&pool).await; assert_eq!(tables_before, tables_after); }