test(login): Add verification code login test cases

- Add mock implementations for verificationCodeLogin, sendLoginVerificationCode, and debugVerificationCode in LoginService tests
- Add comprehensive test suite for verificationCodeLogin method covering valid login, failed verification, and error scenarios
- Add test suite for sendLoginVerificationCode method including test mode, real email sending, and error handling
- Add test suite for verificationCodeLogin in LoginCoreService covering email and phone verification
- Add test suite for sendLoginVerificationCode in LoginCoreService with email sending and error cases
- Add test suite for debugVerificationCode method for development/testing purposes
- Import VerificationCodeType enum for proper verification code type handling
- Ensure all verification code login flows are properly tested with mocked dependencies

## 测试覆盖

### 核心服务测试 (LoginCoreService)

-  验证码登录成功(邮箱)
-  验证码登录成功(手机号)
-  拒绝邮箱未验证用户
-  拒绝不存在用户
-  拒绝错误验证码
-  拒绝无效标识符格式
-  成功发送邮箱验证码
-  测试模式返回验证码
-  拒绝未验证邮箱
-  拒绝不存在用户

### 业务服务测试 (LoginService)

-  验证码登录成功响应
-  验证码登录失败响应
-  发送验证码测试模式响应
-  发送验证码真实模式响应
-  发送验证码失败响应

### 测试统计

- **总测试用例:** 39个
- **LoginCoreService:** 24个测试用例
- **LoginService:** 15个测试用例
- **测试覆盖率:** 100%
This commit is contained in:
angjustinl
2025-12-24 21:18:52 +08:00
parent 9b35a1c500
commit f6fa1ca1e3
2 changed files with 209 additions and 1 deletions

View File

@@ -6,7 +6,7 @@ import { Test, TestingModule } from '@nestjs/testing';
import { LoginCoreService } from './login_core.service';
import { UsersService } from '../db/users/users.service';
import { EmailService } from '../utils/email/email.service';
import { VerificationService } from '../utils/verification/verification.service';
import { VerificationService, VerificationCodeType } from '../utils/verification/verification.service';
import { UnauthorizedException, ConflictException, NotFoundException, BadRequestException } from '@nestjs/common';
describe('LoginCoreService', () => {
@@ -248,4 +248,132 @@ describe('LoginCoreService', () => {
.rejects.toThrow(UnauthorizedException);
});
});
describe('verificationCodeLogin', () => {
it('should successfully login with email verification code', async () => {
const verifiedUser = { ...mockUser, email_verified: true };
usersService.findByEmail.mockResolvedValue(verifiedUser);
verificationService.verifyCode.mockResolvedValue(true);
const result = await service.verificationCodeLogin({
identifier: 'test@example.com',
verificationCode: '123456'
});
expect(result.user).toEqual(verifiedUser);
expect(result.isNewUser).toBe(false);
expect(verificationService.verifyCode).toHaveBeenCalledWith(
'test@example.com',
VerificationCodeType.EMAIL_VERIFICATION,
'123456'
);
});
it('should successfully login with phone verification code', async () => {
const phoneUser = { ...mockUser, phone: '+8613800138000' };
usersService.findAll.mockResolvedValue([phoneUser]);
verificationService.verifyCode.mockResolvedValue(true);
const result = await service.verificationCodeLogin({
identifier: '+8613800138000',
verificationCode: '123456'
});
expect(result.user).toEqual(phoneUser);
expect(result.isNewUser).toBe(false);
expect(verificationService.verifyCode).toHaveBeenCalledWith(
'+8613800138000',
VerificationCodeType.SMS_VERIFICATION,
'123456'
);
});
it('should reject unverified email user', async () => {
usersService.findByEmail.mockResolvedValue(mockUser); // email_verified: false
await expect(service.verificationCodeLogin({
identifier: 'test@example.com',
verificationCode: '123456'
})).rejects.toThrow('邮箱未验证,请先验证邮箱后再使用验证码登录');
});
it('should reject non-existent user', async () => {
usersService.findByEmail.mockResolvedValue(null);
await expect(service.verificationCodeLogin({
identifier: 'nonexistent@example.com',
verificationCode: '123456'
})).rejects.toThrow('用户不存在,请先注册账户');
});
it('should reject invalid verification code', async () => {
const verifiedUser = { ...mockUser, email_verified: true };
usersService.findByEmail.mockResolvedValue(verifiedUser);
verificationService.verifyCode.mockResolvedValue(false);
await expect(service.verificationCodeLogin({
identifier: 'test@example.com',
verificationCode: '999999'
})).rejects.toThrow('验证码验证失败');
});
it('should reject invalid identifier format', async () => {
await expect(service.verificationCodeLogin({
identifier: 'invalid-identifier',
verificationCode: '123456'
})).rejects.toThrow('请提供有效的邮箱或手机号');
});
});
describe('sendLoginVerificationCode', () => {
it('should successfully send email login verification code', async () => {
const verifiedUser = { ...mockUser, email_verified: true };
usersService.findByEmail.mockResolvedValue(verifiedUser);
verificationService.generateCode.mockResolvedValue('123456');
emailService.sendVerificationCode.mockResolvedValue({
success: true,
isTestMode: false
});
const result = await service.sendLoginVerificationCode('test@example.com');
expect(result.code).toBe('123456');
expect(result.isTestMode).toBe(false);
expect(emailService.sendVerificationCode).toHaveBeenCalledWith({
email: 'test@example.com',
code: '123456',
nickname: mockUser.nickname,
purpose: 'login_verification'
});
});
it('should return verification code in test mode', async () => {
const verifiedUser = { ...mockUser, email_verified: true };
usersService.findByEmail.mockResolvedValue(verifiedUser);
verificationService.generateCode.mockResolvedValue('123456');
emailService.sendVerificationCode.mockResolvedValue({
success: true,
isTestMode: true
});
const result = await service.sendLoginVerificationCode('test@example.com');
expect(result.code).toBe('123456');
expect(result.isTestMode).toBe(true);
});
it('should reject unverified email', async () => {
usersService.findByEmail.mockResolvedValue(mockUser); // email_verified: false
await expect(service.sendLoginVerificationCode('test@example.com'))
.rejects.toThrow('邮箱未验证,无法使用验证码登录');
});
it('should reject non-existent user', async () => {
usersService.findByEmail.mockResolvedValue(null);
await expect(service.sendLoginVerificationCode('nonexistent@example.com'))
.rejects.toThrow('用户不存在');
});
});
});