/** * 聊天控制器测试 * * 功能描述: * - 测试聊天消息发送功能 * - 验证消息过滤和验证逻辑 * - 测试错误处理和异常情况 * - 验证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; let messageFilterService: jest.Mocked; let websocketGateway: jest.Mocked; 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); 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), }); }); }); });