anycms-core 0.5.4

A unified API response library supporting multiple Rust web frameworks
Documentation
#![cfg(feature = "validator")]

use anycms_core::{ApiResult, FieldError, validation_errors_to_field_errors};
use validator::Validate;

// ============================================================
// 测试数据模型
// ============================================================

#[derive(Debug, Validate)]
struct SimpleForm {
    #[validate(email(message = "邮箱格式不正确"))]
    email: String,
    #[validate(length(min = 1, message = "名称不能为空"))]
    name: String,
}

#[derive(Debug, Validate)]
struct NestedChild {
    #[validate(length(min = 1))]
    city: String,
}

#[derive(Debug, Validate)]
struct NestedParent {
    #[validate(length(min = 1))]
    title: String,
    #[validate(nested)]
    address: NestedChild,
}

#[derive(Debug, Validate)]
struct ListParent {
    #[validate(length(min = 1))]
    id: String,
    #[validate(nested)]
    items: Vec<NestedChild>,
}

// ============================================================
// 测试用例
// ============================================================

#[test]
fn test_valid_data_passes() {
    let form = SimpleForm {
        email: "test@example.com".into(),
        name: "Alice".into(),
    };
    assert!(form.validate().is_ok());
}

#[test]
fn test_invalid_data_to_field_errors() {
    let form = SimpleForm {
        email: "not-email".into(),
        name: "".into(),
    };

    let errors = form.validate().unwrap_err();
    let field_errors = validation_errors_to_field_errors(&errors);

    assert_eq!(field_errors.len(), 2);

    let fields: Vec<&str> = field_errors.iter().map(|e| e.field.as_str()).collect();
    assert!(fields.contains(&"email"));
    assert!(fields.contains(&"name"));

    // 验证自定义消息被保留
    let email_error = field_errors.iter().find(|e| e.field == "email").unwrap();
    assert_eq!(email_error.message, "邮箱格式不正确");
}

#[test]
fn test_from_validation_errors_to_api_result() {
    let form = SimpleForm {
        email: "bad".into(),
        name: "".into(),
    };

    let errors = form.validate().unwrap_err();
    let result: ApiResult<String> = errors.into();

    assert!(!result.success);
    assert!(result.data.is_none());
    assert_eq!(result.code, Some(422));
    assert!(result.message.is_some());
    assert!(result.errors.is_some());

    let errors = result.errors.unwrap();
    assert_eq!(errors.len(), 2);
}

#[test]
fn test_api_result_json_format() {
    let form = SimpleForm {
        email: "not-email".into(),
        name: "".into(),
    };

    let errors = form.validate().unwrap_err();
    let result: ApiResult<String> = errors.into();

    let json = serde_json::to_value(&result).unwrap();
    assert_eq!(json["success"], false);
    assert_eq!(json["code"], 422);
    assert_eq!(json["message"], "Validation failed");
    assert!(json["errors"].is_array());
    assert!(json["data"].is_null());
}

#[test]
fn test_nested_struct_field_prefixing() {
    let parent = NestedParent {
        title: "valid title".into(),
        address: NestedChild { city: "".into() },
    };

    let errors = parent.validate().unwrap_err();
    let field_errors = validation_errors_to_field_errors(&errors);

    // 应该只有一个错误: address.city
    assert_eq!(field_errors.len(), 1);
    assert_eq!(field_errors[0].field, "address.city");
}

#[test]
fn test_nested_struct_multiple_errors() {
    let parent = NestedParent {
        title: "".into(),
        address: NestedChild { city: "".into() },
    };

    let errors = parent.validate().unwrap_err();
    let field_errors = validation_errors_to_field_errors(&errors);

    assert_eq!(field_errors.len(), 2);
    let fields: Vec<&str> = field_errors.iter().map(|e| e.field.as_str()).collect();
    assert!(fields.contains(&"title"));
    assert!(fields.contains(&"address.city"));
}

#[test]
fn test_list_validation_with_index_prefixing() {
    let parent = ListParent {
        id: "".into(),
        items: vec![
            NestedChild {
                city: "valid".into(),
            },
            NestedChild { city: "".into() },
        ],
    };

    let errors = parent.validate().unwrap_err();
    let field_errors = validation_errors_to_field_errors(&errors);

    let fields: Vec<&str> = field_errors.iter().map(|e| e.field.as_str()).collect();
    assert!(fields.contains(&"id"));
    assert!(fields.contains(&"items.1.city"));
}

#[test]
fn test_empty_validation_errors() {
    let form = SimpleForm {
        email: "ok@test.com".into(),
        name: "valid".into(),
    };

    let result = form.validate();
    assert!(result.is_ok());
}

#[test]
fn test_single_field_error() {
    let form = SimpleForm {
        email: "good@example.com".into(),
        name: "".into(),
    };

    let errors = form.validate().unwrap_err();
    let field_errors = validation_errors_to_field_errors(&errors);

    assert_eq!(field_errors.len(), 1);
    assert_eq!(field_errors[0].field, "name");
    assert_eq!(field_errors[0].message, "名称不能为空");
}

#[test]
fn test_merge_validator_errors_with_custom() {
    let form = SimpleForm {
        email: "ok@test.com".into(),
        name: "admin".into(),
    };

    let mut errors: Vec<FieldError> = Vec::new();

    if let Err(ve) = form.validate() {
        errors.extend(validation_errors_to_field_errors(&ve));
    }

    // 添加自定义业务校验错误
    errors.push(FieldError::new("name", "用户名 'admin' 已被保留"));

    let result = ApiResult::<String>::validation_errors(errors);

    assert!(!result.success);
    assert_eq!(result.code, Some(422));
    let errors = result.errors.unwrap();
    assert_eq!(errors.len(), 1);
    assert_eq!(errors[0].message, "用户名 'admin' 已被保留");
}