import request from 'supertest'; import { DeviceStatus, 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, uniqueSeedValue, } from '../helpers/e2e-http.helper.js'; describe('BDevicesController (e2e)', () => { let ctx: E2EContext; beforeAll(async () => { ctx = await createE2EContext(); }); afterAll(async () => { await closeE2EContext(ctx); }); async function createDevice(token: string, patientId: number) { const response = await request(ctx.app.getHttpServer()) .post('/b/devices') .set('Authorization', `Bearer ${token}`) .send({ snCode: uniqueSeedValue('device-sn'), currentPressure: 118, status: DeviceStatus.ACTIVE, patientId, }); expectSuccessEnvelope(response, 201); return response.body.data as { id: number; snCode: string; status: DeviceStatus; patient: { id: number }; }; } describe('GET /b/devices', () => { it('成功:SYSTEM_ADMIN 可分页查询设备列表', async () => { const response = await request(ctx.app.getHttpServer()) .get('/b/devices') .set('Authorization', `Bearer ${ctx.tokens[Role.SYSTEM_ADMIN]}`); expectSuccessEnvelope(response, 200); expect(Array.isArray(response.body.data.list)).toBe(true); expect(response.body.data.total).toBeGreaterThan(0); }); it('成功:HOSPITAL_ADMIN 仅能看到本院设备', async () => { const response = await request(ctx.app.getHttpServer()) .get('/b/devices') .set('Authorization', `Bearer ${ctx.tokens[Role.HOSPITAL_ADMIN]}`); expectSuccessEnvelope(response, 200); const hospitalIds = ( response.body.data.list as Array<{ patient?: { hospital?: { id: number } }; }> ) .map((item) => item.patient?.hospital?.id) .filter(Boolean); expect(hospitalIds.every((id) => id === ctx.fixtures.hospitalAId)).toBe( true, ); }); it('角色矩阵:仅 SYSTEM_ADMIN/HOSPITAL_ADMIN 可访问列表,其他角色 403,未登录 401', async () => { await assertRoleMatrix({ name: 'GET /b/devices role matrix', tokens: ctx.tokens, expectedStatusByRole: { [Role.SYSTEM_ADMIN]: 200, [Role.HOSPITAL_ADMIN]: 200, [Role.DIRECTOR]: 403, [Role.LEADER]: 403, [Role.DOCTOR]: 403, [Role.ENGINEER]: 403, }, sendAsRole: async (_role, token) => request(ctx.app.getHttpServer()) .get('/b/devices') .set('Authorization', `Bearer ${token}`), sendWithoutToken: async () => request(ctx.app.getHttpServer()).get('/b/devices'), }); }); }); describe('设备 CRUD 流程', () => { it('成功:HOSPITAL_ADMIN 可创建设备', async () => { const created = await createDevice( ctx.tokens[Role.HOSPITAL_ADMIN], ctx.fixtures.patients.patientA1Id, ); expect(created.status).toBe(DeviceStatus.ACTIVE); expect(created.patient.id).toBe(ctx.fixtures.patients.patientA1Id); expect(created.snCode).toMatch(/^DEVICE-SN-/); }); it('失败:HOSPITAL_ADMIN 绑定跨院患者返回 403', async () => { const response = await request(ctx.app.getHttpServer()) .post('/b/devices') .set('Authorization', `Bearer ${ctx.tokens[Role.HOSPITAL_ADMIN]}`) .send({ snCode: uniqueSeedValue('cross-hospital-device'), currentPressure: 120, status: DeviceStatus.ACTIVE, patientId: ctx.fixtures.patients.patientB1Id, }); expectErrorEnvelope(response, 403, '仅可绑定当前权限范围内患者'); }); it('成功:SYSTEM_ADMIN 可更新设备状态与归属患者', async () => { const created = await createDevice( ctx.tokens[Role.SYSTEM_ADMIN], ctx.fixtures.patients.patientA1Id, ); const response = await request(ctx.app.getHttpServer()) .patch(`/b/devices/${created.id}`) .set('Authorization', `Bearer ${ctx.tokens[Role.SYSTEM_ADMIN]}`) .send({ status: DeviceStatus.INACTIVE, patientId: ctx.fixtures.patients.patientA2Id, currentPressure: 99, }); expectSuccessEnvelope(response, 200); expect(response.body.data.status).toBe(DeviceStatus.INACTIVE); expect(response.body.data.patient.id).toBe( ctx.fixtures.patients.patientA2Id, ); expect(response.body.data.currentPressure).toBe(99); }); it('成功:SYSTEM_ADMIN 可删除未被任务引用的设备', async () => { const created = await createDevice( ctx.tokens[Role.SYSTEM_ADMIN], ctx.fixtures.patients.patientA1Id, ); const response = await request(ctx.app.getHttpServer()) .delete(`/b/devices/${created.id}`) .set('Authorization', `Bearer ${ctx.tokens[Role.SYSTEM_ADMIN]}`); expectSuccessEnvelope(response, 200); expect(response.body.data.id).toBe(created.id); }); }); });