reuse db container between tests
Container is now never stopped. Since there's only ever going to be one, this is probably fine.
This commit is contained in:
parent
ee36efecad
commit
741268ef1e
1 changed files with 163 additions and 155 deletions
|
@ -1,11 +1,13 @@
|
|||
use anyhow::Result;
|
||||
use bollard::query_parameters::CreateImageOptionsBuilder;
|
||||
use anyhow::{bail, Result};
|
||||
use bollard::query_parameters::{CreateImageOptionsBuilder, ListContainersOptionsBuilder};
|
||||
use bollard::secret::{
|
||||
ContainerCreateBody, ContainerInspectResponse, ContainerState, CreateImageInfo, Health,
|
||||
HealthConfig, HealthStatusEnum, HostConfig, PortBinding,
|
||||
};
|
||||
use bollard::Docker;
|
||||
use futures_util::{FutureExt, StreamExt as _};
|
||||
use rand::distr::slice::Choose;
|
||||
use rand::{rng, Rng};
|
||||
use sqlx::migrate::MigrateDatabase;
|
||||
use sqlx::{Connection, PgConnection};
|
||||
use std::net::SocketAddr;
|
||||
|
@ -39,7 +41,6 @@ impl TestServer {
|
|||
|
||||
// TODO: allow per-test DBs in some cases
|
||||
// let db = TestDb::spawn().await;
|
||||
// TODO: share test server between test file, somehow?
|
||||
let db = get_shared_db().await;
|
||||
let url = dbg!(db.get_url());
|
||||
|
||||
|
@ -97,11 +98,6 @@ impl TestServer {
|
|||
self.server_task_handle.abort();
|
||||
let _ = self.server_task_handle.await;
|
||||
|
||||
// Shutdown server if this is the last one.
|
||||
if let Some(db) = Arc::into_inner(self.db) {
|
||||
db.stop().await;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -109,14 +105,12 @@ impl TestServer {
|
|||
const TEST_DB_IMAGE_NAME: &str = "postgres";
|
||||
const TEST_DB_SUPERUSER: &str = "postgres";
|
||||
const TEST_DB_SUPERUSER_PASS: &str = "password";
|
||||
const TEST_DB_APP_USER: &str = "app";
|
||||
const TEST_DB_APP_PASS: &str = "apppass";
|
||||
const TEST_DB_APP_NAME: &str = "ztoa";
|
||||
|
||||
pub struct TestDb {
|
||||
docker: Docker,
|
||||
// image_id: String,
|
||||
container: bollard::secret::ContainerInspectResponse,
|
||||
user: String,
|
||||
pass: String,
|
||||
name: String,
|
||||
}
|
||||
|
||||
impl TestDb {
|
||||
|
@ -129,6 +123,27 @@ impl TestDb {
|
|||
|
||||
trace!("version: {version:?}");
|
||||
|
||||
// check for existing container
|
||||
let mut found_containers = docker
|
||||
.list_containers(Some(
|
||||
ListContainersOptionsBuilder::new()
|
||||
.filters(
|
||||
&([(
|
||||
"label".to_string(),
|
||||
vec!["zero-to-axum_test-db".to_string()],
|
||||
)]
|
||||
.into()),
|
||||
)
|
||||
.build(),
|
||||
))
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let container_id;
|
||||
if let Some(container) = found_containers.pop() {
|
||||
container_id = container.id.unwrap();
|
||||
} else {
|
||||
// build container
|
||||
let mut image_id = None;
|
||||
|
||||
// check for image
|
||||
|
@ -196,7 +211,6 @@ impl TestDb {
|
|||
let image_id = image_id.expect("get image id for built docker image");
|
||||
|
||||
// create and start docker container
|
||||
let container_id;
|
||||
{
|
||||
let container_config = ContainerCreateBody {
|
||||
image: Some(image_id.clone()),
|
||||
|
@ -226,6 +240,7 @@ impl TestDb {
|
|||
retries: Some(5 * 1000 * 1000 * 1000),
|
||||
..Default::default()
|
||||
}),
|
||||
labels: Some([("zero-to-axum_test-db".to_string(), String::new())].into()),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
|
@ -250,6 +265,7 @@ impl TestDb {
|
|||
.await
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
// wait for container to be started
|
||||
let container = loop {
|
||||
|
@ -284,30 +300,48 @@ impl TestDb {
|
|||
sleep(Duration::from_secs(2)).await;
|
||||
};
|
||||
|
||||
let lowercase_alpha = Choose::new(b"abcdefghijklmnopqrstuvwxyz").unwrap();
|
||||
|
||||
let db = TestDb {
|
||||
docker,
|
||||
// image_id,
|
||||
container,
|
||||
user: rng()
|
||||
.sample_iter(lowercase_alpha)
|
||||
.take(16)
|
||||
.map(|i| char::from(*i))
|
||||
.collect(),
|
||||
pass: rng()
|
||||
.sample_iter(lowercase_alpha)
|
||||
.take(32)
|
||||
.map(|i| char::from(*i))
|
||||
.collect(),
|
||||
name: rng()
|
||||
.sample_iter(lowercase_alpha)
|
||||
.take(8)
|
||||
.map(|i| char::from(*i))
|
||||
.collect(),
|
||||
};
|
||||
|
||||
// setup app db
|
||||
{
|
||||
let mut conn = PgConnection::connect(&db.get_superuser_url())
|
||||
let mut conn = PgConnection::connect(&dbg!(db.get_superuser_url()))
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// create application user
|
||||
// Note: In general, string formtting a query is bad practice, but it's required here.
|
||||
sqlx::query(&format!(
|
||||
"CREATE USER {TEST_DB_APP_USER} WITH PASSWORD '{TEST_DB_APP_PASS}';"
|
||||
))
|
||||
dbg!(
|
||||
sqlx::query(&dbg!(format!(
|
||||
"CREATE USER {} WITH PASSWORD '{}';",
|
||||
db.user, db.pass
|
||||
)))
|
||||
.execute(&mut conn)
|
||||
.await
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// grant privs to app user
|
||||
// Note: In general, string formtting a query is bad practice, but it's required here.
|
||||
sqlx::query(&format!("ALTER USER {TEST_DB_APP_USER} CREATEDB;"))
|
||||
sqlx::query(&format!("ALTER USER {} CREATEDB;", db.user))
|
||||
.execute(&mut conn)
|
||||
.await
|
||||
.unwrap();
|
||||
|
@ -348,7 +382,10 @@ impl TestDb {
|
|||
.unwrap();
|
||||
let host_ip = binding.host_ip.as_ref().unwrap().clone();
|
||||
let host_port = binding.host_port.as_ref().unwrap().clone();
|
||||
format!("postgres://{TEST_DB_APP_USER}:{TEST_DB_APP_PASS}@{host_ip}:{host_port}/{TEST_DB_APP_NAME}")
|
||||
format!(
|
||||
"postgres://{}:{}@{host_ip}:{host_port}/{}",
|
||||
self.user, self.pass, self.name
|
||||
)
|
||||
}
|
||||
|
||||
/// Get the superuser-authenticated URL for accessing the `postgres` db from the host.
|
||||
|
@ -372,33 +409,4 @@ impl TestDb {
|
|||
let host_port = binding.host_port.as_ref().unwrap().clone();
|
||||
format!("postgres://{TEST_DB_SUPERUSER}:{TEST_DB_SUPERUSER_PASS}@{host_ip}:{host_port}/postgres")
|
||||
}
|
||||
|
||||
pub async fn stop(&self) {
|
||||
self.docker
|
||||
.stop_container(
|
||||
&self.container.id.as_deref().unwrap(),
|
||||
#[allow(deprecated)] // is deprecated, but also required
|
||||
None::<bollard::container::StopContainerOptions>,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
self.docker
|
||||
.remove_container(
|
||||
&self.container.id.as_deref().unwrap(),
|
||||
None::<bollard::query_parameters::RemoveContainerOptions>,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// // TODO: images seem like they might be reused, this is probably a bad idea, but it seems to work?
|
||||
// let _ = self
|
||||
// .docker
|
||||
// .remove_image(
|
||||
// &self.image_id,
|
||||
// None::<bollard::query_parameters::RemoveImageOptions>,
|
||||
// None,
|
||||
// )
|
||||
// .await;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue