- 更新location_broadcast网关以支持原生WebSocket - 修改WebSocket认证守卫和中间件 - 更新相关的测试文件和规范 - 添加WebSocket测试工具 - 完善Zulip服务的测试覆盖 技术改进: - 统一WebSocket实现架构 - 优化性能监控和限流中间件 - 更新测试用例以适配新的WebSocket实现
665 lines
23 KiB
TypeScript
665 lines
23 KiB
TypeScript
/**
|
||
* 会话清理定时任务服务测试
|
||
*
|
||
* 功能描述:
|
||
* - 测试SessionCleanupService的核心功能
|
||
* - 包含属性测试验证定时清理机制
|
||
* - 包含属性测试验证资源释放完整性
|
||
*
|
||
* **Feature: zulip-integration, Property 13: 定时清理机制**
|
||
* **Validates: Requirements 6.1, 6.2, 6.3**
|
||
*
|
||
* **Feature: zulip-integration, Property 14: 资源释放完整性**
|
||
* **Validates: Requirements 6.4, 6.5**
|
||
*
|
||
* @author angjustinl
|
||
* @version 1.0.0
|
||
* @since 2025-12-31
|
||
*/
|
||
|
||
import { Test, TestingModule } from '@nestjs/testing';
|
||
import * as fc from 'fast-check';
|
||
import {
|
||
SessionCleanupService,
|
||
CleanupConfig,
|
||
CleanupResult
|
||
} from './session_cleanup.service';
|
||
import { SessionManagerService } from './session_manager.service';
|
||
import { IZulipClientPoolService } from '../../../core/zulip_core/zulip_core.interfaces';
|
||
|
||
describe('SessionCleanupService', () => {
|
||
let service: SessionCleanupService;
|
||
let mockSessionManager: jest.Mocked<SessionManagerService>;
|
||
let mockZulipClientPool: jest.Mocked<IZulipClientPoolService>;
|
||
|
||
// 模拟清理结果
|
||
const createMockCleanupResult = (overrides: Partial<any> = {}): any => ({
|
||
cleanedCount: 3,
|
||
zulipQueueIds: ['queue-1', 'queue-2', 'queue-3'],
|
||
duration: 150,
|
||
timestamp: new Date(),
|
||
...overrides,
|
||
});
|
||
|
||
beforeEach(async () => {
|
||
jest.clearAllMocks();
|
||
jest.clearAllTimers();
|
||
// 确保每个测试开始时都使用真实定时器
|
||
jest.useRealTimers();
|
||
|
||
mockSessionManager = {
|
||
cleanupExpiredSessions: jest.fn(),
|
||
getSession: jest.fn(),
|
||
destroySession: jest.fn(),
|
||
createSession: jest.fn(),
|
||
updatePlayerPosition: jest.fn(),
|
||
getSocketsInMap: jest.fn(),
|
||
injectContext: jest.fn(),
|
||
} as any;
|
||
|
||
mockZulipClientPool = {
|
||
createUserClient: jest.fn(),
|
||
getUserClient: jest.fn(),
|
||
hasUserClient: jest.fn(),
|
||
sendMessage: jest.fn(),
|
||
registerEventQueue: jest.fn(),
|
||
deregisterEventQueue: jest.fn(),
|
||
destroyUserClient: jest.fn(),
|
||
getPoolStats: jest.fn(),
|
||
cleanupIdleClients: jest.fn(),
|
||
} as any;
|
||
|
||
const module: TestingModule = await Test.createTestingModule({
|
||
providers: [
|
||
SessionCleanupService,
|
||
{
|
||
provide: SessionManagerService,
|
||
useValue: mockSessionManager,
|
||
},
|
||
{
|
||
provide: 'ZULIP_CLIENT_POOL_SERVICE',
|
||
useValue: mockZulipClientPool,
|
||
},
|
||
],
|
||
}).compile();
|
||
|
||
service = module.get<SessionCleanupService>(SessionCleanupService);
|
||
});
|
||
|
||
afterEach(async () => {
|
||
// 确保停止所有清理任务
|
||
service.stopCleanupTask();
|
||
|
||
// 等待任何正在进行的异步操作完成
|
||
await new Promise(resolve => setImmediate(resolve));
|
||
|
||
// 清理定时器
|
||
jest.clearAllTimers();
|
||
|
||
// 恢复真实定时器
|
||
jest.useRealTimers();
|
||
});
|
||
|
||
it('should be defined', () => {
|
||
expect(service).toBeDefined();
|
||
});
|
||
|
||
describe('startCleanupTask - 启动清理任务', () => {
|
||
it('应该启动定时清理任务', () => {
|
||
service.startCleanupTask();
|
||
|
||
const status = service.getStatus();
|
||
expect(status.isEnabled).toBe(true);
|
||
});
|
||
|
||
it('应该在已启动时不重复启动', () => {
|
||
service.startCleanupTask();
|
||
service.startCleanupTask(); // 第二次调用
|
||
|
||
const status = service.getStatus();
|
||
expect(status.isEnabled).toBe(true);
|
||
});
|
||
|
||
it('应该立即执行一次清理', async () => {
|
||
jest.useFakeTimers();
|
||
|
||
mockSessionManager.cleanupExpiredSessions.mockResolvedValue(
|
||
createMockCleanupResult({ cleanedCount: 2 })
|
||
);
|
||
|
||
service.startCleanupTask();
|
||
|
||
// 等待立即执行的清理完成
|
||
await jest.runOnlyPendingTimersAsync();
|
||
|
||
expect(mockSessionManager.cleanupExpiredSessions).toHaveBeenCalledWith(30);
|
||
|
||
// 确保清理任务被停止
|
||
service.stopCleanupTask();
|
||
jest.useRealTimers();
|
||
});
|
||
});
|
||
|
||
describe('stopCleanupTask - 停止清理任务', () => {
|
||
it('应该停止定时清理任务', () => {
|
||
service.startCleanupTask();
|
||
service.stopCleanupTask();
|
||
|
||
const status = service.getStatus();
|
||
expect(status.isEnabled).toBe(false);
|
||
});
|
||
|
||
it('应该在未启动时安全停止', () => {
|
||
service.stopCleanupTask();
|
||
|
||
const status = service.getStatus();
|
||
expect(status.isEnabled).toBe(false);
|
||
});
|
||
});
|
||
|
||
describe('runCleanup - 执行清理', () => {
|
||
it('应该成功执行清理并返回结果', async () => {
|
||
const mockResult = createMockCleanupResult({
|
||
cleanedCount: 5,
|
||
zulipQueueIds: ['queue-1', 'queue-2', 'queue-3', 'queue-4', 'queue-5'],
|
||
});
|
||
|
||
mockSessionManager.cleanupExpiredSessions.mockResolvedValue(mockResult);
|
||
|
||
const result = await service.runCleanup();
|
||
|
||
expect(result.success).toBe(true);
|
||
expect(result.cleanedSessions).toBe(5);
|
||
expect(result.deregisteredQueues).toBe(5);
|
||
expect(result.duration).toBeGreaterThanOrEqual(0); // 修改为 >= 0,因为测试环境可能很快
|
||
expect(mockSessionManager.cleanupExpiredSessions).toHaveBeenCalledWith(30);
|
||
});
|
||
|
||
it('应该处理清理过程中的错误', async () => {
|
||
const error = new Error('清理失败');
|
||
mockSessionManager.cleanupExpiredSessions.mockRejectedValue(error);
|
||
|
||
const result = await service.runCleanup();
|
||
|
||
expect(result.success).toBe(false);
|
||
expect(result.error).toBe('清理失败');
|
||
expect(result.cleanedSessions).toBe(0);
|
||
expect(result.deregisteredQueues).toBe(0);
|
||
});
|
||
|
||
it('应该防止并发执行', async () => {
|
||
let resolveFirst: () => void;
|
||
const firstPromise = new Promise<any>(resolve => {
|
||
resolveFirst = () => resolve(createMockCleanupResult());
|
||
});
|
||
|
||
mockSessionManager.cleanupExpiredSessions.mockReturnValueOnce(firstPromise);
|
||
|
||
// 同时启动两个清理任务
|
||
const promise1 = service.runCleanup();
|
||
const promise2 = service.runCleanup();
|
||
|
||
// 第二个应该立即返回失败
|
||
const result2 = await promise2;
|
||
expect(result2.success).toBe(false);
|
||
expect(result2.error).toContain('正在执行中');
|
||
|
||
// 完成第一个任务
|
||
resolveFirst!();
|
||
const result1 = await promise1;
|
||
expect(result1.success).toBe(true);
|
||
}, 15000);
|
||
|
||
it('应该记录最后一次清理结果', async () => {
|
||
const mockResult = createMockCleanupResult({ cleanedCount: 3 });
|
||
mockSessionManager.cleanupExpiredSessions.mockResolvedValue(mockResult);
|
||
|
||
await service.runCleanup();
|
||
|
||
const lastResult = service.getLastCleanupResult();
|
||
expect(lastResult).not.toBeNull();
|
||
expect(lastResult!.cleanedSessions).toBe(3);
|
||
expect(lastResult!.success).toBe(true);
|
||
});
|
||
});
|
||
|
||
describe('getStatus - 获取状态', () => {
|
||
it('应该返回正确的状态信息', () => {
|
||
const status = service.getStatus();
|
||
|
||
expect(status).toHaveProperty('isRunning');
|
||
expect(status).toHaveProperty('isEnabled');
|
||
expect(status).toHaveProperty('config');
|
||
expect(status).toHaveProperty('lastResult');
|
||
expect(typeof status.isRunning).toBe('boolean');
|
||
expect(typeof status.isEnabled).toBe('boolean');
|
||
});
|
||
|
||
it('应该反映任务启动状态', () => {
|
||
let status = service.getStatus();
|
||
expect(status.isEnabled).toBe(false);
|
||
|
||
service.startCleanupTask();
|
||
status = service.getStatus();
|
||
expect(status.isEnabled).toBe(true);
|
||
|
||
service.stopCleanupTask();
|
||
status = service.getStatus();
|
||
expect(status.isEnabled).toBe(false);
|
||
});
|
||
});
|
||
|
||
describe('updateConfig - 更新配置', () => {
|
||
it('应该更新清理配置', () => {
|
||
const newConfig: Partial<CleanupConfig> = {
|
||
intervalMs: 10 * 60 * 1000, // 10分钟
|
||
sessionTimeoutMinutes: 60, // 60分钟
|
||
};
|
||
|
||
service.updateConfig(newConfig);
|
||
|
||
const status = service.getStatus();
|
||
expect(status.config.intervalMs).toBe(10 * 60 * 1000);
|
||
expect(status.config.sessionTimeoutMinutes).toBe(60);
|
||
});
|
||
|
||
it('应该在配置更改后重启任务', () => {
|
||
service.startCleanupTask();
|
||
|
||
const newConfig: Partial<CleanupConfig> = {
|
||
intervalMs: 2 * 60 * 1000, // 2分钟
|
||
};
|
||
|
||
service.updateConfig(newConfig);
|
||
|
||
const status = service.getStatus();
|
||
expect(status.isEnabled).toBe(true);
|
||
expect(status.config.intervalMs).toBe(2 * 60 * 1000);
|
||
});
|
||
|
||
it('应该支持禁用清理任务', () => {
|
||
service.startCleanupTask();
|
||
|
||
service.updateConfig({ enabled: false });
|
||
|
||
const status = service.getStatus();
|
||
expect(status.isEnabled).toBe(false);
|
||
});
|
||
});
|
||
/**
|
||
* 属性测试: 定时清理机制
|
||
*
|
||
* **Feature: zulip-integration, Property 13: 定时清理机制**
|
||
* **Validates: Requirements 6.1, 6.2, 6.3**
|
||
*
|
||
* 系统应该定期清理过期的游戏会话,释放相关资源,
|
||
* 并确保清理过程不影响正常的游戏服务
|
||
*/
|
||
describe('Property 13: 定时清理机制', () => {
|
||
/**
|
||
* 属性: 对于任何有效的清理配置,系统应该按配置间隔执行清理
|
||
* 验证需求 6.1: 系统应定期检查并清理过期的游戏会话
|
||
*/
|
||
it('对于任何有效的清理配置,系统应该按配置间隔执行清理', async () => {
|
||
await fc.assert(
|
||
fc.asyncProperty(
|
||
// 生成有效的清理间隔(1-5分钟,减少范围)
|
||
fc.integer({ min: 1, max: 5 }).map(minutes => minutes * 60 * 1000),
|
||
// 生成有效的会话超时时间(10-60分钟,减少范围)
|
||
fc.integer({ min: 10, max: 60 }),
|
||
async (intervalMs, sessionTimeoutMinutes) => {
|
||
// 重置mock以确保每次测试都是干净的状态
|
||
jest.clearAllMocks();
|
||
jest.useFakeTimers();
|
||
|
||
try {
|
||
const config: Partial<CleanupConfig> = {
|
||
intervalMs,
|
||
sessionTimeoutMinutes,
|
||
enabled: true,
|
||
};
|
||
|
||
// 模拟清理结果
|
||
mockSessionManager.cleanupExpiredSessions.mockResolvedValue(
|
||
createMockCleanupResult({ cleanedCount: 2 })
|
||
);
|
||
|
||
service.updateConfig(config);
|
||
service.startCleanupTask();
|
||
|
||
// 验证配置被正确设置
|
||
const status = service.getStatus();
|
||
expect(status.config.intervalMs).toBe(intervalMs);
|
||
expect(status.config.sessionTimeoutMinutes).toBe(sessionTimeoutMinutes);
|
||
expect(status.isEnabled).toBe(true);
|
||
|
||
// 验证立即执行了一次清理
|
||
await jest.runOnlyPendingTimersAsync();
|
||
expect(mockSessionManager.cleanupExpiredSessions).toHaveBeenCalledWith(sessionTimeoutMinutes);
|
||
|
||
} finally {
|
||
service.stopCleanupTask();
|
||
jest.useRealTimers();
|
||
}
|
||
}
|
||
),
|
||
{ numRuns: 20, timeout: 5000 } // 减少运行次数并添加超时
|
||
);
|
||
}, 15000);
|
||
|
||
/**
|
||
* 属性: 对于任何清理操作,都应该记录清理结果和统计信息
|
||
* 验证需求 6.2: 清理过程中系统应记录清理的会话数量和释放的资源
|
||
*/
|
||
it('对于任何清理操作,都应该记录清理结果和统计信息', async () => {
|
||
await fc.assert(
|
||
fc.asyncProperty(
|
||
// 生成清理的会话数量
|
||
fc.integer({ min: 0, max: 10 }),
|
||
// 生成Zulip队列ID列表
|
||
fc.array(
|
||
fc.string({ minLength: 5, maxLength: 15 }).filter(s => s.trim().length > 0),
|
||
{ minLength: 0, maxLength: 10 }
|
||
),
|
||
async (cleanedCount, queueIds) => {
|
||
// 重置mock以确保每次测试都是干净的状态
|
||
jest.clearAllMocks();
|
||
|
||
const mockResult = createMockCleanupResult({
|
||
cleanedCount,
|
||
zulipQueueIds: queueIds.slice(0, cleanedCount), // 确保队列数量不超过清理数量
|
||
});
|
||
|
||
mockSessionManager.cleanupExpiredSessions.mockResolvedValue(mockResult);
|
||
|
||
const result = await service.runCleanup();
|
||
|
||
// 验证清理结果被正确记录
|
||
expect(result.success).toBe(true);
|
||
expect(result.cleanedSessions).toBe(cleanedCount);
|
||
expect(result.deregisteredQueues).toBe(Math.min(queueIds.length, cleanedCount));
|
||
expect(result.duration).toBeGreaterThanOrEqual(0);
|
||
expect(result.timestamp).toBeInstanceOf(Date);
|
||
|
||
// 验证最后一次清理结果被保存
|
||
const lastResult = service.getLastCleanupResult();
|
||
expect(lastResult).not.toBeNull();
|
||
expect(lastResult!.cleanedSessions).toBe(cleanedCount);
|
||
}
|
||
),
|
||
{ numRuns: 20, timeout: 3000 } // 减少运行次数并添加超时
|
||
);
|
||
}, 10000);
|
||
|
||
/**
|
||
* 属性: 清理过程中发生错误时,系统应该正确处理并记录错误信息
|
||
* 验证需求 6.3: 清理过程中出现错误时系统应记录错误信息并继续正常服务
|
||
*/
|
||
it('清理过程中发生错误时,系统应该正确处理并记录错误信息', async () => {
|
||
await fc.assert(
|
||
fc.asyncProperty(
|
||
// 生成各种错误消息
|
||
fc.string({ minLength: 5, maxLength: 50 }).filter(s => s.trim().length > 0),
|
||
async (errorMessage) => {
|
||
// 重置mock以确保每次测试都是干净的状态
|
||
jest.clearAllMocks();
|
||
|
||
const error = new Error(errorMessage.trim());
|
||
mockSessionManager.cleanupExpiredSessions.mockRejectedValue(error);
|
||
|
||
const result = await service.runCleanup();
|
||
|
||
// 验证错误被正确处理
|
||
expect(result.success).toBe(false);
|
||
expect(result.error).toBe(errorMessage.trim());
|
||
expect(result.cleanedSessions).toBe(0);
|
||
expect(result.deregisteredQueues).toBe(0);
|
||
expect(result.duration).toBeGreaterThanOrEqual(0);
|
||
|
||
// 验证错误结果被保存
|
||
const lastResult = service.getLastCleanupResult();
|
||
expect(lastResult).not.toBeNull();
|
||
expect(lastResult!.success).toBe(false);
|
||
expect(lastResult!.error).toBe(errorMessage.trim());
|
||
}
|
||
),
|
||
{ numRuns: 20, timeout: 3000 } // 减少运行次数并添加超时
|
||
);
|
||
}, 10000);
|
||
|
||
/**
|
||
* 属性: 并发清理请求应该被正确处理,避免重复执行
|
||
* 验证需求 6.1: 系统应避免同时执行多个清理任务
|
||
*/
|
||
it('并发清理请求应该被正确处理,避免重复执行', async () => {
|
||
// 重置mock
|
||
jest.clearAllMocks();
|
||
|
||
// 创建一个可控的Promise,使用实际的异步行为
|
||
let resolveCleanup: (value: any) => void;
|
||
const cleanupPromise = new Promise<any>(resolve => {
|
||
resolveCleanup = resolve;
|
||
});
|
||
|
||
mockSessionManager.cleanupExpiredSessions.mockReturnValue(cleanupPromise);
|
||
|
||
// 启动第一个清理请求(应该成功)
|
||
const promise1 = service.runCleanup();
|
||
|
||
// 等待一个微任务周期,确保第一个请求开始执行
|
||
await Promise.resolve();
|
||
|
||
// 启动第二个和第三个清理请求(应该被拒绝)
|
||
const promise2 = service.runCleanup();
|
||
const promise3 = service.runCleanup();
|
||
|
||
// 第二个和第三个请求应该立即返回失败
|
||
const result2 = await promise2;
|
||
const result3 = await promise3;
|
||
|
||
expect(result2.success).toBe(false);
|
||
expect(result2.error).toContain('正在执行中');
|
||
expect(result3.success).toBe(false);
|
||
expect(result3.error).toContain('正在执行中');
|
||
|
||
// 完成第一个清理操作
|
||
resolveCleanup!(createMockCleanupResult({ cleanedCount: 1 }));
|
||
const result1 = await promise1;
|
||
|
||
expect(result1.success).toBe(true);
|
||
}, 10000);
|
||
});
|
||
/**
|
||
* 属性测试: 资源释放完整性
|
||
*
|
||
* **Feature: zulip-integration, Property 14: 资源释放完整性**
|
||
* **Validates: Requirements 6.4, 6.5**
|
||
*
|
||
* 清理过期会话时,系统应该完整释放所有相关资源,
|
||
* 包括Zulip事件队列、内存缓存等,确保不会造成资源泄漏
|
||
*/
|
||
describe('Property 14: 资源释放完整性', () => {
|
||
/**
|
||
* 属性: 对于任何过期会话,清理时应该释放所有相关的Zulip资源
|
||
* 验证需求 6.4: 清理会话时系统应注销对应的Zulip事件队列
|
||
*/
|
||
it('对于任何过期会话,清理时应该释放所有相关的Zulip资源', async () => {
|
||
await fc.assert(
|
||
fc.asyncProperty(
|
||
// 生成过期会话数量
|
||
fc.integer({ min: 1, max: 5 }),
|
||
// 生成每个会话对应的Zulip队列ID
|
||
fc.array(
|
||
fc.string({ minLength: 8, maxLength: 15 }).filter(s => s.trim().length > 0),
|
||
{ minLength: 1, maxLength: 5 }
|
||
),
|
||
async (sessionCount, queueIds) => {
|
||
// 重置mock以确保每次测试都是干净的状态
|
||
jest.clearAllMocks();
|
||
|
||
const actualQueueIds = queueIds.slice(0, sessionCount);
|
||
const mockResult = createMockCleanupResult({
|
||
cleanedCount: sessionCount,
|
||
zulipQueueIds: actualQueueIds,
|
||
});
|
||
|
||
mockSessionManager.cleanupExpiredSessions.mockResolvedValue(mockResult);
|
||
|
||
const result = await service.runCleanup();
|
||
|
||
// 验证清理成功
|
||
expect(result.success).toBe(true);
|
||
expect(result.cleanedSessions).toBe(sessionCount);
|
||
|
||
// 验证Zulip队列被处理(这里简化为计数验证)
|
||
expect(result.deregisteredQueues).toBe(actualQueueIds.length);
|
||
|
||
// 验证SessionManager被调用清理过期会话
|
||
expect(mockSessionManager.cleanupExpiredSessions).toHaveBeenCalledWith(30);
|
||
}
|
||
),
|
||
{ numRuns: 20, timeout: 3000 } // 减少运行次数并添加超时
|
||
);
|
||
}, 10000);
|
||
|
||
/**
|
||
* 属性: 清理操作应该是原子性的,要么全部成功要么全部回滚
|
||
* 验证需求 6.5: 清理过程应确保数据一致性,避免部分清理导致的不一致状态
|
||
*/
|
||
it('清理操作应该是原子性的,要么全部成功要么全部回滚', async () => {
|
||
await fc.assert(
|
||
fc.asyncProperty(
|
||
// 生成是否模拟清理失败
|
||
fc.boolean(),
|
||
// 生成会话数量
|
||
fc.integer({ min: 1, max: 3 }),
|
||
async (shouldFail, sessionCount) => {
|
||
// 重置mock以确保每次测试都是干净的状态
|
||
jest.clearAllMocks();
|
||
|
||
if (shouldFail) {
|
||
// 模拟清理失败
|
||
const error = new Error('清理操作失败');
|
||
mockSessionManager.cleanupExpiredSessions.mockRejectedValue(error);
|
||
} else {
|
||
// 模拟清理成功
|
||
const mockResult = createMockCleanupResult({
|
||
cleanedCount: sessionCount,
|
||
zulipQueueIds: Array.from({ length: sessionCount }, (_, i) => `queue-${i}`),
|
||
});
|
||
mockSessionManager.cleanupExpiredSessions.mockResolvedValue(mockResult);
|
||
}
|
||
|
||
const result = await service.runCleanup();
|
||
|
||
if (shouldFail) {
|
||
// 失败时应该没有任何资源被释放
|
||
expect(result.success).toBe(false);
|
||
expect(result.cleanedSessions).toBe(0);
|
||
expect(result.deregisteredQueues).toBe(0);
|
||
expect(result.error).toBeDefined();
|
||
} else {
|
||
// 成功时所有资源都应该被正确处理
|
||
expect(result.success).toBe(true);
|
||
expect(result.cleanedSessions).toBe(sessionCount);
|
||
expect(result.deregisteredQueues).toBe(sessionCount);
|
||
expect(result.error).toBeUndefined();
|
||
}
|
||
|
||
// 验证结果的一致性
|
||
expect(result.timestamp).toBeInstanceOf(Date);
|
||
expect(result.duration).toBeGreaterThanOrEqual(0);
|
||
}
|
||
),
|
||
{ numRuns: 20, timeout: 3000 } // 减少运行次数并添加超时
|
||
);
|
||
}, 10000);
|
||
|
||
/**
|
||
* 属性: 清理配置更新应该正确重启清理任务而不丢失状态
|
||
* 验证需求 6.5: 配置更新时系统应保持服务连续性
|
||
*/
|
||
it('清理配置更新应该正确重启清理任务而不丢失状态', async () => {
|
||
await fc.assert(
|
||
fc.asyncProperty(
|
||
// 生成初始配置
|
||
fc.record({
|
||
intervalMs: fc.integer({ min: 1, max: 3 }).map(m => m * 60 * 1000),
|
||
sessionTimeoutMinutes: fc.integer({ min: 10, max: 30 }),
|
||
}),
|
||
// 生成新配置
|
||
fc.record({
|
||
intervalMs: fc.integer({ min: 1, max: 3 }).map(m => m * 60 * 1000),
|
||
sessionTimeoutMinutes: fc.integer({ min: 10, max: 30 }),
|
||
}),
|
||
async (initialConfig, newConfig) => {
|
||
// 重置mock以确保每次测试都是干净的状态
|
||
jest.clearAllMocks();
|
||
|
||
try {
|
||
// 设置初始配置并启动任务
|
||
service.updateConfig(initialConfig);
|
||
service.startCleanupTask();
|
||
|
||
let status = service.getStatus();
|
||
expect(status.isEnabled).toBe(true);
|
||
expect(status.config.intervalMs).toBe(initialConfig.intervalMs);
|
||
|
||
// 更新配置
|
||
service.updateConfig(newConfig);
|
||
|
||
// 验证配置更新后任务仍在运行
|
||
status = service.getStatus();
|
||
expect(status.isEnabled).toBe(true);
|
||
expect(status.config.intervalMs).toBe(newConfig.intervalMs);
|
||
expect(status.config.sessionTimeoutMinutes).toBe(newConfig.sessionTimeoutMinutes);
|
||
|
||
} finally {
|
||
service.stopCleanupTask();
|
||
}
|
||
}
|
||
),
|
||
{ numRuns: 15, timeout: 3000 } // 减少运行次数并添加超时
|
||
);
|
||
}, 10000);
|
||
});
|
||
|
||
describe('模块生命周期', () => {
|
||
it('应该在模块初始化时启动清理任务', async () => {
|
||
// 重新创建服务实例来测试模块初始化
|
||
const module: TestingModule = await Test.createTestingModule({
|
||
providers: [
|
||
SessionCleanupService,
|
||
{
|
||
provide: SessionManagerService,
|
||
useValue: mockSessionManager,
|
||
},
|
||
{
|
||
provide: 'ZULIP_CLIENT_POOL_SERVICE',
|
||
useValue: mockZulipClientPool,
|
||
},
|
||
],
|
||
}).compile();
|
||
|
||
const newService = module.get<SessionCleanupService>(SessionCleanupService);
|
||
|
||
// 模拟模块初始化
|
||
await newService.onModuleInit();
|
||
|
||
const status = newService.getStatus();
|
||
expect(status.isEnabled).toBe(true);
|
||
|
||
// 清理
|
||
await newService.onModuleDestroy();
|
||
});
|
||
|
||
it('应该在模块销毁时停止清理任务', async () => {
|
||
service.startCleanupTask();
|
||
|
||
await service.onModuleDestroy();
|
||
|
||
const status = service.getStatus();
|
||
expect(status.isEnabled).toBe(false);
|
||
});
|
||
});
|
||
}); |