391 lines
14 KiB
Rust
391 lines
14 KiB
Rust
//! 分页功能的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");
|
||
} |