rust-ef 1.5.3

Rust Entity Framework - An EFCore-inspired ORM for Rust
Documentation
#[cfg(test)]
mod tests {
    use rust_ef::entity::EntityState;
    use rust_ef::metadata::{EntityTypeMeta, PropertyMeta};
    use rust_ef::migration::{MigrationDialect, SnapshotColumn};
    use rust_ef::provider::DbValue;
    use rust_ef::query::{FilterCondition, OrderBy, OrderDirection, QueryState};

    #[test]
    fn test_entity_state_enum() {
        assert_ne!(EntityState::Added, EntityState::Unchanged);
        assert_ne!(EntityState::Modified, EntityState::Deleted);
    }

    #[test]
    fn test_query_state_to_sql_basic() {
        let state = QueryState::new("blogs");
        let sql = state.to_sql();
        assert_eq!(sql, "SELECT * FROM blogs");
    }

    #[test]
    fn test_query_state_to_sql_with_filter() {
        let mut state = QueryState::new("blogs");
        state.filters.push(FilterCondition::new("rating", ">", 1));
        state.parameters.push(DbValue::I32(3));
        let sql = state.to_sql();
        assert!(sql.contains("WHERE"));
        assert!(sql.contains("rating"));
        assert!(sql.contains("?"));
    }

    #[test]
    fn test_query_state_to_sql_with_order() {
        let mut state = QueryState::new("posts");
        state
            .orderings
            .push(OrderBy::new("title", OrderDirection::Ascending));
        let sql = state.to_sql();
        assert!(sql.contains("ORDER BY"));
    }

    #[test]
    fn test_query_state_to_sql_pagination() {
        let mut state = QueryState::new("posts");
        state.limit = Some(10);
        state.offset = Some(20);
        let sql = state.to_sql();
        assert!(sql.contains("LIMIT 10"));
        assert!(sql.contains("OFFSET 20"));
    }

    #[test]
    fn test_query_state_to_sql_count() {
        let mut state = QueryState::new("posts");
        state.is_count = true;
        let sql = state.to_sql();
        assert!(sql.contains("SELECT COUNT(*)"));
    }

    #[test]
    fn test_save_changes_result() {
        let result = rust_ef::db_context::SaveChangesResult {
            added: 3,
            updated: 2,
            deleted: 1,
        };
        assert_eq!(result.total(), 6);
        let display = format!("{}", result);
        assert!(display.contains("3 added"));
    }

    #[test]
    fn test_metadata_entity_type() {
        let mut meta = EntityTypeMeta {
            type_id: std::any::TypeId::of::<i32>(),
            type_name: std::borrow::Cow::Borrowed("TestEntity"),
            table_name: std::borrow::Cow::Borrowed("test_entities"),
            properties: vec![],
            navigations: vec![],
            primary_keys: vec![],
            ..EntityTypeMeta::default()
        };

        meta.properties.push(PropertyMeta {
            field_name: std::borrow::Cow::Borrowed("id"),
            column_name: std::borrow::Cow::Borrowed("id"),
            type_id: std::any::TypeId::of::<i32>(),
            type_name: std::borrow::Cow::Borrowed("i32"),
            is_primary_key: true,
            is_auto_increment: true,
            is_sequence: false,
            sequence_name: None,
            is_required: true,
            is_foreign_key: false,
            is_concurrency_token: false,
            max_length: None,
            is_unique: false,
            has_index: false,
            is_not_mapped: false,
        });

        assert!(meta.primary_key_property().is_some());
        assert_eq!(meta.mapped_scalar_properties().count(), 1);
    }

    #[test]
    fn test_migration_dialect_quote() {
        assert_eq!(MigrationDialect::Postgres.quote("test"), "\"test\"");
        assert_eq!(MigrationDialect::MySql.quote("test"), "`test`");
        assert_eq!(MigrationDialect::Sqlite.quote("test"), "\"test\"");
    }

    #[test]
    fn test_migration_foreign_key_naming() {
        let name =
            rust_ef::migration::MigrationEngine::foreign_key_name("posts", "blog_id", "blogs");
        assert_eq!(name, "fk_posts_blog_id_blogs");
    }

    #[test]
    fn test_migration_binary_type_mapping() {
        let col = SnapshotColumn {
            field_name: "data".into(),
            column_name: "data".into(),
            type_name: "Vec<u8>".into(),
            is_primary_key: false,
            is_required: false,
            is_foreign_key: false,
            max_length: None,
            is_auto_increment: false,
            is_sequence: false,
            sequence_name: None,
            fk_referenced_table: None,
            fk_referenced_column: None,
            has_index: false,
            is_unique: false,
            fk_on_delete: None,
        };
        assert_eq!(MigrationDialect::Postgres.map_column_type(&col), "BYTEA");
        assert_eq!(MigrationDialect::MySql.map_column_type(&col), "BLOB");
        assert_eq!(MigrationDialect::Sqlite.map_column_type(&col), "BLOB");
    }

    #[test]
    fn test_migration_alter_column_mysql() {
        let engine = rust_ef::migration::MigrationEngine::new(MigrationDialect::MySql);
        let col = SnapshotColumn {
            field_name: "title".into(),
            column_name: "title".into(),
            type_name: "String".into(),
            is_primary_key: false,
            is_required: true,
            is_foreign_key: false,
            max_length: Some(200),
            is_auto_increment: false,
            is_sequence: false,
            sequence_name: None,
            fk_referenced_table: None,
            fk_referenced_column: None,
            has_index: false,
            is_unique: false,
            fk_on_delete: None,
        };
        let sql = engine.generate_alter_column_sql("posts", "title", &col);
        assert!(sql.contains("MODIFY COLUMN"));
        assert!(sql.contains("VARCHAR(200)"));
    }

    #[test]
    fn test_migration_dialect_type_mapping() {
        let col = SnapshotColumn {
            field_name: "id".into(),
            column_name: "id".into(),
            type_name: "i32".into(),
            is_primary_key: true,
            is_required: true,
            is_foreign_key: false,
            max_length: None,
            is_auto_increment: true,
            is_sequence: false,
            sequence_name: None,
            fk_referenced_table: None,
            fk_referenced_column: None,
            has_index: false,
            is_unique: false,
            fk_on_delete: None,
        };
        assert_eq!(MigrationDialect::Postgres.map_column_type(&col), "SERIAL");
        assert_eq!(
            MigrationDialect::MySql.map_column_type(&col),
            "INT AUTO_INCREMENT"
        );
        assert_eq!(MigrationDialect::Sqlite.map_column_type(&col), "INTEGER");
    }

    #[test]
    fn test_db_value_display() {
        assert_eq!(format!("{}", DbValue::I32(42)), "42");
        assert_eq!(format!("{}", DbValue::Null), "NULL");
    }

    #[test]
    fn test_db_value_from_traits() {
        let v: DbValue = 42i32.into();
        assert!(matches!(v, DbValue::I32(42)));

        let v: DbValue = "hello".into();
        assert!(matches!(v, DbValue::String(s) if s == "hello"));

        let v: DbValue = true.into();
        assert!(matches!(v, DbValue::Bool(true)));

        let v: DbValue = <Option<i32>>::None.into();
        assert!(matches!(v, DbValue::Null));
    }

    #[test]
    fn test_change_tracker() {
        let mut tracker = rust_ef::tracking::ChangeTracker::new();
        assert!(!tracker.has_changes());

        let type_id = std::any::TypeId::of::<i32>();
        tracker.track_entity(type_id, "i32", EntityState::Added);
        assert!(tracker.has_changes());

        tracker.accept_all_changes();
        assert!(!tracker.has_changes());
    }
}