feat: 实现数据库迁移、搜索和分页功能
- 添加数据库迁移系统和初始用户表迁移 - 实现搜索功能模块和API - 实现分页功能支持 - 添加相关测试文件 - 更新项目配置和文档
This commit is contained in:
275
tests/database_tests.rs
Normal file
275
tests/database_tests.rs
Normal file
@@ -0,0 +1,275 @@
|
||||
//! SQLite 数据库存储测试
|
||||
|
||||
use rust_user_api::{
|
||||
models::user::User,
|
||||
storage::{database::DatabaseUserStore, UserStore},
|
||||
utils::errors::ApiError,
|
||||
};
|
||||
use uuid::Uuid;
|
||||
use chrono::Utc;
|
||||
use tempfile::tempdir;
|
||||
|
||||
/// 创建临时数据库用于测试
|
||||
async fn create_test_database() -> Result<DatabaseUserStore, ApiError> {
|
||||
// 使用内存数据库避免文件系统问题
|
||||
let database_url = "sqlite::memory:";
|
||||
DatabaseUserStore::from_url(database_url).await
|
||||
}
|
||||
|
||||
/// 创建测试用户
|
||||
fn create_test_user() -> User {
|
||||
User {
|
||||
id: Uuid::new_v4(),
|
||||
username: "testuser".to_string(),
|
||||
email: "test@example.com".to_string(),
|
||||
password_hash: "hashed_password".to_string(),
|
||||
created_at: Utc::now(),
|
||||
updated_at: Utc::now(),
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_database_connection_and_initialization() {
|
||||
let store = create_test_database().await;
|
||||
assert!(store.is_ok(), "Failed to create database store");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_database_create_user() {
|
||||
let store = create_test_database().await.unwrap();
|
||||
let user = create_test_user();
|
||||
|
||||
let result = store.create_user(user.clone()).await;
|
||||
assert!(result.is_ok(), "Failed to create user: {:?}", result.err());
|
||||
|
||||
let created_user = result.unwrap();
|
||||
assert_eq!(created_user.username, user.username);
|
||||
assert_eq!(created_user.email, user.email);
|
||||
assert_eq!(created_user.id, user.id);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_database_get_user_by_id() {
|
||||
let store = create_test_database().await.unwrap();
|
||||
let user = create_test_user();
|
||||
let user_id = user.id;
|
||||
|
||||
// 先创建用户
|
||||
store.create_user(user.clone()).await.unwrap();
|
||||
|
||||
// 然后获取用户
|
||||
let result = store.get_user(&user_id).await;
|
||||
assert!(result.is_ok(), "Failed to get user: {:?}", result.err());
|
||||
|
||||
let retrieved_user = result.unwrap();
|
||||
assert!(retrieved_user.is_some(), "User not found");
|
||||
|
||||
let retrieved_user = retrieved_user.unwrap();
|
||||
assert_eq!(retrieved_user.id, user_id);
|
||||
assert_eq!(retrieved_user.username, user.username);
|
||||
assert_eq!(retrieved_user.email, user.email);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_database_get_user_by_username() {
|
||||
let store = create_test_database().await.unwrap();
|
||||
let user = create_test_user();
|
||||
let username = user.username.clone();
|
||||
|
||||
// 先创建用户
|
||||
store.create_user(user.clone()).await.unwrap();
|
||||
|
||||
// 然后按用户名获取用户
|
||||
let result = store.get_user_by_username(&username).await;
|
||||
assert!(result.is_ok(), "Failed to get user by username: {:?}", result.err());
|
||||
|
||||
let retrieved_user = result.unwrap();
|
||||
assert!(retrieved_user.is_some(), "User not found by username");
|
||||
|
||||
let retrieved_user = retrieved_user.unwrap();
|
||||
assert_eq!(retrieved_user.username, username);
|
||||
assert_eq!(retrieved_user.id, user.id);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_database_list_users() {
|
||||
let store = create_test_database().await.unwrap();
|
||||
|
||||
// 创建多个用户
|
||||
let user1 = User {
|
||||
id: Uuid::new_v4(),
|
||||
username: "user1".to_string(),
|
||||
email: "user1@example.com".to_string(),
|
||||
password_hash: "hashed_password1".to_string(),
|
||||
created_at: Utc::now(),
|
||||
updated_at: Utc::now(),
|
||||
};
|
||||
|
||||
let user2 = User {
|
||||
id: Uuid::new_v4(),
|
||||
username: "user2".to_string(),
|
||||
email: "user2@example.com".to_string(),
|
||||
password_hash: "hashed_password2".to_string(),
|
||||
created_at: Utc::now(),
|
||||
updated_at: Utc::now(),
|
||||
};
|
||||
|
||||
store.create_user(user1.clone()).await.unwrap();
|
||||
store.create_user(user2.clone()).await.unwrap();
|
||||
|
||||
// 获取所有用户
|
||||
let result = store.list_users().await;
|
||||
assert!(result.is_ok(), "Failed to list users: {:?}", result.err());
|
||||
|
||||
let users = result.unwrap();
|
||||
assert_eq!(users.len(), 2, "Expected 2 users, got {}", users.len());
|
||||
|
||||
// 验证用户存在
|
||||
let usernames: Vec<String> = users.iter().map(|u| u.username.clone()).collect();
|
||||
assert!(usernames.contains(&"user1".to_string()));
|
||||
assert!(usernames.contains(&"user2".to_string()));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_database_update_user() {
|
||||
let store = create_test_database().await.unwrap();
|
||||
let mut user = create_test_user();
|
||||
let user_id = user.id;
|
||||
|
||||
// 先创建用户
|
||||
store.create_user(user.clone()).await.unwrap();
|
||||
|
||||
// 更新用户信息
|
||||
user.username = "updated_user".to_string();
|
||||
user.email = "updated@example.com".to_string();
|
||||
user.updated_at = Utc::now();
|
||||
|
||||
let result = store.update_user(&user_id, user.clone()).await;
|
||||
assert!(result.is_ok(), "Failed to update user: {:?}", result.err());
|
||||
|
||||
let updated_user = result.unwrap();
|
||||
assert!(updated_user.is_some(), "Updated user not returned");
|
||||
|
||||
let updated_user = updated_user.unwrap();
|
||||
assert_eq!(updated_user.username, "updated_user");
|
||||
assert_eq!(updated_user.email, "updated@example.com");
|
||||
|
||||
// 验证数据库中的用户确实被更新了
|
||||
let retrieved_user = store.get_user(&user_id).await.unwrap().unwrap();
|
||||
assert_eq!(retrieved_user.username, "updated_user");
|
||||
assert_eq!(retrieved_user.email, "updated@example.com");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_database_delete_user() {
|
||||
let store = create_test_database().await.unwrap();
|
||||
let user = create_test_user();
|
||||
let user_id = user.id;
|
||||
|
||||
// 先创建用户
|
||||
store.create_user(user.clone()).await.unwrap();
|
||||
|
||||
// 验证用户存在
|
||||
let user_exists = store.get_user(&user_id).await.unwrap();
|
||||
assert!(user_exists.is_some(), "User should exist before deletion");
|
||||
|
||||
// 删除用户
|
||||
let result = store.delete_user(&user_id).await;
|
||||
assert!(result.is_ok(), "Failed to delete user: {:?}", result.err());
|
||||
assert!(result.unwrap(), "Delete operation should return true");
|
||||
|
||||
// 验证用户已被删除
|
||||
let user_after_delete = store.get_user(&user_id).await.unwrap();
|
||||
assert!(user_after_delete.is_none(), "User should not exist after deletion");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_database_duplicate_username_constraint() {
|
||||
let store = create_test_database().await.unwrap();
|
||||
|
||||
let user1 = User {
|
||||
id: Uuid::new_v4(),
|
||||
username: "duplicate_test".to_string(),
|
||||
email: "test1@example.com".to_string(),
|
||||
password_hash: "hashed_password1".to_string(),
|
||||
created_at: Utc::now(),
|
||||
updated_at: Utc::now(),
|
||||
};
|
||||
|
||||
let user2 = User {
|
||||
id: Uuid::new_v4(),
|
||||
username: "duplicate_test".to_string(), // 相同用户名
|
||||
email: "test2@example.com".to_string(),
|
||||
password_hash: "hashed_password2".to_string(),
|
||||
created_at: Utc::now(),
|
||||
updated_at: Utc::now(),
|
||||
};
|
||||
|
||||
// 创建第一个用户应该成功
|
||||
let result1 = store.create_user(user1).await;
|
||||
assert!(result1.is_ok(), "First user creation should succeed");
|
||||
|
||||
// 创建第二个用户应该失败(用户名重复)
|
||||
let result2 = store.create_user(user2).await;
|
||||
assert!(result2.is_err(), "Second user creation should fail due to duplicate username");
|
||||
|
||||
if let Err(ApiError::Conflict(msg)) = result2 {
|
||||
assert!(msg.contains("用户名已存在"), "Error message should mention duplicate username");
|
||||
} else {
|
||||
panic!("Expected Conflict error for duplicate username, got: {:?}", result2);
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_database_nonexistent_user_operations() {
|
||||
let store = create_test_database().await.unwrap();
|
||||
let nonexistent_id = Uuid::new_v4();
|
||||
|
||||
// 获取不存在的用户
|
||||
let result = store.get_user(&nonexistent_id).await;
|
||||
assert!(result.is_ok(), "Getting nonexistent user should not error");
|
||||
assert!(result.unwrap().is_none(), "Nonexistent user should return None");
|
||||
|
||||
// 按用户名获取不存在的用户
|
||||
let result = store.get_user_by_username("nonexistent_user").await;
|
||||
assert!(result.is_ok(), "Getting nonexistent user by username should not error");
|
||||
assert!(result.unwrap().is_none(), "Nonexistent user should return None");
|
||||
|
||||
// 更新不存在的用户
|
||||
let fake_user = create_test_user();
|
||||
let result = store.update_user(&nonexistent_id, fake_user).await;
|
||||
assert!(result.is_ok(), "Updating nonexistent user should not error");
|
||||
assert!(result.unwrap().is_none(), "Updating nonexistent user should return None");
|
||||
|
||||
// 删除不存在的用户
|
||||
let result = store.delete_user(&nonexistent_id).await;
|
||||
assert!(result.is_ok(), "Deleting nonexistent user should not error");
|
||||
assert!(!result.unwrap(), "Deleting nonexistent user should return false");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_database_data_persistence() {
|
||||
let temp_dir = tempdir().expect("Failed to create temp directory");
|
||||
let db_path = temp_dir.path().join("persistence_test.db");
|
||||
let database_url = format!("sqlite://{}?mode=rwc", db_path.display());
|
||||
|
||||
let user = create_test_user();
|
||||
let user_id = user.id;
|
||||
|
||||
// 第一次连接:创建用户
|
||||
{
|
||||
let store = DatabaseUserStore::from_url(&database_url).await.unwrap();
|
||||
store.create_user(user.clone()).await.unwrap();
|
||||
}
|
||||
|
||||
// 第二次连接:验证用户仍然存在
|
||||
{
|
||||
let store = DatabaseUserStore::from_url(&database_url).await.unwrap();
|
||||
let retrieved_user = store.get_user(&user_id).await.unwrap();
|
||||
assert!(retrieved_user.is_some(), "User should persist across connections");
|
||||
|
||||
let retrieved_user = retrieved_user.unwrap();
|
||||
assert_eq!(retrieved_user.username, user.username);
|
||||
assert_eq!(retrieved_user.email, user.email);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user