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;

fn assert_unsupported(err: &tokio_postgres::Error, expected_message: &str) {
    let db_err = err.as_db_error().expect("expected db error");
    assert_eq!(db_err.code().code(), "0A000");
    assert_eq!(db_err.message(), expected_message);
}

#[tokio::test(flavor = "multi_thread")]
async fn recursive_cte_is_rejected() {
    let ctx = common::start().await;
    let err = ctx
        .client
        .query("with recursive c(n) as (select 1) select n from c", &[])
        .await
        .expect_err("expected recursive CTE error");
    assert_unsupported(&err, "WITH RECURSIVE is not supported");
    let _ = ctx.shutdown.send(());
}

#[tokio::test(flavor = "multi_thread")]
async fn data_modifying_cte_body_with_returning_is_supported() {
    let ctx = common::start().await;
    ctx.client
        .batch_execute(
            "create table t(id int primary key);
             insert into t values (1), (2), (3)",
        )
        .await
        .unwrap();
    let rows = ctx
        .client
        .query(
            "with c as (delete from t returning id) select id from c order by id",
            &[],
        )
        .await
        .unwrap();
    let ids: Vec<i32> = rows.into_iter().map(|row| row.get(0)).collect();
    assert_eq!(ids, vec![1, 2, 3]);
    let _ = ctx.shutdown.send(());
}

#[tokio::test(flavor = "multi_thread")]
async fn cte_materialization_variant_is_rejected() {
    let ctx = common::start().await;
    let err = ctx
        .client
        .query(
            "with c as materialized (select 1 as id) select id from c",
            &[],
        )
        .await
        .expect_err("expected CTE materialization error");
    assert_unsupported(&err, "CTE materialization options are not supported");
    let _ = ctx.shutdown.send(());
}

#[tokio::test(flavor = "multi_thread")]
async fn cte_alias_count_mismatch_is_rejected() {
    let ctx = common::start().await;
    let err = ctx
        .client
        .query("with c(a, b) as (select 1) select a from c", &[])
        .await
        .expect_err("expected alias mismatch error");
    let db_err = err.as_db_error().expect("expected db error");
    assert_eq!(db_err.code().code(), "42601");
    assert_eq!(
        db_err.message(),
        "CTE \"c\" has 1 columns but 2 column aliases were provided"
    );
    let _ = ctx.shutdown.send(());
}

#[tokio::test(flavor = "multi_thread")]
async fn cte_forward_reference_is_supported() {
    let ctx = common::start().await;
    let row = ctx
        .client
        .query_one(
            "with second as (select id from first), first as (select 1 as id) select id from second",
            &[],
        )
        .await
        .unwrap();
    let id: i32 = row.get(0);
    assert_eq!(id, 1);
    let _ = ctx.shutdown.send(());
}

#[tokio::test(flavor = "multi_thread")]
async fn circular_cte_dependencies_are_rejected() {
    let ctx = common::start().await;
    let err = ctx
        .client
        .query(
            "with first as (select id from second), second as (select id from first) select id from first",
            &[],
        )
        .await
        .expect_err("expected circular dependency error");
    assert_unsupported(&err, "circular CTE dependencies are not supported");
    let _ = ctx.shutdown.send(());
}