-
Notifications
You must be signed in to change notification settings - Fork 176
Description
Hello,
I'm writing integration tests in Rust, where I want to test the HTTP endpoints of my application. For this purpose, I need to mock the Keycloak login server, as I did not build a test mode into my application. To achieve this, I decided to start a real Keycloak server for my integration tests to ensure everything works as expected. The same I did for the database.
To do this, I need to start a Docker container with a specific image and run some setup scripts. However, starting a Docker container for each test is time-consuming, specially the keycloak auth container needs 10 seconds to receive connections, so I want to start a single auth container for all tests in a file. So 10 test methods needs only to wait one time for keycloak and not 10 times (10 x 10 seconds = 100 seconds test execution).
Previously, I found a solution that worked for a long time but now does not:
use ctor::{ctor, dtor};
use lazy_static::lazy_static;
use log::debug;
use mongodb::{
bson::{doc, oid::ObjectId},
options::{ClientOptions, UpdateModifications},
Client, Collection,
};
use serde::Serialize;
use std::{env, thread};
use testcontainers::runners::AsyncRunner;
use testcontainers::{core::Mount, ContainerRequest, GenericImage, ImageExt};
use tokio::sync::Notify;
use common::{channel, execute_blocking, Channel, ContainerCommands};
#[path = "../common/mod.rs"]
pub mod common;
lazy_static! {
static ref MONGODB_IN: Channel<ContainerCommands> = channel();
static ref MONGODB_CONNECTION_STRING: Channel<String> = channel();
static ref RUN_FINISHED: Notify = Notify::new();
}
#[ctor]
fn on_startup() {
thread::spawn(|| {
execute_blocking(start_mongodb());
// This needs to be here otherwise the MongoDB container did not call the drop function before the application stops
RUN_FINISHED.notify_one();
});
}
#[dtor]
fn on_shutdown() {
execute_blocking(clean_up());
}
async fn clean_up() {
MONGODB_IN.tx.send(ContainerCommands::Stop).unwrap();
// Wait until Docker is successfully stopped
RUN_FINISHED.notified().await;
debug!("MongoDB stopped.")
}
async fn start_mongodb() {
let mongodb = get_mongodb_image().start().await.unwrap();
let port = mongodb.get_host_port_ipv4(27017).await.unwrap();
debug!("MongoDB started on port {}", port);
let mut rx = MONGODB_IN.rx.lock().await;
while let Some(command) = rx.recv().await {
debug!("Received container command: {:?}", command);
match command {
ContainerCommands::FetchConnectionString => MONGODB_CONNECTION_STRING
.tx
.send(format!("mongodb://localhost:{}", port))
.unwrap(),
ContainerCommands::Stop => {
mongodb.stop().await.unwrap();
rx.close();
}
}
}
}
fn get_mongodb_image() -> ContainerRequest<GenericImage> {
let mount = Mount::bind_mount(
format!(
"{}/../../../../tests/docker-setup/mongo-init.js",
get_current_absolute_path()
),
"/docker-entrypoint-initdb.d/mongo-init.js",
);
GenericImage::new("mongo", "7.0.7")
.with_cmd(["mongod", "--replSet", "rs0", "--bind_ip", "0.0.0.0"])
.with_mount(mount)
}
fn get_current_absolute_path() -> String {
match env::current_exe() {
Ok(path) => {
let path_str = path.to_string_lossy().into_owned();
path_str
}
Err(_) => "/".to_string(),
}
}
pub async fn get_mongodb_connection_string() -> String {
MONGODB_IN
.tx
.send(ContainerCommands::FetchConnectionString)
.unwrap();
MONGODB_CONNECTION_STRING
.rx
.lock()
.await
.recv()
.await
.unwrap()
}This code is placed in the db_container module. When I use mod db_container in my integration test files, it sets up and starts the container for all tests in the current file. Using get_mongodb_connection_string(), I can get the connection string to feed into my application.
However, I now receive this error on dtor:
thread '<unnamed>' panicked at library/std/src/thread/mod.rs:741:19:
use of std::thread::current() is not possible after the thread's local data has been destroyed
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
fatal runtime error: failed to initiate panic, error 5
The problem appears to be the clean_up() function, which causes this error even when its content is empty.
I'm reaching out to see if anyone using the testcontainers crate has a smart solution for this issue. Any insights or alternative approaches would be greatly appreciated!
Thank you!