use turso_orm_migration::prelude::*;
mod m20240101_000001_create_user {
use turso_orm_migration::prelude::*;
#[derive(DeriveMigrationName)]
pub(crate) struct Migration;
#[async_trait]
impl MigrationTrait for Migration {
async fn up(&self, manager: &SchemaManager<'_>) -> Result<(), DbErr> {
manager
.create_table(
Table::create()
.table("user")
.col(ColumnDef::integer("id").primary_key().auto_increment())
.col(ColumnDef::text("email").not_null().unique_key()),
)
.await?;
manager
.create_index(
CreateIndex::new()
.name("idx-user-email")
.table("user")
.col("email"),
)
.await
}
async fn down(&self, manager: &SchemaManager<'_>) -> Result<(), DbErr> {
manager.drop_table(Table::drop().table("user")).await
}
}
}
mod m20240102_000001_add_name {
use turso_orm_migration::prelude::*;
#[derive(DeriveMigrationName)]
pub(crate) struct Migration;
#[async_trait]
impl MigrationTrait for Migration {
async fn up(&self, manager: &SchemaManager<'_>) -> Result<(), DbErr> {
manager
.alter_table(
Table::alter()
.table("user")
.add_column(ColumnDef::text("name")),
)
.await?;
manager
.get_connection()
.execute_unprepared("INSERT INTO user (email, name) VALUES ('seed@x.io', 'Seed')")
.await?;
Ok(())
}
async fn down(&self, manager: &SchemaManager<'_>) -> Result<(), DbErr> {
manager
.alter_table(Table::alter().table("user").drop_column("name"))
.await
}
}
}
mod m20240103_000001_failing {
use turso_orm_migration::prelude::*;
#[derive(DeriveMigrationName)]
pub(crate) struct Migration;
#[async_trait]
impl MigrationTrait for Migration {
async fn up(&self, manager: &SchemaManager<'_>) -> Result<(), DbErr> {
manager
.create_table(
Table::create()
.table("orphan")
.col(ColumnDef::integer("id").primary_key()),
)
.await?;
Err(DbErr::Migration("boom".into()))
}
}
}
struct Migrator;
#[async_trait]
impl MigratorTrait for Migrator {
fn migrations() -> Vec<Box<dyn MigrationTrait>> {
vec![
Box::new(m20240101_000001_create_user::Migration),
Box::new(m20240102_000001_add_name::Migration),
]
}
fn migration_table_name() -> &'static str {
"schema_history"
}
}
struct FailingMigrator;
#[async_trait]
impl MigratorTrait for FailingMigrator {
fn migrations() -> Vec<Box<dyn MigrationTrait>> {
vec![
Box::new(m20240101_000001_create_user::Migration),
Box::new(m20240103_000001_failing::Migration),
]
}
}
#[tokio::test]
async fn up_down_status_refresh() {
let db = Database::connect(ConnectOptions::in_memory())
.await
.expect("open");
Migrator::up(&db, None).await.expect("up");
let versions = db
.query_all(Statement::from_string("SELECT version FROM schema_history"))
.await
.expect("bookkeeping");
assert_eq!(versions.len(), 2);
let status = Migrator::status(&db).await.expect("status");
assert_eq!(
status
.iter()
.map(|s| (s.name.as_str(), s.applied))
.collect::<Vec<_>>(),
[
("m20240101_000001_create_user", true),
("m20240102_000001_add_name", true)
]
);
let row = db
.query_one(Statement::from_string(
"SELECT name FROM user WHERE email = 'seed@x.io'",
))
.await
.expect("query")
.expect("seeded");
assert_eq!(row.get::<String>("name").unwrap(), "Seed");
Migrator::down(&db, Some(1)).await.expect("down one");
let status = Migrator::status(&db).await.expect("status");
assert!(!status[1].applied);
Migrator::refresh(&db).await.expect("refresh");
assert_eq!(
Migrator::get_applied_migrations(&db)
.await
.expect("applied")
.len(),
2
);
Migrator::fresh(&db).await.expect("fresh");
assert_eq!(
Migrator::get_applied_migrations(&db)
.await
.expect("applied")
.len(),
2
);
Migrator::reset(&db).await.expect("reset");
assert!(
Migrator::get_applied_migrations(&db)
.await
.expect("applied")
.is_empty()
);
}
#[tokio::test]
async fn failed_migration_is_rolled_back() {
let db = Database::connect(ConnectOptions::in_memory())
.await
.expect("open");
let err = FailingMigrator::up(&db, None).await.expect_err("fails");
assert!(matches!(err, DbErr::Migration(_)));
let applied = FailingMigrator::get_applied_migrations(&db)
.await
.expect("applied");
assert_eq!(applied, ["m20240101_000001_create_user"]);
let txn = db.begin().await.expect("begin");
let manager = SchemaManager::new(&txn);
assert!(manager.has_table("user").await.expect("has"));
assert!(!manager.has_table("orphan").await.expect("has"));
assert!(manager.has_index("idx-user-email").await.expect("has"));
assert!(manager.has_column("user", "email").await.expect("has"));
txn.rollback().await.expect("rollback");
}