How can I call drop in a tokio static OnceLock in Rust?

  Kiến thức lập trình

Here is the base implementation of my tests/commom/mod.rs

As everyone knows we cannot drop a static in rust for safety reasons… But, this is a test, and I want to create one docker image and use it without the need to do crazy workaround like this one in the example:

this one is working but is such a heavy workaround that I hate it.

use ctor::dtor;
use tokio::sync::OnceCell;

type PostgresContainer = OnceCell<ContainerAsync<GenericImage>>;

static INSTANCE_POSTGRES: PostgresContainer = OnceCell::const_new();
pub async fn postgres() -> &'static ContainerAsync<GenericImage> {
    INSTANCE_POSTGRES
        .get_or_init(|| async {
            GenericImage::new("postgres", "14.1")
                .with_wait_for(WaitFor::message_on_stdout(
                    "database system is ready to accept connections",
                ))
                .with_wait_for(WaitFor::seconds(5))
                .with_env_var("POSTGRES_USER", "test_user")
                .with_env_var("POSTGRES_PASSWORD", "test_password")
                .with_env_var("POSTGRES_DB", "test_db")
                .with_container_name("integration_test_postgres")
                .start()
                .await
                .expect("Postgres start")
        })
        .await
}


#[dtor]
fn shutdown() {
    std::process::Command::new("docker")
        .arg("kill")
        .arg("integration_test_postgres")
        .output()
        .expect("failed to kill container");

    std::process::Command::new("docker")
        .arg("rm")
        .arg("integration_test_postgres")
        .output()
        .expect("failed to kill container");
}

4

The solution I am using, that is better than setting a fixed name to the container is this one.

So instead of using OnceLock from tokio, I am creating a RwLock, this way on drop, I can access the id from the container and remove it after the tests.

The problem with OnceLock is the fact that drop cannot access the async fields because it is synchronous.

I am not able to run drop from the ContainerAsync because we are using a static variable and for now, I find no clear solution for it, this workaround is the best I have

static STATIC_INSTANCE: Lazy<RwLock<Option<ContainerAsync<GenericImage>>>> = Lazy::new(|| {
    RwLock::new(None) // Initialize with None
});

async fn init_once() {
    let mut instance = STATIC_INSTANCE.write().unwrap();

    if instance.is_none() {
        *instance = Some(
            GenericImage::new("postgres", "14.1")
                .with_wait_for(WaitFor::message_on_stdout(
                    "database system is ready to accept connections",
                ))
                .with_wait_for(WaitFor::seconds(5))
                .with_env_var("POSTGRES_USER", "test_user")
                .with_env_var("POSTGRES_PASSWORD", "test_password")
                .with_env_var("POSTGRES_DB", "test_db")
                .start()
                .await
                .expect("Postgres start"),
        );
    };
}

#[dtor]
fn drop_instance() {
    let container = STATIC_INSTANCE.read().unwrap();

    let id = container.as_ref().unwrap().id();

    std::process::Command::new("docker")
        .arg("kill")
        .arg(id)
        .output()
        .expect("failed to kill container");

    std::process::Command::new("docker")
        .arg("rm")
        .arg(id)
        .output()
        .expect("failed to kill container");
}

pub async fn setup() -> TestServer {    
    let _ = init_once().await;

    let container = STATIC_INSTANCE.read().unwrap();

    let port = container
        .as_ref()
        .unwrap()
        .get_host_port_ipv4(5432)
        .await
        .unwrap();

    let db_connection = format!("postgres://test_user:test_password@localhost:{port}/test_db");
    let assets_path = env!("CARGO_MANIFEST_DIR");
    let app = new_app(&db_connection, assets_path).await.unwrap();

    TestServer::new(app).unwrap()
}

Theme wordpress giá rẻ Theme wordpress giá rẻ Thiết kế website

LEAVE A COMMENT