ormer 0.2.7

A minimalist ORM framework that supports SQLite, PostgreSQL, MySQL, and SqlServer
#![cfg(any(feature = "sqlite", feature = "postgresql", feature = "mysql"))]

/// 事务管理测试
pub mod _test_common;

// 使用宏定义测试专用模型(唯一表名)
define_test_user_with_option_id!(TestUser, "test_transaction_users_1");

/// 测试事务提交功能
async fn test_transaction_commit_impl(
    config: &_test_common::DbConfig,
) -> Result<(), Box<dyn std::error::Error>> {
    // 跳过PostgreSQL测试(已知问题:Option<i64>主键在PostgreSQL中不支持NULL值插入)
    #[cfg(feature = "postgresql")]
    if matches!(config.0, ormer::DbType::PostgreSQL) {
        println!("Skipping PostgreSQL test (known issue with Option<i64> primary key)");
        return Ok(());
    }

    // 跳过MySQL测试(已知问题:Option<i64>主键在MySQL中不支持NULL值插入)
    #[cfg(feature = "mysql")]
    if matches!(config.0, ormer::DbType::MySQL) {
        println!("Skipping MySQL test (known issue with Option<i64> primary key)");
        return Ok(());
    }

    let db = _test_common::create_db_connection(config).await?;

    // 创建表
    db.create_table::<TestUser>().execute().await?;

    // 开始事务
    let mut txn = db.begin().await?;

    // 在事务中插入数据
    let user1 = TestUser {
        id: None,
        name: "Alice".to_string(),
        email: "alice@example.com".to_string(),
    };

    txn.insert(&user1).execute().await?;

    // 提交事务
    txn.commit().await?;

    // 验证数据已插入
    let users: Vec<TestUser> = db.select::<TestUser>().collect::<Vec<TestUser>>().await?;

    assert_eq!(users.len(), 1, "Should have 1 user after commit");
    assert_eq!(users[0].name, "Alice");

    // 清理测试表
    db.drop_table::<TestUser>().execute().await?;

    Ok(())
}

/// 测试事务回滚功能
async fn test_transaction_rollback_impl(
    config: &_test_common::DbConfig,
) -> Result<(), Box<dyn std::error::Error>> {
    // 跳过PostgreSQL测试(已知问题:Option<i64>主键在PostgreSQL中不支持NULL值插入)
    #[cfg(feature = "postgresql")]
    if matches!(config.0, ormer::DbType::PostgreSQL) {
        println!("Skipping PostgreSQL test (known issue with Option<i64> primary key)");
        return Ok(());
    }

    // 跳过MySQL测试(已知问题:Option<i64>主键在MySQL中不支持NULL值插入)
    #[cfg(feature = "mysql")]
    if matches!(config.0, ormer::DbType::MySQL) {
        println!("Skipping MySQL test (known issue with Option<i64> primary key)");
        return Ok(());
    }

    let db = _test_common::create_db_connection(config).await?;

    // 创建表
    db.create_table::<TestUser>().execute().await?;

    // 先插入一条数据
    let initial_user = TestUser {
        id: None,
        name: "Initial".to_string(),
        email: "initial@example.com".to_string(),
    };
    db.insert(&initial_user).execute().await?;

    // 开始事务
    let mut txn = db.begin().await?;

    // 在事务中插入数据
    let user1 = TestUser {
        id: None,
        name: "Should Rollback".to_string(),
        email: "rollback@example.com".to_string(),
    };

    txn.insert(&user1).execute().await?;

    // 回滚事务
    txn.rollback().await?;

    // 验证事务中的数据未插入
    let users: Vec<TestUser> = db.select::<TestUser>().collect::<Vec<TestUser>>().await?;

    assert_eq!(users.len(), 1, "Should have only 1 user after rollback");
    assert_eq!(users[0].name, "Initial");

    // 清理测试表
    db.drop_table::<TestUser>().execute().await?;

    Ok(())
}

/// 测试事务中的查询功能
async fn test_transaction_with_query_impl(
    config: &_test_common::DbConfig,
) -> Result<(), Box<dyn std::error::Error>> {
    // 跳过PostgreSQL测试(已知问题:Option<i64>主键在PostgreSQL中不支持NULL值插入)
    #[cfg(feature = "postgresql")]
    if matches!(config.0, ormer::DbType::PostgreSQL) {
        println!("Skipping PostgreSQL test (known issue with Option<i64> primary key)");
        return Ok(());
    }

    // 跳过MySQL测试(已知问题:Option<i64>主键在MySQL中不支持NULL值插入)
    #[cfg(feature = "mysql")]
    if matches!(config.0, ormer::DbType::MySQL) {
        println!("Skipping MySQL test (known issue with Option<i64> primary key)");
        return Ok(());
    }

    let db = _test_common::create_db_connection(config).await?;

    // 创建表
    db.create_table::<TestUser>().execute().await?;

    // 开始事务
    let mut txn = db.begin().await?;

    // 在事务中插入数据
    let user = TestUser {
        id: None,
        name: "Query Test".to_string(),
        email: "query@example.com".to_string(),
    };

    txn.insert(&user).execute().await?;

    // 在事务中查询(应该能看到未提交的数据)
    let users: Vec<TestUser> = txn.select::<TestUser>().collect::<Vec<TestUser>>().await?;

    assert_eq!(users.len(), 1, "Should see 1 user in transaction");
    assert_eq!(users[0].name, "Query Test");

    // 提交事务
    txn.commit().await?;

    // 验证提交后数据仍然存在
    let users: Vec<TestUser> = db.select::<TestUser>().collect::<Vec<TestUser>>().await?;

    assert_eq!(users.len(), 1, "Should have 1 user after commit");

    // 清理测试表
    db.drop_table::<TestUser>().execute().await?;

    Ok(())
}

/// 测试事务中的更新操作
async fn test_transaction_with_update_impl(
    config: &_test_common::DbConfig,
) -> Result<(), Box<dyn std::error::Error>> {
    // 跳过PostgreSQL测试(已知问题:Option<i64>主键在PostgreSQL中不支持NULL值插入)
    #[cfg(feature = "postgresql")]
    if matches!(config.0, ormer::DbType::PostgreSQL) {
        println!("Skipping PostgreSQL test (known issue with Option<i64> primary key)");
        return Ok(());
    }

    // 跳过MySQL测试(已知问题:Option<i64>主键在MySQL中不支持NULL值插入)
    #[cfg(feature = "mysql")]
    if matches!(config.0, ormer::DbType::MySQL) {
        println!("Skipping MySQL test (known issue with Option<i64> primary key)");
        return Ok(());
    }

    let db = _test_common::create_db_connection(config).await?;

    // 创建表
    db.create_table::<TestUser>().execute().await?;

    // 先插入一条数据
    let user = TestUser {
        id: None,
        name: "Original".to_string(),
        email: "original@example.com".to_string(),
    };
    db.insert(&user).execute().await?;

    // 开始事务
    let txn = db.begin().await?;

    // 在事务中更新数据
    txn.update::<TestUser>()
        .filter(|w| w.name.eq("Original"))
        .set(|w| w.name = w.name.set("Updated".to_string()))
        .execute()
        .await?;

    // 提交事务
    txn.commit().await?;

    // 验证提交后更新生效
    let users: Vec<TestUser> = db
        .select::<TestUser>()
        .filter(|w| w.name.eq("Updated"))
        .collect::<Vec<TestUser>>()
        .await?;

    assert_eq!(users.len(), 1, "Should have 1 updated user");
    assert_eq!(users[0].email, "original@example.com");

    // 清理测试表
    db.drop_table::<TestUser>().execute().await?;

    Ok(())
}

test_on_all_dbs_result!(test_transaction_commit_impl);
test_on_all_dbs_result!(test_transaction_rollback_impl);
test_on_all_dbs_result!(test_transaction_with_query_impl);
test_on_all_dbs_result!(test_transaction_with_update_impl);

#[cfg(feature = "sqlite")]
#[tokio::test]
async fn test_closure_transaction_and_savepoint_sqlite() -> Result<(), Box<dyn std::error::Error>> {
    let db = ormer::Database::connect(ormer::DbType::Sqlite, ":memory:").await?;
    db.create_table::<TestUser>().execute().await?;

    let committed = TestUser {
        id: None,
        name: "Committed".to_string(),
        email: "committed@example.com".to_string(),
    };
    db.transaction(|txn| {
        Box::pin(async move {
            txn.insert(&committed).execute().await?;
            Ok(())
        })
    })
    .await?;

    let rolled_back = TestUser {
        id: None,
        name: "Rolled back".to_string(),
        email: "rolled-back@example.com".to_string(),
    };
    let rolled_back_for_savepoint = rolled_back.clone();
    let result = db
        .transaction(|txn| {
            Box::pin(async move {
                txn.insert(&rolled_back).execute().await?;
                Err::<(), _>(ormer::ormer_error!("rollback closure"))
            })
        })
        .await;
    assert!(result.is_err());

    let savepoint_after = TestUser {
        id: None,
        name: "After savepoint".to_string(),
        email: "after-savepoint@example.com".to_string(),
    };
    db.transaction(|txn| {
        Box::pin(async move {
            let nested = txn
                .savepoint(|txn| {
                    Box::pin(async move {
                        txn.insert(&rolled_back_for_savepoint).execute().await?;
                        Err::<(), _>(ormer::ormer_error!("rollback savepoint"))
                    })
                })
                .await;
            assert!(nested.is_err());
            txn.insert(&savepoint_after).execute().await?;
            Ok(())
        })
    })
    .await?;

    let users = db.select::<TestUser>().collect::<Vec<TestUser>>().await?;
    assert_eq!(users.len(), 2);
    assert!(users.iter().any(|user| user.name == "Committed"));
    assert!(users.iter().any(|user| user.name == "After savepoint"));
    assert!(!users.iter().any(|user| user.name == "Rolled back"));

    db.drop_table::<TestUser>().execute().await?;
    Ok(())
}