How do I get a the connection back during a unit test?

Hi I'm writing an web application using axum, rusqlite and minijinja. I've been able to setup the axum::Router with a State that holds the Jinja templates and a database connection to SQLite. It works when try it on the browser. I also would like to test the endpoints. I've been able to use oneshot to send a request and get a response in the tests.

However I've encountered a problem now that I want to write assertions against the database. IIUC constructing the axum::Router consumes the connection so I can't use it after the axum::Router has been constructed.

I'm unsure how to modify the code to be able to get the connection back after the application has processed the request. In the test context I'm using a in-memory database so I can't open a second connection to the database either. Any pointers to restructure the code?

Below are the relevant snippets of the application

// in lib.rs
#[derive(Clone)]
pub(crate) struct WebContext {
    templates: Environment<'static>,
    db: Arc<Mutex<Connection>>
}

pub fn new(db: Connection, template_dir: String) -> Router {
    let source = path_loader(template_dir);
    let mut env = Environment::new();
    env.set_loader(source);
    let db = Arc::new(Mutex::new(db));
    let ctx = WebContext { templates: env, db };
    Router::new()
        .route("/", get(handler::index))
        .route("/page/new", get(handler::new_page))
        .route("/page/", post(handler::page_create))
        .route("/upload/", post(handler::upload))
        .with_state(ctx)
}

and the test code

fn setup_db() -> Connection {
    let db = Connection::open_in_memory().expect("open db");
    let mut file = File::open("sql/schema.sql").expect("open schema file");
    let mut schema = String::new();
    file.read_to_string(&mut schema)
        .expect("reading schema file");
    db.execute_batch(&schema).expect("Initialized the schema.");
    db
}

fn setup_app(db: Connection) -> Router {
    bliki::new(db, "./template/".to_string())
}

#[tokio::test]
async fn test_create_article() {
    let db = setup_db();
    let app = setup_app(db);
    let mut params = HashMap::new();
    params.insert("title", "Some title");
    let body = serde_urlencoded::to_string(params).expect("encoding body");
    let req = Request::builder()
        .method("POST")
        .uri("/page/")
        .header("Content-Type", "application/x-www-form-urlencoded")
        .body(body)
        .unwrap();
    let res = app.oneshot(req).await.unwrap();

    assert_eq!(res.status(), StatusCode::OK);
    // The assertion that results in the problem arising
    let got_title: String = db.query_row_and_then("SELECT title FROM pages limit 1", [], |row| row.get(0)).expect("query title");
    assert_eq!(got_title, "Some Title");
}

You are already using sharing: Arc<Mutex<Connection>>. Just create it outside of fn new() instead of inside, and in the unit test, pass a clone() of it into setup_app() so you still have another Arc to use.

If you wish, you can then call Arc::try_unwrap() and Mutex::into_inner() to recover the bare Connection value, but I wouldn’t consider that necessary.

1 Like