297 lines
12 KiB
Rust
297 lines
12 KiB
Rust
/**
|
||
* 服装搭配功能集成测试
|
||
* 测试从图像分析到搭配推荐的完整流程
|
||
*/
|
||
|
||
#[cfg(test)]
|
||
mod outfit_integration_tests {
|
||
use std::sync::Arc;
|
||
use chrono::Utc;
|
||
|
||
use crate::data::models::outfit_analysis::{
|
||
OutfitAnalysis, CreateOutfitAnalysisRequest, AnalysisStatus, ColorHSV
|
||
};
|
||
use crate::data::models::outfit_item::{
|
||
OutfitItem, CreateOutfitItemRequest, OutfitCategory, OutfitStyle
|
||
};
|
||
use crate::data::models::outfit_matching::{
|
||
OutfitMatching, CreateOutfitMatchingRequest, MatchingType
|
||
};
|
||
use crate::data::repositories::outfit_analysis_repository::OutfitAnalysisRepository;
|
||
use crate::data::repositories::outfit_item_repository::OutfitItemRepository;
|
||
use crate::data::repositories::outfit_matching_repository::OutfitMatchingRepository;
|
||
use crate::business::services::outfit_analysis_service::OutfitAnalysisService;
|
||
use crate::business::services::outfit_item_service::OutfitItemService;
|
||
use crate::business::services::outfit_matching_service::OutfitMatchingService;
|
||
use crate::infrastructure::database::Database;
|
||
use crate::infrastructure::gemini_service::GeminiService;
|
||
|
||
/// 测试完整的服装分析流程
|
||
#[tokio::test]
|
||
async fn test_complete_outfit_analysis_workflow() {
|
||
// 创建内存数据库
|
||
let database = Arc::new(Database::new_in_memory().unwrap());
|
||
|
||
// 创建仓库
|
||
let analysis_repo = Arc::new(OutfitAnalysisRepository::new(database.clone()).unwrap());
|
||
let item_repo = Arc::new(OutfitItemRepository::new(database.clone()).unwrap());
|
||
let matching_repo = Arc::new(OutfitMatchingRepository::new(database.clone()).unwrap());
|
||
|
||
// 创建服务(注意:在实际测试中,应该使用模拟的GeminiService)
|
||
let gemini_service = Arc::new(GeminiService::new().unwrap());
|
||
let analysis_service = OutfitAnalysisService::new(analysis_repo.clone(), gemini_service);
|
||
let item_service = OutfitItemService::new(item_repo.clone());
|
||
let matching_service = OutfitMatchingService::new(matching_repo.clone(), item_repo.clone());
|
||
|
||
// 1. 创建分析记录
|
||
let analysis_request = CreateOutfitAnalysisRequest {
|
||
project_id: "test_project".to_string(),
|
||
image_path: "/test/image.jpg".to_string(),
|
||
image_name: "test_image.jpg".to_string(),
|
||
};
|
||
|
||
let analysis = analysis_service.create_analysis(analysis_request).await.unwrap();
|
||
assert_eq!(analysis.analysis_status, AnalysisStatus::Pending);
|
||
|
||
// 2. 模拟分析完成,手动创建服装单品
|
||
let item1_request = CreateOutfitItemRequest {
|
||
project_id: "test_project".to_string(),
|
||
analysis_id: Some(analysis.id.clone()),
|
||
name: "红色T恤".to_string(),
|
||
category: OutfitCategory::Top,
|
||
brand: Some("测试品牌".to_string()),
|
||
model: None,
|
||
color_primary: ColorHSV::new(0.0, 0.8, 0.9), // 红色
|
||
color_secondary: None,
|
||
styles: vec![OutfitStyle::Casual],
|
||
design_elements: vec!["纯色".to_string()],
|
||
size: None,
|
||
material: None,
|
||
price: Some(99.0),
|
||
purchase_date: None,
|
||
image_urls: vec![],
|
||
tags: vec!["夏季".to_string(), "休闲".to_string()],
|
||
notes: Some("舒适的棉质T恤".to_string()),
|
||
};
|
||
|
||
let item1 = item_service.create_item(item1_request).await.unwrap();
|
||
assert_eq!(item1.category, OutfitCategory::Top);
|
||
|
||
let item2_request = CreateOutfitItemRequest {
|
||
project_id: "test_project".to_string(),
|
||
analysis_id: Some(analysis.id.clone()),
|
||
name: "蓝色牛仔裤".to_string(),
|
||
category: OutfitCategory::Bottom,
|
||
brand: Some("测试品牌".to_string()),
|
||
model: None,
|
||
color_primary: ColorHSV::new(0.6, 0.7, 0.8), // 蓝色
|
||
color_secondary: None,
|
||
styles: vec![OutfitStyle::Casual],
|
||
design_elements: vec!["水洗".to_string()],
|
||
size: None,
|
||
material: None,
|
||
price: Some(199.0),
|
||
purchase_date: None,
|
||
image_urls: vec![],
|
||
tags: vec!["四季".to_string(), "百搭".to_string()],
|
||
notes: Some("经典款牛仔裤".to_string()),
|
||
};
|
||
|
||
let item2 = item_service.create_item(item2_request).await.unwrap();
|
||
assert_eq!(item2.category, OutfitCategory::Bottom);
|
||
|
||
// 3. 创建搭配
|
||
let matching_request = CreateOutfitMatchingRequest {
|
||
project_id: "test_project".to_string(),
|
||
matching_name: "休闲夏日搭配".to_string(),
|
||
matching_type: MatchingType::StyleConsistent,
|
||
item_ids: vec![item1.id.clone(), item2.id.clone()],
|
||
occasion_tags: vec!["日常".to_string(), "休闲".to_string()],
|
||
season_tags: vec!["夏季".to_string()],
|
||
style_description: Some("轻松休闲的夏日搭配".to_string()),
|
||
};
|
||
|
||
let matching = matching_service.create_matching(matching_request).await.unwrap();
|
||
assert_eq!(matching.matching_type, MatchingType::StyleConsistent);
|
||
assert_eq!(matching.items.len(), 2);
|
||
|
||
// 4. 验证搭配评分
|
||
assert!(matching.score_details.overall_score >= 0.0);
|
||
assert!(matching.score_details.overall_score <= 1.0);
|
||
|
||
// 5. 测试搭配推荐
|
||
let recommended_items = item_service.recommend_matching_items(
|
||
"test_project",
|
||
&item1.id,
|
||
Some(3)
|
||
).await.unwrap();
|
||
|
||
// 应该推荐item2,因为它与item1风格匹配且类别互补
|
||
assert!(recommended_items.iter().any(|item| item.id == item2.id));
|
||
|
||
println!("✅ 服装分析完整流程测试通过");
|
||
}
|
||
|
||
/// 测试颜色匹配功能
|
||
#[tokio::test]
|
||
async fn test_color_matching_functionality() {
|
||
let database = Arc::new(Database::new_in_memory().unwrap());
|
||
let item_repo = Arc::new(OutfitItemRepository::new(database.clone()).unwrap());
|
||
let item_service = OutfitItemService::new(item_repo.clone());
|
||
|
||
// 创建不同颜色的服装单品
|
||
let red_item = CreateOutfitItemRequest {
|
||
project_id: "test_project".to_string(),
|
||
analysis_id: None,
|
||
name: "红色上衣".to_string(),
|
||
category: OutfitCategory::Top,
|
||
brand: None,
|
||
model: None,
|
||
color_primary: ColorHSV::new(0.0, 0.8, 0.9), // 纯红色
|
||
color_secondary: None,
|
||
styles: vec![OutfitStyle::Casual],
|
||
design_elements: vec![],
|
||
size: None,
|
||
material: None,
|
||
price: None,
|
||
purchase_date: None,
|
||
image_urls: vec![],
|
||
tags: vec![],
|
||
notes: None,
|
||
};
|
||
|
||
let blue_item = CreateOutfitItemRequest {
|
||
project_id: "test_project".to_string(),
|
||
analysis_id: None,
|
||
name: "蓝色上衣".to_string(),
|
||
category: OutfitCategory::Top,
|
||
brand: None,
|
||
model: None,
|
||
color_primary: ColorHSV::new(0.6, 0.8, 0.9), // 纯蓝色
|
||
color_secondary: None,
|
||
styles: vec![OutfitStyle::Casual],
|
||
design_elements: vec![],
|
||
size: None,
|
||
material: None,
|
||
price: None,
|
||
purchase_date: None,
|
||
image_urls: vec![],
|
||
tags: vec![],
|
||
notes: None,
|
||
};
|
||
|
||
let similar_red_item = CreateOutfitItemRequest {
|
||
project_id: "test_project".to_string(),
|
||
analysis_id: None,
|
||
name: "深红色上衣".to_string(),
|
||
category: OutfitCategory::Top,
|
||
brand: None,
|
||
model: None,
|
||
color_primary: ColorHSV::new(0.02, 0.75, 0.85), // 相似的红色
|
||
color_secondary: None,
|
||
styles: vec![OutfitStyle::Casual],
|
||
design_elements: vec![],
|
||
size: None,
|
||
material: None,
|
||
price: None,
|
||
purchase_date: None,
|
||
image_urls: vec![],
|
||
tags: vec![],
|
||
notes: None,
|
||
};
|
||
|
||
// 创建服装单品
|
||
let red = item_service.create_item(red_item).await.unwrap();
|
||
let blue = item_service.create_item(blue_item).await.unwrap();
|
||
let similar_red = item_service.create_item(similar_red_item).await.unwrap();
|
||
|
||
// 测试颜色搜索
|
||
let target_color = ColorHSV::new(0.0, 0.8, 0.9); // 红色
|
||
let matching_items = item_service.search_by_color(
|
||
"test_project",
|
||
&target_color,
|
||
0.7 // 70% 相似度阈值
|
||
).await.unwrap();
|
||
|
||
// 应该找到红色和相似红色的单品,但不包括蓝色
|
||
assert!(matching_items.iter().any(|item| item.id == red.id));
|
||
assert!(matching_items.iter().any(|item| item.id == similar_red.id));
|
||
assert!(!matching_items.iter().any(|item| item.id == blue.id));
|
||
|
||
println!("✅ 颜色匹配功能测试通过");
|
||
}
|
||
|
||
/// 测试搭配评分算法
|
||
#[tokio::test]
|
||
async fn test_outfit_scoring_algorithm() {
|
||
let database = Arc::new(Database::new_in_memory().unwrap());
|
||
let matching_repo = Arc::new(OutfitMatchingRepository::new(database.clone()).unwrap());
|
||
let item_repo = Arc::new(OutfitItemRepository::new(database.clone()).unwrap());
|
||
let matching_service = OutfitMatchingService::new(matching_repo, item_repo);
|
||
|
||
// 创建测试用的服装单品
|
||
let items = vec![
|
||
OutfitItem {
|
||
id: "1".to_string(),
|
||
project_id: "test".to_string(),
|
||
analysis_id: None,
|
||
name: "白色T恤".to_string(),
|
||
category: OutfitCategory::Top,
|
||
brand: None,
|
||
model: None,
|
||
color_primary: ColorHSV::new(0.0, 0.0, 1.0), // 白色
|
||
color_secondary: None,
|
||
styles: vec![OutfitStyle::Casual, OutfitStyle::Minimalist],
|
||
design_elements: vec![],
|
||
size: None,
|
||
material: None,
|
||
price: None,
|
||
purchase_date: None,
|
||
image_urls: vec![],
|
||
tags: vec![],
|
||
notes: None,
|
||
created_at: Utc::now(),
|
||
updated_at: Utc::now(),
|
||
},
|
||
OutfitItem {
|
||
id: "2".to_string(),
|
||
project_id: "test".to_string(),
|
||
analysis_id: None,
|
||
name: "黑色裤子".to_string(),
|
||
category: OutfitCategory::Bottom,
|
||
brand: None,
|
||
model: None,
|
||
color_primary: ColorHSV::new(0.0, 0.0, 0.0), // 黑色
|
||
color_secondary: None,
|
||
styles: vec![OutfitStyle::Casual, OutfitStyle::Minimalist],
|
||
design_elements: vec![],
|
||
size: None,
|
||
material: None,
|
||
price: None,
|
||
purchase_date: None,
|
||
image_urls: vec![],
|
||
tags: vec![],
|
||
notes: None,
|
||
created_at: Utc::now(),
|
||
updated_at: Utc::now(),
|
||
},
|
||
];
|
||
|
||
// 测试评分计算
|
||
let (score_details, suggestions) = matching_service.calculate_matching_score_and_suggestions(&items);
|
||
|
||
// 验证评分范围
|
||
assert!(score_details.color_harmony_score >= 0.0 && score_details.color_harmony_score <= 1.0);
|
||
assert!(score_details.style_consistency_score >= 0.0 && score_details.style_consistency_score <= 1.0);
|
||
assert!(score_details.overall_score >= 0.0 && score_details.overall_score <= 1.0);
|
||
|
||
// 黑白搭配应该有较高的风格一致性(都是极简风格)
|
||
assert!(score_details.style_consistency_score > 0.5);
|
||
|
||
println!("✅ 搭配评分算法测试通过");
|
||
println!(" 颜色和谐度: {:.2}", score_details.color_harmony_score);
|
||
println!(" 风格一致性: {:.2}", score_details.style_consistency_score);
|
||
println!(" 综合评分: {:.2}", score_details.overall_score);
|
||
}
|
||
}
|