ormer 0.2.9

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

use ormer::DbType;
#[cfg(any(feature = "sqlite", feature = "duckdb"))]
use ormer::OrmerError;

#[derive(Debug, ormer::Model)]
#[table = "partial_index_capability_users"]
struct PartialIndexCapabilityUser {
    #[primary]
    id: i32,
    #[index(where = "active = 1")]
    email: String,
    active: bool,
}

#[derive(Debug, ormer::Model)]
#[table = "partial_index_migration_users"]
struct PartialIndexMigrationUserV1 {
    #[primary]
    id: i32,
    active: bool,
}

#[derive(Debug, ormer::Model)]
#[table = "partial_index_migration_users"]
struct PartialIndexMigrationUserV2 {
    #[primary]
    id: i32,
    active: bool,
    #[index(where = "active = 1")]
    email: Option<String>,
}

#[cfg(any(feature = "sqlite", feature = "duckdb"))]
fn assert_partial_index_error(error: OrmerError, db_type: DbType) {
    assert!(matches!(
        error,
        OrmerError::UnsupportedFeature {
            backend,
            feature: "partial index WHERE clauses",
        } if backend == db_type
    ));
}

#[test]
#[cfg(feature = "sqlite")]
fn sqlite_partial_index_create_sql_is_capability_gated() {
    let error = ormer::generate_create_table_sql::<PartialIndexCapabilityUser>(DbType::Sqlite)
        .expect_err("SQLite must reject partial index metadata");
    assert_partial_index_error(error, DbType::Sqlite);
}

#[tokio::test]
#[cfg(feature = "sqlite")]
async fn sqlite_partial_index_create_table_is_capability_gated() -> ormer::Result<()> {
    let db = ormer::Database::connect(DbType::Sqlite, ":memory:").await?;
    let error = db
        .create_table::<PartialIndexCapabilityUser>()
        .execute()
        .await
        .expect_err("SQLite create_table must reject partial index metadata");
    assert_partial_index_error(error, DbType::Sqlite);
    Ok(())
}

#[tokio::test]
#[cfg(feature = "sqlite")]
async fn sqlite_partial_index_migration_plan_is_capability_gated() -> ormer::Result<()> {
    let db = ormer::Database::connect(DbType::Sqlite, ":memory:").await?;
    db.migrate_table::<PartialIndexMigrationUserV1>()
        .execute()
        .await?;

    let error = db
        .migrate_table::<PartialIndexMigrationUserV2>()
        .plan()
        .await
        .expect_err("SQLite migration planning must reject partial index metadata");
    assert_partial_index_error(error, DbType::Sqlite);
    Ok(())
}

#[test]
#[cfg(feature = "duckdb")]
fn duckdb_partial_index_create_sql_is_capability_gated() {
    let error = ormer::generate_create_table_sql::<PartialIndexCapabilityUser>(DbType::DuckDB)
        .expect_err("DuckDB must reject partial index metadata");
    assert_partial_index_error(error, DbType::DuckDB);
}

#[tokio::test]
#[cfg(feature = "duckdb")]
async fn duckdb_partial_index_create_table_is_capability_gated() -> ormer::Result<()> {
    let db = ormer::Database::connect(DbType::DuckDB, ":memory:").await?;
    let error = db
        .create_table::<PartialIndexCapabilityUser>()
        .execute()
        .await
        .expect_err("DuckDB create_table must reject partial index metadata");
    assert_partial_index_error(error, DbType::DuckDB);
    Ok(())
}

#[tokio::test]
#[cfg(feature = "duckdb")]
async fn duckdb_partial_index_migration_plan_is_capability_gated() -> ormer::Result<()> {
    let db = ormer::Database::connect(DbType::DuckDB, ":memory:").await?;
    db.migrate_table::<PartialIndexMigrationUserV1>()
        .execute()
        .await?;

    let error = db
        .migrate_table::<PartialIndexMigrationUserV2>()
        .plan()
        .await
        .expect_err("DuckDB migration planning must reject partial index metadata");
    assert_partial_index_error(error, DbType::DuckDB);
    Ok(())
}

#[test]
#[cfg(feature = "postgresql")]
fn postgresql_partial_index_create_sql_keeps_where_clause() -> ormer::Result<()> {
    let sql = ormer::generate_create_table_sql::<PartialIndexCapabilityUser>(DbType::PostgreSQL)?;
    assert!(sql.contains("WHERE active = 1"));
    Ok(())
}