新功能: - 项目-模板绑定管理系统 - 支持主要/次要模板绑定类型 - 绑定优先级和状态管理 - 批量绑定操作 - 绑定关系的CRUD操作 - 素材-模特绑定管理系统 - 素材与模特的关联管理 - 批量绑定/解绑操作 - 绑定统计和分析 - 素材编辑对话框 架构改进: - 新增项目-模板绑定数据模型和仓库层 - 新增素材-模特绑定业务服务层 - 完善的API命令层实现 - 响应式前端界面设计 用户体验优化: - 统一的通知系统 - 增强的加载状态组件 - 流畅的交互动画 - 优雅的确认对话框 测试覆盖: - 单元测试和集成测试 - 业务逻辑验证 - API接口测试 技术栈: - 后端: Rust + Tauri + SQLite - 前端: React + TypeScript + TailwindCSS - 状态管理: Zustand - 测试: Vitest + Rust测试框架 配置更新: - 更新数据库迁移脚本 - 完善测试配置 - 优化构建流程
380 lines
14 KiB
Rust
380 lines
14 KiB
Rust
/**
|
|
* 项目-模板绑定功能测试
|
|
* 遵循 Tauri 开发规范的测试设计原则
|
|
*/
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use crate::data::models::project_template_binding::{
|
|
ProjectTemplateBinding, BindingType, BindingStatus,
|
|
CreateProjectTemplateBindingRequest, UpdateProjectTemplateBindingRequest,
|
|
ProjectTemplateBindingQueryParams, BatchCreateProjectTemplateBindingRequest,
|
|
};
|
|
use crate::data::repositories::project_template_binding_repository::ProjectTemplateBindingRepository;
|
|
use crate::business::services::project_template_binding_service::ProjectTemplateBindingService;
|
|
use crate::infrastructure::database::Database;
|
|
use std::sync::{Arc, Mutex};
|
|
use uuid::Uuid;
|
|
|
|
/// 创建测试数据库连接
|
|
fn create_test_database() -> Arc<Mutex<rusqlite::Connection>> {
|
|
let conn = rusqlite::Connection::open_in_memory().unwrap();
|
|
|
|
// 创建项目-模板绑定表
|
|
conn.execute(
|
|
"CREATE TABLE project_template_bindings (
|
|
id TEXT PRIMARY KEY,
|
|
project_id TEXT NOT NULL,
|
|
template_id TEXT NOT NULL,
|
|
binding_name TEXT,
|
|
description TEXT,
|
|
priority INTEGER DEFAULT 0,
|
|
is_active INTEGER DEFAULT 1,
|
|
binding_type TEXT NOT NULL,
|
|
binding_status TEXT NOT NULL,
|
|
metadata TEXT,
|
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
UNIQUE (project_id, template_id)
|
|
)",
|
|
[],
|
|
).unwrap();
|
|
|
|
Arc::new(Mutex::new(conn))
|
|
}
|
|
|
|
/// 创建测试绑定请求
|
|
fn create_test_binding_request() -> CreateProjectTemplateBindingRequest {
|
|
CreateProjectTemplateBindingRequest {
|
|
project_id: Uuid::new_v4().to_string(),
|
|
template_id: Uuid::new_v4().to_string(),
|
|
binding_name: Some("测试绑定".to_string()),
|
|
description: Some("这是一个测试绑定".to_string()),
|
|
priority: Some(1),
|
|
binding_type: BindingType::Primary,
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_create_project_template_binding() {
|
|
let conn = create_test_database();
|
|
let repository = ProjectTemplateBindingRepository::new(conn);
|
|
let request = create_test_binding_request();
|
|
|
|
let result = repository.create(request.clone());
|
|
assert!(result.is_ok());
|
|
|
|
let binding = result.unwrap();
|
|
assert_eq!(binding.project_id, request.project_id);
|
|
assert_eq!(binding.template_id, request.template_id);
|
|
assert_eq!(binding.binding_name, request.binding_name);
|
|
assert_eq!(binding.description, request.description);
|
|
assert_eq!(binding.priority, request.priority.unwrap());
|
|
assert_eq!(binding.binding_type, request.binding_type);
|
|
assert_eq!(binding.binding_status, BindingStatus::Active);
|
|
assert!(binding.is_active);
|
|
}
|
|
|
|
#[test]
|
|
fn test_get_binding_by_id() {
|
|
let conn = create_test_database();
|
|
let repository = ProjectTemplateBindingRepository::new(conn);
|
|
let request = create_test_binding_request();
|
|
|
|
// 创建绑定
|
|
let created_binding = repository.create(request).unwrap();
|
|
|
|
// 获取绑定
|
|
let result = repository.get_by_id(&created_binding.id);
|
|
assert!(result.is_ok());
|
|
|
|
let binding = result.unwrap();
|
|
assert!(binding.is_some());
|
|
let binding = binding.unwrap();
|
|
assert_eq!(binding.id, created_binding.id);
|
|
assert_eq!(binding.project_id, created_binding.project_id);
|
|
assert_eq!(binding.template_id, created_binding.template_id);
|
|
}
|
|
|
|
#[test]
|
|
fn test_update_binding() {
|
|
let conn = create_test_database();
|
|
let repository = ProjectTemplateBindingRepository::new(conn);
|
|
let request = create_test_binding_request();
|
|
|
|
// 创建绑定
|
|
let created_binding = repository.create(request).unwrap();
|
|
|
|
// 更新绑定
|
|
let update_request = UpdateProjectTemplateBindingRequest {
|
|
binding_name: Some("更新后的绑定名称".to_string()),
|
|
description: Some("更新后的描述".to_string()),
|
|
priority: Some(5),
|
|
binding_type: Some(BindingType::Secondary),
|
|
binding_status: Some(BindingStatus::Paused),
|
|
is_active: Some(false),
|
|
};
|
|
|
|
let result = repository.update(&created_binding.id, update_request);
|
|
assert!(result.is_ok());
|
|
|
|
let updated_binding = result.unwrap();
|
|
assert!(updated_binding.is_some());
|
|
let updated_binding = updated_binding.unwrap();
|
|
assert_eq!(updated_binding.binding_name, Some("更新后的绑定名称".to_string()));
|
|
assert_eq!(updated_binding.description, Some("更新后的描述".to_string()));
|
|
assert_eq!(updated_binding.priority, 5);
|
|
assert_eq!(updated_binding.binding_type, BindingType::Secondary);
|
|
assert_eq!(updated_binding.binding_status, BindingStatus::Paused);
|
|
assert!(!updated_binding.is_active);
|
|
}
|
|
|
|
#[test]
|
|
fn test_delete_binding() {
|
|
let conn = create_test_database();
|
|
let repository = ProjectTemplateBindingRepository::new(conn);
|
|
let request = create_test_binding_request();
|
|
|
|
// 创建绑定
|
|
let created_binding = repository.create(request).unwrap();
|
|
|
|
// 删除绑定
|
|
let result = repository.delete(&created_binding.id);
|
|
assert!(result.is_ok());
|
|
assert!(result.unwrap());
|
|
|
|
// 验证绑定已被删除
|
|
let get_result = repository.get_by_id(&created_binding.id);
|
|
assert!(get_result.is_ok());
|
|
assert!(get_result.unwrap().is_none());
|
|
}
|
|
|
|
#[test]
|
|
fn test_list_bindings_with_filters() {
|
|
let conn = create_test_database();
|
|
let repository = ProjectTemplateBindingRepository::new(conn);
|
|
let project_id = Uuid::new_v4().to_string();
|
|
|
|
// 创建多个绑定
|
|
let requests = vec![
|
|
CreateProjectTemplateBindingRequest {
|
|
project_id: project_id.clone(),
|
|
template_id: Uuid::new_v4().to_string(),
|
|
binding_name: Some("主要绑定".to_string()),
|
|
description: None,
|
|
priority: Some(0),
|
|
binding_type: BindingType::Primary,
|
|
},
|
|
CreateProjectTemplateBindingRequest {
|
|
project_id: project_id.clone(),
|
|
template_id: Uuid::new_v4().to_string(),
|
|
binding_name: Some("次要绑定".to_string()),
|
|
description: None,
|
|
priority: Some(1),
|
|
binding_type: BindingType::Secondary,
|
|
},
|
|
CreateProjectTemplateBindingRequest {
|
|
project_id: Uuid::new_v4().to_string(), // 不同项目
|
|
template_id: Uuid::new_v4().to_string(),
|
|
binding_name: Some("其他项目绑定".to_string()),
|
|
description: None,
|
|
priority: Some(0),
|
|
binding_type: BindingType::Primary,
|
|
},
|
|
];
|
|
|
|
for request in requests {
|
|
repository.create(request).unwrap();
|
|
}
|
|
|
|
// 测试按项目ID过滤
|
|
let query_params = ProjectTemplateBindingQueryParams {
|
|
project_id: Some(project_id.clone()),
|
|
template_id: None,
|
|
binding_type: None,
|
|
binding_status: None,
|
|
is_active: None,
|
|
limit: None,
|
|
offset: None,
|
|
};
|
|
|
|
let result = repository.list(query_params);
|
|
assert!(result.is_ok());
|
|
let bindings = result.unwrap();
|
|
assert_eq!(bindings.len(), 2);
|
|
|
|
// 验证排序(按优先级排序)
|
|
assert_eq!(bindings[0].priority, 0);
|
|
assert_eq!(bindings[1].priority, 1);
|
|
|
|
// 测试按绑定类型过滤
|
|
let query_params = ProjectTemplateBindingQueryParams {
|
|
project_id: Some(project_id),
|
|
template_id: None,
|
|
binding_type: Some(BindingType::Primary),
|
|
binding_status: None,
|
|
is_active: None,
|
|
limit: None,
|
|
offset: None,
|
|
};
|
|
|
|
let result = repository.list(query_params);
|
|
assert!(result.is_ok());
|
|
let bindings = result.unwrap();
|
|
assert_eq!(bindings.len(), 1);
|
|
assert_eq!(bindings[0].binding_type, BindingType::Primary);
|
|
}
|
|
|
|
#[test]
|
|
fn test_batch_create_bindings() {
|
|
let conn = create_test_database();
|
|
let repository = ProjectTemplateBindingRepository::new(conn);
|
|
let project_id = Uuid::new_v4().to_string();
|
|
let template_ids = vec![
|
|
Uuid::new_v4().to_string(),
|
|
Uuid::new_v4().to_string(),
|
|
Uuid::new_v4().to_string(),
|
|
];
|
|
|
|
let request = BatchCreateProjectTemplateBindingRequest {
|
|
project_id: project_id.clone(),
|
|
template_ids: template_ids.clone(),
|
|
binding_type: BindingType::Secondary,
|
|
priority_start: Some(10),
|
|
};
|
|
|
|
let result = repository.batch_create(request);
|
|
assert!(result.is_ok());
|
|
let bindings = result.unwrap();
|
|
assert_eq!(bindings.len(), 3);
|
|
|
|
// 验证优先级递增
|
|
for (i, binding) in bindings.iter().enumerate() {
|
|
assert_eq!(binding.project_id, project_id);
|
|
assert_eq!(binding.template_id, template_ids[i]);
|
|
assert_eq!(binding.binding_type, BindingType::Secondary);
|
|
assert_eq!(binding.priority, 10 + i as u32);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_binding_exists() {
|
|
let conn = create_test_database();
|
|
let repository = ProjectTemplateBindingRepository::new(conn);
|
|
let request = create_test_binding_request();
|
|
|
|
// 绑定不存在时
|
|
let result = repository.exists(&request.project_id, &request.template_id);
|
|
assert!(result.is_ok());
|
|
assert!(!result.unwrap());
|
|
|
|
// 创建绑定
|
|
repository.create(request.clone()).unwrap();
|
|
|
|
// 绑定存在时
|
|
let result = repository.exists(&request.project_id, &request.template_id);
|
|
assert!(result.is_ok());
|
|
assert!(result.unwrap());
|
|
}
|
|
|
|
#[test]
|
|
fn test_unique_constraint() {
|
|
let conn = create_test_database();
|
|
let repository = ProjectTemplateBindingRepository::new(conn);
|
|
let request = create_test_binding_request();
|
|
|
|
// 第一次创建应该成功
|
|
let result1 = repository.create(request.clone());
|
|
assert!(result1.is_ok());
|
|
|
|
// 第二次创建相同的项目-模板绑定应该失败
|
|
let result2 = repository.create(request);
|
|
assert!(result2.is_err());
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_business_service_create_binding() {
|
|
let conn = create_test_database();
|
|
let service = ProjectTemplateBindingService::new(conn).unwrap();
|
|
let request = create_test_binding_request();
|
|
|
|
let result = service.create_binding(request.clone()).await;
|
|
assert!(result.is_ok());
|
|
|
|
let binding = result.unwrap();
|
|
assert_eq!(binding.project_id, request.project_id);
|
|
assert_eq!(binding.template_id, request.template_id);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_business_service_prevent_duplicate_primary() {
|
|
let conn = create_test_database();
|
|
let service = ProjectTemplateBindingService::new(conn).unwrap();
|
|
let project_id = Uuid::new_v4().to_string();
|
|
|
|
// 创建第一个主要绑定
|
|
let request1 = CreateProjectTemplateBindingRequest {
|
|
project_id: project_id.clone(),
|
|
template_id: Uuid::new_v4().to_string(),
|
|
binding_name: Some("第一个主要绑定".to_string()),
|
|
description: None,
|
|
priority: Some(0),
|
|
binding_type: BindingType::Primary,
|
|
};
|
|
|
|
let result1 = service.create_binding(request1).await;
|
|
assert!(result1.is_ok());
|
|
|
|
// 尝试创建第二个主要绑定应该失败
|
|
let request2 = CreateProjectTemplateBindingRequest {
|
|
project_id: project_id.clone(),
|
|
template_id: Uuid::new_v4().to_string(),
|
|
binding_name: Some("第二个主要绑定".to_string()),
|
|
description: None,
|
|
priority: Some(0),
|
|
binding_type: BindingType::Primary,
|
|
};
|
|
|
|
let result2 = service.create_binding(request2).await;
|
|
assert!(result2.is_err());
|
|
}
|
|
|
|
#[test]
|
|
fn test_binding_validation() {
|
|
let mut binding = ProjectTemplateBinding::new(
|
|
"project_id".to_string(),
|
|
"template_id".to_string(),
|
|
BindingType::Primary,
|
|
);
|
|
|
|
// 有效的绑定应该通过验证
|
|
assert!(binding.validate().is_ok());
|
|
|
|
// 空的项目ID应该失败
|
|
binding.project_id = "".to_string();
|
|
assert!(binding.validate().is_err());
|
|
|
|
// 重置项目ID
|
|
binding.project_id = "project_id".to_string();
|
|
|
|
// 空的模板ID应该失败
|
|
binding.template_id = "".to_string();
|
|
assert!(binding.validate().is_err());
|
|
|
|
// 重置模板ID
|
|
binding.template_id = "template_id".to_string();
|
|
|
|
// 过长的绑定名称应该失败
|
|
binding.binding_name = Some("a".repeat(101));
|
|
assert!(binding.validate().is_err());
|
|
|
|
// 重置绑定名称
|
|
binding.binding_name = Some("正常名称".to_string());
|
|
|
|
// 过长的描述应该失败
|
|
binding.description = Some("a".repeat(501));
|
|
assert!(binding.validate().is_err());
|
|
}
|
|
}
|