范围: src/business/zulip/README.md - 补充对外提供的接口章节(14个公共方法) - 添加使用的项目内部依赖说明(7个依赖) - 完善核心特性描述(5个特性) - 添加潜在风险评估(4个风险及缓解措施) - 优化文档结构和内容完整性
338 lines
10 KiB
TypeScript
338 lines
10 KiB
TypeScript
/**
|
||
* Zulip账号管理控制器测试
|
||
*
|
||
* 功能描述:
|
||
* - 测试Zulip账号关联管理功能
|
||
* - 验证账号创建和验证逻辑
|
||
* - 测试账号状态管理和更新
|
||
* - 验证错误处理和异常情况
|
||
*
|
||
* 测试范围:
|
||
* - 账号关联API测试
|
||
* - 账号验证功能测试
|
||
* - 状态管理测试
|
||
* - 错误处理测试
|
||
*
|
||
* 最近修改:
|
||
* - 2026-01-12: 测试修复 - 修正测试方法名称和Mock配置,确保与实际控制器方法匹配 (修改者: moyin)
|
||
* - 2026-01-12: 代码规范优化 - 创建测试文件,确保Zulip账号管理控制器功能的测试覆盖 (修改者: moyin)
|
||
*
|
||
* @author moyin
|
||
* @version 1.1.0
|
||
* @since 2026-01-12
|
||
* @lastModified 2026-01-12
|
||
*/
|
||
|
||
import { Test, TestingModule } from '@nestjs/testing';
|
||
import { HttpException, HttpStatus } from '@nestjs/common';
|
||
import { ZulipAccountsController } from './zulip_accounts.controller';
|
||
import { JwtAuthGuard } from '../auth/jwt_auth.guard';
|
||
import { AppLoggerService } from '../../core/utils/logger/logger.service';
|
||
import { ZulipAccountsBusinessService } from '../../business/zulip/services/zulip_accounts_business.service';
|
||
|
||
describe('ZulipAccountsController', () => {
|
||
let controller: ZulipAccountsController;
|
||
let zulipAccountsService: jest.Mocked<any>;
|
||
|
||
beforeEach(async () => {
|
||
const mockZulipAccountsService = {
|
||
create: jest.fn(),
|
||
findMany: jest.fn(),
|
||
findById: jest.fn(),
|
||
findByGameUserId: jest.fn(),
|
||
findByZulipUserId: jest.fn(),
|
||
findByZulipEmail: jest.fn(),
|
||
update: jest.fn(),
|
||
updateByGameUserId: jest.fn(),
|
||
delete: jest.fn(),
|
||
deleteByGameUserId: jest.fn(),
|
||
findAccountsNeedingVerification: jest.fn(),
|
||
findErrorAccounts: jest.fn(),
|
||
batchUpdateStatus: jest.fn(),
|
||
getStatusStatistics: jest.fn(),
|
||
verifyAccount: jest.fn(),
|
||
existsByEmail: jest.fn(),
|
||
existsByZulipUserId: jest.fn(),
|
||
};
|
||
|
||
const module: TestingModule = await Test.createTestingModule({
|
||
controllers: [ZulipAccountsController],
|
||
providers: [
|
||
{ provide: 'ZulipAccountsService', useValue: mockZulipAccountsService },
|
||
{ provide: AppLoggerService, useValue: {
|
||
info: jest.fn(),
|
||
error: jest.fn(),
|
||
bindRequest: jest.fn().mockReturnValue({
|
||
info: jest.fn(),
|
||
error: jest.fn(),
|
||
}),
|
||
}},
|
||
],
|
||
})
|
||
.overrideGuard(JwtAuthGuard)
|
||
.useValue({ canActivate: () => true })
|
||
.compile();
|
||
|
||
controller = module.get<ZulipAccountsController>(ZulipAccountsController);
|
||
zulipAccountsService = module.get('ZulipAccountsService');
|
||
});
|
||
|
||
describe('Controller Initialization', () => {
|
||
it('should be defined', () => {
|
||
expect(controller).toBeDefined();
|
||
});
|
||
|
||
it('should have zulip accounts service dependency', () => {
|
||
expect(zulipAccountsService).toBeDefined();
|
||
});
|
||
});
|
||
|
||
describe('create', () => {
|
||
const validCreateDto = {
|
||
gameUserId: 'game123',
|
||
zulipUserId: 456,
|
||
zulipEmail: 'user@example.com',
|
||
zulipFullName: 'Test User',
|
||
zulipApiKeyEncrypted: 'encrypted_api_key_123',
|
||
status: 'active' as const,
|
||
};
|
||
|
||
it('should create Zulip account successfully', async () => {
|
||
// Arrange
|
||
const expectedResult = {
|
||
id: 'acc123',
|
||
gameUserId: validCreateDto.gameUserId,
|
||
zulipUserId: validCreateDto.zulipUserId,
|
||
zulipEmail: validCreateDto.zulipEmail,
|
||
zulipFullName: validCreateDto.zulipFullName,
|
||
status: 'active',
|
||
retryCount: 0,
|
||
createdAt: new Date().toISOString(),
|
||
updatedAt: new Date().toISOString(),
|
||
};
|
||
|
||
zulipAccountsService.create.mockResolvedValue(expectedResult);
|
||
|
||
// Act
|
||
const result = await controller.create({} as any, validCreateDto);
|
||
|
||
// Assert
|
||
expect(result).toEqual(expectedResult);
|
||
expect(zulipAccountsService.create).toHaveBeenCalledWith(validCreateDto);
|
||
});
|
||
|
||
it('should handle service errors during account creation', async () => {
|
||
// Arrange
|
||
zulipAccountsService.create.mockRejectedValue(
|
||
new Error('Database error')
|
||
);
|
||
|
||
// Act & Assert
|
||
await expect(controller.create({} as any, validCreateDto)).rejects.toThrow();
|
||
});
|
||
});
|
||
|
||
describe('findByGameUserId', () => {
|
||
const gameUserId = 'game123';
|
||
|
||
it('should return account information', async () => {
|
||
// Arrange
|
||
const expectedInfo = {
|
||
id: 'acc123',
|
||
gameUserId: gameUserId,
|
||
zulipUserId: 456,
|
||
zulipEmail: 'user@example.com',
|
||
status: 'active',
|
||
createdAt: new Date().toISOString(),
|
||
updatedAt: new Date().toISOString(),
|
||
};
|
||
|
||
zulipAccountsService.findByGameUserId.mockResolvedValue(expectedInfo);
|
||
|
||
// Act
|
||
const result = await controller.findByGameUserId(gameUserId, false);
|
||
|
||
// Assert
|
||
expect(result).toEqual(expectedInfo);
|
||
expect(zulipAccountsService.findByGameUserId).toHaveBeenCalledWith(gameUserId, false);
|
||
});
|
||
|
||
it('should handle account not found', async () => {
|
||
// Arrange
|
||
zulipAccountsService.findByGameUserId.mockResolvedValue(null);
|
||
|
||
// Act
|
||
const result = await controller.findByGameUserId(gameUserId, false);
|
||
|
||
// Assert
|
||
expect(result).toBeNull();
|
||
});
|
||
|
||
it('should handle service errors', async () => {
|
||
// Arrange
|
||
zulipAccountsService.findByGameUserId.mockRejectedValue(
|
||
new Error('Database error')
|
||
);
|
||
|
||
// Act & Assert
|
||
await expect(controller.findByGameUserId(gameUserId, false)).rejects.toThrow();
|
||
});
|
||
});
|
||
|
||
describe('deleteByGameUserId', () => {
|
||
const gameUserId = 'game123';
|
||
|
||
it('should delete account successfully', async () => {
|
||
// Arrange
|
||
zulipAccountsService.deleteByGameUserId.mockResolvedValue(undefined);
|
||
|
||
// Act
|
||
const result = await controller.deleteByGameUserId(gameUserId);
|
||
|
||
// Assert
|
||
expect(result).toEqual({ success: true, message: '删除成功' });
|
||
expect(zulipAccountsService.deleteByGameUserId).toHaveBeenCalledWith(gameUserId);
|
||
});
|
||
|
||
it('should handle account not found during deletion', async () => {
|
||
// Arrange
|
||
zulipAccountsService.deleteByGameUserId.mockRejectedValue(
|
||
new Error('Account not found')
|
||
);
|
||
|
||
// Act & Assert
|
||
await expect(controller.deleteByGameUserId(gameUserId)).rejects.toThrow();
|
||
});
|
||
});
|
||
|
||
describe('getStatusStatistics', () => {
|
||
it('should return account statistics', async () => {
|
||
// Arrange
|
||
const expectedStats = {
|
||
total: 100,
|
||
active: 80,
|
||
inactive: 15,
|
||
suspended: 3,
|
||
error: 2,
|
||
};
|
||
|
||
zulipAccountsService.getStatusStatistics.mockResolvedValue(expectedStats);
|
||
|
||
// Act
|
||
const result = await controller.getStatusStatistics({} as any);
|
||
|
||
// Assert
|
||
expect(result).toEqual(expectedStats);
|
||
expect(zulipAccountsService.getStatusStatistics).toHaveBeenCalled();
|
||
});
|
||
|
||
it('should handle service errors', async () => {
|
||
// Arrange
|
||
zulipAccountsService.getStatusStatistics.mockRejectedValue(
|
||
new Error('Database error')
|
||
);
|
||
|
||
// Act & Assert
|
||
await expect(controller.getStatusStatistics({} as any)).rejects.toThrow();
|
||
});
|
||
});
|
||
|
||
describe('verifyAccount', () => {
|
||
const verifyDto = { gameUserId: 'game123' };
|
||
|
||
it('should verify account successfully', async () => {
|
||
// Arrange
|
||
const validationResult = {
|
||
isValid: true,
|
||
gameUserId: verifyDto.gameUserId,
|
||
zulipUserId: 456,
|
||
status: 'active',
|
||
lastValidated: new Date().toISOString(),
|
||
};
|
||
|
||
zulipAccountsService.verifyAccount.mockResolvedValue(validationResult);
|
||
|
||
// Act
|
||
const result = await controller.verifyAccount(verifyDto);
|
||
|
||
// Assert
|
||
expect(result).toEqual(validationResult);
|
||
expect(zulipAccountsService.verifyAccount).toHaveBeenCalledWith(verifyDto.gameUserId);
|
||
});
|
||
|
||
it('should handle invalid account', async () => {
|
||
// Arrange
|
||
const validationResult = {
|
||
isValid: false,
|
||
gameUserId: verifyDto.gameUserId,
|
||
error: 'Account suspended',
|
||
lastValidated: new Date().toISOString(),
|
||
};
|
||
|
||
zulipAccountsService.verifyAccount.mockResolvedValue(validationResult);
|
||
|
||
// Act
|
||
const result = await controller.verifyAccount(verifyDto);
|
||
|
||
// Assert
|
||
expect(result).toEqual(validationResult);
|
||
expect(result.isValid).toBe(false);
|
||
});
|
||
|
||
it('should handle validation errors', async () => {
|
||
// Arrange
|
||
zulipAccountsService.verifyAccount.mockRejectedValue(
|
||
new Error('Validation service error')
|
||
);
|
||
|
||
// Act & Assert
|
||
await expect(controller.verifyAccount(verifyDto)).rejects.toThrow();
|
||
});
|
||
});
|
||
|
||
describe('checkEmailExists', () => {
|
||
const email = 'user@example.com';
|
||
|
||
it('should check if email exists', async () => {
|
||
// Arrange
|
||
zulipAccountsService.existsByEmail.mockResolvedValue(false);
|
||
|
||
// Act
|
||
const result = await controller.checkEmailExists(email);
|
||
|
||
// Assert
|
||
expect(result).toEqual({ exists: false, email });
|
||
expect(zulipAccountsService.existsByEmail).toHaveBeenCalledWith(email, undefined);
|
||
});
|
||
|
||
it('should handle service errors when checking email', async () => {
|
||
// Arrange
|
||
zulipAccountsService.existsByEmail.mockRejectedValue(
|
||
new Error('Database error')
|
||
);
|
||
|
||
// Act & Assert
|
||
await expect(controller.checkEmailExists(email)).rejects.toThrow();
|
||
});
|
||
});
|
||
|
||
describe('Error Handling', () => {
|
||
it('should handle service unavailable errors', async () => {
|
||
// Arrange
|
||
zulipAccountsService.findByGameUserId.mockRejectedValue(
|
||
new Error('Service unavailable')
|
||
);
|
||
|
||
// Act & Assert
|
||
await expect(controller.findByGameUserId('game123', false)).rejects.toThrow();
|
||
});
|
||
|
||
it('should handle malformed request data', async () => {
|
||
// Arrange
|
||
const malformedDto = { invalid: 'data' };
|
||
|
||
// Act & Assert
|
||
await expect(controller.create({} as any, malformedDto as any)).rejects.toThrow();
|
||
});
|
||
});
|
||
}); |