Files
hms/crates/erp-server/tests/integration/health_follow_up_template_tests.rs
iven 6d5a711d2c
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
fix: 修复测试发现的 7 个问题 + 全 workspace clippy 清零
功能修复:
1. 患者创建空名称验证:后端添加 name.trim().is_empty() 检查
2. 仪表盘统计容错:单个查询失败返回零值而非 500
3. FHIR 路由修复:从 /fhir 移到 /api/v1/fhir 保持一致
4. 冻结模块后端中间件:新增 frozen_module_middleware 拦截冻结路径
5. 积分端点权限码:health.health-data.list → health.points.list
6. 角色权限迁移:护士补充 devices.list,运营补充 points.list/manage
7. 测试结果文档:R01-R05 角色测试 + T00/T10 结果归档

Clippy 全 workspace 清零(14→0 errors):
- erp-core: 修复 empty doc line、collapsible if、redundant closure 等 9 处
- erp-health: 修复 too_many_arguments、unused var、unnecessary parens 等 58 处
- erp-ai: 修复 dead_code、unused import 等 11 处
- erp-plugin: 修复 too_many_arguments、wildcard pattern 等 11 处
- erp-server-migration: 修复 enum_variant_names 5 处
- erp-auth/config/workflow/message: 各 1-3 处

工程改进:
- lint-staged 配置迁移到 .lintstagedrc.js(函数式避免文件列表传给 clippy)
- cargo fmt 统一格式化
2026-05-07 23:43:14 +08:00

278 lines
8.1 KiB
Rust

//! erp-health 随访模板集成测试
//!
//! 验证模板 CRUD + 字段管理、列表过滤、租户隔离、乐观锁。
use erp_health::dto::follow_up_template_dto::*;
use erp_health::service::follow_up_template_service;
use super::test_fixture::TestApp;
fn default_field_req() -> TemplateFieldReq {
TemplateFieldReq {
label: "血压".to_string(),
field_key: "blood_pressure".to_string(),
field_type: "text".to_string(),
required: true,
options: None,
placeholder: Some("请输入血压值".to_string()),
validation: None,
sort_order: 0,
}
}
fn default_create_req() -> CreateFollowUpTemplateReq {
CreateFollowUpTemplateReq {
name: "电话随访模板".to_string(),
description: Some("标准电话随访".to_string()),
follow_up_type: "phone".to_string(),
applicable_scope: None,
fields: vec![default_field_req()],
}
}
async fn seed_template(app: &TestApp) -> FollowUpTemplateResp {
follow_up_template_service::create_template(
app.health_state(),
app.tenant_id(),
Some(app.operator_id()),
default_create_req(),
)
.await
.expect("创建模板应成功")
}
// ---------------------------------------------------------------------------
// 测试 1: 创建模板(含字段)
// ---------------------------------------------------------------------------
#[tokio::test]
async fn test_template_create_with_fields() {
let app = TestApp::new().await;
let tmpl = seed_template(&app).await;
assert_eq!(tmpl.name, "电话随访模板");
assert_eq!(tmpl.follow_up_type, "phone");
assert_eq!(tmpl.status, "active");
assert_eq!(tmpl.fields.len(), 1);
assert_eq!(tmpl.fields[0].field_key, "blood_pressure");
assert_eq!(tmpl.version, 1);
}
// ---------------------------------------------------------------------------
// 测试 2: 查询模板(含字段)
// ---------------------------------------------------------------------------
#[tokio::test]
async fn test_template_get_with_fields() {
let app = TestApp::new().await;
let tmpl = seed_template(&app).await;
let fetched =
follow_up_template_service::get_template(app.health_state(), app.tenant_id(), tmpl.id)
.await
.expect("查询应成功");
assert_eq!(fetched.id, tmpl.id);
assert_eq!(fetched.fields.len(), 1);
}
// ---------------------------------------------------------------------------
// 测试 3: 更新模板(替换字段)
// ---------------------------------------------------------------------------
#[tokio::test]
async fn test_template_update_replace_fields() {
let app = TestApp::new().await;
let tmpl = seed_template(&app).await;
let updated = follow_up_template_service::update_template(
app.health_state(),
app.tenant_id(),
tmpl.id,
Some(app.operator_id()),
UpdateFollowUpTemplateReq {
name: Some("更新后的模板".to_string()),
fields: Some(vec![
TemplateFieldReq {
label: "体重".to_string(),
field_key: "weight".to_string(),
field_type: "number".to_string(),
required: false,
options: None,
placeholder: None,
validation: None,
sort_order: 0,
},
TemplateFieldReq {
label: "症状".to_string(),
field_key: "symptoms".to_string(),
field_type: "textarea".to_string(),
required: true,
options: None,
placeholder: None,
validation: None,
sort_order: 1,
},
]),
description: None,
follow_up_type: None,
applicable_scope: None,
status: None,
},
tmpl.version,
)
.await
.expect("更新应成功");
assert_eq!(updated.name, "更新后的模板");
assert_eq!(updated.fields.len(), 2);
assert_eq!(updated.version, 2);
}
// ---------------------------------------------------------------------------
// 测试 4: 列表过滤
// ---------------------------------------------------------------------------
#[tokio::test]
async fn test_template_list_filter() {
let app = TestApp::new().await;
follow_up_template_service::create_template(
app.health_state(),
app.tenant_id(),
Some(app.operator_id()),
CreateFollowUpTemplateReq {
name: "门诊随访".to_string(),
follow_up_type: "outpatient".to_string(),
fields: vec![],
description: None,
applicable_scope: None,
},
)
.await
.unwrap();
follow_up_template_service::create_template(
app.health_state(),
app.tenant_id(),
Some(app.operator_id()),
default_create_req(),
)
.await
.unwrap();
// 全量
let all = follow_up_template_service::list_templates(
app.health_state(),
app.tenant_id(),
1,
20,
None,
None,
)
.await
.unwrap();
assert_eq!(all.total, 2);
// 按类型过滤
let phone = follow_up_template_service::list_templates(
app.health_state(),
app.tenant_id(),
1,
20,
Some("phone".to_string()),
None,
)
.await
.unwrap();
assert_eq!(phone.total, 1);
}
// ---------------------------------------------------------------------------
// 测试 5: 软删除(含字段)
// ---------------------------------------------------------------------------
#[tokio::test]
async fn test_template_soft_delete() {
let app = TestApp::new().await;
let tmpl = seed_template(&app).await;
follow_up_template_service::delete_template(
app.health_state(),
app.tenant_id(),
tmpl.id,
Some(app.operator_id()),
tmpl.version,
)
.await
.expect("删除应成功");
let result =
follow_up_template_service::get_template(app.health_state(), app.tenant_id(), tmpl.id)
.await;
assert!(result.is_err(), "软删除后查询应失败");
}
// ---------------------------------------------------------------------------
// 测试 6: 租户隔离
// ---------------------------------------------------------------------------
#[tokio::test]
async fn test_template_tenant_isolation() {
let app = TestApp::new().await;
seed_template(&app).await;
let other_tenant = uuid::Uuid::new_v4();
let list = follow_up_template_service::list_templates(
app.health_state(),
other_tenant,
1,
20,
None,
None,
)
.await
.unwrap();
assert_eq!(list.total, 0, "不同租户不应看到模板");
}
// ---------------------------------------------------------------------------
// 测试 7: 乐观锁冲突
// ---------------------------------------------------------------------------
#[tokio::test]
async fn test_template_version_conflict() {
let app = TestApp::new().await;
let tmpl = seed_template(&app).await;
// 先更新一次
follow_up_template_service::update_template(
app.health_state(),
app.tenant_id(),
tmpl.id,
Some(app.operator_id()),
UpdateFollowUpTemplateReq {
name: Some("第一次".to_string()),
description: None,
follow_up_type: None,
applicable_scope: None,
status: None,
fields: None,
},
tmpl.version,
)
.await
.unwrap();
// 用旧 version 再更新应失败
let result = follow_up_template_service::update_template(
app.health_state(),
app.tenant_id(),
tmpl.id,
Some(app.operator_id()),
UpdateFollowUpTemplateReq {
name: Some("冲突".to_string()),
description: None,
follow_up_type: None,
applicable_scope: None,
status: None,
fields: None,
},
tmpl.version,
)
.await;
assert!(result.is_err(), "乐观锁冲突应返回错误");
}