toasty-driver-integration-suite 0.5.0

Integration test suite for Toasty database drivers
Documentation
use crate::helpers::column;
use crate::prelude::*;

use toasty_core::{
    driver::{Operation, Rows},
    stmt::{Assignment, BinaryOp, Expr, ExprColumn, ExprSet, Source, Statement, Type},
};

#[driver_test(id(ID))]
pub async fn basic_crud(test: &mut Test) -> Result<()> {
    #[derive(Debug, toasty::Model)]
    struct User {
        #[key]
        #[auto]
        id: ID,

        name: String,
        age: i32,
    }

    let mut db = test.setup_db(models!(User)).await;

    // Helper to get the table ID (handles database-specific prefixes automatically)
    let user_table_id = table_id(&db, "users");
    let user_id_column = column(&db, "users", "id");

    // Clear any setup operations (from reset_db, etc.)
    test.log().clear();

    let is_sql = test.capability().sql;

    // ========== CREATE ==========
    let user = User::create().name("Alice").age(30).exec(&mut db).await?;

    // Check the CREATE operation
    let (op, resp) = test.log().pop();

    assert_struct!(op, Operation::QuerySql({
        stmt: Statement::Insert({
            target: toasty_core::stmt::InsertTarget::Table({
                table: == user_table_id,
                columns.len(): 3,
                columns: == columns(&db, "users", &["id", "name", "age"]),
            }),
            source: {
                body: _,
            },
        }),
        // ret: None,
    }));

    if driver_test_cfg!(id_u64) && test.capability().returning_from_mutation {
        assert_struct!(op, Operation::QuerySql({
            ret: Some([Type::U64]),
            last_insert_id_hack: None,
        }));

        let rows = resp.values.collect_as_value().await?;

        // Check response
        assert_struct!(rows, == [(1u64,)]);
    } else if driver_test_cfg!(id_u64) {
        assert_struct!(op, Operation::QuerySql({
            ret: None,
            last_insert_id_hack: Some(1),
        }));

        let rows = resp.values.collect_as_value().await?;

        // Check response
        assert_struct!(rows, == [(1u64,)]);
    } else {
        assert_struct!(op, Operation::QuerySql({
            ret: None,
        }));

        // Check response
        assert_struct!(resp, {
            values: Rows::Count(1),
        });
    }

    let user_id = user.id;

    // ========== READ ==========
    let fetched = User::get_by_id(&mut db, &user_id).await?;
    assert_eq!(fetched.name, "Alice");
    assert_eq!(fetched.age, 30);

    // Check the READ operation
    let (op, resp) = test.log().pop();

    if is_sql {
        assert_struct!(op, Operation::QuerySql({
            stmt: Statement::Query({
                body: ExprSet::Select({
                    source: Source::Table({
                        tables: [== user_table_id, ..],
                    }),
                    filter.expr: Some(Expr::BinaryOp({
                        lhs.as_expr_column_unwrap(): ExprColumn {
                            nesting: 0,
                            table: 0,
                            column: == user_id_column.index,
                        },
                        op: BinaryOp::Eq,
                        rhs: _,
                    })),
                }),
            }),
            ret: Some(_),
        }));
    } else {
        assert_struct!(op, Operation::GetByKey({
            table: == user_table_id,
            keys: _,
            select.len(): 3,
        }));
    }

    assert_struct!(resp.values, Rows::Stream(_));

    // ========== UPDATE ==========
    User::filter_by_id(user_id)
        .update()
        .age(31)
        .exec(&mut db)
        .await?;

    // Check the UPDATE operation
    let (op, resp) = test.log().pop();

    if is_sql {
        assert_struct!(op, Operation::QuerySql({
            stmt: Statement::Update({
                target: toasty_core::stmt::UpdateTarget::Table(== user_table_id),
                assignments: #{ [2]: Assignment::Set(Expr::Arg({ position: 0 }))},
                filter.expr: Some(Expr::BinaryOp({
                    lhs.as_expr_column_unwrap(): ExprColumn {
                        nesting: 0,
                        table: 0,
                        column: == user_id_column.index,
                    },
                    op: BinaryOp::Eq,
                    rhs: _,
                })),
            }),
            params: [{ value: == 31i32 }, ..],
            ret: None,
        }));
    } else {
        assert_struct!(op, Operation::UpdateByKey({
            table: == user_table_id,
            filter: None,
            keys: _,
            assignments: #{ [2]: Assignment::Set(== 31i32)},
            returning: false,
        }));
    }

    assert_struct!(resp, {
        values: Rows::Count(1),
    });

    // ========== DELETE ==========
    User::filter_by_id(user_id).delete().exec(&mut db).await?;

    // Check the DELETE operation
    let (op, resp) = test.log().pop();

    if is_sql {
        assert_struct!(op, Operation::QuerySql({
            stmt: Statement::Delete({
                from: Source::Table({
                    tables: [== user_table_id, ..],
                }),
                filter.expr: Some(Expr::BinaryOp({
                    lhs.as_expr_column_unwrap(): ExprColumn {
                        nesting: 0,
                        table: 0,
                        column: == user_id_column.index,
                    },
                    op: BinaryOp::Eq,
                    rhs: _,
                })),
            }),
        }));
    } else {
        assert_struct!(op, Operation::DeleteByKey({
            table: == user_table_id,
            filter: None,
            keys: _,
        }));
    }

    // Check response
    assert_struct!(resp, {
        values: Rows::Count(1),
    });

    // ========== VERIFY LOG IS EMPTY ==========
    assert!(test.log().is_empty(), "Log should be empty");
    Ok(())
}