From 1e3e1fa3b7e70a10c3e270fbd6a9044c8470157d Mon Sep 17 00:00:00 2001 From: Reinier Maas Date: Fri, 10 Jul 2026 16:13:35 +0200 Subject: [PATCH] Test: Add `sqlx` `migrator` preventing duplicating migrations XRef: https://kobzol.github.io/rust/2026/06/21/optimizing-sqlx-test-rebuild-time.html --- .pre-commit-config.yaml | 5 +++++ opsqueue/src/common/chunk.rs | 10 +++++----- opsqueue/src/common/submission.rs | 22 +++++++++++----------- opsqueue/src/consumer/client.rs | 2 +- opsqueue/src/consumer/strategy.rs | 16 ++++++++-------- opsqueue/src/lib.rs | 6 ++++++ opsqueue/src/producer/client.rs | 6 +++--- 7 files changed, 39 insertions(+), 28 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 672f66c5..1a5d0a34 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -37,3 +37,8 @@ repos: language: system types: [nix] exclude: extra + - id: sqlx-test-needs-migrator + name: Require `#[sqlx::test(migrator = "crate::MIGRATOR")]` for test-build speed + entry: '#\[sqlx::test\]' + language: pygrep + types: [rust] diff --git a/opsqueue/src/common/chunk.rs b/opsqueue/src/common/chunk.rs index 534b6569..740fc616 100644 --- a/opsqueue/src/common/chunk.rs +++ b/opsqueue/src/common/chunk.rs @@ -605,7 +605,7 @@ pub mod test { use super::db::*; use super::*; - #[sqlx::test] + #[sqlx::test(migrator = "crate::MIGRATOR")] pub async fn test_insert_chunk(db: sqlx::SqlitePool) { let db = WriterPool::new(db); let mut conn = db.writer_conn().await.unwrap(); @@ -622,7 +622,7 @@ pub mod test { assert!(count_chunks(&mut conn).await.unwrap() == u63::new(1)); } - #[sqlx::test] + #[sqlx::test(migrator = "crate::MIGRATOR")] pub async fn test_get_chunk(db: sqlx::SqlitePool) { let db = WriterPool::new(db); let mut conn = db.writer_conn().await.unwrap(); @@ -642,7 +642,7 @@ pub mod test { assert!(chunk == fetched_chunk); } - #[sqlx::test] + #[sqlx::test(migrator = "crate::MIGRATOR")] pub async fn test_complete_chunk_raw(db: sqlx::SqlitePool) { let db = WriterPool::new(db); let mut conn = db.writer_conn().await.unwrap(); @@ -680,7 +680,7 @@ pub mod test { assert!(count_chunks_failed(&mut conn).await.unwrap() == u63::new(0)); } - #[sqlx::test] + #[sqlx::test(migrator = "crate::MIGRATOR")] pub async fn test_complete_chunk_raw_updates_submissions_chunk_total(db: sqlx::SqlitePool) { let db = WriterPool::new(db); let mut conn = db.writer_conn().await.unwrap(); @@ -723,7 +723,7 @@ pub mod test { } } - #[sqlx::test] + #[sqlx::test(migrator = "crate::MIGRATOR")] pub async fn test_fail_chunk(db: sqlx::SqlitePool) { let db = WriterPool::new(db); let mut conn = db.writer_conn().await.unwrap(); diff --git a/opsqueue/src/common/submission.rs b/opsqueue/src/common/submission.rs index c51e9958..3c2f9ee5 100644 --- a/opsqueue/src/common/submission.rs +++ b/opsqueue/src/common/submission.rs @@ -1070,7 +1070,7 @@ pub mod test { ); } - #[sqlx::test] + #[sqlx::test(migrator = "crate::MIGRATOR")] pub async fn test_query_plan_submission_status_in_progress(db: sqlx::SqlitePool) { let mut conn = db.acquire().await.unwrap(); let query = r#" @@ -1098,7 +1098,7 @@ pub mod test { "); } - #[sqlx::test] + #[sqlx::test(migrator = "crate::MIGRATOR")] pub async fn test_query_plan_submission_status_completed(db: sqlx::SqlitePool) { let mut conn = db.acquire().await.unwrap(); let query = r#" @@ -1126,7 +1126,7 @@ pub mod test { "); } - #[sqlx::test] + #[sqlx::test(migrator = "crate::MIGRATOR")] pub async fn test_query_plan_submission_status_failed(db: sqlx::SqlitePool) { let mut conn = db.acquire().await.unwrap(); let query = r#" @@ -1156,7 +1156,7 @@ pub mod test { "); } - #[sqlx::test] + #[sqlx::test(migrator = "crate::MIGRATOR")] pub async fn test_query_plan_submission_status_cancelled(db: sqlx::SqlitePool) { let mut conn = db.acquire().await.unwrap(); let query = r#" @@ -1183,7 +1183,7 @@ pub mod test { "); } - #[sqlx::test] + #[sqlx::test(migrator = "crate::MIGRATOR")] pub async fn test_insert_submission(db: sqlx::SqlitePool) { let db = WriterPool::new(db); let mut conn = db.writer_conn().await.unwrap(); @@ -1203,7 +1203,7 @@ pub mod test { assert_matches!(count_submissions(&mut conn).await, Ok(1)); } - #[sqlx::test] + #[sqlx::test(migrator = "crate::MIGRATOR")] pub async fn test_get_submission(db: sqlx::SqlitePool) { let db = WriterPool::new(db); let mut conn = db.writer_conn().await.unwrap(); @@ -1226,7 +1226,7 @@ pub mod test { assert_eq!(fetched_submission, submission); } - #[sqlx::test] + #[sqlx::test(migrator = "crate::MIGRATOR")] pub async fn test_submission_strategic_metadata(db: sqlx::SqlitePool) { let strategic_metadata: StrategicMetadataMap = [("company_id".to_string(), 123), ("flavour".to_string(), 42)] @@ -1253,7 +1253,7 @@ pub mod test { assert_eq!(fetched_metadata, strategic_metadata); } - #[sqlx::test] + #[sqlx::test(migrator = "crate::MIGRATOR")] pub async fn test_complete_submission_raw(db: sqlx::SqlitePool) { let db = WriterPool::new(db); let mut conn = db.writer_conn().await.unwrap(); @@ -1278,7 +1278,7 @@ pub mod test { assert_matches!(count_submissions_failed(&mut conn).await, Ok(0)); } - #[sqlx::test] + #[sqlx::test(migrator = "crate::MIGRATOR")] pub async fn test_fail_submission_raw(db: sqlx::SqlitePool) { let db = WriterPool::new(db); let mut conn = db.writer_conn().await.unwrap(); @@ -1305,7 +1305,7 @@ pub mod test { assert_matches!(count_submissions_failed(&mut conn).await, Ok(1)); } - #[sqlx::test] + #[sqlx::test(migrator = "crate::MIGRATOR")] pub async fn test_cleanup_old(db: sqlx::SqlitePool) { let db = WriterPool::new(db); let mut conn = db.writer_conn().await.unwrap(); @@ -1432,7 +1432,7 @@ pub mod test { let _sub2 = submission_status(old_four_unfailed, &mut conn).await; } - #[sqlx::test] + #[sqlx::test(migrator = "crate::MIGRATOR")] /// Test whether empty submissions are marked as completed right away by `insert_submission_from_chunks`. pub async fn auto_complete_empty_submission(db: sqlx::SqlitePool) { let db = WriterPool::new(db); diff --git a/opsqueue/src/consumer/client.rs b/opsqueue/src/consumer/client.rs index b92d7d48..3b2c4aee 100644 --- a/opsqueue/src/consumer/client.rs +++ b/opsqueue/src/consumer/client.rs @@ -499,7 +499,7 @@ mod tests { use super::*; - #[sqlx::test] + #[sqlx::test(migrator = "crate::MIGRATOR")] pub async fn test_fetch_chunks(pool: sqlx::SqlitePool) { let db_pools = db::DBPools::from_test_pool(&pool); let uri = "0.0.0.0:10083"; diff --git a/opsqueue/src/consumer/strategy.rs b/opsqueue/src/consumer/strategy.rs index 0a9f197c..573475d1 100644 --- a/opsqueue/src/consumer/strategy.rs +++ b/opsqueue/src/consumer/strategy.rs @@ -139,7 +139,7 @@ pub mod test { ); } - #[sqlx::test] + #[sqlx::test(migrator = "crate::MIGRATOR")] pub async fn test_query_plan_oldest(db: sqlx::SqlitePool) { let mut conn = db.acquire().await.unwrap(); let mut qb = QueryBuilder::new(""); @@ -162,7 +162,7 @@ pub mod test { assert_eq!(explained, "3, 0, SCAN chunks"); } - #[sqlx::test] + #[sqlx::test(migrator = "crate::MIGRATOR")] pub async fn test_query_plan_newest(db: sqlx::SqlitePool) { let mut conn = db.acquire().await.unwrap(); let mut qb = QueryBuilder::new(""); @@ -185,7 +185,7 @@ pub mod test { assert_eq!(explained, "3, 0, SCAN chunks"); } - #[sqlx::test] + #[sqlx::test(migrator = "crate::MIGRATOR")] pub async fn test_query_plan_random(db: sqlx::SqlitePool) { let mut conn = db.acquire().await.unwrap(); let metastate = MetaState::default(); @@ -221,7 +221,7 @@ pub mod test { "); } - #[sqlx::test] + #[sqlx::test(migrator = "crate::MIGRATOR")] pub async fn test_query_plan_prefer_distinct_oldest(db: sqlx::SqlitePool) { use Strategy::*; let mut conn = db.acquire().await.unwrap(); @@ -307,7 +307,7 @@ pub mod test { "); } - #[sqlx::test] + #[sqlx::test(migrator = "crate::MIGRATOR")] pub async fn test_query_plan_prefer_distinct_newest(db: sqlx::SqlitePool) { use Strategy::*; let mut conn = db.acquire().await.unwrap(); @@ -393,7 +393,7 @@ pub mod test { "); } - #[sqlx::test] + #[sqlx::test(migrator = "crate::MIGRATOR")] pub async fn test_query_plan_prefer_distinct_random(db: sqlx::SqlitePool) { use Strategy::*; let mut conn = db.acquire().await.unwrap(); @@ -502,7 +502,7 @@ pub mod test { "); } - #[sqlx::test] + #[sqlx::test(migrator = "crate::MIGRATOR")] pub async fn test_query_plan_prefer_distinct_nested(db: sqlx::SqlitePool) { use Strategy::*; let mut conn = db.acquire().await.unwrap(); @@ -726,7 +726,7 @@ pub mod test { use crate::db::Connection; use futures::stream::TryStreamExt as _; - #[sqlx::test] + #[sqlx::test(migrator = "crate::MIGRATOR")] /// Tests whether the 'cutting the deck' technique is working /// /// We do this by checking whether two selects in a huge amount of available chunks diff --git a/opsqueue/src/lib.rs b/opsqueue/src/lib.rs index 0389bbdb..6668cef5 100644 --- a/opsqueue/src/lib.rs +++ b/opsqueue/src/lib.rs @@ -47,3 +47,9 @@ pub const VERSION_CARGO_SEMVER: &str = env!("CARGO_PKG_VERSION"); pub fn version_info() -> String { format!("v{VERSION_CARGO_SEMVER}") } + +/// Shared constant with the migrations that all the tests can reference, to avoid the generated +/// code bloat as described in +/// https://kobzol.github.io/rust/2026/06/21/optimizing-sqlx-test-rebuild-time.html +#[cfg(all(test, feature = "server-logic"))] +const MIGRATOR: sqlx::migrate::Migrator = sqlx::migrate!(); diff --git a/opsqueue/src/producer/client.rs b/opsqueue/src/producer/client.rs index 55ce0209..890459e3 100644 --- a/opsqueue/src/producer/client.rs +++ b/opsqueue/src/producer/client.rs @@ -313,7 +313,7 @@ mod tests { tokio::task::yield_now().await; } - #[sqlx::test] + #[sqlx::test(migrator = "crate::MIGRATOR")] async fn test_count_submissions(pool: sqlx::SqlitePool) { let url = "0.0.0.0:4002"; start_server_in_background(&pool, url).await; @@ -339,7 +339,7 @@ mod tests { assert_eq!(count, 1); } - #[sqlx::test] + #[sqlx::test(migrator = "crate::MIGRATOR")] async fn test_insert_submission(pool: sqlx::SqlitePool) { let url = "0.0.0.0:4000"; start_server_in_background(&pool, url).await; @@ -389,7 +389,7 @@ mod tests { assert_eq!(count, 4); } - #[sqlx::test] + #[sqlx::test(migrator = "crate::MIGRATOR")] async fn test_get_submission(pool: sqlx::SqlitePool) { let url = "0.0.0.0:4001"; start_server_in_background(&pool, url).await;