195 lines
5.4 KiB
TypeScript
195 lines
5.4 KiB
TypeScript
/**
|
||
* 聊天控制器测试
|
||
*
|
||
* 功能描述:
|
||
* - 测试聊天消息发送功能
|
||
* - 验证消息过滤和验证逻辑
|
||
* - 测试错误处理和异常情况
|
||
* - 验证WebSocket消息广播功能
|
||
*
|
||
* 测试范围:
|
||
* - 消息发送API测试
|
||
* - 参数验证测试
|
||
* - 错误处理测试
|
||
* - 业务逻辑验证
|
||
*
|
||
* 最近修改:
|
||
* - 2026-01-12: Bug修复 - 修复测试用例中的方法名和DTO结构 (修改者: moyin)
|
||
* - 2026-01-12: 代码规范优化 - 创建测试文件,确保控制器功能的测试覆盖 (修改者: moyin)
|
||
*
|
||
* @author moyin
|
||
* @version 1.0.1
|
||
* @since 2026-01-12
|
||
* @lastModified 2026-01-12
|
||
*/
|
||
|
||
import { Test, TestingModule } from '@nestjs/testing';
|
||
import { HttpException, HttpStatus } from '@nestjs/common';
|
||
import { ChatController } from './chat.controller';
|
||
import { ZulipService } from './zulip.service';
|
||
import { MessageFilterService } from './services/message_filter.service';
|
||
import { CleanWebSocketGateway } from './clean_websocket.gateway';
|
||
import { JwtAuthGuard } from '../auth/jwt_auth.guard';
|
||
|
||
// Mock JwtAuthGuard
|
||
const mockJwtAuthGuard = {
|
||
canActivate: jest.fn(() => true),
|
||
};
|
||
|
||
describe('ChatController', () => {
|
||
let controller: ChatController;
|
||
let zulipService: jest.Mocked<ZulipService>;
|
||
let messageFilterService: jest.Mocked<MessageFilterService>;
|
||
let websocketGateway: jest.Mocked<CleanWebSocketGateway>;
|
||
|
||
beforeEach(async () => {
|
||
const mockZulipService = {
|
||
sendChatMessage: jest.fn(),
|
||
};
|
||
|
||
const mockMessageFilterService = {
|
||
validateMessage: jest.fn(),
|
||
};
|
||
|
||
const mockWebSocketGateway = {
|
||
broadcastToRoom: jest.fn(),
|
||
getActiveConnections: jest.fn(),
|
||
};
|
||
|
||
const module: TestingModule = await Test.createTestingModule({
|
||
controllers: [ChatController],
|
||
providers: [
|
||
{
|
||
provide: ZulipService,
|
||
useValue: mockZulipService,
|
||
},
|
||
{
|
||
provide: MessageFilterService,
|
||
useValue: mockMessageFilterService,
|
||
},
|
||
{
|
||
provide: CleanWebSocketGateway,
|
||
useValue: mockWebSocketGateway,
|
||
},
|
||
{
|
||
provide: JwtAuthGuard,
|
||
useValue: mockJwtAuthGuard,
|
||
},
|
||
],
|
||
})
|
||
.overrideGuard(JwtAuthGuard)
|
||
.useValue(mockJwtAuthGuard)
|
||
.compile();
|
||
|
||
controller = module.get<ChatController>(ChatController);
|
||
zulipService = module.get(ZulipService);
|
||
messageFilterService = module.get(MessageFilterService);
|
||
websocketGateway = module.get(CleanWebSocketGateway);
|
||
});
|
||
|
||
it('should be defined', () => {
|
||
expect(controller).toBeDefined();
|
||
});
|
||
|
||
describe('sendMessage', () => {
|
||
const validMessageDto = {
|
||
content: 'Hello, world!',
|
||
stream: 'general',
|
||
topic: 'chat',
|
||
userId: 'user123',
|
||
scope: 'local',
|
||
};
|
||
|
||
it('should reject REST API message sending and suggest WebSocket', async () => {
|
||
// Act & Assert
|
||
await expect(controller.sendMessage(validMessageDto)).rejects.toThrow(
|
||
new HttpException(
|
||
'聊天消息发送需要通过 WebSocket 连接。请使用 WebSocket 接口:wss://whaletownend.xinghangee.icu',
|
||
HttpStatus.BAD_REQUEST,
|
||
)
|
||
);
|
||
});
|
||
|
||
it('should log the REST API request attempt', async () => {
|
||
// Arrange
|
||
const loggerSpy = jest.spyOn(controller['logger'], 'log');
|
||
|
||
// Act
|
||
try {
|
||
await controller.sendMessage(validMessageDto);
|
||
} catch (error) {
|
||
// Expected to throw
|
||
}
|
||
|
||
// Assert
|
||
expect(loggerSpy).toHaveBeenCalledWith('收到REST API聊天消息发送请求', {
|
||
operation: 'sendMessage',
|
||
content: validMessageDto.content.substring(0, 50),
|
||
scope: validMessageDto.scope,
|
||
timestamp: expect.any(String),
|
||
});
|
||
});
|
||
|
||
it('should handle different message content lengths', async () => {
|
||
// Arrange
|
||
const longMessageDto = {
|
||
...validMessageDto,
|
||
content: 'a'.repeat(100), // Long message
|
||
};
|
||
|
||
// Act & Assert
|
||
await expect(controller.sendMessage(longMessageDto)).rejects.toThrow(HttpException);
|
||
});
|
||
|
||
it('should handle empty message content', async () => {
|
||
// Arrange
|
||
const emptyMessageDto = { ...validMessageDto, content: '' };
|
||
|
||
// Act & Assert
|
||
await expect(controller.sendMessage(emptyMessageDto)).rejects.toThrow(HttpException);
|
||
});
|
||
});
|
||
|
||
describe('Error Handling', () => {
|
||
it('should always throw HttpException for REST API requests', async () => {
|
||
// Arrange
|
||
const validMessageDto = {
|
||
content: 'Hello, world!',
|
||
stream: 'general',
|
||
topic: 'chat',
|
||
userId: 'user123',
|
||
scope: 'local',
|
||
};
|
||
|
||
// Act & Assert
|
||
await expect(controller.sendMessage(validMessageDto)).rejects.toThrow(HttpException);
|
||
});
|
||
|
||
it('should log error when REST API is used', async () => {
|
||
// Arrange
|
||
const validMessageDto = {
|
||
content: 'Hello, world!',
|
||
stream: 'general',
|
||
topic: 'chat',
|
||
userId: 'user123',
|
||
scope: 'local',
|
||
};
|
||
|
||
const loggerSpy = jest.spyOn(controller['logger'], 'error');
|
||
|
||
// Act
|
||
try {
|
||
await controller.sendMessage(validMessageDto);
|
||
} catch (error) {
|
||
// Expected to throw
|
||
}
|
||
|
||
// Assert
|
||
expect(loggerSpy).toHaveBeenCalledWith('REST API消息发送失败', {
|
||
operation: 'sendMessage',
|
||
error: expect.any(String),
|
||
timestamp: expect.any(String),
|
||
});
|
||
});
|
||
});
|
||
}); |