mockgres 0.0.29

An in-memory database that replicates a reasonable subset of Postgres functionality to make unit tests that rely on a database to run.
Documentation
mod common;

use tokio_postgres::types::Type;

#[tokio::test(flavor = "multi_thread")]
async fn prepared_select_supports_multiple_portals() {
    let mut ctx = common::start().await;

    ctx.client
        .execute(
            "create table portal_demo(id int primary key, score int, active bool)",
            &[],
        )
        .await
        .expect("create table");
    ctx.client
        .execute(
            "insert into portal_demo values
                (1, 5, true),
                (2, 15, true),
                (3, 25, false),
                (4, 35, false)",
            &[],
        )
        .await
        .expect("insert rows");

    let stmt = ctx
        .client
        .prepare(
            "select id from portal_demo
             where score >= $1 and active = $2
             order by id",
        )
        .await
        .expect("prepare select");

    let txn = ctx.client.transaction().await.expect("start txn");
    let portal_active = txn.bind(&stmt, &[&10_i32, &true]).await.expect("bind 1");
    let portal_inactive = txn.bind(&stmt, &[&10_i32, &false]).await.expect("bind 2");

    let ids_active: Vec<i32> = txn
        .query_portal(&portal_active, 0)
        .await
        .expect("query portal 1")
        .into_iter()
        .map(|row| row.get(0))
        .collect();
    let ids_inactive: Vec<i32> = txn
        .query_portal(&portal_inactive, 0)
        .await
        .expect("query portal 2")
        .into_iter()
        .map(|row| row.get(0))
        .collect();

    assert_eq!(ids_active, vec![2]);
    assert_eq!(ids_inactive, vec![3, 4]);
    txn.commit().await.expect("commit txn");

    let _ = ctx.shutdown.send(());
}

#[tokio::test(flavor = "multi_thread")]
async fn prepared_update_behaves_inside_and_outside_transactions() {
    let ctx = common::start().await;

    ctx.client
        .execute(
            "create table prepared_stock(id int primary key, qty int)",
            &[],
        )
        .await
        .expect("create table");
    ctx.client
        .execute("insert into prepared_stock values (1, 10)", &[])
        .await
        .expect("seed row");

    let select_stmt = ctx
        .client
        .prepare("select qty from prepared_stock where id = $1")
        .await
        .expect("prepare select");
    let update_stmt = ctx
        .client
        .prepare("update prepared_stock set qty = qty + $1 where id = $2")
        .await
        .expect("prepare update");

    let original: i32 = ctx
        .client
        .query_one(&select_stmt, &[&1_i32])
        .await
        .expect("initial select")
        .get(0);
    assert_eq!(original, 10);

    ctx.client.execute("begin", &[]).await.expect("begin");
    ctx.client
        .execute(&update_stmt, &[&5_i32, &1_i32])
        .await
        .expect("update in txn");
    let mid: i32 = ctx
        .client
        .query_one(&select_stmt, &[&1_i32])
        .await
        .expect("select in txn")
        .get(0);
    assert_eq!(mid, original + 5);
    ctx.client.execute("rollback", &[]).await.expect("rollback");

    let after_rollback: i32 = ctx
        .client
        .query_one(&select_stmt, &[&1_i32])
        .await
        .expect("select after rollback")
        .get(0);
    assert_eq!(after_rollback, original);

    ctx.client
        .execute(&update_stmt, &[&3_i32, &1_i32])
        .await
        .expect("autocommit update");
    let final_qty: i32 = ctx
        .client
        .query_one(&select_stmt, &[&1_i32])
        .await
        .expect("select after commit")
        .get(0);
    assert_eq!(final_qty, original + 3);

    let _ = ctx.shutdown.send(());
}

#[tokio::test(flavor = "multi_thread")]
async fn prepared_bool_parameter_works_in_update_case_condition() {
    let ctx = common::start().await;
    ctx.client
        .batch_execute(
            "create table case_updates (
                 id text primary key,
                 state text not null
             );
             insert into case_updates values ('1', 'ARMED');",
        )
        .await
        .unwrap();

    let statement = ctx
        .client
        .prepare(
            "update case_updates
                set state = case when $1 then 'ACTIVE' else state end
              where id = '1'",
        )
        .await
        .unwrap();
    assert_eq!(statement.params(), &[Type::BOOL]);
    assert_eq!(ctx.client.execute(&statement, &[&true]).await.unwrap(), 1);

    let row = ctx
        .client
        .query_one("select state from case_updates where id = '1'", &[])
        .await
        .unwrap();
    assert_eq!(row.get::<_, String>(0), "ACTIVE");

    let _ = ctx.shutdown.send(());
}