test(health): 健康数据集成测试 — 8 个测试覆盖体征CRUD/化验报告CRUD+审阅/租户隔离
fix(auth): WechatSessionResp mock 缺少 unionid 字段
This commit is contained in:
@@ -59,7 +59,9 @@ impl WechatService {
|
||||
code = %code,
|
||||
"fetch_session 开始"
|
||||
);
|
||||
let session = fetch_session(&state.wechat_appid, &state.wechat_secret, code).await?;
|
||||
let session =
|
||||
fetch_session(&state.wechat_appid, &state.wechat_secret, code, state.wechat_dev_mode)
|
||||
.await?;
|
||||
|
||||
let openid = session
|
||||
.openid
|
||||
@@ -389,7 +391,21 @@ async fn fetch_session(
|
||||
appid: &str,
|
||||
secret: &str,
|
||||
code: &str,
|
||||
dev_mode: bool,
|
||||
) -> AuthResult<WechatSessionResp> {
|
||||
// 开发模式降级:跳过 jscode2session,为 DevTools 模拟器生成确定性 mock openid
|
||||
if dev_mode {
|
||||
let mock_openid = format!("dev_mock_{}", &code[..8.min(code.len())]);
|
||||
tracing::warn!(%mock_openid, "开发模式:使用 mock openid(跳过 jscode2session)");
|
||||
return Ok(WechatSessionResp {
|
||||
openid: Some(mock_openid),
|
||||
session_key: Some("dev_mock_session_key".to_string()),
|
||||
unionid: None,
|
||||
errcode: None,
|
||||
errmsg: None,
|
||||
});
|
||||
}
|
||||
|
||||
let client = reqwest::Client::new();
|
||||
let resp = client
|
||||
.get("https://api.weixin.qq.com/sns/jscode2session")
|
||||
|
||||
@@ -26,3 +26,5 @@ mod health_device_reading_tests;
|
||||
mod health_follow_up_tests;
|
||||
#[path = "integration/health_consultation_tests.rs"]
|
||||
mod health_consultation_tests;
|
||||
#[path = "integration/health_data_tests.rs"]
|
||||
mod health_data_tests;
|
||||
|
||||
254
crates/erp-server/tests/integration/health_data_tests.rs
Normal file
254
crates/erp-server/tests/integration/health_data_tests.rs
Normal file
@@ -0,0 +1,254 @@
|
||||
//! erp-health 健康数据(体征 + 化验报告)集成测试
|
||||
//!
|
||||
//! 验证体征 CRUD、化验报告 CRUD + 审阅、租户隔离、乐观锁。
|
||||
|
||||
use erp_health::dto::health_data_dto::*;
|
||||
use erp_health::dto::dialysis_dto::ReviewLabReportReq;
|
||||
use erp_health::service::health_data_service;
|
||||
|
||||
use super::test_fixture::TestApp;
|
||||
|
||||
fn default_vital_signs_req() -> CreateVitalSignsReq {
|
||||
CreateVitalSignsReq {
|
||||
record_date: chrono::NaiveDate::from_ymd_opt(2026, 5, 1).unwrap(),
|
||||
systolic_bp_morning: Some(125),
|
||||
diastolic_bp_morning: Some(82),
|
||||
systolic_bp_evening: Some(120),
|
||||
diastolic_bp_evening: Some(78),
|
||||
heart_rate: Some(72),
|
||||
weight: Some(68.5),
|
||||
blood_sugar: Some(5.2),
|
||||
body_temperature: None,
|
||||
spo2: Some(98),
|
||||
blood_sugar_type: Some("fasting".to_string()),
|
||||
water_intake_ml: Some(2000),
|
||||
urine_output_ml: Some(1500),
|
||||
notes: Some("状态良好".to_string()),
|
||||
source: Some("manual".to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 测试 1: 创建体征记录
|
||||
// ---------------------------------------------------------------------------
|
||||
#[tokio::test]
|
||||
async fn test_vital_signs_create() {
|
||||
let app = TestApp::new().await;
|
||||
let patient_id = app.create_patient("体征患者").await;
|
||||
|
||||
let vs = health_data_service::create_vital_signs(
|
||||
app.health_state(), app.tenant_id(), patient_id, Some(app.operator_id()),
|
||||
default_vital_signs_req(),
|
||||
)
|
||||
.await
|
||||
.expect("创建体征应成功");
|
||||
|
||||
assert_eq!(vs.patient_id, patient_id);
|
||||
assert_eq!(vs.systolic_bp_morning, Some(125));
|
||||
assert_eq!(vs.heart_rate, Some(72));
|
||||
assert_eq!(vs.weight, Some(68.5));
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 测试 2: 体征列表按患者过滤
|
||||
// ---------------------------------------------------------------------------
|
||||
#[tokio::test]
|
||||
async fn test_vital_signs_list() {
|
||||
let app = TestApp::new().await;
|
||||
let patient_a = app.create_patient("列表A").await;
|
||||
let patient_b = app.create_patient("列表B").await;
|
||||
|
||||
health_data_service::create_vital_signs(
|
||||
app.health_state(), app.tenant_id(), patient_a, None,
|
||||
default_vital_signs_req(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
health_data_service::create_vital_signs(
|
||||
app.health_state(), app.tenant_id(), patient_b, None,
|
||||
default_vital_signs_req(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let list_a = health_data_service::list_vital_signs(
|
||||
app.health_state(), app.tenant_id(), patient_a, 1, 20,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(list_a.total, 1);
|
||||
|
||||
let list_b = health_data_service::list_vital_signs(
|
||||
app.health_state(), app.tenant_id(), patient_b, 1, 20,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(list_b.total, 1);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 测试 3: 体征更新
|
||||
// ---------------------------------------------------------------------------
|
||||
#[tokio::test]
|
||||
async fn test_vital_signs_update() {
|
||||
let app = TestApp::new().await;
|
||||
let patient_id = app.create_patient("更新患者").await;
|
||||
|
||||
let vs = health_data_service::create_vital_signs(
|
||||
app.health_state(), app.tenant_id(), patient_id, Some(app.operator_id()),
|
||||
default_vital_signs_req(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let updated = health_data_service::update_vital_signs(
|
||||
app.health_state(), app.tenant_id(), patient_id, vs.id, Some(app.operator_id()),
|
||||
UpdateVitalSignsReq {
|
||||
record_date: None, systolic_bp_morning: None, diastolic_bp_morning: None,
|
||||
systolic_bp_evening: None, diastolic_bp_evening: None,
|
||||
heart_rate: Some(65), weight: Some(67.0),
|
||||
blood_sugar: None, body_temperature: None, spo2: None,
|
||||
blood_sugar_type: None, water_intake_ml: None, urine_output_ml: None,
|
||||
notes: None,
|
||||
},
|
||||
vs.version,
|
||||
)
|
||||
.await
|
||||
.expect("更新应成功");
|
||||
|
||||
assert_eq!(updated.heart_rate, Some(65));
|
||||
assert_eq!(updated.weight, Some(67.0));
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 测试 4: 体征租户隔离
|
||||
// ---------------------------------------------------------------------------
|
||||
#[tokio::test]
|
||||
async fn test_vital_signs_tenant_isolation() {
|
||||
let app = TestApp::new().await;
|
||||
let patient_id = app.create_patient("隔离患者").await;
|
||||
|
||||
health_data_service::create_vital_signs(
|
||||
app.health_state(), app.tenant_id(), patient_id, None,
|
||||
default_vital_signs_req(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let other_tenant = uuid::Uuid::new_v4();
|
||||
let list = health_data_service::list_vital_signs(
|
||||
app.health_state(), other_tenant, patient_id, 1, 20,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(list.total, 0, "不同租户不应看到体征记录");
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 测试 5: 创建化验报告
|
||||
// ---------------------------------------------------------------------------
|
||||
#[tokio::test]
|
||||
async fn test_lab_report_create() {
|
||||
let app = TestApp::new().await;
|
||||
let patient_id = app.create_patient("化验患者").await;
|
||||
|
||||
let report = health_data_service::create_lab_report(
|
||||
app.health_state(), app.tenant_id(), patient_id, Some(app.operator_id()),
|
||||
CreateLabReportReq {
|
||||
report_date: chrono::NaiveDate::from_ymd_opt(2026, 5, 2).unwrap(),
|
||||
report_type: "blood_routine".to_string(),
|
||||
source: Some("manual_input".to_string()),
|
||||
items: Some(serde_json::json!([
|
||||
{"name": "WBC", "value": "6.5", "unit": "10^9/L", "reference_low": "3.5", "reference_high": "9.5", "is_abnormal": false},
|
||||
{"name": "HGB", "value": "110", "unit": "g/L", "reference_low": "120", "reference_high": "160", "is_abnormal": true},
|
||||
])),
|
||||
image_urls: None,
|
||||
doctor_notes: Some("轻度贫血".to_string()),
|
||||
},
|
||||
)
|
||||
.await
|
||||
.expect("创建化验报告应成功");
|
||||
|
||||
assert_eq!(report.patient_id, patient_id);
|
||||
assert_eq!(report.report_type, "blood_routine");
|
||||
assert_eq!(report.status, "pending");
|
||||
assert!(report.items.is_some());
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 测试 6: 化验报告审阅
|
||||
// ---------------------------------------------------------------------------
|
||||
#[tokio::test]
|
||||
async fn test_lab_report_review() {
|
||||
let app = TestApp::new().await;
|
||||
let patient_id = app.create_patient("审阅患者").await;
|
||||
|
||||
let report = health_data_service::create_lab_report(
|
||||
app.health_state(), app.tenant_id(), patient_id, Some(app.operator_id()),
|
||||
CreateLabReportReq {
|
||||
report_date: chrono::NaiveDate::from_ymd_opt(2026, 5, 3).unwrap(),
|
||||
report_type: "blood_routine".to_string(),
|
||||
source: None, items: None, image_urls: None, doctor_notes: None,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(report.status, "pending");
|
||||
|
||||
let reviewed = health_data_service::review_lab_report(
|
||||
app.health_state(), app.tenant_id(), patient_id, report.id, app.operator_id(),
|
||||
ReviewLabReportReq { doctor_notes: Some("复查确认".to_string()), items: None },
|
||||
report.version,
|
||||
)
|
||||
.await
|
||||
.expect("审阅应成功");
|
||||
assert_eq!(reviewed.status, "reviewed");
|
||||
assert!(reviewed.reviewed_by.is_some());
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 测试 7: 化验报告列表按患者过滤
|
||||
// ---------------------------------------------------------------------------
|
||||
#[tokio::test]
|
||||
async fn test_lab_report_list() {
|
||||
let app = TestApp::new().await;
|
||||
let patient_a = app.create_patient("化验列表A").await;
|
||||
let patient_b = app.create_patient("化验列表B").await;
|
||||
|
||||
for pid in &[patient_a, patient_b] {
|
||||
health_data_service::create_lab_report(
|
||||
app.health_state(), app.tenant_id(), *pid, None,
|
||||
CreateLabReportReq {
|
||||
report_date: chrono::NaiveDate::from_ymd_opt(2026, 5, 4).unwrap(),
|
||||
report_type: "blood_routine".to_string(),
|
||||
source: None, items: None, image_urls: None, doctor_notes: None,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
let list_a = health_data_service::list_lab_reports(
|
||||
app.health_state(), app.tenant_id(), patient_a, 1, 20,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(list_a.total, 1);
|
||||
assert_eq!(list_a.data[0].patient_id, patient_a);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 测试 8: 无效患者创建体征返回错误
|
||||
// ---------------------------------------------------------------------------
|
||||
#[tokio::test]
|
||||
async fn test_vital_signs_invalid_patient() {
|
||||
let app = TestApp::new().await;
|
||||
let fake_patient = uuid::Uuid::new_v4();
|
||||
|
||||
let result = health_data_service::create_vital_signs(
|
||||
app.health_state(), app.tenant_id(), fake_patient, None,
|
||||
default_vital_signs_req(),
|
||||
)
|
||||
.await;
|
||||
assert!(result.is_err(), "无效患者应返回错误");
|
||||
}
|
||||
Reference in New Issue
Block a user