refactor:项目架构重构和命名规范化

- 统一文件命名为snake_case格式(kebab-case  snake_case)
- 重构zulip模块为zulip_core,明确Core层职责
- 重构user-mgmt模块为user_mgmt,统一命名规范
- 调整模块依赖关系,优化架构分层
- 删除过时的文件和目录结构
- 更新相关文档和配置文件

本次重构涉及大量文件重命名和模块重组,
旨在建立更清晰的项目架构和统一的命名规范。
This commit is contained in:
moyin
2026-01-08 00:14:14 +08:00
parent 4fa4bd1a70
commit bb796a2469
178 changed files with 24767 additions and 3484 deletions

View File

@@ -0,0 +1,366 @@
/**
* 登录业务服务测试
*
* 功能描述:
* - 测试登录相关的业务逻辑
* - 测试业务层与核心层的集成
* - 测试各种异常情况处理
*
* 注意JWT相关功能已移至Core层此测试专注于Business层逻辑
*
* @author moyin
* @version 1.0.1
* @since 2025-01-06
* @lastModified 2026-01-07
*/
import { Test, TestingModule } from '@nestjs/testing';
import { LoginService } from './login.service';
import { LoginCoreService } from '../../core/login_core/login_core.service';
import { ZulipAccountService } from '../../core/zulip_core/services/zulip_account.service';
import { ZulipAccountsService } from '../../core/db/zulip_accounts/zulip_accounts.service';
import { ApiKeySecurityService } from '../../core/zulip_core/services/api_key_security.service';
import { UserStatus } from '../../core/db/users/user_status.enum';
describe('LoginService', () => {
let service: LoginService;
let loginCoreService: jest.Mocked<LoginCoreService>;
let zulipAccountService: jest.Mocked<ZulipAccountService>;
let zulipAccountsService: jest.Mocked<ZulipAccountsService>;
let apiKeySecurityService: jest.Mocked<ApiKeySecurityService>;
const mockUser = {
id: BigInt(1),
username: 'testuser',
email: 'test@example.com',
phone: '+8613800138000',
password_hash: '$2b$12$hashedpassword',
nickname: '测试用户',
github_id: null as string | null,
avatar_url: null as string | null,
role: 1,
status: UserStatus.ACTIVE,
email_verified: true,
created_at: new Date(),
updated_at: new Date(),
};
const mockTokenPair = {
access_token: 'mock_access_token',
refresh_token: 'mock_refresh_token',
expires_in: 604800,
token_type: 'Bearer'
};
beforeEach(async () => {
// Mock environment variables for Zulip
process.env.ZULIP_SERVER_URL = 'https://test.zulipchat.com';
process.env.ZULIP_BOT_EMAIL = 'test-bot@test.zulipchat.com';
process.env.ZULIP_BOT_API_KEY = 'test_api_key_12345';
const mockLoginCoreService = {
login: jest.fn(),
register: jest.fn(),
githubOAuth: jest.fn(),
sendPasswordResetCode: jest.fn(),
resetPassword: jest.fn(),
changePassword: jest.fn(),
sendEmailVerification: jest.fn(),
verifyEmailCode: jest.fn(),
resendEmailVerification: jest.fn(),
verificationCodeLogin: jest.fn(),
sendLoginVerificationCode: jest.fn(),
debugVerificationCode: jest.fn(),
deleteUser: jest.fn(),
generateTokenPair: jest.fn(),
};
const mockZulipAccountService = {
initializeAdminClient: jest.fn(),
createZulipAccount: jest.fn(),
linkGameAccount: jest.fn(),
};
const mockZulipAccountsService = {
findByGameUserId: jest.fn(),
create: jest.fn(),
deleteByGameUserId: jest.fn(),
};
const mockApiKeySecurityService = {
storeApiKey: jest.fn(),
};
const module: TestingModule = await Test.createTestingModule({
providers: [
LoginService,
{
provide: LoginCoreService,
useValue: mockLoginCoreService,
},
{
provide: ZulipAccountService,
useValue: mockZulipAccountService,
},
{
provide: 'ZulipAccountsService',
useValue: mockZulipAccountsService,
},
{
provide: ApiKeySecurityService,
useValue: mockApiKeySecurityService,
},
],
}).compile();
service = module.get<LoginService>(LoginService);
loginCoreService = module.get(LoginCoreService);
zulipAccountService = module.get(ZulipAccountService);
zulipAccountsService = module.get('ZulipAccountsService');
apiKeySecurityService = module.get(ApiKeySecurityService);
// Setup default mocks
loginCoreService.generateTokenPair.mockResolvedValue(mockTokenPair);
zulipAccountService.initializeAdminClient.mockResolvedValue(true);
zulipAccountService.createZulipAccount.mockResolvedValue({
success: true,
userId: 123,
email: 'test@example.com',
apiKey: 'mock_api_key'
});
zulipAccountsService.findByGameUserId.mockResolvedValue(null);
zulipAccountsService.create.mockResolvedValue({} as any);
apiKeySecurityService.storeApiKey.mockResolvedValue(undefined);
});
afterEach(() => {
jest.clearAllMocks();
});
it('should be defined', () => {
expect(service).toBeDefined();
});
describe('login', () => {
it('should login successfully and return JWT tokens', async () => {
loginCoreService.login.mockResolvedValue({
user: mockUser,
isNewUser: false
});
const result = await service.login({
identifier: 'testuser',
password: 'password123'
});
expect(result.success).toBe(true);
expect(result.data?.user.username).toBe('testuser');
expect(result.data?.access_token).toBe(mockTokenPair.access_token);
expect(result.data?.refresh_token).toBe(mockTokenPair.refresh_token);
expect(loginCoreService.login).toHaveBeenCalledWith({
identifier: 'testuser',
password: 'password123'
});
expect(loginCoreService.generateTokenPair).toHaveBeenCalledWith(mockUser);
});
it('should handle login failure', async () => {
loginCoreService.login.mockRejectedValue(new Error('用户名或密码错误'));
const result = await service.login({
identifier: 'testuser',
password: 'wrongpassword'
});
expect(result.success).toBe(false);
expect(result.message).toBe('用户名或密码错误');
expect(result.error_code).toBe('LOGIN_FAILED');
});
});
describe('register', () => {
it('should register successfully with JWT tokens', async () => {
loginCoreService.register.mockResolvedValue({
user: mockUser,
isNewUser: true
});
const result = await service.register({
username: 'newuser',
password: 'password123',
nickname: '新用户',
email: 'newuser@example.com',
email_verification_code: '123456'
});
expect(result.success).toBe(true);
expect(result.data?.user.username).toBe('testuser');
expect(result.data?.access_token).toBe(mockTokenPair.access_token);
expect(result.data?.is_new_user).toBe(true);
expect(loginCoreService.register).toHaveBeenCalled();
expect(loginCoreService.generateTokenPair).toHaveBeenCalledWith(mockUser);
});
it('should handle register failure', async () => {
loginCoreService.register.mockRejectedValue(new Error('用户名已存在'));
const result = await service.register({
username: 'existinguser',
password: 'password123',
nickname: '用户'
});
expect(result.success).toBe(false);
expect(result.message).toBe('用户名已存在');
expect(result.error_code).toBe('REGISTER_FAILED');
});
});
describe('githubOAuth', () => {
it('should handle GitHub OAuth successfully', async () => {
loginCoreService.githubOAuth.mockResolvedValue({
user: mockUser,
isNewUser: false
});
const result = await service.githubOAuth({
github_id: '12345',
username: 'githubuser',
nickname: 'GitHub用户',
email: 'github@example.com'
});
expect(result.success).toBe(true);
expect(result.data?.user.username).toBe('testuser');
expect(result.data?.access_token).toBe(mockTokenPair.access_token);
expect(loginCoreService.githubOAuth).toHaveBeenCalled();
expect(loginCoreService.generateTokenPair).toHaveBeenCalledWith(mockUser);
});
});
describe('sendPasswordResetCode', () => {
it('should handle sendPasswordResetCode in test mode', async () => {
loginCoreService.sendPasswordResetCode.mockResolvedValue({
code: '123456',
isTestMode: true
});
const result = await service.sendPasswordResetCode('test@example.com');
expect(result.success).toBe(false); // Test mode returns false
expect(result.data?.verification_code).toBe('123456');
expect(result.data?.is_test_mode).toBe(true);
expect(loginCoreService.sendPasswordResetCode).toHaveBeenCalledWith('test@example.com');
});
});
describe('resetPassword', () => {
it('should handle resetPassword successfully', async () => {
loginCoreService.resetPassword.mockResolvedValue(undefined);
const result = await service.resetPassword({
identifier: 'test@example.com',
verificationCode: '123456',
newPassword: 'newpassword123'
});
expect(result.success).toBe(true);
expect(result.message).toBe('密码重置成功');
expect(loginCoreService.resetPassword).toHaveBeenCalled();
});
});
describe('changePassword', () => {
it('should handle changePassword successfully', async () => {
loginCoreService.changePassword.mockResolvedValue(undefined);
const result = await service.changePassword(BigInt(1), 'oldpassword', 'newpassword');
expect(result.success).toBe(true);
expect(result.message).toBe('密码修改成功');
expect(loginCoreService.changePassword).toHaveBeenCalledWith(BigInt(1), 'oldpassword', 'newpassword');
});
});
describe('sendEmailVerification', () => {
it('should handle sendEmailVerification in test mode', async () => {
loginCoreService.sendEmailVerification.mockResolvedValue({
code: '123456',
isTestMode: true
});
const result = await service.sendEmailVerification('test@example.com');
expect(result.success).toBe(false); // Test mode returns false
expect(result.data?.verification_code).toBe('123456');
expect(result.data?.is_test_mode).toBe(true);
expect(loginCoreService.sendEmailVerification).toHaveBeenCalledWith('test@example.com');
});
});
describe('verifyEmailCode', () => {
it('should handle verifyEmailCode successfully', async () => {
loginCoreService.verifyEmailCode.mockResolvedValue(true);
const result = await service.verifyEmailCode('test@example.com', '123456');
expect(result.success).toBe(true);
expect(result.message).toBe('邮箱验证成功');
expect(loginCoreService.verifyEmailCode).toHaveBeenCalledWith('test@example.com', '123456');
});
});
describe('verificationCodeLogin', () => {
it('should handle verificationCodeLogin successfully', async () => {
loginCoreService.verificationCodeLogin.mockResolvedValue({
user: mockUser,
isNewUser: false
});
const result = await service.verificationCodeLogin({
identifier: 'test@example.com',
verificationCode: '123456'
});
expect(result.success).toBe(true);
expect(result.data?.user.username).toBe('testuser');
expect(result.data?.access_token).toBe(mockTokenPair.access_token);
expect(loginCoreService.verificationCodeLogin).toHaveBeenCalled();
expect(loginCoreService.generateTokenPair).toHaveBeenCalledWith(mockUser);
});
});
describe('sendLoginVerificationCode', () => {
it('should handle sendLoginVerificationCode successfully', async () => {
loginCoreService.sendLoginVerificationCode.mockResolvedValue({
code: '123456',
isTestMode: true
});
const result = await service.sendLoginVerificationCode('test@example.com');
expect(result.success).toBe(false); // Test mode returns false
expect(result.data?.verification_code).toBe('123456');
expect(result.data?.is_test_mode).toBe(true);
expect(loginCoreService.sendLoginVerificationCode).toHaveBeenCalledWith('test@example.com');
});
});
describe('debugVerificationCode', () => {
it('should handle debugVerificationCode successfully', async () => {
const mockDebugInfo = {
email: 'test@example.com',
hasCode: true,
codeExpiry: new Date()
};
loginCoreService.debugVerificationCode.mockResolvedValue(mockDebugInfo);
const result = await service.debugVerificationCode('test@example.com');
expect(result.success).toBe(true);
expect(result.data).toEqual(mockDebugInfo);
expect(loginCoreService.debugVerificationCode).toHaveBeenCalledWith('test@example.com');
});
});
});