Skip to content

Commit b0f628e

Browse files
committed
Add helper code for running opt-in tests with a DB
1 parent 176c5c3 commit b0f628e

File tree

3 files changed

+86
-1
lines changed

3 files changed

+86
-1
lines changed

src/db.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ impl ClientPool {
9393
}
9494
}
9595

96-
async fn make_client(db_url: &str) -> anyhow::Result<tokio_postgres::Client> {
96+
pub async fn make_client(db_url: &str) -> anyhow::Result<tokio_postgres::Client> {
9797
if db_url.contains("rds.amazonaws.com") {
9898
let mut builder = TlsConnector::builder();
9999
for cert in make_certificates() {

src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ mod team_data;
2929
pub mod triage;
3030
pub mod zulip;
3131

32+
#[cfg(test)]
33+
mod tests;
34+
3235
/// The name of a webhook event.
3336
#[derive(Debug)]
3437
pub enum EventName {

src/tests.rs

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
use crate::db;
2+
use crate::db::make_client;
3+
use std::future::Future;
4+
use tokio_postgres::Config;
5+
6+
/// Represents a connection to a Postgres database that can be
7+
/// used in integration tests to test logic that interacts with
8+
/// a database.
9+
pub struct TestContext {
10+
client: tokio_postgres::Client,
11+
db_name: String,
12+
original_db_url: String,
13+
conn_handle: tokio::task::JoinHandle<()>,
14+
}
15+
16+
impl TestContext {
17+
async fn new(db_url: &str) -> Self {
18+
let mut config: Config = db_url.parse().expect("Cannot parse connection string");
19+
20+
// Create a new database that will be used for this specific test
21+
let client = make_client(&db_url)
22+
.await
23+
.expect("Cannot connect to database");
24+
let db_name = format!("db{}", uuid::Uuid::new_v4().to_string().replace("-", ""));
25+
client
26+
.execute(&format!("CREATE DATABASE {db_name}"), &[])
27+
.await
28+
.expect("Cannot create database");
29+
drop(client);
30+
31+
// We need to connect to the database against, because Postgres doesn't allow
32+
// changing the active database mid-connection.
33+
config.dbname(&db_name);
34+
let (mut client, connection) = config
35+
.connect(tokio_postgres::NoTls)
36+
.await
37+
.expect("Cannot connect to the newly created database");
38+
let conn_handle = tokio::spawn(async move {
39+
connection.await.unwrap();
40+
});
41+
42+
db::run_migrations(&mut client)
43+
.await
44+
.expect("Cannot run database migrations");
45+
Self {
46+
client,
47+
db_name,
48+
original_db_url: db_url.to_string(),
49+
conn_handle,
50+
}
51+
}
52+
53+
async fn finish(self) {
54+
// Cleanup the test database
55+
// First, we need to stop using the database
56+
drop(self.client);
57+
self.conn_handle.await.unwrap();
58+
59+
// Then we need to connect to the default database and drop our test DB
60+
let client = make_client(&self.original_db_url)
61+
.await
62+
.expect("Cannot connect to database");
63+
client
64+
.execute(&format!("DROP DATABASE {}", self.db_name), &[])
65+
.await
66+
.unwrap();
67+
}
68+
}
69+
70+
pub async fn run_test<F, Fut>(f: F)
71+
where
72+
F: FnOnce(TestContext) -> Fut,
73+
Fut: Future<Output = anyhow::Result<TestContext>>,
74+
{
75+
if let Ok(db_url) = std::env::var("TEST_DB_URL") {
76+
let ctx = TestContext::new(&db_url).await;
77+
let ctx = f(ctx).await.expect("Test failed");
78+
ctx.finish().await;
79+
} else {
80+
eprintln!("Skipping test because TEST_DB_URL was not passed");
81+
}
82+
}

0 commit comments

Comments
 (0)