Files
rust-user-api/tests/pagination_integration_tests.rs
enoch cf01d557b9 feat: 实现数据库迁移、搜索和分页功能
- 添加数据库迁移系统和初始用户表迁移
- 实现搜索功能模块和API
- 实现分页功能支持
- 添加相关测试文件
- 更新项目配置和文档
2025-08-05 23:44:01 +08:00

391 lines
14 KiB
Rust
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//! 分页功能的HTTP 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())
}
}
/// 测试辅助函数:创建多个测试用户
async fn create_multiple_test_users(client: &reqwest::Client, count: usize) -> Result<Vec<Value>, Box<dyn std::error::Error>> {
let mut users = Vec::new();
for i in 0..count {
let username = format!("pagination_test_user_{:02}", i + 1);
let email = format!("pagination_test_{}@example.com", i + 1);
match create_test_user(client, &username, &email).await {
Ok(user) => users.push(user),
Err(e) => {
// 如果用户已存在,跳过
if e.to_string().contains("409") {
continue;
} else {
return Err(e);
}
}
}
// 添加小延迟确保创建时间不同
tokio::time::sleep(tokio::time::Duration::from_millis(10)).await;
}
Ok(users)
}
#[tokio::test]
async fn test_users_list_pagination_basic() {
let client = create_client();
// 创建测试用户
let _users = create_multiple_test_users(&client, 15).await
.expect("Failed to create test users");
// 测试第一页(默认参数)
let response = client
.get(&format!("{}/api/users", BASE_URL))
.send()
.await
.expect("Failed to get 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");
let pagination = &json["pagination"];
assert!(pagination["current_page"].is_number(), "Should have current_page");
assert!(pagination["per_page"].is_number(), "Should have per_page");
assert!(pagination["total_pages"].is_number(), "Should have total_pages");
assert!(pagination["total_items"].is_number(), "Should have total_items");
assert!(pagination["has_next"].is_boolean(), "Should have has_next");
assert!(pagination["has_prev"].is_boolean(), "Should have has_prev");
// 验证默认值
assert_eq!(pagination["current_page"], 1);
assert_eq!(pagination["per_page"], 10);
assert!(!pagination["has_prev"].as_bool().unwrap(), "First page should not have previous");
}
#[tokio::test]
async fn test_users_list_pagination_with_params() {
let client = create_client();
// 确保有足够的测试数据
let _users = create_multiple_test_users(&client, 12).await
.expect("Failed to create test users");
// 测试第一页每页5个
let response = client
.get(&format!("{}/api/users?page=1&limit=5", BASE_URL))
.send()
.await
.expect("Failed to get users page 1");
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(), 5, "First page should have 5 users");
assert_eq!(pagination["current_page"], 1);
assert_eq!(pagination["per_page"], 5);
assert!(!pagination["has_prev"].as_bool().unwrap());
// 测试第二页
let response = client
.get(&format!("{}/api/users?page=2&limit=5", BASE_URL))
.send()
.await
.expect("Failed to get 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(), 5, "Second page should have 5 users");
assert_eq!(pagination["current_page"], 2);
assert_eq!(pagination["per_page"], 5);
assert!(pagination["has_prev"].as_bool().unwrap(), "Second page should have previous");
}
#[tokio::test]
async fn test_users_list_pagination_edge_cases() {
let client = create_client();
// 测试页码为0应该被修正为1
let response = client
.get(&format!("{}/api/users?page=0&limit=5", BASE_URL))
.send()
.await
.expect("Failed to get users with page=0");
assert_eq!(response.status(), 200);
let json = parse_json_response(response).await.expect("Failed to parse JSON");
let pagination = &json["pagination"];
assert_eq!(pagination["current_page"], 1, "Page 0 should be corrected to 1");
// 测试超大限制应该被限制为100
let response = client
.get(&format!("{}/api/users?page=1&limit=200", BASE_URL))
.send()
.await
.expect("Failed to get users with large limit");
assert_eq!(response.status(), 200);
let json = parse_json_response(response).await.expect("Failed to parse JSON");
let pagination = &json["pagination"];
assert_eq!(pagination["per_page"], 100, "Limit should be capped at 100");
// 测试限制为0应该被修正为1
let response = client
.get(&format!("{}/api/users?page=1&limit=0", BASE_URL))
.send()
.await
.expect("Failed to get users with limit=0");
assert_eq!(response.status(), 200);
let json = parse_json_response(response).await.expect("Failed to parse JSON");
let pagination = &json["pagination"];
assert_eq!(pagination["per_page"], 1, "Limit 0 should be corrected to 1");
}
#[tokio::test]
async fn test_users_list_pagination_beyond_range() {
let client = create_client();
// 确保有一些测试数据
let _users = create_multiple_test_users(&client, 5).await
.expect("Failed to create test users");
// 测试超出范围的页码
let response = client
.get(&format!("{}/api/users?page=100&limit=5", BASE_URL))
.send()
.await
.expect("Failed to get users beyond range");
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(), 0, "Beyond range page should return empty array");
assert_eq!(pagination["current_page"], 100);
assert!(!pagination["has_next"].as_bool().unwrap(), "Beyond range should not have next");
}
#[tokio::test]
async fn test_users_search_pagination() {
let client = create_client();
// 创建一些包含特定关键词的用户
let admin_users = vec![
("admin_user_1", "admin1@example.com"),
("admin_user_2", "admin2@example.com"),
("admin_user_3", "admin3@example.com"),
("admin_user_4", "admin4@example.com"),
("admin_user_5", "admin5@example.com"),
];
for (username, email) in admin_users {
let _ = create_test_user(&client, username, email).await;
}
// 创建一些普通用户
let _ = create_multiple_test_users(&client, 3).await;
// 搜索admin用户第一页
let response = client
.get(&format!("{}/api/users/search?q=admin&page=1&limit=3", 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(), "Search response should have data array");
assert!(json["pagination"].is_object(), "Search response should have pagination");
assert!(json["search_params"].is_object(), "Search response should have search_params");
assert!(json["total_filtered"].is_number(), "Search response should have total_filtered");
let data = json["data"].as_array().unwrap();
let pagination = &json["pagination"];
// 验证搜索结果
assert!(data.len() <= 3, "Should return at most 3 results per page");
assert_eq!(pagination["current_page"], 1);
assert_eq!(pagination["per_page"], 3);
// 验证搜索结果包含关键词
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"
);
}
// 测试搜索第二页
let response = client
.get(&format!("{}/api/users/search?q=admin&page=2&limit=3", 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 pagination = &json["pagination"];
assert_eq!(pagination["current_page"], 2);
assert!(pagination["has_prev"].as_bool().unwrap(), "Second page should have previous");
}
#[tokio::test]
async fn test_users_search_with_filters_and_pagination() {
let client = create_client();
// 创建一些测试用户
let test_users = vec![
("filter_test_1", "filter1@test.com"),
("filter_test_2", "filter2@test.com"),
("filter_test_3", "filter3@test.com"),
("other_user_1", "other1@example.com"),
("other_user_2", "other2@example.com"),
];
for (username, email) in test_users {
let _ = create_test_user(&client, username, email).await;
}
// 按邮箱域名搜索,带分页
let response = client
.get(&format!("{}/api/users/search?email=test.com&page=1&limit=2&sort_by=username&sort_order=asc", BASE_URL))
.send()
.await
.expect("Failed to search users with email filter");
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"];
let search_params = &json["search_params"];
// 验证过滤结果
assert!(data.len() <= 2, "Should return at most 2 results per page");
assert_eq!(pagination["per_page"], 2);
// 验证搜索参数被正确返回
assert_eq!(search_params["email"], "test.com");
assert_eq!(search_params["sort_by"], "username");
assert_eq!(search_params["sort_order"], "asc");
// 验证结果包含正确的邮箱域名
for user in data {
let email = user["email"].as_str().unwrap();
assert!(email.contains("test.com"), "Filtered result should contain test.com domain");
}
// 验证排序(用户名升序)
if data.len() > 1 {
let first_username = data[0]["username"].as_str().unwrap();
let second_username = data[1]["username"].as_str().unwrap();
assert!(first_username <= second_username, "Results should be sorted by username ascending");
}
}
#[tokio::test]
async fn test_pagination_consistency_across_requests() {
let client = create_client();
// 创建固定数量的用户
let _users = create_multiple_test_users(&client, 10).await
.expect("Failed to create test users");
// 获取第一页
let response1 = client
.get(&format!("{}/api/users?page=1&limit=3", BASE_URL))
.send()
.await
.expect("Failed to get first page");
let json1 = parse_json_response(response1).await.expect("Failed to parse JSON");
let data1 = json1["data"].as_array().unwrap();
let total_items1 = json1["pagination"]["total_items"].as_u64().unwrap();
// 获取第二页
let response2 = client
.get(&format!("{}/api/users?page=2&limit=3", BASE_URL))
.send()
.await
.expect("Failed to get second page");
let json2 = parse_json_response(response2).await.expect("Failed to parse JSON");
let data2 = json2["data"].as_array().unwrap();
let total_items2 = json2["pagination"]["total_items"].as_u64().unwrap();
// 验证总数一致性
assert_eq!(total_items1, total_items2, "Total items should be consistent across pages");
// 验证没有重复用户
let mut user_ids1: Vec<String> = data1.iter()
.map(|u| u["id"].as_str().unwrap().to_string())
.collect();
let user_ids2: Vec<String> = data2.iter()
.map(|u| u["id"].as_str().unwrap().to_string())
.collect();
user_ids1.extend(user_ids2);
user_ids1.sort();
user_ids1.dedup();
// 去重后的长度应该等于原始长度(没有重复)
let expected_length = data1.len() + data2.len();
assert_eq!(user_ids1.len(), expected_length, "No duplicate users should appear across pages");
}