2. 扩展患者手术与材料模型,更新种子数据 3. 新增字典模块,增强设备植入目录管理能力 4. 重构患者后台服务与表单链路,统一权限与参数校验 5. 管理台新增字典页面并改造患者/设备页面与路由权限 6. 补充字典及相关领域 e2e 测试并更新文档"
134 lines
4.5 KiB
TypeScript
134 lines
4.5 KiB
TypeScript
import request from 'supertest';
|
||
import { Role } from '../../../src/generated/prisma/enums.js';
|
||
import {
|
||
closeE2EContext,
|
||
createE2EContext,
|
||
type E2EContext,
|
||
} from '../helpers/e2e-context.helper.js';
|
||
import {
|
||
expectSuccessEnvelope,
|
||
uniqueSeedValue,
|
||
} from '../helpers/e2e-http.helper.js';
|
||
import { assertRoleMatrix } from '../helpers/e2e-matrix.helper.js';
|
||
|
||
describe('BDictionariesController (e2e)', () => {
|
||
let ctx: E2EContext;
|
||
|
||
beforeAll(async () => {
|
||
ctx = await createE2EContext();
|
||
});
|
||
|
||
afterAll(async () => {
|
||
await closeE2EContext(ctx);
|
||
});
|
||
|
||
describe('GET /b/dictionaries', () => {
|
||
it('成功:DOCTOR 可查询启用中的系统字典', async () => {
|
||
const response = await request(ctx.app.getHttpServer())
|
||
.get('/b/dictionaries')
|
||
.query({ type: 'PRIMARY_DISEASE' })
|
||
.set('Authorization', `Bearer ${ctx.tokens[Role.DOCTOR]}`);
|
||
|
||
expectSuccessEnvelope(response, 200);
|
||
expect(
|
||
(response.body.data as Array<{ label: string; enabled: boolean }>).some(
|
||
(item) => item.label === '先天性脑积水' && item.enabled === true,
|
||
),
|
||
).toBe(true);
|
||
});
|
||
});
|
||
|
||
describe('字典维护流程', () => {
|
||
it('成功:SYSTEM_ADMIN 可新增、更新、删除字典项,非管理员读取不到停用项', async () => {
|
||
const createLabel = uniqueSeedValue('字典项');
|
||
const updateLabel = `${createLabel}-启用`;
|
||
|
||
const createResponse = await request(ctx.app.getHttpServer())
|
||
.post('/b/dictionaries')
|
||
.set('Authorization', `Bearer ${ctx.tokens[Role.SYSTEM_ADMIN]}`)
|
||
.send({
|
||
type: 'SHUNT_MODE',
|
||
label: createLabel,
|
||
sortOrder: 999,
|
||
enabled: false,
|
||
});
|
||
|
||
expectSuccessEnvelope(createResponse, 201);
|
||
const createdId = createResponse.body.data.id as number;
|
||
|
||
const doctorReadResponse = await request(ctx.app.getHttpServer())
|
||
.get('/b/dictionaries')
|
||
.query({ type: 'SHUNT_MODE' })
|
||
.set('Authorization', `Bearer ${ctx.tokens[Role.DOCTOR]}`);
|
||
|
||
expectSuccessEnvelope(doctorReadResponse, 200);
|
||
expect(
|
||
(doctorReadResponse.body.data as Array<{ label: string }>).some(
|
||
(item) => item.label === createLabel,
|
||
),
|
||
).toBe(false);
|
||
|
||
const adminReadResponse = await request(ctx.app.getHttpServer())
|
||
.get('/b/dictionaries')
|
||
.query({ type: 'SHUNT_MODE', includeDisabled: true })
|
||
.set('Authorization', `Bearer ${ctx.tokens[Role.SYSTEM_ADMIN]}`);
|
||
|
||
expectSuccessEnvelope(adminReadResponse, 200);
|
||
expect(
|
||
(adminReadResponse.body.data as Array<{ label: string }>).some(
|
||
(item) => item.label === createLabel,
|
||
),
|
||
).toBe(true);
|
||
|
||
const updateResponse = await request(ctx.app.getHttpServer())
|
||
.patch(`/b/dictionaries/${createdId}`)
|
||
.set('Authorization', `Bearer ${ctx.tokens[Role.SYSTEM_ADMIN]}`)
|
||
.send({
|
||
label: updateLabel,
|
||
enabled: true,
|
||
});
|
||
|
||
expectSuccessEnvelope(updateResponse, 200);
|
||
expect(updateResponse.body.data.label).toBe(updateLabel);
|
||
expect(updateResponse.body.data.enabled).toBe(true);
|
||
|
||
const deleteResponse = await request(ctx.app.getHttpServer())
|
||
.delete(`/b/dictionaries/${createdId}`)
|
||
.set('Authorization', `Bearer ${ctx.tokens[Role.SYSTEM_ADMIN]}`);
|
||
|
||
expectSuccessEnvelope(deleteResponse, 200);
|
||
expect(deleteResponse.body.data.id).toBe(createdId);
|
||
});
|
||
|
||
it('角色矩阵:仅 SYSTEM_ADMIN 可维护字典,其他角色 403,未登录 401', async () => {
|
||
await assertRoleMatrix({
|
||
name: 'POST /b/dictionaries role matrix',
|
||
tokens: ctx.tokens,
|
||
expectedStatusByRole: {
|
||
[Role.SYSTEM_ADMIN]: 201,
|
||
[Role.HOSPITAL_ADMIN]: 403,
|
||
[Role.DIRECTOR]: 403,
|
||
[Role.LEADER]: 403,
|
||
[Role.DOCTOR]: 403,
|
||
[Role.ENGINEER]: 403,
|
||
},
|
||
sendAsRole: async (_role, token) =>
|
||
request(ctx.app.getHttpServer())
|
||
.post('/b/dictionaries')
|
||
.set('Authorization', `Bearer ${token}`)
|
||
.send({
|
||
type: 'DISTAL_SHUNT_DIRECTION',
|
||
label: uniqueSeedValue('矩阵字典项'),
|
||
}),
|
||
sendWithoutToken: async () =>
|
||
request(ctx.app.getHttpServer())
|
||
.post('/b/dictionaries')
|
||
.send({
|
||
type: 'DISTAL_SHUNT_DIRECTION',
|
||
label: uniqueSeedValue('匿名字典项'),
|
||
}),
|
||
});
|
||
});
|
||
});
|
||
});
|