208 lines
5.5 KiB
TypeScript
208 lines
5.5 KiB
TypeScript
/**
|
||
* /api/auth/me 端点测试
|
||
*
|
||
* 测试范围:
|
||
* - Cookie 认证验证
|
||
* - 用户信息返回
|
||
* - 错误处理
|
||
*
|
||
* 注意:这些是单元测试,不依赖数据库连接
|
||
*/
|
||
|
||
import { describe, it, expect } from '@jest/globals';
|
||
|
||
describe('/api/auth/me 端点测试', () => {
|
||
// 模拟测试用户数据
|
||
const testUser = {
|
||
id: 'test-user-id',
|
||
email: 'me-test@test.com',
|
||
name: '认证测试用户',
|
||
};
|
||
|
||
describe('认证验证', () => {
|
||
it('应该在没有 Cookie 时返回 401', () => {
|
||
// 模拟无 Cookie 的请求
|
||
const hasAuthCookie = false;
|
||
|
||
expect(hasAuthCookie).toBe(false);
|
||
// 预期响应状态码 401
|
||
});
|
||
|
||
it('应该在 Token 无效时返回 401', () => {
|
||
const invalidToken = 'invalid.token.here';
|
||
|
||
// 验证 token 格式无效
|
||
const isValidFormat = invalidToken.split('.').length === 3;
|
||
const hasValidSignature = false; // 模拟签名验证失败
|
||
|
||
expect(isValidFormat).toBe(true); // 格式正确但签名无效
|
||
expect(hasValidSignature).toBe(false);
|
||
});
|
||
|
||
it('应该在 Token 过期时返回 401 并清除 Cookie', () => {
|
||
// 模拟过期 token 的处理
|
||
const isExpired = true;
|
||
const shouldClearCookie = isExpired;
|
||
|
||
expect(shouldClearCookie).toBe(true);
|
||
});
|
||
|
||
it('应该在用户不存在时返回 401 并清除 Cookie', () => {
|
||
// 模拟用户被删除的情况
|
||
const userExists = false;
|
||
const shouldClearCookie = !userExists;
|
||
|
||
expect(shouldClearCookie).toBe(true);
|
||
});
|
||
|
||
it('应该在用户被禁用时返回 403', () => {
|
||
// 模拟用户状态检查
|
||
const userStatus = 'INACTIVE';
|
||
const isDisabled = userStatus === 'INACTIVE' || userStatus === 'SUSPENDED';
|
||
|
||
expect(isDisabled).toBe(true);
|
||
});
|
||
});
|
||
|
||
describe('用户信息返回', () => {
|
||
it('应该返回完整的用户信息', () => {
|
||
const expectedFields = [
|
||
'id',
|
||
'email',
|
||
'name',
|
||
'role',
|
||
'status',
|
||
'isFirstLogin',
|
||
'department',
|
||
'team',
|
||
'avatar',
|
||
'lastLoginAt',
|
||
];
|
||
|
||
// 模拟用户响应数据
|
||
const userResponse = {
|
||
id: testUser.id,
|
||
email: testUser.email,
|
||
name: testUser.name,
|
||
role: 'DESIGNER',
|
||
status: 'ACTIVE',
|
||
isFirstLogin: false,
|
||
department: null,
|
||
team: null,
|
||
avatar: null,
|
||
lastLoginAt: null,
|
||
};
|
||
|
||
for (const field of expectedFields) {
|
||
expect(userResponse).toHaveProperty(field);
|
||
}
|
||
});
|
||
|
||
it('不应该返回密码字段', () => {
|
||
const userResponse = {
|
||
id: testUser.id,
|
||
email: testUser.email,
|
||
name: testUser.name,
|
||
role: 'DESIGNER',
|
||
status: 'ACTIVE',
|
||
};
|
||
|
||
expect(userResponse).not.toHaveProperty('password');
|
||
expect(userResponse).not.toHaveProperty('hashedPassword');
|
||
});
|
||
|
||
it('应该返回关联的部门和团队信息', () => {
|
||
const userWithDepartment = {
|
||
id: testUser.id,
|
||
department: {
|
||
id: 'dept-1',
|
||
name: '研发部',
|
||
},
|
||
team: {
|
||
id: 'team-1',
|
||
name: '前端组',
|
||
},
|
||
};
|
||
|
||
expect(userWithDepartment.department).toBeDefined();
|
||
expect(userWithDepartment.department.name).toBe('研发部');
|
||
expect(userWithDepartment.team).toBeDefined();
|
||
expect(userWithDepartment.team.name).toBe('前端组');
|
||
});
|
||
|
||
it('应该返回 authenticated 标志', () => {
|
||
const successResponse = {
|
||
authenticated: true,
|
||
user: {
|
||
id: testUser.id,
|
||
email: testUser.email,
|
||
},
|
||
};
|
||
|
||
expect(successResponse.authenticated).toBe(true);
|
||
expect(successResponse.user).toBeDefined();
|
||
});
|
||
});
|
||
|
||
describe('响应格式', () => {
|
||
it('成功响应应该包含 authenticated: true', () => {
|
||
const response = {
|
||
authenticated: true,
|
||
user: { id: 'user-id' },
|
||
};
|
||
|
||
expect(response.authenticated).toBe(true);
|
||
});
|
||
|
||
it('失败响应应该包含 authenticated: false', () => {
|
||
const response = {
|
||
error: '未登录',
|
||
authenticated: false,
|
||
};
|
||
|
||
expect(response.authenticated).toBe(false);
|
||
expect(response.error).toBeDefined();
|
||
});
|
||
|
||
it('错误响应应该包含错误消息', () => {
|
||
const errorCases = [
|
||
{ error: '未登录', authenticated: false },
|
||
{ error: 'Token 无效或已过期', authenticated: false },
|
||
{ error: '用户不存在', authenticated: false },
|
||
{ error: '账号已被禁用', authenticated: false },
|
||
];
|
||
|
||
for (const errorCase of errorCases) {
|
||
expect(errorCase.error).toBeDefined();
|
||
expect(typeof errorCase.error).toBe('string');
|
||
expect(errorCase.authenticated).toBe(false);
|
||
}
|
||
});
|
||
});
|
||
});
|
||
|
||
describe('Cookie 安全配置', () => {
|
||
it('应该使用正确的 Cookie 配置', () => {
|
||
const cookieConfig = {
|
||
name: 'auth-token',
|
||
httpOnly: true,
|
||
secure: process.env.NODE_ENV === 'production',
|
||
sameSite: 'lax' as const,
|
||
maxAge: 60 * 60 * 24 * 7, // 7 天
|
||
path: '/',
|
||
};
|
||
|
||
expect(cookieConfig.httpOnly).toBe(true);
|
||
expect(cookieConfig.sameSite).toBe('lax');
|
||
expect(cookieConfig.path).toBe('/');
|
||
});
|
||
|
||
it('在生产环境应该启用 secure 标志', () => {
|
||
const isProduction = process.env.NODE_ENV === 'production';
|
||
const shouldBeSecure = isProduction;
|
||
|
||
// 在测试环境中,NODE_ENV 通常是 'test'
|
||
expect(typeof shouldBeSecure).toBe('boolean');
|
||
});
|
||
});
|