tyt-api-nest/test/e2e/specs/auth-token-revocation.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

60 lines
1.8 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 {
expectErrorEnvelope,
expectSuccessEnvelope,
} from '../helpers/e2e-http.helper.js';
describe('Auth token revocation (e2e)', () => {
let ctx: E2EContext;
beforeAll(async () => {
ctx = await createE2EContext();
});
afterAll(async () => {
await closeE2EContext(ctx);
});
it('旧 token 在 tokenValidAfter 推进后失效', async () => {
const token = ctx.tokens[Role.DOCTOR];
const originalUser = await ctx.prisma.user.findUnique({
where: { id: ctx.fixtures.users.doctorAId },
select: { tokenValidAfter: true },
});
const beforeResponse = await request(ctx.app.getHttpServer())
.get('/auth/me')
.set('Authorization', `Bearer ${token}`);
expectSuccessEnvelope(beforeResponse, 200);
try {
await ctx.prisma.user.update({
where: { id: ctx.fixtures.users.doctorAId },
// 往未来推进一分钟,确保当前 token 的 iat 一定早于失效时间。
data: { tokenValidAfter: new Date(Date.now() + 60_000) },
});
const afterResponse = await request(ctx.app.getHttpServer())
.get('/auth/me')
.set('Authorization', `Bearer ${token}`);
expectErrorEnvelope(afterResponse, 401, 'Token 已失效,请重新登录');
} finally {
if (originalUser) {
// 恢复种子用户状态,避免串行 E2E 后续用例继续拿到失效 token。
await ctx.prisma.user.update({
where: { id: ctx.fixtures.users.doctorAId },
data: { tokenValidAfter: originalUser.tokenValidAfter },
});
}
}
});
});