|
@@ -0,0 +1,109 @@
|
|
|
|
|
+import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
|
|
|
+import { JWTUtil } from '../../src/utils/jwt.util';
|
|
|
|
|
+
|
|
|
|
|
+describe('JWTUtil', () => {
|
|
|
|
|
+ const mockUser = {
|
|
|
|
|
+ id: 1,
|
|
|
|
|
+ username: 'testuser',
|
|
|
|
|
+ roles: [{ name: 'admin' }, { name: 'user' }],
|
|
|
|
|
+ openid: 'test-openid'
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ beforeEach(() => {
|
|
|
|
|
+ // 重置环境变量
|
|
|
|
|
+ process.env.JWT_SECRET = 'test-secret-key';
|
|
|
|
|
+ process.env.JWT_EXPIRES_IN = '1h';
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ describe('generateToken', () => {
|
|
|
|
|
+ it('应该成功生成JWT token', () => {
|
|
|
|
|
+ const token = JWTUtil.generateToken(mockUser);
|
|
|
|
|
+
|
|
|
|
|
+ expect(token).toBeDefined();
|
|
|
|
|
+ expect(typeof token).toBe('string');
|
|
|
|
|
+ expect(token.split('.')).toHaveLength(3); // JWT token 应该有3部分
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ it('应该使用额外的payload数据', () => {
|
|
|
|
|
+ const additionalPayload = { customField: 'customValue' };
|
|
|
|
|
+ const token = JWTUtil.generateToken(mockUser, additionalPayload);
|
|
|
|
|
+
|
|
|
|
|
+ const decoded = JWTUtil.decodeToken(token);
|
|
|
|
|
+ expect(decoded).toMatchObject({
|
|
|
|
|
+ id: mockUser.id,
|
|
|
|
|
+ username: mockUser.username,
|
|
|
|
|
+ roles: ['admin', 'user'],
|
|
|
|
|
+ openid: mockUser.openid,
|
|
|
|
|
+ customField: 'customValue'
|
|
|
|
|
+ });
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ it('当用户缺少必要字段时应该抛出错误', () => {
|
|
|
|
|
+ const invalidUser = { id: 1 } as any; // 缺少 username
|
|
|
|
|
+
|
|
|
|
|
+ expect(() => {
|
|
|
|
|
+ JWTUtil.generateToken(invalidUser);
|
|
|
|
|
+ }).toThrow('用户ID和用户名不能为空');
|
|
|
|
|
+ });
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ describe('verifyToken', () => {
|
|
|
|
|
+ it('应该成功验证有效的token', () => {
|
|
|
|
|
+ const token = JWTUtil.generateToken(mockUser);
|
|
|
|
|
+ const payload = JWTUtil.verifyToken(token);
|
|
|
|
|
+
|
|
|
|
|
+ expect(payload).toMatchObject({
|
|
|
|
|
+ id: mockUser.id,
|
|
|
|
|
+ username: mockUser.username,
|
|
|
|
|
+ roles: ['admin', 'user'],
|
|
|
|
|
+ openid: mockUser.openid
|
|
|
|
|
+ });
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ it('当token无效时应该抛出错误', () => {
|
|
|
|
|
+ const invalidToken = 'invalid.token.here';
|
|
|
|
|
+
|
|
|
|
|
+ expect(() => {
|
|
|
|
|
+ JWTUtil.verifyToken(invalidToken);
|
|
|
|
|
+ }).toThrow('无效的token');
|
|
|
|
|
+ });
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ describe('decodeToken', () => {
|
|
|
|
|
+ it('应该成功解码token', () => {
|
|
|
|
|
+ const token = JWTUtil.generateToken(mockUser);
|
|
|
|
|
+ const payload = JWTUtil.decodeToken(token);
|
|
|
|
|
+
|
|
|
|
|
+ expect(payload).toMatchObject({
|
|
|
|
|
+ id: mockUser.id,
|
|
|
|
|
+ username: mockUser.username,
|
|
|
|
|
+ roles: ['admin', 'user'],
|
|
|
|
|
+ openid: mockUser.openid
|
|
|
|
|
+ });
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ it('当token格式错误时应该返回null', () => {
|
|
|
|
|
+ const invalidToken = 'invalid';
|
|
|
|
|
+ const payload = JWTUtil.decodeToken(invalidToken);
|
|
|
|
|
+
|
|
|
|
|
+ expect(payload).toBeNull();
|
|
|
|
|
+ });
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ describe('getTokenRemainingTime', () => {
|
|
|
|
|
+ it('应该返回有效的剩余时间', () => {
|
|
|
|
|
+ const token = JWTUtil.generateToken(mockUser, {}, '1h'); // 明确指定1小时过期
|
|
|
|
|
+ const remainingTime = JWTUtil.getTokenRemainingTime(token);
|
|
|
|
|
+
|
|
|
|
|
+ expect(remainingTime).toBeGreaterThan(0);
|
|
|
|
|
+ expect(remainingTime).toBeLessThanOrEqual(3600); // 1小时
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ it('当token无效时应该返回0', () => {
|
|
|
|
|
+ const invalidToken = 'invalid.token.here';
|
|
|
|
|
+ const remainingTime = JWTUtil.getTokenRemainingTime(invalidToken);
|
|
|
|
|
+
|
|
|
|
|
+ expect(remainingTime).toBe(0);
|
|
|
|
|
+ });
|
|
|
|
|
+ });
|
|
|
|
|
+});
|