Skip to content

Commit 88b3e56

Browse files
committed
fix(database) :: test the connection pool hooks and the retry backoff
`on_connect.sql` and `on_reset.sql` exit but are not used for any tests. The retry test runs on a paused clock, so it asserts that each retry waits five seconds and that the loop then gives up.
1 parent 6d32b40 commit 88b3e56

1 file changed

Lines changed: 88 additions & 0 deletions

File tree

src/webserver/database/connect.rs

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,3 +271,91 @@ fn set_database_password(options: &mut AnyConnectOptions, password: &str) {
271271
unreachable!("Unsupported database type");
272272
}
273273
}
274+
275+
#[cfg(test)]
276+
mod tests {
277+
use super::*;
278+
use sqlx::row::Row;
279+
use tempfile::TempDir;
280+
281+
fn config_in(dir: &TempDir, database_url: &str) -> AppConfig {
282+
let mut config = crate::app_config::tests::test_config();
283+
config.database_url = database_url.to_owned();
284+
config.configuration_directory = dir.path().to_path_buf();
285+
config.max_database_pool_connections = Some(1);
286+
config
287+
}
288+
289+
#[actix_web::test]
290+
async fn on_connect_sql_runs_on_new_pool_connections() {
291+
let dir = TempDir::new().unwrap();
292+
std::fs::write(
293+
dir.path().join(ON_CONNECT_FILE),
294+
"CREATE TEMPORARY TABLE on_connect_marker(value TEXT);
295+
INSERT INTO on_connect_marker(value) VALUES ('on_connect ran');",
296+
)
297+
.unwrap();
298+
299+
let db = Database::init(&config_in(&dir, "sqlite::memory:"))
300+
.await
301+
.unwrap();
302+
let value: String = sqlx::query::query("SELECT value FROM on_connect_marker")
303+
.fetch_one(&db.connection)
304+
.await
305+
.unwrap()
306+
.try_get(0)
307+
.unwrap();
308+
309+
assert_eq!(value, "on_connect ran");
310+
}
311+
312+
#[actix_web::test]
313+
async fn on_reset_sql_runs_when_a_connection_returns_to_the_pool() {
314+
let dir = TempDir::new().unwrap();
315+
std::fs::write(
316+
dir.path().join(ON_CONNECT_FILE),
317+
"CREATE TABLE IF NOT EXISTS reset_log(id INTEGER);",
318+
)
319+
.unwrap();
320+
std::fs::write(
321+
dir.path().join(ON_RESET_FILE),
322+
"INSERT INTO reset_log(id) VALUES (1);
323+
SELECT 1 AS is_healthy;",
324+
)
325+
.unwrap();
326+
let db_file = dir.path().join("on_reset.db");
327+
let config = config_in(&dir, &format!("sqlite://{}?mode=rwc", db_file.display()));
328+
329+
let db = Database::init(&config).await.unwrap();
330+
drop(db.connection.acquire().await.unwrap());
331+
let resets: i64 = sqlx::query::query("SELECT COUNT(*) FROM reset_log")
332+
.fetch_one(&db.connection)
333+
.await
334+
.unwrap()
335+
.try_get(0)
336+
.unwrap();
337+
338+
assert!(resets > 0);
339+
}
340+
341+
#[tokio::test(start_paused = true)]
342+
async fn connection_retries_wait_five_seconds_before_giving_up() {
343+
for (retries, expected_wait) in [(0, Duration::ZERO), (2, Duration::from_secs(10))] {
344+
let dir = TempDir::new().unwrap();
345+
let missing = dir.path().join("nonexistent_directory").join("db.sqlite");
346+
let mut config = config_in(&dir, &format!("sqlite://{}", missing.display()));
347+
config.database_connection_retries = retries;
348+
349+
let start = tokio::time::Instant::now();
350+
let Err(error) = Database::init(&config).await else {
351+
panic!("connecting to a missing database must fail");
352+
};
353+
354+
assert_eq!(start.elapsed(), expected_wait, "{retries} retries");
355+
assert!(
356+
format!("{error:#}").contains("Unable to open connection to"),
357+
"{error:#}"
358+
);
359+
}
360+
}
361+
}

0 commit comments

Comments
 (0)