use std::{iter::FromIterator, num::NonZeroUsize};
use rusqlite::{Connection, OpenFlags, Transaction};
use crate::tests::helpers::all_valid_down;
use crate::{
tests::helpers::{all_valid_up, m_valid0_up, m_valid10_up, m_valid11_up, m_valid_fk_up},
user_version, Error, MigrationDefinitionError, Migrations, SchemaVersion, SchemaVersionError,
M,
};
use super::helpers::{m_invalid0, m_invalid1, m_valid20_up, m_valid21_up, raw_set_user_version};
#[test]
fn max_migration_test() {
use crate::{set_user_version, user_version};
let mut conn = Connection::open_in_memory().unwrap();
let migrations_max = crate::MIGRATIONS_MAX;
set_user_version(&conn, migrations_max).unwrap();
assert_eq!(
user_version(&conn),
Ok(migrations_max),
"Migration max is too high, it’s not the actual limit",
);
assert_eq!(
set_user_version(&conn, migrations_max + 1),
Err(Error::SpecifiedSchemaVersion(SchemaVersionError::TooHigh))
);
assert_eq!(
user_version(&conn),
Ok(migrations_max),
"set_user_version returned an error but user_version was changed",
);
raw_set_user_version(&mut conn, migrations_max as isize + 1);
assert_eq!(
user_version(&conn),
Ok(0),
"Migration max is too low, it’s not the actual limit",
);
}
#[test]
fn min_migrations_test() {
let mut conn = Connection::open_in_memory().unwrap();
crate::set_user_version(&conn, 0).unwrap();
raw_set_user_version(&mut conn, -3);
assert_eq!(
conn.query_row("PRAGMA user_version", [], |row| row.get(0)),
Ok(-3),
);
assert_eq!(crate::user_version(&conn), Err(Error::InvalidUserVersion));
raw_set_user_version(&mut conn, i32::MIN as isize);
assert_eq!(
conn.query_row("PRAGMA user_version", [], |row| row.get(0)),
Ok(i32::MIN),
);
assert_eq!(crate::user_version(&conn), Err(Error::InvalidUserVersion));
raw_set_user_version(&mut conn, (i32::MIN as isize).checked_sub(1).unwrap());
assert_eq!(
conn.query_row("PRAGMA user_version", [], |row| row.get(0)),
Ok(0),
);
}
#[test]
fn empty_migrations_test() {
let mut conn = Connection::open_in_memory().unwrap();
let m = Migrations::new(vec![]);
assert_eq!(
Err(Error::MigrationDefinition(
MigrationDefinitionError::NoMigrationsDefined
)),
m.to_latest(&mut conn)
);
for v in 0..4 {
assert_eq!(
Err(Error::MigrationDefinition(
MigrationDefinitionError::NoMigrationsDefined
)),
m.to_version(&mut conn, v)
)
}
}
#[test]
fn test_db_version_to_schema_empty() {
let m = Migrations::new(vec![]);
assert_eq!(m.db_version_to_schema(0), SchemaVersion::NoneSet);
assert_eq!(
m.db_version_to_schema(1),
SchemaVersion::Outside(NonZeroUsize::new(1).unwrap())
);
assert_eq!(
m.db_version_to_schema(10),
SchemaVersion::Outside(NonZeroUsize::new(10).unwrap())
);
}
#[test]
fn test_db_version_to_schema_two() {
let m = Migrations::new(vec![m_valid10_up(), m_valid11_up()]);
assert_eq!(m.db_version_to_schema(0), SchemaVersion::NoneSet);
assert_eq!(
m.db_version_to_schema(1),
SchemaVersion::Inside(NonZeroUsize::new(1).unwrap())
);
assert_eq!(
m.db_version_to_schema(10),
SchemaVersion::Outside(NonZeroUsize::new(10).unwrap())
);
}
#[test]
fn schema_version_partial_cmp_test() {
assert_eq!(SchemaVersion::NoneSet, SchemaVersion::NoneSet);
assert_eq!(
SchemaVersion::Inside(NonZeroUsize::new(1).unwrap()),
SchemaVersion::Inside(NonZeroUsize::new(1).unwrap())
);
assert_eq!(
SchemaVersion::Outside(NonZeroUsize::new(1).unwrap()),
SchemaVersion::Outside(NonZeroUsize::new(1).unwrap())
);
assert_ne!(
SchemaVersion::Outside(NonZeroUsize::new(1).unwrap()),
SchemaVersion::Inside(NonZeroUsize::new(1).unwrap())
);
assert_ne!(
SchemaVersion::Outside(NonZeroUsize::new(1).unwrap()),
SchemaVersion::NoneSet
);
assert_ne!(
SchemaVersion::Inside(NonZeroUsize::new(1).unwrap()),
SchemaVersion::NoneSet
);
assert!(SchemaVersion::NoneSet < SchemaVersion::Inside(NonZeroUsize::new(1).unwrap()));
assert!(SchemaVersion::NoneSet < SchemaVersion::Outside(NonZeroUsize::new(1).unwrap()));
assert!(
SchemaVersion::Inside(NonZeroUsize::new(1).unwrap())
< SchemaVersion::Outside(NonZeroUsize::new(2).unwrap())
);
assert!(
SchemaVersion::Outside(NonZeroUsize::new(1).unwrap())
< SchemaVersion::Inside(NonZeroUsize::new(2).unwrap())
);
}
#[test]
fn test_migration_hook_debug() {
let m = M::up_with_hook("", |_: &Transaction| Ok(()));
insta::assert_debug_snapshot!(m);
}
#[test]
fn user_version_convert_test() {
let mut conn = Connection::open_in_memory().unwrap();
let migrations = Migrations::new(vec![m_valid10_up()]);
assert_eq!(Ok(()), migrations.to_latest(&mut conn));
assert_eq!(Ok(1), user_version(&conn));
assert_eq!(
Ok(SchemaVersion::Inside(NonZeroUsize::new(1).unwrap())),
migrations.current_version(&conn)
);
assert_eq!(1usize, migrations.current_version(&conn).unwrap().into());
}
#[test]
fn user_version_migrate_test() {
let mut conn = Connection::open_in_memory().unwrap();
let migrations = Migrations::new(vec![m_valid10_up()]);
assert_eq!(Ok(0), user_version(&conn));
assert_eq!(Ok(()), migrations.to_latest(&mut conn));
assert_eq!(Ok(1), user_version(&conn));
assert_eq!(
Ok(SchemaVersion::Inside(NonZeroUsize::new(1).unwrap())),
migrations.current_version(&conn)
);
let migrations = Migrations::new(vec![m_valid10_up(), m_valid11_up()]);
assert_eq!(Ok(()), migrations.to_latest(&mut conn));
assert_eq!(Ok(2), user_version(&conn));
assert_eq!(
Ok(SchemaVersion::Inside(NonZeroUsize::new(2).unwrap())),
migrations.current_version(&conn)
);
}
#[test]
fn migration_partial_eq_test() {
let m1 = M::up("");
let m2 = M::up("");
let m3 = M::up("TEST");
assert_eq!(m1, m2);
assert_ne!(m1, m3);
}
#[test]
fn user_version_start_0_test() {
let conn = Connection::open_in_memory().unwrap();
assert_eq!(Ok(0), user_version(&conn))
}
#[test]
fn invalid_migration_statement_test() {
for m in &[m_invalid0(), m_invalid1(), m_valid11_up(), m_valid21_up()] {
let migrations = Migrations::new(vec![m.clone()]);
assert_ne!(Ok(()), migrations.validate())
}
}
#[test]
fn invalid_migration_multiple_statement_test() {
let migrations = Migrations::new(vec![m_valid0_up(), m_invalid1()]);
assert!(matches!(
dbg!(migrations.validate()),
Err(Error::RusqliteError { query: _, err: _ })
));
}
#[test]
fn valid_migration_multiple_statement_test() {
for m in &[m_valid0_up(), m_valid10_up(), m_valid20_up()] {
let migrations = Migrations::new(vec![m.clone()]);
assert_eq!(Ok(()), migrations.validate())
}
}
#[test]
fn all_valid_up_test() {
let migrations = Migrations::new(all_valid_up());
assert_eq!(Ok(()), migrations.validate());
insta::assert_debug_snapshot!(migrations)
}
#[test]
fn all_valid_down_test() {
let migrations = Migrations::new(all_valid_down());
assert_eq!(Ok(()), migrations.validate());
insta::assert_debug_snapshot!(migrations)
}
#[test]
fn test_read_only_db_all_valid() {
let mut conn = Connection::open_in_memory_with_flags(OpenFlags::SQLITE_OPEN_READ_ONLY).unwrap();
let migrations = Migrations::new(all_valid_up());
let e = migrations.to_latest(&mut conn);
assert!(e.is_err());
insta::assert_debug_snapshot!(e)
}
#[test]
fn current_version_gt_max_schema_version_test() {
let mut conn = Connection::open_in_memory().unwrap();
{
let migrations = Migrations::new(vec![m_valid0_up(), m_valid10_up()]);
migrations.to_latest(&mut conn).unwrap();
}
let migrations = Migrations::new(vec![m_valid0_up()]);
assert_eq!(
migrations.to_latest(&mut conn),
Err(Error::MigrationDefinition(
MigrationDefinitionError::DatabaseTooFarAhead
))
);
}
#[test]
fn hook_test() {
let mut conn = Connection::open_in_memory().unwrap();
let text = "Lorem ipsum dolor sit amet, consectetur adipisici elit …".to_string();
let cloned = text.clone();
let migrations = Migrations::new(vec![
M::up_with_hook(
"CREATE TABLE novels (text TEXT);",
move |tx: &Transaction| {
tx.execute("INSERT INTO novels (text) VALUES (?1)", (&cloned,))?;
Ok(())
},
),
M::up_with_hook(
"ALTER TABLE novels ADD compressed TEXT;",
|tx: &Transaction| {
let mut stmt = tx.prepare("SELECT rowid, text FROM novels").unwrap();
let rows = stmt.query_map([], |row| {
Ok((row.get_unwrap::<_, i64>(0), row.get_unwrap::<_, String>(1)))
})?;
for row in rows {
let row = row.unwrap();
let rowid = row.0;
let text = row.1;
let compressed = &text[..text.len() / 2];
tx.execute(
"UPDATE novels SET compressed = ?1 WHERE rowid = ?2;",
rusqlite::params![compressed, rowid],
)?;
}
Ok(())
},
)
.down_with_hook(
"ALTER TABLE novels DROP COLUMN compressed",
|_: &Transaction| Ok(()),
),
]);
assert_eq!(Ok(()), migrations.to_version(&mut conn, 2));
let result: (String, String) = conn
.query_row(
"SELECT text, compressed FROM novels WHERE rowid = 1",
[],
|row| Ok((row.get(0).unwrap(), row.get(1).unwrap())),
)
.unwrap();
assert_eq!(result.0, text);
assert!(text.starts_with(&result.1));
assert_eq!(Ok(()), migrations.to_version(&mut conn, 1));
}
#[test]
fn eq_hook_test() {
let vec_migrations = vec![
M::up("CREATE TABLE novels (text TEXT);"),
M::up("CREATE TABLE IF NOT EXISTS novels (text TEXT);"),
M::up("CREATE TABLE IF NOT EXISTS novels (text TEXT);").down("DROP TABLE novels;"),
M::up_with_hook(
"ALTER TABLE novels ADD compressed TEXT;",
|_: &Transaction| Ok(()),
)
.down_with_hook(
"ALTER TABLE novels DROP COLUMN compressed",
|_: &Transaction| Ok(()),
),
M::up_with_hook(
"ALTER TABLE novels ADD compressed TEXT;",
|_: &Transaction| Ok(()),
)
.down_with_hook(
"ALTER TABLE novels DROP COLUMN compressed",
|_: &Transaction| Ok(()),
),
M::up_with_hook(
"ALTER TABLE novels ADD compressed TEXT;",
|_: &Transaction| Ok(()),
)
.down_with_hook(
"ALTER TABLE novels DROP COLUMN compressed",
|_: &Transaction| Ok(()),
),
M::up_with_hook(
"ALTER TABLE novels ADD compressed TEXT;",
|_: &Transaction| Ok(()),
)
.down_with_hook(
"ALTER TABLE novels DROP COLUMN compressed",
|_: &Transaction| Ok(()),
),
];
{
let migrations = Migrations::from_iter(vec_migrations.clone().into_iter().take(2));
assert_eq!(migrations, migrations.clone());
}
for i in 0..vec_migrations.len() {
for j in 0..vec_migrations.len() {
if i == j {
assert_eq!(&vec_migrations[i], &vec_migrations[j]);
continue;
}
assert_ne!(&vec_migrations[i], &vec_migrations[j]);
}
}
assert_eq!(&vec_migrations[1], &vec_migrations[1]);
assert_ne!(&vec_migrations[0], &vec_migrations[1]);
}
#[test]
fn test_from_iter() {
let migrations = Migrations::from_iter(vec![m_valid0_up(), m_valid10_up()]);
assert_eq!(Ok(()), migrations.validate());
}
#[test]
fn test_user_version_error() {
let conn = Connection::open_in_memory_with_flags(OpenFlags::SQLITE_OPEN_READ_ONLY).unwrap();
let e = crate::set_user_version(&conn, 1);
assert!(e.is_err(), "{:?}", e);
insta::assert_debug_snapshot!(e)
}
#[test]
fn test_missing_down_migration() {
let mut conn = Connection::open_in_memory().unwrap();
let ms = vec![
M::up("CREATE TABLE t1(a)").down("DROP TABLE t1"),
M::up("CREATE TABLE t2(a)").down("DROP TABLE t2"),
M::up("CREATE TABLE t3(a)"),
M::up("CREATE TABLE t4(a)").down("DROP TABLE t4"),
M::up("CREATE TABLE t5(a)"),
];
let m = Migrations::new(ms);
m.to_version(&mut conn, 4).unwrap();
m.to_version(&mut conn, 3).unwrap();
assert_eq!(
Err(Error::MigrationDefinition(
MigrationDefinitionError::DownNotDefined { migration_index: 2 }
)),
m.to_version(&mut conn, 2)
);
}
#[test]
fn test_build_from_cow() {
use std::borrow::Cow;
let _ = Migrations::from_slice(&Cow::from(vec![m_valid0_up()]));
}
#[test]
fn test_pending_migrations() -> Result<(), Box<dyn std::error::Error>> {
let ms = &[
m_valid0_up(),
m_valid10_up(),
m_valid11_up(),
m_valid20_up(),
m_valid21_up(),
m_valid_fk_up(),
];
let migrations_0 = Migrations::from_slice(&[]);
let migrations_1 = Migrations::from_slice(&ms[..1]);
let migrations_2 = Migrations::from_slice(&ms[..3]);
let migrations_3 = Migrations::from_slice(&ms[..]);
{
let mut conn = Connection::open_in_memory()?;
migrations_1.to_latest(&mut conn)?;
assert_eq!(migrations_0.pending_migrations(&conn), Ok(-1));
assert_eq!(migrations_1.pending_migrations(&conn), Ok(0));
assert_eq!(migrations_2.pending_migrations(&conn), Ok(2));
assert_eq!(migrations_3.pending_migrations(&conn), Ok(5));
}
{
let mut conn = Connection::open_in_memory()?;
migrations_2.to_latest(&mut conn)?;
assert_eq!(migrations_1.pending_migrations(&conn), Ok(-2));
assert_eq!(migrations_2.pending_migrations(&conn), Ok(0));
assert_eq!(migrations_3.pending_migrations(&conn), Ok(3));
}
{
let mut conn = Connection::open_in_memory()?;
migrations_3.to_latest(&mut conn)?;
assert_eq!(migrations_0.pending_migrations(&conn), Ok(-6));
assert_eq!(migrations_1.pending_migrations(&conn), Ok(-5));
assert_eq!(migrations_2.pending_migrations(&conn), Ok(-3));
assert_eq!(migrations_3.pending_migrations(&conn), Ok(0));
}
Ok(())
}
#[test]
fn test_pending_migrations_errors() -> Result<(), Box<dyn std::error::Error>> {
let mut conn = Connection::open_in_memory()?;
let migrations = Migrations::new(vec![m_valid0_up(), m_valid10_up()]);
raw_set_user_version(&mut conn, -325);
assert_eq!(
migrations.pending_migrations(&conn),
Err(Error::InvalidUserVersion)
);
Ok(())
}