feat: 实现数据库迁移、搜索和分页功能

- 添加数据库迁移系统和初始用户表迁移
- 实现搜索功能模块和API
- 实现分页功能支持
- 添加相关测试文件
- 更新项目配置和文档
This commit is contained in:
2025-08-05 23:41:40 +08:00
parent c18f345475
commit cf01d557b9
26 changed files with 3578 additions and 27 deletions

352
tests/pagination_tests.rs Normal file
View File

@@ -0,0 +1,352 @@
//! 分页功能专项测试
use rust_user_api::{
models::{
user::User,
pagination::{PaginationParams, PaginatedResponse},
search::UserSearchParams,
},
storage::{database::DatabaseUserStore, memory::MemoryUserStore, UserStore},
utils::errors::ApiError,
};
use uuid::Uuid;
use chrono::Utc;
use std::sync::Arc;
/// 创建测试用户
fn create_test_user(username: &str, email: &str) -> User {
User {
id: Uuid::new_v4(),
username: username.to_string(),
email: email.to_string(),
password_hash: "hashed_password".to_string(),
created_at: Utc::now(),
updated_at: Utc::now(),
}
}
/// 创建多个测试用户
async fn create_multiple_users(store: &dyn UserStore, count: usize) -> Vec<User> {
let mut users = Vec::new();
for i in 0..count {
let user = create_test_user(
&format!("user{:02}", i + 1),
&format!("user{}@example.com", i + 1),
);
let created_user = store.create_user(user).await.unwrap();
users.push(created_user);
// 添加小延迟确保创建时间不同
tokio::time::sleep(tokio::time::Duration::from_millis(1)).await;
}
users
}
/// 测试内存存储的分页功能
#[tokio::test]
async fn test_memory_store_pagination() {
let store = MemoryUserStore::new();
// 创建15个用户
let users = create_multiple_users(&store, 15).await;
// 测试第一页每页5个
let params = PaginationParams {
page: Some(1),
limit: Some(5),
};
let (paginated_users, total_count) = store.list_users_paginated(&params).await.unwrap();
assert_eq!(total_count, 15, "总用户数应该是15");
assert_eq!(paginated_users.len(), 5, "第一页应该有5个用户");
// 测试第二页
let params = PaginationParams {
page: Some(2),
limit: Some(5),
};
let (paginated_users, total_count) = store.list_users_paginated(&params).await.unwrap();
assert_eq!(total_count, 15, "总用户数应该是15");
assert_eq!(paginated_users.len(), 5, "第二页应该有5个用户");
// 测试第三页
let params = PaginationParams {
page: Some(3),
limit: Some(5),
};
let (paginated_users, total_count) = store.list_users_paginated(&params).await.unwrap();
assert_eq!(total_count, 15, "总用户数应该是15");
assert_eq!(paginated_users.len(), 5, "第三页应该有5个用户");
// 测试第四页(超出范围)
let params = PaginationParams {
page: Some(4),
limit: Some(5),
};
let (paginated_users, total_count) = store.list_users_paginated(&params).await.unwrap();
assert_eq!(total_count, 15, "总用户数应该是15");
assert_eq!(paginated_users.len(), 0, "第四页应该没有用户");
}
/// 测试数据库存储的分页功能
#[tokio::test]
async fn test_database_store_pagination() {
let database_url = "sqlite::memory:";
let store = DatabaseUserStore::from_url(database_url).await.unwrap();
// 创建12个用户
let users = create_multiple_users(&store, 12).await;
// 测试第一页每页4个
let params = PaginationParams {
page: Some(1),
limit: Some(4),
};
let (paginated_users, total_count) = store.list_users_paginated(&params).await.unwrap();
assert_eq!(total_count, 12, "总用户数应该是12");
assert_eq!(paginated_users.len(), 4, "第一页应该有4个用户");
// 验证排序(应该按创建时间倒序)
for i in 0..paginated_users.len() - 1 {
assert!(
paginated_users[i].created_at >= paginated_users[i + 1].created_at,
"用户应该按创建时间倒序排列"
);
}
// 测试最后一页
let params = PaginationParams {
page: Some(3),
limit: Some(4),
};
let (paginated_users, total_count) = store.list_users_paginated(&params).await.unwrap();
assert_eq!(total_count, 12, "总用户数应该是12");
assert_eq!(paginated_users.len(), 4, "第三页应该有4个用户");
}
/// 测试分页参数的默认值和边界情况
#[tokio::test]
async fn test_pagination_params_edge_cases() {
let store = MemoryUserStore::new();
// 创建8个用户
create_multiple_users(&store, 8).await;
// 测试默认参数
let params = PaginationParams {
page: None,
limit: None,
};
let (paginated_users, total_count) = store.list_users_paginated(&params).await.unwrap();
assert_eq!(total_count, 8, "总用户数应该是8");
assert_eq!(paginated_users.len(), 8, "默认应该返回所有用户限制为10");
// 测试页码为0应该被修正为1
let params = PaginationParams {
page: Some(0),
limit: Some(3),
};
let (paginated_users, total_count) = store.list_users_paginated(&params).await.unwrap();
assert_eq!(total_count, 8, "总用户数应该是8");
assert_eq!(paginated_users.len(), 3, "页码0应该被修正为1返回3个用户");
// 测试超大限制应该被限制为100
let params = PaginationParams {
page: Some(1),
limit: Some(200),
};
let (paginated_users, total_count) = store.list_users_paginated(&params).await.unwrap();
assert_eq!(total_count, 8, "总用户数应该是8");
assert_eq!(paginated_users.len(), 8, "应该返回所有8个用户限制为100");
// 测试限制为0应该被修正为1
let params = PaginationParams {
page: Some(1),
limit: Some(0),
};
let (paginated_users, total_count) = store.list_users_paginated(&params).await.unwrap();
assert_eq!(total_count, 8, "总用户数应该是8");
assert_eq!(paginated_users.len(), 1, "限制0应该被修正为1");
}
/// 测试空数据库的分页
#[tokio::test]
async fn test_pagination_empty_database() {
let store = MemoryUserStore::new();
let params = PaginationParams {
page: Some(1),
limit: Some(10),
};
let (paginated_users, total_count) = store.list_users_paginated(&params).await.unwrap();
assert_eq!(total_count, 0, "空数据库总用户数应该是0");
assert_eq!(paginated_users.len(), 0, "空数据库应该返回空列表");
}
/// 测试搜索功能的分页
#[tokio::test]
async fn test_search_pagination() {
let store = MemoryUserStore::new();
// 创建用户,其中一些包含"admin"
let users = vec![
create_test_user("admin1", "admin1@example.com"),
create_test_user("user1", "user1@example.com"),
create_test_user("admin2", "admin2@example.com"),
create_test_user("user2", "user2@example.com"),
create_test_user("admin3", "admin3@example.com"),
create_test_user("user3", "user3@example.com"),
];
for user in users {
store.create_user(user).await.unwrap();
}
// 搜索包含"admin"的用户,第一页
let search_params = UserSearchParams {
q: Some("admin".to_string()),
..Default::default()
};
let pagination_params = PaginationParams {
page: Some(1),
limit: Some(2),
};
let (search_results, total_count) = store.search_users(&search_params, &pagination_params).await.unwrap();
assert_eq!(total_count, 3, "应该找到3个admin用户");
assert_eq!(search_results.len(), 2, "第一页应该返回2个用户");
// 验证搜索结果
for user in &search_results {
assert!(
user.username.contains("admin") || user.email.contains("admin"),
"搜索结果应该包含admin关键词"
);
}
// 搜索第二页
let pagination_params = PaginationParams {
page: Some(2),
limit: Some(2),
};
let (search_results, total_count) = store.search_users(&search_params, &pagination_params).await.unwrap();
assert_eq!(total_count, 3, "总数应该仍然是3");
assert_eq!(search_results.len(), 1, "第二页应该返回1个用户");
}
/// 测试PaginatedResponse结构
#[tokio::test]
async fn test_paginated_response_structure() {
let store = MemoryUserStore::new();
// 创建7个用户
create_multiple_users(&store, 7).await;
let params = PaginationParams {
page: Some(2),
limit: Some(3),
};
let (users, total_count) = store.list_users_paginated(&params).await.unwrap();
let response = PaginatedResponse::new(
users.into_iter().map(|u| u.username).collect(),
&params,
total_count
);
// 验证分页信息
assert_eq!(response.pagination.current_page, 2);
assert_eq!(response.pagination.per_page, 3);
assert_eq!(response.pagination.total_pages, 3); // 7个用户每页3个共3页
assert_eq!(response.pagination.total_items, 7);
assert!(response.pagination.has_prev, "第二页应该有上一页");
assert!(response.pagination.has_next, "第二页应该有下一页");
// 测试第一页
let params = PaginationParams {
page: Some(1),
limit: Some(3),
};
let (users, total_count) = store.list_users_paginated(&params).await.unwrap();
let response = PaginatedResponse::new(
users.into_iter().map(|u| u.username).collect(),
&params,
total_count
);
assert!(!response.pagination.has_prev, "第一页不应该有上一页");
assert!(response.pagination.has_next, "第一页应该有下一页");
// 测试最后一页
let params = PaginationParams {
page: Some(3),
limit: Some(3),
};
let (users, total_count) = store.list_users_paginated(&params).await.unwrap();
let response = PaginatedResponse::new(
users.into_iter().map(|u| u.username).collect(),
&params,
total_count
);
assert!(response.pagination.has_prev, "最后一页应该有上一页");
assert!(!response.pagination.has_next, "最后一页不应该有下一页");
assert_eq!(response.data.len(), 1, "最后一页应该有1个用户");
}
/// 测试分页功能的性能(大数据量)
#[tokio::test]
async fn test_pagination_performance() {
let store = MemoryUserStore::new();
// 创建100个用户
create_multiple_users(&store, 100).await;
let start = std::time::Instant::now();
// 测试获取中间页面的性能
let params = PaginationParams {
page: Some(50),
limit: Some(2),
};
let (paginated_users, total_count) = store.list_users_paginated(&params).await.unwrap();
let duration = start.elapsed();
assert_eq!(total_count, 100, "总用户数应该是100");
assert_eq!(paginated_users.len(), 2, "应该返回2个用户");
// 性能检查应该在合理时间内完成这里设置为100ms实际应该更快
assert!(duration.as_millis() < 100, "分页查询应该在100ms内完成实际用时: {:?}", duration);
}