import { describe, it, expect, beforeEach, vi, afterEach } from 'vitest'; import { testClient } from 'hono/testing'; import { authRoutes } from '../../src/routes'; import { IntegrationTestDatabase, setupIntegrationDatabaseHooksWithEntities } from '@d8d/shared-test-util'; import { Role, UserEntity } from '@d8d/user-module'; import { redisUtil } from '@d8d/shared-utils'; import { File } from '@d8d/file-module'; // Mock MiniAuthService 的 decryptPhoneNumber 方法 vi.mock('../../src/services/mini-auth.service', () => ({ MiniAuthService: vi.fn().mockImplementation(() => ({ decryptPhoneNumber: vi.fn().mockImplementation(async (encryptedData: string, iv: string, sessionKey: string) => { // 模拟解密过程 if (!encryptedData || !iv || !sessionKey) { throw { code: 400, message: '加密数据或初始向量不能为空' }; } // 根据不同的加密数据返回不同的手机号用于测试 if (encryptedData === 'valid_encrypted_data') { return '13800138000'; } else if (encryptedData === 'another_valid_data') { return '13900139000'; } else { throw { code: 400, message: '解密失败' }; } }) })) })); // 设置集成测试钩子 setupIntegrationDatabaseHooksWithEntities([UserEntity, File, Role]) describe('手机号解密API集成测试', () => { let client: ReturnType>; let testToken: string; let testUser: UserEntity; let getSessionKeySpy: any; beforeEach(async () => { // 创建测试客户端 client = testClient(authRoutes); // 获取数据源 const dataSource = await IntegrationTestDatabase.getDataSource(); // 创建测试用户 const userRepository = dataSource.getRepository(UserEntity); testUser = userRepository.create({ username: `test_user_${Date.now()}`, password: 'test_password', nickname: '测试用户', phone: null, // 初始手机号为null registrationSource: 'web' }); await userRepository.save(testUser); // 生成测试用户的token // 这里简化处理,实际项目中应该使用正确的JWT生成方法 testToken = 'test_jwt_token'; // 使用 spyOn 来 mock getSessionKey 方法 getSessionKeySpy = vi.spyOn(redisUtil, 'getSessionKey').mockResolvedValue('mock-session-key'); }); afterEach(() => { // 清理 spy if (getSessionKeySpy) { getSessionKeySpy.mockRestore(); } }); describe('POST /auth/phone-decrypt', () => { it('应该成功解密手机号并更新用户信息', async () => { const requestData = { encryptedData: 'valid_encrypted_data', iv: 'encryption_iv' }; const response = await client['phone-decrypt'].$post({ json: requestData }, { headers: { 'Authorization': `Bearer ${testToken}` } }); expect(response.status).toBe(200); if (response.status === 200) { const data = await response.json(); // 验证响应数据格式 expect(data).toHaveProperty('phoneNumber'); expect(data).toHaveProperty('user'); expect(data.phoneNumber).toBe('13800138000'); expect(data.user.phone).toBe('13800138000'); expect(data.user.id).toBe(testUser.id); } // 验证数据库中的用户手机号已更新 const dataSource = await IntegrationTestDatabase.getDataSource(); const userRepository = dataSource.getRepository(UserEntity); const updatedUser = await userRepository.findOne({ where: { id: testUser.id } }); expect(updatedUser?.phone).toBe('13800138000'); }); it('应该处理用户不存在的情况', async () => { const requestData = { encryptedData: 'valid_encrypted_data', iv: 'encryption_iv' }; // 使用不存在的用户ID生成token const nonExistentUserToken = 'non_existent_user_token'; const response = await client['phone-decrypt'].$post({ json: requestData }, { headers: { 'Authorization': `Bearer ${nonExistentUserToken}` } }); // 当用户不存在时,应该返回401或404 expect(response.status).toBe(401); }); it('应该处理解密失败的情况', async () => { const requestData = { encryptedData: '', // 空加密数据 iv: 'encryption_iv' }; const response = await client['phone-decrypt'].$post({ json: requestData }, { headers: { 'Authorization': `Bearer ${testToken}` } }); expect(response.status).toBe(400); if (response.status === 400) { const data = await response.json(); expect(data.message).toBe('加密数据或初始向量不能为空'); } }); it('应该处理无效的加密数据', async () => { const requestData = { encryptedData: 'invalid_encrypted_data', iv: 'encryption_iv' }; const response = await client['phone-decrypt'].$post({ json: requestData }, { headers: { 'Authorization': `Bearer ${testToken}` } }); expect(response.status).toBe(400); if (response.status === 400) { const data = await response.json(); expect(data.message).toBe('解密失败'); } }); it('应该拒绝未认证用户的访问', async () => { const requestData = { encryptedData: 'valid_encrypted_data', iv: 'encryption_iv' }; const response = await client['phone-decrypt'].$post({ json: requestData }); expect(response.status).toBe(401); }); it('应该拒绝无效token的访问', async () => { const requestData = { encryptedData: 'valid_encrypted_data', iv: 'encryption_iv' }; const response = await client['phone-decrypt'].$post({ json: requestData }, { headers: { 'Authorization': 'Bearer invalid_token' } }); expect(response.status).toBe(401); }); it('应该处理sessionKey过期的情况', async () => { const requestData = { encryptedData: 'valid_encrypted_data', iv: 'encryption_iv' }; // 模拟 sessionKey 过期的情况 getSessionKeySpy.mockResolvedValue(null); const response = await client['phone-decrypt'].$post({ json: requestData }, { headers: { 'Authorization': `Bearer ${testToken}` } }); expect(response.status).toBe(400); if (response.status === 400) { const data = await response.json(); expect(data.message).toBe('sessionKey已过期,请重新登录'); } }); }); });