feat(chat): 新增聊天业务模块
范围:src/business/chat/ - 实现 ChatService 聊天业务服务(登录/登出/消息发送/位置更新) - 实现 ChatSessionService 会话管理服务(会话创建/销毁/上下文注入) - 实现 ChatFilterService 消息过滤服务(频率限制/敏感词/权限验证) - 实现 ChatCleanupService 会话清理服务(定时清理过期会话) - 添加完整的单元测试覆盖 - 添加模块 README 文档
This commit is contained in:
216
src/business/chat/chat.module.spec.ts
Normal file
216
src/business/chat/chat.module.spec.ts
Normal file
@@ -0,0 +1,216 @@
|
||||
/**
|
||||
* 聊天业务模块测试
|
||||
*
|
||||
* 测试范围:
|
||||
* - 模块配置验证
|
||||
* - 服务提供者注册
|
||||
* - 接口导出验证
|
||||
*
|
||||
* @author moyin
|
||||
* @version 1.0.0
|
||||
* @since 2026-01-14
|
||||
* @lastModified 2026-01-14
|
||||
*/
|
||||
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { Logger } from '@nestjs/common';
|
||||
import { ChatService } from './chat.service';
|
||||
import { ChatSessionService } from './services/chat_session.service';
|
||||
import { ChatFilterService } from './services/chat_filter.service';
|
||||
import { ChatCleanupService } from './services/chat_cleanup.service';
|
||||
import { SESSION_QUERY_SERVICE } from '../../core/session_core/session_core.interfaces';
|
||||
import { LoginCoreService } from '../../core/login_core/login_core.service';
|
||||
|
||||
describe('ChatModule', () => {
|
||||
let module: TestingModule;
|
||||
let chatService: ChatService;
|
||||
let sessionService: ChatSessionService;
|
||||
let filterService: ChatFilterService;
|
||||
let cleanupService: ChatCleanupService;
|
||||
|
||||
// Mock依赖
|
||||
const mockZulipClientPool = {
|
||||
createUserClient: jest.fn(),
|
||||
destroyUserClient: jest.fn(),
|
||||
sendMessage: jest.fn(),
|
||||
};
|
||||
|
||||
const mockZulipConfigService = {
|
||||
getStreamByMap: jest.fn().mockReturnValue('Test Stream'),
|
||||
findNearbyObject: jest.fn().mockReturnValue(null),
|
||||
getAllMapIds: jest.fn().mockReturnValue(['novice_village', 'whale_port']),
|
||||
};
|
||||
|
||||
const mockApiKeySecurityService = {
|
||||
getApiKey: jest.fn(),
|
||||
deleteApiKey: jest.fn(),
|
||||
};
|
||||
|
||||
const mockRedisService = {
|
||||
get: jest.fn(),
|
||||
setex: jest.fn(),
|
||||
del: jest.fn(),
|
||||
sadd: jest.fn(),
|
||||
srem: jest.fn(),
|
||||
smembers: jest.fn(),
|
||||
expire: jest.fn(),
|
||||
incr: jest.fn(),
|
||||
};
|
||||
|
||||
const mockLoginCoreService = {
|
||||
verifyToken: jest.fn(),
|
||||
};
|
||||
|
||||
beforeEach(async () => {
|
||||
// 禁用日志输出
|
||||
jest.spyOn(Logger.prototype, 'log').mockImplementation();
|
||||
jest.spyOn(Logger.prototype, 'error').mockImplementation();
|
||||
jest.spyOn(Logger.prototype, 'warn').mockImplementation();
|
||||
|
||||
module = await Test.createTestingModule({
|
||||
providers: [
|
||||
ChatService,
|
||||
ChatSessionService,
|
||||
ChatFilterService,
|
||||
ChatCleanupService,
|
||||
{
|
||||
provide: SESSION_QUERY_SERVICE,
|
||||
useExisting: ChatSessionService,
|
||||
},
|
||||
{
|
||||
provide: 'ZULIP_CLIENT_POOL_SERVICE',
|
||||
useValue: mockZulipClientPool,
|
||||
},
|
||||
{
|
||||
provide: 'ZULIP_CONFIG_SERVICE',
|
||||
useValue: mockZulipConfigService,
|
||||
},
|
||||
{
|
||||
provide: 'API_KEY_SECURITY_SERVICE',
|
||||
useValue: mockApiKeySecurityService,
|
||||
},
|
||||
{
|
||||
provide: 'REDIS_SERVICE',
|
||||
useValue: mockRedisService,
|
||||
},
|
||||
{
|
||||
provide: LoginCoreService,
|
||||
useValue: mockLoginCoreService,
|
||||
},
|
||||
],
|
||||
}).compile();
|
||||
|
||||
chatService = module.get<ChatService>(ChatService);
|
||||
sessionService = module.get<ChatSessionService>(ChatSessionService);
|
||||
filterService = module.get<ChatFilterService>(ChatFilterService);
|
||||
cleanupService = module.get<ChatCleanupService>(ChatCleanupService);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
if (module) {
|
||||
await module.close();
|
||||
}
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('模块配置', () => {
|
||||
it('应该成功编译模块', () => {
|
||||
expect(module).toBeDefined();
|
||||
});
|
||||
|
||||
it('应该提供 ChatService', () => {
|
||||
expect(chatService).toBeDefined();
|
||||
expect(chatService).toBeInstanceOf(ChatService);
|
||||
});
|
||||
|
||||
it('应该提供 ChatSessionService', () => {
|
||||
expect(sessionService).toBeDefined();
|
||||
expect(sessionService).toBeInstanceOf(ChatSessionService);
|
||||
});
|
||||
|
||||
it('应该提供 ChatFilterService', () => {
|
||||
expect(filterService).toBeDefined();
|
||||
expect(filterService).toBeInstanceOf(ChatFilterService);
|
||||
});
|
||||
|
||||
it('应该提供 ChatCleanupService', () => {
|
||||
expect(cleanupService).toBeDefined();
|
||||
expect(cleanupService).toBeInstanceOf(ChatCleanupService);
|
||||
});
|
||||
});
|
||||
|
||||
describe('接口导出', () => {
|
||||
it('应该导出 SESSION_QUERY_SERVICE 接口', () => {
|
||||
const queryService = module.get(SESSION_QUERY_SERVICE);
|
||||
expect(queryService).toBeDefined();
|
||||
});
|
||||
|
||||
it('SESSION_QUERY_SERVICE 应该指向 ChatSessionService', () => {
|
||||
const queryService = module.get(SESSION_QUERY_SERVICE);
|
||||
expect(queryService).toBe(sessionService);
|
||||
});
|
||||
|
||||
it('SESSION_QUERY_SERVICE 应该实现 ISessionManagerService 接口', () => {
|
||||
const queryService = module.get(SESSION_QUERY_SERVICE);
|
||||
expect(typeof queryService.createSession).toBe('function');
|
||||
expect(typeof queryService.getSession).toBe('function');
|
||||
expect(typeof queryService.destroySession).toBe('function');
|
||||
expect(typeof queryService.injectContext).toBe('function');
|
||||
});
|
||||
});
|
||||
|
||||
describe('服务依赖注入', () => {
|
||||
it('ChatService 应该能够获取所有依赖', () => {
|
||||
expect(chatService).toBeDefined();
|
||||
// 验证私有依赖通过检查服务是否正常工作
|
||||
expect(chatService['sessionService']).toBeDefined();
|
||||
expect(chatService['filterService']).toBeDefined();
|
||||
});
|
||||
|
||||
it('ChatSessionService 应该能够获取所有依赖', () => {
|
||||
expect(sessionService).toBeDefined();
|
||||
});
|
||||
|
||||
it('ChatFilterService 应该能够获取所有依赖', () => {
|
||||
expect(filterService).toBeDefined();
|
||||
});
|
||||
|
||||
it('ChatCleanupService 应该能够获取所有依赖', () => {
|
||||
expect(cleanupService).toBeDefined();
|
||||
expect(cleanupService['sessionService']).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('服务协作', () => {
|
||||
it('ChatService 应该能够调用 ChatSessionService', async () => {
|
||||
mockRedisService.get.mockResolvedValue(null);
|
||||
const session = await chatService.getSession('test_socket');
|
||||
expect(session).toBeNull();
|
||||
});
|
||||
|
||||
it('ChatCleanupService 应该能够调用 ChatSessionService', async () => {
|
||||
mockRedisService.smembers.mockResolvedValue([]);
|
||||
const result = await cleanupService.triggerCleanup();
|
||||
expect(result.cleanedCount).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('模块导出验证', () => {
|
||||
it('所有导出的服务应该可用', () => {
|
||||
// ChatModule 导出的服务
|
||||
expect(chatService).toBeDefined();
|
||||
expect(sessionService).toBeDefined();
|
||||
expect(filterService).toBeDefined();
|
||||
expect(cleanupService).toBeDefined();
|
||||
});
|
||||
|
||||
it('SESSION_QUERY_SERVICE 应该可供其他模块使用', () => {
|
||||
const queryService = module.get(SESSION_QUERY_SERVICE);
|
||||
expect(queryService).toBeDefined();
|
||||
// 验证接口方法存在
|
||||
expect(queryService.createSession).toBeDefined();
|
||||
expect(queryService.getSession).toBeDefined();
|
||||
expect(queryService.destroySession).toBeDefined();
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user