/** * 服装搭配功能集成测试 * 测试从图像分析到搭配推荐的完整流程 */ #[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); } }