test(ai): erp-ai 从零增至 34 个单元测试 — 覆盖 DTO/error/prompt/sanitization
Some checks failed
CI / rust-check (push) Has been cancelled
CI / rust-test (push) Has been cancelled
CI / frontend-build (push) Has been cancelled
CI / security-audit (push) Has been cancelled

- dto.rs: 8 个测试(AnalysisType 映射、serde round-trip、SSE 事件、默认值)
- error.rs: 10 个测试(AiError 全部 10 个变体 → AppError 映射)
- prompt: 8 个测试(变量替换、嵌套对象、数组迭代、条件、严格模式缺失变量)
- sanitization: 8 个测试(4 种 DTO 脱敏通过、PII 字段检测、空数据边界)
This commit is contained in:
iven
2026-04-28 18:17:19 +08:00
parent dde6b09017
commit 50e63530d9
4 changed files with 435 additions and 0 deletions

View File

@@ -57,3 +57,108 @@ impl From<sea_orm::DbErr> for AiError {
}
pub type AiResult<T> = Result<T, AiError>;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn validation_maps_to_app_error_validation() {
let err = AiError::Validation("字段缺失".to_string());
let app: AppError = err.into();
match app {
AppError::Validation(msg) => assert!(msg.contains("字段缺失")),
other => panic!("期望 AppError::Validation得到 {:?}", other),
}
}
#[test]
fn analysis_not_found_maps_to_not_found() {
let err = AiError::AnalysisNotFound("abc-123".to_string());
let app: AppError = err.into();
match app {
AppError::NotFound(msg) => assert!(msg.contains("分析结果")),
other => panic!("期望 AppError::NotFound得到 {:?}", other),
}
}
#[test]
fn prompt_not_found_maps_to_not_found() {
let err = AiError::PromptNotFound("lab_report_interpretation".to_string());
let app: AppError = err.into();
match app {
AppError::NotFound(msg) => assert!(msg.contains("Prompt 模板")),
other => panic!("期望 AppError::NotFound得到 {:?}", other),
}
}
#[test]
fn provider_unavailable_maps_to_internal() {
let err = AiError::ProviderUnavailable("Claude".to_string());
let app: AppError = err.into();
match app {
AppError::Internal(msg) => assert!(msg.contains("Claude")),
other => panic!("期望 AppError::Internal得到 {:?}", other),
}
}
#[test]
fn provider_error_maps_to_internal() {
let err = AiError::ProviderError("超时".to_string());
let app: AppError = err.into();
match app {
AppError::Internal(msg) => assert!(msg.contains("超时")),
other => panic!("期望 AppError::Internal得到 {:?}", other),
}
}
#[test]
fn sanitization_error_maps_to_internal() {
let err = AiError::SanitizationError("PII 泄漏".to_string());
let app: AppError = err.into();
match app {
AppError::Internal(msg) => assert!(msg.contains("PII 泄漏")),
other => panic!("期望 AppError::Internal得到 {:?}", other),
}
}
#[test]
fn template_error_maps_to_internal() {
let err = AiError::TemplateError("语法错误".to_string());
let app: AppError = err.into();
match app {
AppError::Internal(msg) => assert!(msg.contains("语法错误")),
other => panic!("期望 AppError::Internal得到 {:?}", other),
}
}
#[test]
fn rate_limit_maps_to_too_many_requests() {
let err = AiError::RateLimitExceeded;
let app: AppError = err.into();
match app {
AppError::TooManyRequests => {},
other => panic!("期望 AppError::TooManyRequests得到 {:?}", other),
}
}
#[test]
fn version_mismatch_maps_directly() {
let err = AiError::VersionMismatch;
let app: AppError = err.into();
match app {
AppError::VersionMismatch => {},
other => panic!("期望 AppError::VersionMismatch得到 {:?}", other),
}
}
#[test]
fn db_error_maps_to_internal() {
let err = AiError::DbError("连接失败".to_string());
let app: AppError = err.into();
match app {
AppError::Internal(msg) => assert!(msg.contains("连接失败")),
other => panic!("期望 AppError::Internal得到 {:?}", other),
}
}
}