//! Reproduction for the fresh-DB multi-connection warm-up write failure. //! Forces several pooled connections to open concurrently on a brand-new DB and //! captures the ACTUAL sqlx/SQLite error (not the service's generic string). use openfut_core::db::{init_pool, run_migrations}; use openfut_core::services::economy; async fn seed_club(pool: &sqlx::SqlitePool) { sqlx::query( "INSERT INTO profiles (id, username, created_at, updated_at) VALUES ('p','p','t','t')", ) .execute(pool) .await .unwrap(); sqlx::query("INSERT INTO clubs (id, profile_id, name, coins, created_at, updated_at) VALUES ('c','p','c',100000,'t','t')") .execute(pool) .await .unwrap(); } #[tokio::test(flavor = "multi_thread", worker_threads = 8)] async fn fresh_db_multiconn_concurrent_writes() { let base = std::env::temp_dir().join(format!("ofut-cc-{}", std::process::id())); std::fs::create_dir_all(&base).unwrap(); let iters = 100usize; let mut failures = 0usize; let mut first_err = String::new(); for i in 0..iters { let url = format!("sqlite://{}/db{i}.db", base.display()); let pool = init_pool(&url, 5).await.expect("init_pool"); run_migrations(&pool).await.expect("migrations"); seed_club(&pool).await; // Fire concurrent credits to force several connections to warm up at once // on the brand-new DB, then a write — the harness's failing shape. let mut handles = Vec::new(); for _ in 0..8 { let p = pool.clone(); handles.push(tokio::spawn(async move { economy::grant_reward(&p, "c", 1).await })); } for h in handles { match h.await.unwrap() { Ok(_) => {} Err(e) => { failures += 1; if first_err.is_empty() { first_err = format!("{e:?}"); } } } } // Serialization correctness: 8 concurrent +1 credits, no lost update. let bal = economy::balance(&pool, "c").await.unwrap(); assert_eq!(bal, 100_008, "iter {i}: lost update under concurrency"); pool.close().await; } std::fs::remove_dir_all(&base).ok(); assert_eq!( failures, 0, "{failures}/{} iterations had a write failure; first error: {first_err}", iters * 8 ); }