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 }, }); } } }); });