backbone-core 3.0.2

Backbone Framework Core - Foundation for generic CRUD system
Documentation
use backbone_core::http::*;
use backbone_core::entity::*;
use serde::{Serialize, Deserialize};
use uuid::Uuid;

// Test entity for integration testing
#[derive(Debug, Clone, Serialize, Deserialize)]
struct TestEntity {
    id: Uuid,
    name: String,
    created_at: chrono::DateTime<chrono::Utc>,
    updated_at: chrono::DateTime<chrono::Utc>,
    deleted_at: Option<chrono::DateTime<chrono::Utc>>,
}

impl Entity for TestEntity {
    fn id(&self) -> &Uuid {
        &self.id
    }

    fn created_at(&self) -> chrono::DateTime<chrono::Utc> {
        self.created_at
    }

    fn updated_at(&self) -> chrono::DateTime<chrono::Utc> {
        self.updated_at
    }

    fn deleted_at(&self) -> Option<chrono::DateTime<chrono::Utc>> {
        self.deleted_at
    }
}

// Mock HTTP handler for testing
struct MockHttpHandler;

impl BackboneHttpHandler<TestEntity> for MockHttpHandler {
    fn list(&self, _request: ListRequest) -> Result<ApiResponse<Vec<TestEntity>>> {
        Ok(ApiResponse::success(vec![], Some("List successful".to_string())))
    }

    fn create(&self, _request: TestEntity) -> Result<ApiResponse<TestEntity>> {
        let entity = TestEntity {
            id: Uuid::new_v4(),
            name: "Test Entity".to_string(),
            created_at: chrono::Utc::now(),
            updated_at: chrono::Utc::now(),
            deleted_at: None,
        };
        Ok(ApiResponse::success(entity, Some("Entity created".to_string())))
    }

    fn get_by_id(&self, _id: &Uuid) -> Result<ApiResponse<TestEntity>> {
        Ok(ApiResponse::success(TestEntity {
            id: *_id,
            name: "Test Entity".to_string(),
            created_at: chrono::Utc::now(),
            updated_at: chrono::Utc::now(),
            deleted_at: None,
        }, Some("Entity retrieved".to_string())))
    }

    fn update(&self, _id: &Uuid, _request: TestEntity) -> Result<ApiResponse<TestEntity>> {
        Ok(ApiResponse::success(TestEntity {
            id: *_id,
            name: "Updated Entity".to_string(),
            created_at: chrono::Utc::now(),
            updated_at: chrono::Utc::now(),
            deleted_at: None,
        }, Some("Entity updated".to_string())))
    }

    fn partial_update(&self, _id: &Uuid, _request: std::collections::HashMap<String, String>) -> Result<ApiResponse<TestEntity>> {
        Ok(ApiResponse::success(TestEntity {
            id: *_id,
            name: "Partially Updated".to_string(),
            created_at: chrono::Utc::now(),
            updated_at: chrono::Utc::now(),
            deleted_at: None,
        }, Some("Entity partially updated".to_string())))
    }

    fn soft_delete(&self, _id: &Uuid) -> Result<ApiResponse<()>> {
        Ok(ApiResponse::success((), Some("Entity deleted".to_string())))
    }

    fn bulk_create(&self, _request: BulkCreateRequest<TestEntity>) -> Result<ApiResponse<BulkResponse<TestEntity>>> {
        Ok(ApiResponse::success(BulkResponse {
            items: vec![],
            total: 0,
            failed: 0,
            errors: vec![],
        }, Some("Bulk create successful".to_string())))
    }

    fn upsert(&self, _request: UpsertRequest<TestEntity>) -> Result<ApiResponse<TestEntity>> {
        Ok(ApiResponse::success(TestEntity {
            id: Uuid::new_v4(),
            name: "Upserted Entity".to_string(),
            created_at: chrono::Utc::now(),
            updated_at: chrono::Utc::now(),
            deleted_at: None,
        }, Some("Entity upserted".to_string())))
    }

    fn list_deleted(&self, _request: ListRequest) -> Result<ApiResponse<Vec<TestEntity>>> {
        Ok(ApiResponse::success(vec![], Some("List deleted successful".to_string())))
    }

    fn restore(&self, _id: &Uuid) -> Result<ApiResponse<TestEntity>> {
        Ok(ApiResponse::success(TestEntity {
            id: *_id,
            name: "Restored Entity".to_string(),
            created_at: chrono::Utc::now(),
            updated_at: chrono::Utc::now(),
            deleted_at: None,
        }, Some("Entity restored".to_string())))
    }

    fn empty_trash(&self) -> Result<ApiResponse<()>> {
        Ok(ApiResponse::success((), Some("Trash emptied".to_string())))
    }
}

#[test]
fn test_api_response_success() {
    let data = "test data";
    let response = ApiResponse::success(data, Some("Success".to_string()));

    assert_eq!(response.success, true);
    assert_eq!(response.data, Some("test data"));
    assert_eq!(response.message, Some("Success".to_string()));
    assert_eq!(response.error, None);
}

#[test]
fn test_api_response_error() {
    let response = ApiResponse::<String>::error("Error message".to_string());

    assert_eq!(response.success, false);
    assert_eq!(response.data, None);
    assert_eq!(response.message, None);
    assert_eq!(response.error, Some("Error message".to_string()));
}

#[test]
fn test_pagination_response() {
    let response = PaginationResponse::new(100, 2, 20);

    assert_eq!(response.total, 100);
    assert_eq!(response.page, 2);
    assert_eq!(response.limit, 20);
    assert_eq!(response.total_pages, 5); // 100 / 20 = 5
}

#[test]
fn test_sort_order_default() {
    let default_order = SortOrder::default();
    match default_order {
        SortOrder::Asc => assert!(true),
        SortOrder::Desc => assert!(false),
    }
}

#[test]
fn test_list_request_default() {
    let request = ListRequest::default();

    assert_eq!(request.page, Some(1));
    assert_eq!(request.limit, Some(20));
    assert_eq!(request.sort_by, None);
    assert_eq!(request.filters, None);
}

#[tokio::test]
async fn test_mock_http_handler_all_endpoints() {
    let handler = MockHttpHandler;
    let test_id = Uuid::new_v4();

    // Test all 11 endpoints
    let list_result = handler.list(ListRequest::default());
    assert!(list_result.is_ok());

    let create_result = handler.create(TestEntity {
        id: Uuid::new_v4(),
        name: "Test".to_string(),
        created_at: chrono::Utc::now(),
        updated_at: chrono::Utc::now(),
        deleted_at: None,
    });
    assert!(create_result.is_ok());

    let get_result = handler.get_by_id(&test_id);
    assert!(get_result.is_ok());

    let update_result = handler.update(&test_id, TestEntity {
        id: test_id,
        name: "Updated".to_string(),
        created_at: chrono::Utc::now(),
        updated_at: chrono::Utc::now(),
        deleted_at: None,
    });
    assert!(update_result.is_ok());

    let partial_update_result = handler.partial_update(&test_id, std::collections::HashMap::new());
    assert!(partial_update_result.is_ok());

    let delete_result = handler.soft_delete(&test_id);
    assert!(delete_result.is_ok());

    let bulk_result = handler.bulk_create(BulkCreateRequest { items: vec![] });
    assert!(bulk_result.is_ok());

    let upsert_result = handler.upsert(UpsertRequest {
        entity: TestEntity {
            id: Uuid::new_v4(),
            name: "Test".to_string(),
            created_at: chrono::Utc::now(),
            updated_at: chrono::Utc::now(),
            deleted_at: None,
        },
        create_if_not_exists: true,
    });
    assert!(upsert_result.is_ok());

    let list_deleted_result = handler.list_deleted(ListRequest::default());
    assert!(list_deleted_result.is_ok());

    let restore_result = handler.restore(&test_id);
    assert!(restore_result.is_ok());

    let empty_trash_result = handler.empty_trash();
    assert!(empty_trash_result.is_ok());
}