103 lines
2.9 KiB
Rust
103 lines
2.9 KiB
Rust
use std::path::Path;
|
|
|
|
use sqlx::{
|
|
SqlitePool,
|
|
sqlite::{SqliteConnectOptions, SqlitePoolOptions},
|
|
};
|
|
|
|
const LEDGER: &str = include_str!("fixtures/migration_ledger.txt");
|
|
const PRE_MERGE_DB: &str = "tests/fixtures/pre_merge.db";
|
|
|
|
async fn migrate_with_todays_entry_points(pool: &SqlitePool) {
|
|
sqlite::migrate(pool).await.unwrap();
|
|
}
|
|
|
|
async fn ledger(pool: &SqlitePool) -> String {
|
|
let rows: Vec<(i64, String)> =
|
|
sqlx::query_as("SELECT version, hex(checksum) FROM _sqlx_migrations ORDER BY version")
|
|
.fetch_all(pool)
|
|
.await
|
|
.unwrap();
|
|
rows.iter().map(|(v, c)| format!("{v} {c}\n")).collect()
|
|
}
|
|
|
|
async fn file_pool(path: &Path, create: bool) -> SqlitePool {
|
|
let opts = SqliteConnectOptions::new()
|
|
.filename(path)
|
|
.create_if_missing(create);
|
|
SqlitePoolOptions::new()
|
|
.max_connections(1)
|
|
.connect_with(opts)
|
|
.await
|
|
.unwrap()
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn migrating_a_fresh_database_produces_the_recorded_ledger() {
|
|
let pool = SqlitePool::connect("sqlite::memory:").await.unwrap();
|
|
|
|
migrate_with_todays_entry_points(&pool).await;
|
|
|
|
assert_eq!(ledger(&pool).await, LEDGER);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn a_database_migrated_by_the_old_layout_migrates_again_cleanly() {
|
|
let dir = tempfile::tempdir().unwrap();
|
|
let path = dir.path().join("pre_merge.db");
|
|
std::fs::copy(
|
|
Path::new(env!("CARGO_MANIFEST_DIR")).join(PRE_MERGE_DB),
|
|
&path,
|
|
)
|
|
.unwrap();
|
|
let pool = file_pool(&path, false).await;
|
|
|
|
sqlite::migrate(&pool).await.unwrap();
|
|
|
|
assert_eq!(ledger(&pool).await, LEDGER);
|
|
}
|
|
|
|
async fn wal_pool(path: &Path, create: bool) -> SqlitePool {
|
|
let opts = SqliteConnectOptions::new()
|
|
.filename(path)
|
|
.create_if_missing(create)
|
|
.journal_mode(sqlx::sqlite::SqliteJournalMode::Wal)
|
|
.busy_timeout(std::time::Duration::from_secs(5));
|
|
SqlitePoolOptions::new()
|
|
.max_connections(2)
|
|
.connect_with(opts)
|
|
.await
|
|
.unwrap()
|
|
}
|
|
|
|
async fn race_two_migrators(start: Option<&Path>) {
|
|
for _ in 0..10 {
|
|
let dir = tempfile::tempdir().unwrap();
|
|
let path = dir.path().join("race.db");
|
|
if let Some(src) = start {
|
|
std::fs::copy(src, &path).unwrap();
|
|
}
|
|
let a = wal_pool(&path, start.is_none()).await;
|
|
let b = wal_pool(&path, false).await;
|
|
|
|
let (ra, rb) = tokio::join!(sqlite::migrate(&a), sqlite::migrate(&b));
|
|
|
|
ra.unwrap();
|
|
rb.unwrap();
|
|
assert_eq!(ledger(&a).await, LEDGER);
|
|
}
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn two_processes_migrating_the_same_fresh_database_both_succeed() {
|
|
race_two_migrators(None).await;
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn two_processes_migrating_a_database_at_the_old_version_both_succeed() {
|
|
race_two_migrators(Some(
|
|
&Path::new(env!("CARGO_MANIFEST_DIR")).join(PRE_MERGE_DB),
|
|
))
|
|
.await;
|
|
}
|