tyt-api-nest/test/e2e/specs/patients.e2e-spec.ts
EL 6ec2d0b0e0 新增 B 端设备模块(后端 CRUD、分页筛选、权限隔离)并接入前端设备管理页面与路由菜单
鉴权改为登录态回库校验,新增 tokenValidAfter 失效时间,支持密码变更与 seed 重置后旧 token 立即失效
患者字段由 idCardHash 统一迁移为 idCard,新增身份证标准化逻辑并同步 C 端生命周期查询参数
组织模块增加小组删除限制(有成员时返回 409)并补充中文错误消息
任务取消接口支持可选 reason 字段(先透传事件层)
补齐 Prisma 迁移、文档说明和 E2E 用例(含设备模块与 token 失效场景)
2026-03-18 20:23:55 +08:00

189 lines
6.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import request from 'supertest';
import { Role } from '../../../src/generated/prisma/enums.js';
import {
closeE2EContext,
createE2EContext,
type E2EContext,
} from '../helpers/e2e-context.helper.js';
import { assertRoleMatrix } from '../helpers/e2e-matrix.helper.js';
import {
expectErrorEnvelope,
expectSuccessEnvelope,
} from '../helpers/e2e-http.helper.js';
describe('Patients Controllers (e2e)', () => {
let ctx: E2EContext;
beforeAll(async () => {
ctx = await createE2EContext();
});
afterAll(async () => {
await closeE2EContext(ctx);
});
describe('GET /b/patients', () => {
it('成功:按角色返回正确可见性范围', async () => {
const systemAdminResponse = await request(ctx.app.getHttpServer())
.get('/b/patients')
.query({ hospitalId: ctx.fixtures.hospitalAId })
.set('Authorization', `Bearer ${ctx.tokens[Role.SYSTEM_ADMIN]}`);
expectSuccessEnvelope(systemAdminResponse, 200);
const systemPatientIds = (
systemAdminResponse.body.data as Array<{ id: number }>
).map((item) => item.id);
expect(systemPatientIds).toEqual(
expect.arrayContaining([
ctx.fixtures.patients.patientA1Id,
ctx.fixtures.patients.patientA2Id,
ctx.fixtures.patients.patientA3Id,
]),
);
const hospitalAdminResponse = await request(ctx.app.getHttpServer())
.get('/b/patients')
.set('Authorization', `Bearer ${ctx.tokens[Role.HOSPITAL_ADMIN]}`);
expectSuccessEnvelope(hospitalAdminResponse, 200);
const hospitalPatientIds = (
hospitalAdminResponse.body.data as Array<{ id: number }>
).map((item) => item.id);
expect(hospitalPatientIds).toEqual(
expect.arrayContaining([
ctx.fixtures.patients.patientA1Id,
ctx.fixtures.patients.patientA2Id,
ctx.fixtures.patients.patientA3Id,
]),
);
const directorResponse = await request(ctx.app.getHttpServer())
.get('/b/patients')
.set('Authorization', `Bearer ${ctx.tokens[Role.DIRECTOR]}`);
expectSuccessEnvelope(directorResponse, 200);
const directorPatientIds = (
directorResponse.body.data as Array<{ id: number }>
).map((item) => item.id);
expect(directorPatientIds).toEqual(
expect.arrayContaining([
ctx.fixtures.patients.patientA1Id,
ctx.fixtures.patients.patientA2Id,
]),
);
expect(directorPatientIds).not.toContain(
ctx.fixtures.patients.patientA3Id,
);
const leaderResponse = await request(ctx.app.getHttpServer())
.get('/b/patients')
.set('Authorization', `Bearer ${ctx.tokens[Role.LEADER]}`);
expectSuccessEnvelope(leaderResponse, 200);
const leaderPatientIds = (
leaderResponse.body.data as Array<{ id: number }>
).map((item) => item.id);
expect(leaderPatientIds).toEqual(
expect.arrayContaining([
ctx.fixtures.patients.patientA1Id,
ctx.fixtures.patients.patientA2Id,
]),
);
expect(leaderPatientIds).not.toContain(ctx.fixtures.patients.patientA3Id);
const doctorResponse = await request(ctx.app.getHttpServer())
.get('/b/patients')
.set('Authorization', `Bearer ${ctx.tokens[Role.DOCTOR]}`);
expectSuccessEnvelope(doctorResponse, 200);
const doctorPatientIds = (
doctorResponse.body.data as Array<{ id: number }>
).map((item) => item.id);
expect(doctorPatientIds).toContain(ctx.fixtures.patients.patientA1Id);
expect(doctorPatientIds).not.toContain(ctx.fixtures.patients.patientA2Id);
expect(doctorPatientIds).not.toContain(ctx.fixtures.patients.patientA3Id);
const engineerResponse = await request(ctx.app.getHttpServer())
.get('/b/patients')
.set('Authorization', `Bearer ${ctx.tokens[Role.ENGINEER]}`);
expectErrorEnvelope(engineerResponse, 403, '无权限执行当前操作');
});
it('失败SYSTEM_ADMIN 不传 hospitalId 返回 400', async () => {
const response = await request(ctx.app.getHttpServer())
.get('/b/patients')
.set('Authorization', `Bearer ${ctx.tokens[Role.SYSTEM_ADMIN]}`);
expectErrorEnvelope(
response,
400,
'系统管理员查询必须显式传入 hospitalId',
);
});
it('角色矩阵SYSTEM_ADMIN/HOSPITAL_ADMIN/DIRECTOR/LEADER/DOCTOR 可访问ENGINEER 403未登录 401', async () => {
await assertRoleMatrix({
name: 'GET /b/patients role matrix',
tokens: ctx.tokens,
expectedStatusByRole: {
[Role.SYSTEM_ADMIN]: 200,
[Role.HOSPITAL_ADMIN]: 200,
[Role.DIRECTOR]: 200,
[Role.LEADER]: 200,
[Role.DOCTOR]: 200,
[Role.ENGINEER]: 403,
},
sendAsRole: async (role, token) => {
const req = request(ctx.app.getHttpServer())
.get('/b/patients')
.set('Authorization', `Bearer ${token}`);
if (role === Role.SYSTEM_ADMIN) {
req.query({ hospitalId: ctx.fixtures.hospitalAId });
}
return req;
},
sendWithoutToken: async () =>
request(ctx.app.getHttpServer()).get('/b/patients'),
});
});
});
describe('GET /c/patients/lifecycle', () => {
it('成功:已登录用户可按 phone + idCard 查询跨院生命周期', async () => {
const response = await request(ctx.app.getHttpServer())
.get('/c/patients/lifecycle')
.query({
phone: '13800002001',
idCard: '110101199001010011',
})
.set('Authorization', `Bearer ${ctx.tokens[Role.DOCTOR]}`);
expectSuccessEnvelope(response, 200);
expect(response.body.data.phone).toBe('13800002001');
expect(response.body.data.idCard).toBe('110101199001010011');
expect(response.body.data.patientCount).toBeGreaterThanOrEqual(2);
expect(Array.isArray(response.body.data.lifecycle)).toBe(true);
});
it('失败:参数缺失返回 400', async () => {
const response = await request(ctx.app.getHttpServer())
.get('/c/patients/lifecycle')
.query({
phone: '13800002001',
})
.set('Authorization', `Bearer ${ctx.tokens[Role.DOCTOR]}`);
expectErrorEnvelope(response, 400, 'idCard 必须是字符串');
});
it('失败:不存在患者返回 404', async () => {
const response = await request(ctx.app.getHttpServer())
.get('/c/patients/lifecycle')
.query({
phone: '13800009999',
idCard: '110101199009090099',
})
.set('Authorization', `Bearer ${ctx.tokens[Role.DOCTOR]}`);
expectErrorEnvelope(response, 404, '未找到匹配的患者档案');
});
});
});