backbone-orm 2.7.36

Backbone Framework ORM - Database layer with PostgreSQL support
Documentation
//! Unit tests for Repository module

#[cfg(test)]
mod tests {
    use super::super::repository::{
        PostgresRepository, DatabaseOperations, Entity,
        PaginationParams, SortParams, SortDirection,
        FilterParams, FilterCondition, PaginatedResult, PaginationInfo
    };
    use serde::{Deserialize, Serialize};
    use sqlx::{FromRow, postgres::PgRow};
    use chrono::NaiveDateTime;
    use std::collections::HashMap;

    // Mock entity for testing
    #[derive(Debug, Clone, Serialize, Deserialize, FromRow)]
    struct TestUser {
        id: String,
        name: String,
        email: String,
        created_at: Option<NaiveDateTime>,
        updated_at: Option<NaiveDateTime>,
        deleted_at: Option<NaiveDateTime>,
    }

    impl Entity for TestUser {
        fn id(&self) -> Option<&str> {
            Some(&self.id)
        }

        fn table_name() -> &'static str {
            "test_users"
        }

        fn is_deleted(&self) -> bool {
            self.deleted_at.is_some()
        }

        fn created_at(&self) -> Option<NaiveDateTime> {
            self.created_at
        }

        fn updated_at(&self) -> Option<NaiveDateTime> {
            self.updated_at
        }
    }

    #[test]
    fn test_pagination_params_new() {
        let pagination = PaginationParams::new(2, 25);
        assert_eq!(pagination.page, 2);
        assert_eq!(pagination.per_page, 25);
    }

    #[test]
    fn test_pagination_params_page_minimum() {
        let pagination = PaginationParams::new(0, 25);
        assert_eq!(pagination.page, 1); // Should be adjusted to minimum 1
        assert_eq!(pagination.per_page, 25);
    }

    #[test]
    fn test_pagination_params_per_page_limits() {
        // Test minimum limit
        let pagination1 = PaginationParams::new(1, 0);
        assert_eq!(pagination1.per_page, 1); // Should be adjusted to minimum 1

        // Test maximum limit
        let pagination2 = PaginationParams::new(1, 200);
        assert_eq!(pagination2.per_page, 100); // Should be adjusted to maximum 100

        // Test within limits
        let pagination3 = PaginationParams::new(1, 50);
        assert_eq!(pagination3.per_page, 50); // Should remain unchanged
    }

    #[test]
    fn test_pagination_params_offset() {
        let pagination = PaginationParams::new(3, 20);
        assert_eq!(pagination.offset(), 40); // (3-1) * 20
    }

    #[test]
    fn test_pagination_params_limit() {
        let pagination = PaginationParams::new(1, 15);
        assert_eq!(pagination.limit(), 15);
    }

    #[test]
    fn test_sort_params_default() {
        let sort = SortParams::default();
        assert_eq!(sort.field, "");
        match sort.direction {
            SortDirection::Asc => assert!(true),
            _ => assert!(false),
        }
    }

    #[test]
    fn test_sort_direction_default() {
        let direction = SortDirection::default();
        match direction {
            SortDirection::Asc => assert!(true),
            _ => assert!(false),
        }
    }

    #[test]
    fn test_filter_params_default() {
        let filter = FilterParams::default();
        assert!(filter.conditions.is_empty());
    }

    #[test]
    fn test_filter_params_with_conditions() {
        let mut conditions = HashMap::new();
        conditions.insert("name".to_string(), FilterCondition::Equals("John".to_string()));
        conditions.insert("age".to_string(), FilterCondition::GreaterThan("25".to_string()));

        let filter = FilterParams { conditions };
        assert_eq!(filter.conditions.len(), 2);
        assert!(filter.conditions.contains_key("name"));
        assert!(filter.conditions.contains_key("age"));
    }

    #[test]
    fn test_filter_condition_variants() {
        // Test all FilterCondition variants can be created
        let equals = FilterCondition::Equals("test".to_string());
        let not_equals = FilterCondition::NotEquals("test".to_string());
        let greater_than = FilterCondition::GreaterThan("100".to_string());
        let less_than = FilterCondition::LessThan("200".to_string());
        let like = FilterCondition::Like("%pattern%".to_string());
        let in_values = FilterCondition::In(vec!["a".to_string(), "b".to_string()]);
        let is_null = FilterCondition::IsNull;
        let is_not_null = FilterCondition::IsNotNull;

        // Verify they can be created without panicking
        match equals {
            FilterCondition::Equals(_) => assert!(true),
            _ => assert!(false),
        }

        match not_equals {
            FilterCondition::NotEquals(_) => assert!(true),
            _ => assert!(false),
        }

        match greater_than {
            FilterCondition::GreaterThan(_) => assert!(true),
            _ => assert!(false),
        }

        match less_than {
            FilterCondition::LessThan(_) => assert!(true),
            _ => assert!(false),
        }

        match like {
            FilterCondition::Like(_) => assert!(true),
            _ => assert!(false),
        }

        match in_values {
            FilterCondition::In(_) => assert!(true),
            _ => assert!(false),
        }

        match is_null {
            FilterCondition::IsNull => assert!(true),
            _ => assert!(false),
        }

        match is_not_null {
            FilterCondition::IsNotNull => assert!(true),
            _ => assert!(false),
        }
    }

    #[test]
    fn test_pagination_info_new() {
        let pagination_info = PaginationInfo::new(2, 10, 95);
        assert_eq!(pagination_info.page, 2);
        assert_eq!(pagination_info.per_page, 10);
        assert_eq!(pagination_info.total, 95);
        assert_eq!(pagination_info.total_pages, 10); // ceil(95/10) = 10
    }

    #[test]
    fn test_pagination_info_exact_pages() {
        let pagination_info = PaginationInfo::new(1, 10, 100);
        assert_eq!(pagination_info.total_pages, 10); // 100/10 = 10 exactly
    }

    #[test]
    fn test_pagination_info_less_than_per_page() {
        let pagination_info = PaginationInfo::new(1, 10, 5);
        assert_eq!(pagination_info.total_pages, 1); // ceil(5/10) = 1
    }

    #[test]
    fn test_paginated_result_structure() {
        let data = vec!["item1", "item2", "item3"];
        let pagination = PaginationInfo::new(1, 10, 100);
        let result = PaginatedResult {
            data: data.clone(),
            pagination: pagination.clone(),
        };

        assert_eq!(result.data, data);
        assert_eq!(result.pagination.page, pagination.page);
        assert_eq!(result.pagination.per_page, pagination.per_page);
        assert_eq!(result.pagination.total, pagination.total);
        assert_eq!(result.pagination.total_pages, pagination.total_pages);
    }

    #[test]
    fn test_test_user_entity_implementation() {
        let user = TestUser {
            id: "123".to_string(),
            name: "John Doe".to_string(),
            email: "john@example.com".to_string(),
            created_at: Some(NaiveDateTime::from_timestamp_opt(1609459200, 0).unwrap()),
            updated_at: Some(NaiveDateTime::from_timestamp_opt(1609459260, 0).unwrap()),
            deleted_at: None,
        };

        // Test Entity trait implementation
        assert_eq!(user.id(), Some("123"));
        assert_eq!(TestUser::table_name(), "test_users");
        assert!(!user.is_deleted()); // deleted_at is None
        assert!(user.created_at().is_some());
        assert!(user.updated_at().is_some());
    }

    #[test]
    fn test_test_user_deleted() {
        let user = TestUser {
            id: "123".to_string(),
            name: "John Doe".to_string(),
            email: "john@example.com".to_string(),
            created_at: None,
            updated_at: None,
            deleted_at: Some(NaiveDateTime::from_timestamp_opt(1609459200, 0).unwrap()),
        };

        assert!(user.is_deleted()); // deleted_at is Some
    }

    #[test]
    fn test_test_user_no_id() {
        // Note: This test shows the limitation of our current TestUser implementation
        // In a real scenario, we might have entities without IDs
        let user = TestUser {
            id: "".to_string(), // Empty string represents no ID
            name: "John Doe".to_string(),
            email: "john@example.com".to_string(),
            created_at: None,
            updated_at: None,
            deleted_at: None,
        };

        assert_eq!(user.id(), Some("")); // Still returns Some("")
    }

    // Mock a PostgresRepository instance for testing structure
    #[test]
    fn test_postgres_repository_structure() {
        // We can't create a real PostgresRepository without a PgPool
        // but we can test the structure and methods are properly defined

        // Test that we can reference the types properly
        fn check_repository_types() {
            // This function just ensures the types compile correctly
            let _: Option<Box<dyn DatabaseOperations<TestUser>>> = None;
            let _: Option<PostgresRepository<TestUser>> = None;
        }

        check_repository_types();
    }

    #[test]
    fn test_all_traits_can_be_implemented() {
        // Verify that all the types and traits can work together

        fn check_trait_compatibility(user: TestUser) {
            // Test Entity trait
            let _id = user.id();
            let _table = TestUser::table_name();
            let _is_deleted = user.is_deleted();

            // Test that the user can be used where FromRow is required
            fn expects_from_row<T: for<'a> FromRow<'a, PgRow>>(_: T) {}
            expects_from_row(user);
        }

        let user = TestUser {
            id: "test".to_string(),
            name: "Test".to_string(),
            email: "test@example.com".to_string(),
            created_at: None,
            updated_at: None,
            deleted_at: None,
        };

        check_trait_compatibility(user);
    }

    #[test]
    fn test_database_operations_trait_exists() {
        // Verify the DatabaseOperations trait has all expected methods
        fn check_database_operations<T: DatabaseOperations<TestUser>>(_: T) {
            // This function checks that all required methods exist
            // We can't call them without a database connection, but we can verify the signature
        }

        check_database_operations_type::<PostgresRepository<TestUser>>();
    }

    fn check_database_operations_type<T: DatabaseOperations<TestUser>>() {}
}