feat: 实现数据库迁移、搜索和分页功能
- 添加数据库迁移系统和初始用户表迁移 - 实现搜索功能模块和API - 实现分页功能支持 - 添加相关测试文件 - 更新项目配置和文档
This commit is contained in:
332
tests/search_api_tests.rs
Normal file
332
tests/search_api_tests.rs
Normal file
@@ -0,0 +1,332 @@
|
||||
//! 搜索API端点测试
|
||||
|
||||
use reqwest;
|
||||
use serde_json::{json, Value};
|
||||
use tokio;
|
||||
|
||||
const BASE_URL: &str = "http://127.0.0.1:3000";
|
||||
|
||||
/// 测试辅助函数:创建 HTTP 客户端
|
||||
fn create_client() -> reqwest::Client {
|
||||
reqwest::Client::new()
|
||||
}
|
||||
|
||||
/// 测试辅助函数:解析 JSON 响应
|
||||
async fn parse_json_response(response: reqwest::Response) -> Result<Value, Box<dyn std::error::Error>> {
|
||||
let text = response.text().await?;
|
||||
let json: Value = serde_json::from_str(&text)?;
|
||||
Ok(json)
|
||||
}
|
||||
|
||||
/// 测试辅助函数:创建测试用户
|
||||
async fn create_test_user(client: &reqwest::Client, username: &str, email: &str) -> Result<Value, Box<dyn std::error::Error>> {
|
||||
let user_data = json!({
|
||||
"username": username,
|
||||
"email": email,
|
||||
"password": "password123"
|
||||
});
|
||||
|
||||
let response = client
|
||||
.post(&format!("{}/api/users", BASE_URL))
|
||||
.json(&user_data)
|
||||
.send()
|
||||
.await?;
|
||||
|
||||
if response.status().is_success() {
|
||||
parse_json_response(response).await
|
||||
} else {
|
||||
Err(format!("Failed to create user: {}", response.status()).into())
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_search_api_basic() {
|
||||
let client = create_client();
|
||||
|
||||
// 创建一些测试用户
|
||||
let test_users = vec![
|
||||
("search_admin_1", "admin1@company.com"),
|
||||
("search_user_1", "user1@example.com"),
|
||||
("search_admin_2", "admin2@company.com"),
|
||||
("search_manager", "manager@company.com"),
|
||||
];
|
||||
|
||||
for (username, email) in test_users {
|
||||
let _ = create_test_user(&client, username, email).await;
|
||||
}
|
||||
|
||||
// 测试基本搜索功能
|
||||
let response = client
|
||||
.get(&format!("{}/api/users/search?q=admin", BASE_URL))
|
||||
.send()
|
||||
.await
|
||||
.expect("Failed to search users");
|
||||
|
||||
assert_eq!(response.status(), 200);
|
||||
let json = parse_json_response(response).await.expect("Failed to parse JSON");
|
||||
|
||||
// 验证搜索响应结构
|
||||
assert!(json["data"].is_array(), "Response should have data array");
|
||||
assert!(json["pagination"].is_object(), "Response should have pagination object");
|
||||
assert!(json["search_params"].is_object(), "Response should have search_params object");
|
||||
assert!(json["total_filtered"].is_number(), "Response should have total_filtered");
|
||||
|
||||
let data = json["data"].as_array().unwrap();
|
||||
let search_params = &json["search_params"];
|
||||
|
||||
// 验证搜索参数被正确返回
|
||||
assert_eq!(search_params["q"], "admin");
|
||||
|
||||
// 验证搜索结果
|
||||
for user in data {
|
||||
let username = user["username"].as_str().unwrap();
|
||||
let email = user["email"].as_str().unwrap();
|
||||
assert!(
|
||||
username.contains("admin") || email.contains("admin"),
|
||||
"Search result should contain 'admin' keyword"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_search_api_with_filters() {
|
||||
let client = create_client();
|
||||
|
||||
// 创建测试用户
|
||||
let test_users = vec![
|
||||
("filter_test_1", "test1@example.com"),
|
||||
("filter_test_2", "test2@company.com"),
|
||||
("other_user", "other@example.com"),
|
||||
];
|
||||
|
||||
for (username, email) in test_users {
|
||||
let _ = create_test_user(&client, username, email).await;
|
||||
}
|
||||
|
||||
// 测试用户名过滤
|
||||
let response = client
|
||||
.get(&format!("{}/api/users/search?username=filter_test", BASE_URL))
|
||||
.send()
|
||||
.await
|
||||
.expect("Failed to search users by username");
|
||||
|
||||
assert_eq!(response.status(), 200);
|
||||
let json = parse_json_response(response).await.expect("Failed to parse JSON");
|
||||
|
||||
let data = json["data"].as_array().unwrap();
|
||||
let search_params = &json["search_params"];
|
||||
|
||||
// 验证搜索参数
|
||||
assert_eq!(search_params["username"], "filter_test");
|
||||
|
||||
// 验证结果
|
||||
for user in data {
|
||||
let username = user["username"].as_str().unwrap();
|
||||
assert!(username.contains("filter_test"), "Username should contain filter_test");
|
||||
}
|
||||
|
||||
// 测试邮箱过滤
|
||||
let response = client
|
||||
.get(&format!("{}/api/users/search?email=company.com", BASE_URL))
|
||||
.send()
|
||||
.await
|
||||
.expect("Failed to search users by email");
|
||||
|
||||
assert_eq!(response.status(), 200);
|
||||
let json = parse_json_response(response).await.expect("Failed to parse JSON");
|
||||
|
||||
let data = json["data"].as_array().unwrap();
|
||||
let search_params = &json["search_params"];
|
||||
|
||||
// 验证搜索参数
|
||||
assert_eq!(search_params["email"], "company.com");
|
||||
|
||||
// 验证结果
|
||||
for user in data {
|
||||
let email = user["email"].as_str().unwrap();
|
||||
assert!(email.contains("company.com"), "Email should contain company.com");
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_search_api_with_sorting() {
|
||||
let client = create_client();
|
||||
|
||||
// 创建测试用户
|
||||
let test_users = vec![
|
||||
("sort_zebra", "zebra@test.com"),
|
||||
("sort_alpha", "alpha@test.com"),
|
||||
("sort_beta", "beta@test.com"),
|
||||
];
|
||||
|
||||
for (username, email) in test_users {
|
||||
let _ = create_test_user(&client, username, email).await;
|
||||
}
|
||||
|
||||
// 测试按用户名升序排序
|
||||
let response = client
|
||||
.get(&format!("{}/api/users/search?username=sort_&sort_by=username&sort_order=asc", BASE_URL))
|
||||
.send()
|
||||
.await
|
||||
.expect("Failed to search users with sorting");
|
||||
|
||||
assert_eq!(response.status(), 200);
|
||||
let json = parse_json_response(response).await.expect("Failed to parse JSON");
|
||||
|
||||
let data = json["data"].as_array().unwrap();
|
||||
let search_params = &json["search_params"];
|
||||
|
||||
// 验证搜索参数
|
||||
assert_eq!(search_params["sort_by"], "username");
|
||||
assert_eq!(search_params["sort_order"], "asc");
|
||||
|
||||
// 验证排序结果
|
||||
if data.len() > 1 {
|
||||
for i in 0..data.len() - 1 {
|
||||
let current_username = data[i]["username"].as_str().unwrap();
|
||||
let next_username = data[i + 1]["username"].as_str().unwrap();
|
||||
assert!(
|
||||
current_username <= next_username,
|
||||
"Results should be sorted by username in ascending order"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_search_api_with_pagination() {
|
||||
let client = create_client();
|
||||
|
||||
// 创建多个测试用户
|
||||
for i in 1..=5 {
|
||||
let username = format!("page_test_{}", i);
|
||||
let email = format!("page{}@test.com", i);
|
||||
let _ = create_test_user(&client, &username, &email).await;
|
||||
}
|
||||
|
||||
// 测试第一页
|
||||
let response = client
|
||||
.get(&format!("{}/api/users/search?username=page_test&page=1&limit=2", BASE_URL))
|
||||
.send()
|
||||
.await
|
||||
.expect("Failed to search users with pagination");
|
||||
|
||||
assert_eq!(response.status(), 200);
|
||||
let json = parse_json_response(response).await.expect("Failed to parse JSON");
|
||||
|
||||
let data = json["data"].as_array().unwrap();
|
||||
let pagination = &json["pagination"];
|
||||
|
||||
assert_eq!(data.len(), 2, "First page should have 2 users");
|
||||
assert_eq!(pagination["current_page"], 1);
|
||||
assert_eq!(pagination["per_page"], 2);
|
||||
assert!(!pagination["has_prev"].as_bool().unwrap(), "First page should not have previous");
|
||||
|
||||
// 测试第二页
|
||||
let response = client
|
||||
.get(&format!("{}/api/users/search?username=page_test&page=2&limit=2", BASE_URL))
|
||||
.send()
|
||||
.await
|
||||
.expect("Failed to search users page 2");
|
||||
|
||||
assert_eq!(response.status(), 200);
|
||||
let json = parse_json_response(response).await.expect("Failed to parse JSON");
|
||||
|
||||
let data = json["data"].as_array().unwrap();
|
||||
let pagination = &json["pagination"];
|
||||
|
||||
assert_eq!(data.len(), 2, "Second page should have 2 users");
|
||||
assert_eq!(pagination["current_page"], 2);
|
||||
assert!(pagination["has_prev"].as_bool().unwrap(), "Second page should have previous");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_search_api_validation_errors() {
|
||||
let client = create_client();
|
||||
|
||||
// 测试无效的排序字段
|
||||
let response = client
|
||||
.get(&format!("{}/api/users/search?sort_by=invalid_field", BASE_URL))
|
||||
.send()
|
||||
.await
|
||||
.expect("Failed to send request with invalid sort field");
|
||||
|
||||
assert_eq!(response.status(), 400);
|
||||
let json = parse_json_response(response).await.expect("Failed to parse JSON");
|
||||
assert!(json["error"].as_str().unwrap().contains("无效的排序字段"));
|
||||
|
||||
// 测试无效的排序方向
|
||||
let response = client
|
||||
.get(&format!("{}/api/users/search?sort_order=invalid_order", BASE_URL))
|
||||
.send()
|
||||
.await
|
||||
.expect("Failed to send request with invalid sort order");
|
||||
|
||||
assert_eq!(response.status(), 400);
|
||||
let json = parse_json_response(response).await.expect("Failed to parse JSON");
|
||||
assert!(json["error"].as_str().unwrap().contains("无效的排序方向"));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_search_api_empty_results() {
|
||||
let client = create_client();
|
||||
|
||||
// 搜索不存在的关键词
|
||||
let response = client
|
||||
.get(&format!("{}/api/users/search?q=nonexistent_keyword_12345", BASE_URL))
|
||||
.send()
|
||||
.await
|
||||
.expect("Failed to search for nonexistent keyword");
|
||||
|
||||
assert_eq!(response.status(), 200);
|
||||
let json = parse_json_response(response).await.expect("Failed to parse JSON");
|
||||
|
||||
let data = json["data"].as_array().unwrap();
|
||||
let total_filtered = json["total_filtered"].as_i64().unwrap();
|
||||
|
||||
assert_eq!(data.len(), 0, "Should return empty results");
|
||||
assert_eq!(total_filtered, 0, "Total filtered should be 0");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_search_api_complex_query() {
|
||||
let client = create_client();
|
||||
|
||||
// 创建测试用户
|
||||
let test_users = vec![
|
||||
("complex_admin", "admin@company.com"),
|
||||
("complex_user", "user@company.com"),
|
||||
("simple_admin", "admin@example.com"),
|
||||
];
|
||||
|
||||
for (username, email) in test_users {
|
||||
let _ = create_test_user(&client, username, email).await;
|
||||
}
|
||||
|
||||
// 复合搜索:用户名包含admin且邮箱包含company
|
||||
let response = client
|
||||
.get(&format!("{}/api/users/search?username=admin&email=company&sort_by=username&sort_order=asc", BASE_URL))
|
||||
.send()
|
||||
.await
|
||||
.expect("Failed to perform complex search");
|
||||
|
||||
assert_eq!(response.status(), 200);
|
||||
let json = parse_json_response(response).await.expect("Failed to parse JSON");
|
||||
|
||||
let data = json["data"].as_array().unwrap();
|
||||
let search_params = &json["search_params"];
|
||||
|
||||
// 验证搜索参数
|
||||
assert_eq!(search_params["username"], "admin");
|
||||
assert_eq!(search_params["email"], "company");
|
||||
assert_eq!(search_params["sort_by"], "username");
|
||||
assert_eq!(search_params["sort_order"], "asc");
|
||||
|
||||
// 验证结果同时满足两个条件
|
||||
for user in data {
|
||||
let username = user["username"].as_str().unwrap();
|
||||
let email = user["email"].as_str().unwrap();
|
||||
assert!(username.contains("admin"), "Username should contain admin");
|
||||
assert!(email.contains("company"), "Email should contain company");
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user