test(health): 随访 + 咨询集成测试 — 9 个随访测试 + 8 个咨询测试覆盖 CRUD/状态/批量/消息/租户隔离
This commit is contained in:
@@ -22,3 +22,7 @@ mod health_dialysis_tests;
|
||||
mod health_alert_tests;
|
||||
#[path = "integration/health_device_reading_tests.rs"]
|
||||
mod health_device_reading_tests;
|
||||
#[path = "integration/health_follow_up_tests.rs"]
|
||||
mod health_follow_up_tests;
|
||||
#[path = "integration/health_consultation_tests.rs"]
|
||||
mod health_consultation_tests;
|
||||
|
||||
211
crates/erp-server/tests/integration/health_consultation_tests.rs
Normal file
211
crates/erp-server/tests/integration/health_consultation_tests.rs
Normal file
@@ -0,0 +1,211 @@
|
||||
//! erp-health 咨询管理集成测试
|
||||
//!
|
||||
//! 验证咨询会话 CRUD、消息收发、会话关闭、未读计数、租户隔离。
|
||||
|
||||
use erp_health::dto::consultation_dto::*;
|
||||
use erp_health::service::consultation_service;
|
||||
|
||||
use super::test_fixture::TestApp;
|
||||
|
||||
/// 创建测试用会话(无医护)
|
||||
async fn seed_session(app: &TestApp, patient_id: uuid::Uuid) -> SessionResp {
|
||||
consultation_service::create_session(
|
||||
app.health_state(), app.tenant_id(), Some(app.operator_id()),
|
||||
CreateSessionReq { patient_id, doctor_id: None, consultation_type: None },
|
||||
)
|
||||
.await
|
||||
.expect("创建会话应成功")
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 测试 1: 创建会话(带医护)
|
||||
// ---------------------------------------------------------------------------
|
||||
#[tokio::test]
|
||||
async fn test_consultation_session_create() {
|
||||
let app = TestApp::new().await;
|
||||
let patient_id = app.create_patient("咨询患者").await;
|
||||
let doctor_id = app.create_doctor("咨询医生").await;
|
||||
|
||||
let session = consultation_service::create_session(
|
||||
app.health_state(), app.tenant_id(), Some(app.operator_id()),
|
||||
CreateSessionReq {
|
||||
patient_id,
|
||||
doctor_id: Some(doctor_id),
|
||||
consultation_type: Some("doctor".to_string()),
|
||||
},
|
||||
)
|
||||
.await
|
||||
.expect("创建会话应成功");
|
||||
|
||||
assert_eq!(session.patient_id, patient_id);
|
||||
assert_eq!(session.doctor_id, Some(doctor_id));
|
||||
assert_eq!(session.consultation_type, "doctor");
|
||||
assert_eq!(session.status, "waiting");
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 测试 2: 查询会话
|
||||
// ---------------------------------------------------------------------------
|
||||
#[tokio::test]
|
||||
async fn test_consultation_session_get() {
|
||||
let app = TestApp::new().await;
|
||||
let patient_id = app.create_patient("查询患者").await;
|
||||
|
||||
let session = seed_session(&app, patient_id).await;
|
||||
|
||||
let fetched = consultation_service::get_session(
|
||||
app.health_state(), app.tenant_id(), session.id,
|
||||
)
|
||||
.await
|
||||
.expect("查询应成功");
|
||||
assert_eq!(fetched.id, session.id);
|
||||
assert_eq!(fetched.status, "waiting");
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 测试 3: 列表按患者过滤
|
||||
// ---------------------------------------------------------------------------
|
||||
#[tokio::test]
|
||||
async fn test_consultation_session_list_by_patient() {
|
||||
let app = TestApp::new().await;
|
||||
let patient_a = app.create_patient("列表患者A").await;
|
||||
let patient_b = app.create_patient("列表患者B").await;
|
||||
|
||||
seed_session(&app, patient_a).await;
|
||||
seed_session(&app, patient_a).await;
|
||||
seed_session(&app, patient_b).await;
|
||||
|
||||
let list_a = consultation_service::list_sessions(
|
||||
app.health_state(), app.tenant_id(), 1, 20, None, Some(patient_a), None,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(list_a.total, 2);
|
||||
|
||||
let list_b = consultation_service::list_sessions(
|
||||
app.health_state(), app.tenant_id(), 1, 20, None, Some(patient_b), None,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(list_b.total, 1);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 测试 4: 发送消息
|
||||
// ---------------------------------------------------------------------------
|
||||
#[tokio::test]
|
||||
async fn test_consultation_message_send() {
|
||||
let app = TestApp::new().await;
|
||||
let patient_id = app.create_patient("消息患者").await;
|
||||
|
||||
let session = seed_session(&app, patient_id).await;
|
||||
|
||||
let msg = consultation_service::create_message(
|
||||
app.health_state(), app.tenant_id(), Some(app.operator_id()),
|
||||
CreateMessageReq {
|
||||
session_id: session.id,
|
||||
sender_id: app.operator_id(),
|
||||
sender_role: "doctor".to_string(),
|
||||
content_type: Some("text".to_string()),
|
||||
content: "您好,有什么可以帮您?".to_string(),
|
||||
},
|
||||
)
|
||||
.await
|
||||
.expect("发送消息应成功");
|
||||
|
||||
assert_eq!(msg.session_id, session.id);
|
||||
assert_eq!(msg.content, "您好,有什么可以帮您?");
|
||||
assert_eq!(msg.sender_role, "doctor");
|
||||
assert!(!msg.is_read);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 测试 5: 查询消息列表
|
||||
// ---------------------------------------------------------------------------
|
||||
#[tokio::test]
|
||||
async fn test_consultation_message_list() {
|
||||
let app = TestApp::new().await;
|
||||
let patient_id = app.create_patient("消息列表患者").await;
|
||||
|
||||
let session = seed_session(&app, patient_id).await;
|
||||
|
||||
for i in 0..3 {
|
||||
consultation_service::create_message(
|
||||
app.health_state(), app.tenant_id(), Some(app.operator_id()),
|
||||
CreateMessageReq {
|
||||
session_id: session.id,
|
||||
sender_id: app.operator_id(),
|
||||
sender_role: "doctor".to_string(),
|
||||
content_type: None,
|
||||
content: format!("消息{}", i + 1),
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
let messages = consultation_service::list_messages(
|
||||
app.health_state(), app.tenant_id(), session.id, 1, 20, None,
|
||||
)
|
||||
.await
|
||||
.expect("查询消息应成功");
|
||||
|
||||
assert_eq!(messages.total, 3);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 测试 6: 关闭会话
|
||||
// ---------------------------------------------------------------------------
|
||||
#[tokio::test]
|
||||
async fn test_consultation_session_close() {
|
||||
let app = TestApp::new().await;
|
||||
let patient_id = app.create_patient("关闭患者").await;
|
||||
|
||||
let session = seed_session(&app, patient_id).await;
|
||||
assert_eq!(session.status, "waiting");
|
||||
|
||||
let closed = consultation_service::close_session(
|
||||
app.health_state(), app.tenant_id(), session.id, Some(app.operator_id()), session.version,
|
||||
)
|
||||
.await
|
||||
.expect("关闭应成功");
|
||||
assert_eq!(closed.status, "closed");
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 测试 7: 租户隔离
|
||||
// ---------------------------------------------------------------------------
|
||||
#[tokio::test]
|
||||
async fn test_consultation_session_tenant_isolation() {
|
||||
let app = TestApp::new().await;
|
||||
let patient_id = app.create_patient("隔离患者").await;
|
||||
|
||||
let session = seed_session(&app, patient_id).await;
|
||||
|
||||
let other_tenant = uuid::Uuid::new_v4();
|
||||
let result = consultation_service::get_session(
|
||||
app.health_state(), other_tenant, session.id,
|
||||
)
|
||||
.await;
|
||||
assert!(result.is_err(), "不同租户不应看到此会话");
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 测试 8: 无效患者创建会话返回错误
|
||||
// ---------------------------------------------------------------------------
|
||||
#[tokio::test]
|
||||
async fn test_consultation_session_invalid_patient() {
|
||||
let app = TestApp::new().await;
|
||||
let fake_patient = uuid::Uuid::new_v4();
|
||||
|
||||
let result = consultation_service::create_session(
|
||||
app.health_state(), app.tenant_id(), None,
|
||||
CreateSessionReq {
|
||||
patient_id: fake_patient,
|
||||
doctor_id: None,
|
||||
consultation_type: None,
|
||||
},
|
||||
)
|
||||
.await;
|
||||
assert!(result.is_err(), "无效患者应返回错误");
|
||||
}
|
||||
297
crates/erp-server/tests/integration/health_follow_up_tests.rs
Normal file
297
crates/erp-server/tests/integration/health_follow_up_tests.rs
Normal file
@@ -0,0 +1,297 @@
|
||||
//! erp-health 随访管理集成测试
|
||||
//!
|
||||
//! 验证随访任务 CRUD、状态流转、批量操作、记录创建、租户隔离、乐观锁。
|
||||
|
||||
use erp_health::dto::follow_up_dto::*;
|
||||
use erp_health::service::follow_up_service;
|
||||
|
||||
use super::test_fixture::TestApp;
|
||||
|
||||
fn default_create_task(patient_id: uuid::Uuid) -> CreateFollowUpTaskReq {
|
||||
CreateFollowUpTaskReq {
|
||||
patient_id,
|
||||
assigned_to: None,
|
||||
follow_up_type: "phone".to_string(),
|
||||
planned_date: chrono::NaiveDate::from_ymd_opt(2026, 5, 15).unwrap(),
|
||||
content_template: Some("请询问血压情况".to_string()),
|
||||
related_appointment_id: None,
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 测试 1: 创建 + 查询随访任务
|
||||
// ---------------------------------------------------------------------------
|
||||
#[tokio::test]
|
||||
async fn test_follow_up_task_create_and_get() {
|
||||
let app = TestApp::new().await;
|
||||
let patient_id = app.create_patient("随访患者").await;
|
||||
|
||||
let task = follow_up_service::create_task(
|
||||
app.health_state(), app.tenant_id(), Some(app.operator_id()),
|
||||
default_create_task(patient_id),
|
||||
)
|
||||
.await
|
||||
.expect("创建任务应成功");
|
||||
|
||||
assert_eq!(task.patient_id, patient_id);
|
||||
assert_eq!(task.follow_up_type, "phone");
|
||||
assert_eq!(task.status, "pending");
|
||||
assert_eq!(task.version, 1);
|
||||
|
||||
let fetched = follow_up_service::get_task(app.health_state(), app.tenant_id(), task.id)
|
||||
.await
|
||||
.expect("查询应成功");
|
||||
assert_eq!(fetched.id, task.id);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 测试 2: 列表按患者过滤
|
||||
// ---------------------------------------------------------------------------
|
||||
#[tokio::test]
|
||||
async fn test_follow_up_task_list_by_patient() {
|
||||
let app = TestApp::new().await;
|
||||
let patient_a = app.create_patient("列表A").await;
|
||||
let patient_b = app.create_patient("列表B").await;
|
||||
|
||||
follow_up_service::create_task(
|
||||
app.health_state(), app.tenant_id(), None,
|
||||
default_create_task(patient_a),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
follow_up_service::create_task(
|
||||
app.health_state(), app.tenant_id(), None,
|
||||
default_create_task(patient_a),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
follow_up_service::create_task(
|
||||
app.health_state(), app.tenant_id(), None,
|
||||
default_create_task(patient_b),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let list_a = follow_up_service::list_tasks(
|
||||
app.health_state(), app.tenant_id(), 1, 20, Some(patient_a), None, None,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(list_a.total, 2);
|
||||
|
||||
let list_b = follow_up_service::list_tasks(
|
||||
app.health_state(), app.tenant_id(), 1, 20, Some(patient_b), None, None,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(list_b.total, 1);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 测试 3: 状态流转 pending → in_progress → completed
|
||||
// ---------------------------------------------------------------------------
|
||||
#[tokio::test]
|
||||
async fn test_follow_up_task_status_flow() {
|
||||
let app = TestApp::new().await;
|
||||
let patient_id = app.create_patient("流转患者").await;
|
||||
|
||||
let task = follow_up_service::create_task(
|
||||
app.health_state(), app.tenant_id(), Some(app.operator_id()),
|
||||
default_create_task(patient_id),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(task.status, "pending");
|
||||
|
||||
// pending → in_progress
|
||||
let started = follow_up_service::update_task(
|
||||
app.health_state(), app.tenant_id(), task.id, Some(app.operator_id()),
|
||||
UpdateFollowUpTaskReq {
|
||||
status: Some("in_progress".to_string()),
|
||||
assigned_to: None, follow_up_type: None, planned_date: None,
|
||||
content_template: None,
|
||||
},
|
||||
task.version,
|
||||
)
|
||||
.await
|
||||
.expect("开始应成功");
|
||||
assert_eq!(started.status, "in_progress");
|
||||
|
||||
// in_progress → completed
|
||||
let completed = follow_up_service::update_task(
|
||||
app.health_state(), app.tenant_id(), task.id, Some(app.operator_id()),
|
||||
UpdateFollowUpTaskReq {
|
||||
status: Some("completed".to_string()),
|
||||
assigned_to: None, follow_up_type: None, planned_date: None,
|
||||
content_template: None,
|
||||
},
|
||||
started.version,
|
||||
)
|
||||
.await
|
||||
.expect("完成应成功");
|
||||
assert_eq!(completed.status, "completed");
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 测试 4: 乐观锁冲突
|
||||
// ---------------------------------------------------------------------------
|
||||
#[tokio::test]
|
||||
async fn test_follow_up_task_version_conflict() {
|
||||
let app = TestApp::new().await;
|
||||
let patient_id = app.create_patient("乐观锁患者").await;
|
||||
|
||||
let task = follow_up_service::create_task(
|
||||
app.health_state(), app.tenant_id(), Some(app.operator_id()),
|
||||
default_create_task(patient_id),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// 正确版本更新
|
||||
follow_up_service::update_task(
|
||||
app.health_state(), app.tenant_id(), task.id, Some(app.operator_id()),
|
||||
UpdateFollowUpTaskReq {
|
||||
status: Some("in_progress".to_string()),
|
||||
assigned_to: None, follow_up_type: None, planned_date: None,
|
||||
content_template: None,
|
||||
},
|
||||
task.version,
|
||||
)
|
||||
.await
|
||||
.expect("更新应成功");
|
||||
|
||||
// 旧版本更新应失败
|
||||
let result = follow_up_service::update_task(
|
||||
app.health_state(), app.tenant_id(), task.id, Some(app.operator_id()),
|
||||
UpdateFollowUpTaskReq {
|
||||
status: Some("cancelled".to_string()),
|
||||
assigned_to: None, follow_up_type: None, planned_date: None,
|
||||
content_template: None,
|
||||
},
|
||||
task.version,
|
||||
)
|
||||
.await;
|
||||
assert!(result.is_err(), "旧版本应失败");
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 测试 5: 软删除后不可见
|
||||
// ---------------------------------------------------------------------------
|
||||
#[tokio::test]
|
||||
async fn test_follow_up_task_soft_delete() {
|
||||
let app = TestApp::new().await;
|
||||
let patient_id = app.create_patient("软删除患者").await;
|
||||
|
||||
let task = follow_up_service::create_task(
|
||||
app.health_state(), app.tenant_id(), Some(app.operator_id()),
|
||||
default_create_task(patient_id),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
follow_up_service::delete_task(
|
||||
app.health_state(), app.tenant_id(), task.id, Some(app.operator_id()), task.version,
|
||||
)
|
||||
.await
|
||||
.expect("删除应成功");
|
||||
|
||||
let result = follow_up_service::get_task(app.health_state(), app.tenant_id(), task.id).await;
|
||||
assert!(result.is_err(), "软删除后应不可见");
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 测试 6: 租户隔离
|
||||
// ---------------------------------------------------------------------------
|
||||
#[tokio::test]
|
||||
async fn test_follow_up_task_tenant_isolation() {
|
||||
let app = TestApp::new().await;
|
||||
let patient_id = app.create_patient("隔离患者").await;
|
||||
|
||||
let task = follow_up_service::create_task(
|
||||
app.health_state(), app.tenant_id(), None,
|
||||
default_create_task(patient_id),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let other_tenant = uuid::Uuid::new_v4();
|
||||
let result = follow_up_service::get_task(app.health_state(), other_tenant, task.id).await;
|
||||
assert!(result.is_err(), "不同租户不应看到此任务");
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 测试 7: 批量创建随访任务
|
||||
// ---------------------------------------------------------------------------
|
||||
#[tokio::test]
|
||||
async fn test_follow_up_batch_create() {
|
||||
let app = TestApp::new().await;
|
||||
let patient_a = app.create_patient("批量A").await;
|
||||
let patient_b = app.create_patient("批量B").await;
|
||||
|
||||
let result = follow_up_service::batch_create_tasks(
|
||||
app.health_state(), app.tenant_id(), Some(app.operator_id()),
|
||||
BatchCreateTasksReq {
|
||||
patient_ids: vec![patient_a, patient_b],
|
||||
assigned_to: None,
|
||||
follow_up_type: "phone".to_string(),
|
||||
planned_date: chrono::NaiveDate::from_ymd_opt(2026, 5, 20).unwrap(),
|
||||
content_template: None,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.expect("批量创建应成功");
|
||||
|
||||
assert_eq!(result.succeeded, 2);
|
||||
assert_eq!(result.failed, 0);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 测试 8: 创建随访记录
|
||||
// ---------------------------------------------------------------------------
|
||||
#[tokio::test]
|
||||
async fn test_follow_up_record_create() {
|
||||
let app = TestApp::new().await;
|
||||
let patient_id = app.create_patient("记录患者").await;
|
||||
|
||||
let task = follow_up_service::create_task(
|
||||
app.health_state(), app.tenant_id(), Some(app.operator_id()),
|
||||
default_create_task(patient_id),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let record = follow_up_service::create_record(
|
||||
app.health_state(), app.tenant_id(), Some(app.operator_id()),
|
||||
CreateFollowUpRecordReq {
|
||||
task_id: task.id,
|
||||
executed_by: Some(app.operator_id()),
|
||||
executed_date: chrono::NaiveDate::from_ymd_opt(2026, 5, 16).unwrap(),
|
||||
result: "已跟进".to_string(),
|
||||
patient_condition: Some("血压正常".to_string()),
|
||||
medical_advice: Some("继续服药".to_string()),
|
||||
next_follow_up_date: Some(chrono::NaiveDate::from_ymd_opt(2026, 6, 16).unwrap()),
|
||||
},
|
||||
)
|
||||
.await
|
||||
.expect("创建记录应成功");
|
||||
|
||||
assert_eq!(record.task_id, task.id);
|
||||
assert_eq!(record.result, "已跟进");
|
||||
assert_eq!(record.medical_advice, Some("继续服药".to_string()));
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 测试 9: 无效患者返回错误
|
||||
// ---------------------------------------------------------------------------
|
||||
#[tokio::test]
|
||||
async fn test_follow_up_task_invalid_patient() {
|
||||
let app = TestApp::new().await;
|
||||
let fake_patient = uuid::Uuid::new_v4();
|
||||
|
||||
let result = follow_up_service::create_task(
|
||||
app.health_state(), app.tenant_id(), None,
|
||||
default_create_task(fake_patient),
|
||||
)
|
||||
.await;
|
||||
assert!(result.is_err(), "无效患者应返回错误");
|
||||
}
|
||||
Reference in New Issue
Block a user