552 lines
16 KiB
Rust
552 lines
16 KiB
Rust
//! NAC AI估值系统集成测试
|
||
//!
|
||
//! 测试所有模块的集成和端到端功能
|
||
|
||
use nac_ai_valuation::*;
|
||
use rust_decimal::Decimal;
|
||
use std::collections::HashMap;
|
||
|
||
/// 创建测试资产
|
||
fn create_test_asset() -> Asset {
|
||
Asset::new(
|
||
"test_asset_001".to_string(),
|
||
AssetType::RealEstate,
|
||
"GNACS-RE-001".to_string(),
|
||
"Manhattan Office Building".to_string(),
|
||
Decimal::new(50_000_000, 0),
|
||
"USD".to_string(),
|
||
)
|
||
}
|
||
|
||
/// 创建测试估值结果
|
||
fn create_test_valuation_result() -> FinalValuationResult {
|
||
FinalValuationResult {
|
||
valuation_xtzh: Decimal::new(500_000, 0),
|
||
confidence: 0.85,
|
||
model_results: vec![],
|
||
weights: HashMap::new(),
|
||
is_anomaly: false,
|
||
anomaly_report: None,
|
||
divergence_report: "Test divergence report".to_string(),
|
||
requires_human_review: false,
|
||
}
|
||
}
|
||
|
||
#[test]
|
||
fn test_asset_creation() {
|
||
let asset = create_test_asset();
|
||
assert_eq!(asset.id, "test_asset_001");
|
||
assert_eq!(asset.asset_type, AssetType::RealEstate);
|
||
assert_eq!(asset.gnacs_code, "GNACS-RE-001");
|
||
assert_eq!(asset.base_valuation_local, Decimal::new(50_000_000, 0));
|
||
}
|
||
|
||
#[test]
|
||
fn test_jurisdiction_info() {
|
||
let us_info = Jurisdiction::US.info();
|
||
assert_eq!(us_info.code, Jurisdiction::US);
|
||
assert!(us_info.corporate_tax_rate > 0.0);
|
||
|
||
let china_info = Jurisdiction::China.info();
|
||
assert_eq!(china_info.code, Jurisdiction::China);
|
||
assert!(china_info.corporate_tax_rate > 0.0);
|
||
}
|
||
|
||
#[test]
|
||
fn test_international_agreement_info() {
|
||
let wto_info = InternationalAgreement::WTO.info();
|
||
assert_eq!(wto_info.name, "世界贸易组织");
|
||
|
||
let rcep_info = InternationalAgreement::RCEP.info();
|
||
assert_eq!(rcep_info.name, "区域全面经济伙伴关系协定");
|
||
}
|
||
|
||
#[test]
|
||
fn test_valuation_history() {
|
||
let mut history = ValuationHistory::new(100);
|
||
|
||
let entry = ValuationHistoryEntry::new(
|
||
"test_asset_001".to_string(),
|
||
Jurisdiction::US,
|
||
InternationalAgreement::WTO,
|
||
create_test_valuation_result(),
|
||
);
|
||
|
||
history.add(entry.clone()).expect("mainnet: handle error");
|
||
|
||
let entries = history.get_by_asset("test_asset_001");
|
||
assert_eq!(entries.len(), 1);
|
||
assert_eq!(entries[0].asset_id, "test_asset_001");
|
||
}
|
||
|
||
#[test]
|
||
fn test_trend_analysis() {
|
||
let entries = vec![
|
||
ValuationHistoryEntry::new(
|
||
"test_asset_001".to_string(),
|
||
Jurisdiction::US,
|
||
InternationalAgreement::WTO,
|
||
FinalValuationResult {
|
||
valuation_xtzh: Decimal::new(1_000_000, 0),
|
||
confidence: 0.85,
|
||
model_results: vec![],
|
||
weights: HashMap::new(),
|
||
is_anomaly: false,
|
||
anomaly_report: None,
|
||
divergence_report: "Test".to_string(),
|
||
requires_human_review: false,
|
||
},
|
||
),
|
||
ValuationHistoryEntry::new(
|
||
"test_asset_001".to_string(),
|
||
Jurisdiction::US,
|
||
InternationalAgreement::WTO,
|
||
FinalValuationResult {
|
||
valuation_xtzh: Decimal::new(1_100_000, 0),
|
||
confidence: 0.88,
|
||
model_results: vec![],
|
||
weights: HashMap::new(),
|
||
is_anomaly: false,
|
||
anomaly_report: None,
|
||
divergence_report: "Test".to_string(),
|
||
requires_human_review: false,
|
||
},
|
||
),
|
||
ValuationHistoryEntry::new(
|
||
"test_asset_001".to_string(),
|
||
Jurisdiction::US,
|
||
InternationalAgreement::WTO,
|
||
FinalValuationResult {
|
||
valuation_xtzh: Decimal::new(1_200_000, 0),
|
||
confidence: 0.90,
|
||
model_results: vec![],
|
||
weights: HashMap::new(),
|
||
is_anomaly: false,
|
||
anomaly_report: None,
|
||
divergence_report: "Test".to_string(),
|
||
requires_human_review: false,
|
||
},
|
||
),
|
||
];
|
||
|
||
let trend = TrendAnalyzer::analyze(&entries).expect("mainnet: handle error");
|
||
assert_eq!(trend.data_points, 3);
|
||
assert!(trend.change_rate > 0.0); // 上升趋势
|
||
// 趋势可能是Upward或Volatile,取决于标准差
|
||
assert!(trend.trend_direction == TrendDirection::Upward || trend.trend_direction == TrendDirection::Volatile);
|
||
}
|
||
|
||
#[test]
|
||
fn test_validation_rules() {
|
||
let validator = ValuationValidator::with_default_rules();
|
||
|
||
let result = FinalValuationResult {
|
||
valuation_xtzh: Decimal::new(1_000_000, 0),
|
||
confidence: 0.85,
|
||
model_results: vec![],
|
||
weights: HashMap::new(),
|
||
is_anomaly: false,
|
||
anomaly_report: None,
|
||
divergence_report: "Test".to_string(),
|
||
requires_human_review: false,
|
||
};
|
||
|
||
let validation_results = validator.validate(&result);
|
||
assert!(!validation_results.is_empty());
|
||
|
||
// 应该通过所有默认验证
|
||
let all_passed = validator.validate_all(&result);
|
||
assert!(all_passed);
|
||
}
|
||
|
||
#[test]
|
||
fn test_validation_failure() {
|
||
let validator = ValuationValidator::with_default_rules();
|
||
|
||
// 置信度过低的结果
|
||
let result = FinalValuationResult {
|
||
valuation_xtzh: Decimal::new(1_000_000, 0),
|
||
confidence: 0.3, // 低于默认的0.5
|
||
model_results: vec![],
|
||
weights: HashMap::new(),
|
||
is_anomaly: false,
|
||
anomaly_report: None,
|
||
divergence_report: "Test".to_string(),
|
||
requires_human_review: false,
|
||
};
|
||
|
||
let all_passed = validator.validate_all(&result);
|
||
assert!(!all_passed);
|
||
}
|
||
|
||
#[test]
|
||
fn test_accuracy_evaluation() {
|
||
let metrics = AccuracyEvaluator::evaluate(
|
||
Decimal::new(1_000_000, 0),
|
||
Decimal::new(1_100_000, 0),
|
||
);
|
||
|
||
assert_eq!(metrics.estimated, Decimal::new(1_000_000, 0));
|
||
assert_eq!(metrics.actual, Decimal::new(1_100_000, 0));
|
||
assert!(metrics.relative_error > 9.0 && metrics.relative_error < 10.0);
|
||
assert!(metrics.accuracy > 90.0);
|
||
}
|
||
|
||
#[test]
|
||
fn test_batch_accuracy_evaluation() {
|
||
let pairs = vec![
|
||
(Decimal::new(1_000_000, 0), Decimal::new(1_100_000, 0)),
|
||
(Decimal::new(2_000_000, 0), Decimal::new(2_050_000, 0)),
|
||
(Decimal::new(3_000_000, 0), Decimal::new(2_900_000, 0)),
|
||
];
|
||
|
||
let batch_metrics = AccuracyEvaluator::evaluate_batch(pairs);
|
||
assert_eq!(batch_metrics.total_samples, 3);
|
||
assert!(batch_metrics.avg_accuracy > 90.0);
|
||
}
|
||
|
||
#[test]
|
||
fn test_divergence_analysis() {
|
||
use chrono::Utc;
|
||
|
||
let model_results = vec![
|
||
AIValuationResult {
|
||
provider: AIProvider::ChatGPT,
|
||
valuation_xtzh: Decimal::new(1_000_000, 0),
|
||
confidence: 0.85,
|
||
reasoning: "ChatGPT reasoning".to_string(),
|
||
timestamp: Utc::now(),
|
||
},
|
||
AIValuationResult {
|
||
provider: AIProvider::DeepSeek,
|
||
valuation_xtzh: Decimal::new(1_100_000, 0),
|
||
confidence: 0.88,
|
||
reasoning: "DeepSeek reasoning".to_string(),
|
||
timestamp: Utc::now(),
|
||
},
|
||
AIValuationResult {
|
||
provider: AIProvider::DouBao,
|
||
valuation_xtzh: Decimal::new(1_050_000, 0),
|
||
confidence: 0.82,
|
||
reasoning: "DouBao reasoning".to_string(),
|
||
timestamp: Utc::now(),
|
||
},
|
||
];
|
||
|
||
let analysis = DivergenceAnalyzer::analyze(&model_results);
|
||
assert_eq!(analysis.model_count, 3);
|
||
assert_eq!(analysis.min_valuation, Decimal::new(1_000_000, 0));
|
||
assert_eq!(analysis.max_valuation, Decimal::new(1_100_000, 0));
|
||
assert!(analysis.divergence_rate < 15.0);
|
||
assert!(analysis.consistency_score > 85.0);
|
||
}
|
||
|
||
#[test]
|
||
fn test_divergence_with_outlier() {
|
||
use chrono::Utc;
|
||
|
||
let model_results = vec![
|
||
AIValuationResult {
|
||
provider: AIProvider::ChatGPT,
|
||
valuation_xtzh: Decimal::new(1_000_000, 0),
|
||
confidence: 0.85,
|
||
reasoning: "ChatGPT reasoning".to_string(),
|
||
timestamp: Utc::now(),
|
||
},
|
||
AIValuationResult {
|
||
provider: AIProvider::DeepSeek,
|
||
valuation_xtzh: Decimal::new(1_050_000, 0),
|
||
confidence: 0.88,
|
||
reasoning: "DeepSeek reasoning".to_string(),
|
||
timestamp: Utc::now(),
|
||
},
|
||
AIValuationResult {
|
||
provider: AIProvider::DouBao,
|
||
valuation_xtzh: Decimal::new(2_000_000, 0), // 异常值
|
||
confidence: 0.82,
|
||
reasoning: "DouBao reasoning".to_string(),
|
||
timestamp: Utc::now(),
|
||
},
|
||
];
|
||
|
||
let analysis = DivergenceAnalyzer::analyze(&model_results);
|
||
assert_eq!(analysis.model_count, 3);
|
||
assert!(analysis.divergence_rate > 50.0);
|
||
assert!(!analysis.outliers.is_empty());
|
||
assert!(analysis.outliers.contains(&AIProvider::DouBao));
|
||
}
|
||
|
||
#[test]
|
||
fn test_optimization_suggestions() {
|
||
use chrono::Utc;
|
||
|
||
let model_results = vec![
|
||
AIValuationResult {
|
||
provider: AIProvider::ChatGPT,
|
||
valuation_xtzh: Decimal::new(1_000_000, 0),
|
||
confidence: 0.85,
|
||
reasoning: "Test".to_string(),
|
||
timestamp: Utc::now(),
|
||
},
|
||
AIValuationResult {
|
||
provider: AIProvider::DeepSeek,
|
||
valuation_xtzh: Decimal::new(2_000_000, 0),
|
||
confidence: 0.88,
|
||
reasoning: "Test".to_string(),
|
||
timestamp: Utc::now(),
|
||
},
|
||
];
|
||
|
||
let result = FinalValuationResult {
|
||
valuation_xtzh: Decimal::new(1_500_000, 0),
|
||
confidence: 0.60,
|
||
model_results: model_results.clone(),
|
||
weights: HashMap::new(),
|
||
is_anomaly: false,
|
||
anomaly_report: None,
|
||
divergence_report: "Test".to_string(),
|
||
requires_human_review: false,
|
||
};
|
||
|
||
let divergence = DivergenceAnalyzer::analyze(&model_results);
|
||
let suggestions = ModelOptimizer::generate_suggestions(&result, &divergence);
|
||
|
||
assert!(!suggestions.is_empty());
|
||
}
|
||
|
||
#[test]
|
||
fn test_cache_operations() {
|
||
let cache = ValuationCache::new(10);
|
||
|
||
let result = create_test_valuation_result();
|
||
|
||
// 设置缓存
|
||
cache.set(
|
||
"test_asset_001",
|
||
Jurisdiction::US,
|
||
InternationalAgreement::WTO,
|
||
result.clone(),
|
||
);
|
||
|
||
// 获取缓存
|
||
let cached = cache.get(
|
||
"test_asset_001",
|
||
Jurisdiction::US,
|
||
InternationalAgreement::WTO,
|
||
);
|
||
|
||
assert!(cached.is_some());
|
||
assert_eq!(cached.expect("mainnet: handle error").valuation_xtzh, result.valuation_xtzh);
|
||
|
||
// 统计
|
||
let stats = cache.stats();
|
||
assert_eq!(stats.total_entries, 1);
|
||
assert_eq!(stats.total_accesses, 1);
|
||
}
|
||
|
||
#[test]
|
||
fn test_cache_expiry() {
|
||
let cache = ValuationCache::new(10);
|
||
|
||
let result = create_test_valuation_result();
|
||
|
||
cache.set(
|
||
"test_asset_001",
|
||
Jurisdiction::US,
|
||
InternationalAgreement::WTO,
|
||
result,
|
||
);
|
||
|
||
// 清除过期缓存(刚设置的不会过期)
|
||
cache.clear_expired();
|
||
|
||
let stats = cache.stats();
|
||
assert_eq!(stats.total_entries, 1);
|
||
assert_eq!(stats.expired_entries, 0);
|
||
}
|
||
|
||
#[test]
|
||
fn test_realtime_data_source() {
|
||
let data_source = RealtimeDataSource::new();
|
||
assert_eq!(data_source.xtzh_price_usd, Decimal::new(100, 0));
|
||
assert!(!data_source.is_stale());
|
||
}
|
||
|
||
#[test]
|
||
fn test_final_valuation_result_report() {
|
||
let result = create_test_valuation_result();
|
||
let report = result.generate_report();
|
||
|
||
assert!(report.contains("NAC AI资产估值报告"));
|
||
assert!(report.contains("500000 XTZH"));
|
||
assert!(report.contains("85.0%"));
|
||
}
|
||
|
||
#[test]
|
||
fn test_final_valuation_result_json() {
|
||
let result = create_test_valuation_result();
|
||
let json = result.to_json().expect("mainnet: handle error");
|
||
|
||
assert!(json.contains("valuation_xtzh"));
|
||
assert!(json.contains("confidence"));
|
||
assert!(json.contains("500000"));
|
||
}
|
||
|
||
#[test]
|
||
fn test_data_export_json() {
|
||
use std::path::Path;
|
||
|
||
let entries = vec![
|
||
ValuationHistoryEntry::new(
|
||
"test_asset_001".to_string(),
|
||
Jurisdiction::US,
|
||
InternationalAgreement::WTO,
|
||
create_test_valuation_result(),
|
||
),
|
||
];
|
||
|
||
let path = Path::new("/tmp/test_valuation_export.json");
|
||
let result = DataExporter::export_json(&entries, path);
|
||
assert!(result.is_ok());
|
||
|
||
// 验证文件存在
|
||
assert!(path.exists());
|
||
}
|
||
|
||
#[test]
|
||
fn test_data_export_csv() {
|
||
use std::path::Path;
|
||
|
||
let entries = vec![
|
||
ValuationHistoryEntry::new(
|
||
"test_asset_001".to_string(),
|
||
Jurisdiction::US,
|
||
InternationalAgreement::WTO,
|
||
create_test_valuation_result(),
|
||
),
|
||
];
|
||
|
||
let path = Path::new("/tmp/test_valuation_export.csv");
|
||
let result = DataExporter::export_csv(&entries, path);
|
||
assert!(result.is_ok());
|
||
|
||
// 验证文件存在
|
||
assert!(path.exists());
|
||
}
|
||
|
||
#[test]
|
||
fn test_data_export_markdown() {
|
||
use std::path::Path;
|
||
|
||
let entries = vec![
|
||
ValuationHistoryEntry::new(
|
||
"test_asset_001".to_string(),
|
||
Jurisdiction::US,
|
||
InternationalAgreement::WTO,
|
||
create_test_valuation_result(),
|
||
),
|
||
];
|
||
|
||
let trend = TrendAnalyzer::analyze(&entries).expect("mainnet: handle error");
|
||
let path = Path::new("/tmp/test_valuation_export.md");
|
||
let result = DataExporter::export_markdown(&entries, &trend, path);
|
||
assert!(result.is_ok());
|
||
|
||
// 验证文件存在
|
||
assert!(path.exists());
|
||
}
|
||
|
||
#[tokio::test]
|
||
#[ignore] // 需要真实的API密钥
|
||
async fn test_full_valuation_flow() {
|
||
// 创建估值引擎
|
||
let engine = ValuationEngine::new(
|
||
std::env::var("CHATGPT_API_KEY").unwrap_or("test_key".to_string()),
|
||
std::env::var("DEEPSEEK_API_KEY").unwrap_or("test_key".to_string()),
|
||
std::env::var("DOUBAO_API_KEY").unwrap_or("test_key".to_string()),
|
||
ValuationEngineConfig::default(),
|
||
).expect("mainnet: handle error");
|
||
|
||
// 创建资产
|
||
let asset = create_test_asset();
|
||
|
||
// 执行估值
|
||
let result = engine.appraise(
|
||
&asset,
|
||
Jurisdiction::US,
|
||
InternationalAgreement::WTO,
|
||
).await;
|
||
|
||
// 验证结果
|
||
if let Ok(result) = result {
|
||
assert!(result.valuation_xtzh > Decimal::ZERO);
|
||
assert!(result.confidence >= 0.0 && result.confidence <= 1.0);
|
||
assert!(!result.model_results.is_empty());
|
||
}
|
||
}
|
||
|
||
#[test]
|
||
fn test_all_asset_types() {
|
||
let types = vec![
|
||
AssetType::RealEstate,
|
||
AssetType::Commodity,
|
||
AssetType::FinancialAsset,
|
||
AssetType::DigitalAsset,
|
||
AssetType::IntellectualProperty,
|
||
AssetType::ArtCollectible,
|
||
AssetType::Movable,
|
||
AssetType::Receivable,
|
||
AssetType::Infrastructure,
|
||
AssetType::NaturalResource,
|
||
AssetType::ESGAsset,
|
||
AssetType::Other,
|
||
];
|
||
|
||
assert_eq!(types.len(), 12);
|
||
}
|
||
|
||
#[test]
|
||
fn test_all_jurisdictions() {
|
||
let jurisdictions = vec![
|
||
Jurisdiction::US,
|
||
Jurisdiction::EU,
|
||
Jurisdiction::China,
|
||
Jurisdiction::HongKong,
|
||
Jurisdiction::SG,
|
||
Jurisdiction::UK,
|
||
Jurisdiction::JP,
|
||
Jurisdiction::ME,
|
||
];
|
||
|
||
assert_eq!(jurisdictions.len(), 8);
|
||
|
||
// 验证每个辖区都有有效信息
|
||
for jurisdiction in jurisdictions {
|
||
let info = jurisdiction.info();
|
||
assert!(info.corporate_tax_rate >= 0.0);
|
||
assert!(info.corporate_tax_rate >= 0.0);
|
||
}
|
||
}
|
||
|
||
#[test]
|
||
fn test_all_agreements() {
|
||
let agreements = vec![
|
||
InternationalAgreement::EU,
|
||
InternationalAgreement::WTO,
|
||
InternationalAgreement::SCO,
|
||
InternationalAgreement::RCEP,
|
||
InternationalAgreement::CPTPP,
|
||
InternationalAgreement::USMCA,
|
||
InternationalAgreement::AfCFTA,
|
||
InternationalAgreement::None,
|
||
];
|
||
|
||
assert_eq!(agreements.len(), 8);
|
||
|
||
// 验证每个协定都有有效信息
|
||
for agreement in agreements {
|
||
let info = agreement.info();
|
||
assert!(!info.name.is_empty());
|
||
}
|
||
}
|