Skip to main content
Glama
im47cn

Feishu Project MCP Server

by im47cn
task.test.ts6.92 kB
import { TaskManager } from '../../managers/task'; import { Logger, LogLevel } from '../../utils/logger'; import { TaskStatus, TaskType } from '../../types/task'; import { FeishuItemType } from '../../types/feishu'; import * as fs from 'fs'; import * as path from 'path'; jest.mock('../../utils/logger'); describe('TaskManager', () => { let taskManager: TaskManager; let logger: jest.Mocked<Logger>; let tempDir: string; let storageDir: string; beforeEach(() => { // Create temporary test directory tempDir = fs.mkdtempSync('task-manager-test-'); storageDir = path.join(tempDir, 'tasks'); // Mock logger logger = new Logger({ level: LogLevel.INFO, directory: path.join(tempDir, 'logs'), }) as jest.Mocked<Logger>; // Create TaskManager instance taskManager = new TaskManager( { storageDir, maxConcurrentTasks: 5, }, logger ); }); afterEach(() => { // Clean up temporary directory fs.rmSync(tempDir, { recursive: true, force: true }); }); describe('createTask', () => { it('should create a new task successfully', async () => { const task = await taskManager.createTask( TaskType.REQUIREMENT_ANALYSIS, 'req1', FeishuItemType.REQUIREMENT ); expect(task).toMatchObject({ type: TaskType.REQUIREMENT_ANALYSIS, status: TaskStatus.CREATED, itemId: 'req1', itemType: FeishuItemType.REQUIREMENT, }); // Verify task file was created const taskFile = path.join(storageDir, `${task.id}.json`); expect(fs.existsSync(taskFile)).toBe(true); const savedTask = JSON.parse(fs.readFileSync(taskFile, 'utf8')); expect(savedTask).toEqual(task); }); it('should enforce maximum concurrent tasks limit', async () => { const taskManager = new TaskManager( { storageDir, maxConcurrentTasks: 2, }, logger ); // Create two tasks (reaching the limit) await taskManager.createTask( TaskType.REQUIREMENT_ANALYSIS, 'req1', FeishuItemType.REQUIREMENT ); await taskManager.createTask(TaskType.BUG_ANALYSIS, 'bug1', FeishuItemType.BUG); // Attempt to create a third task await expect( taskManager.createTask(TaskType.REQUIREMENT_ANALYSIS, 'req2', FeishuItemType.REQUIREMENT) ).rejects.toThrow('Maximum concurrent tasks limit reached'); }); }); describe('updateTaskStatus', () => { it('should update task status successfully', async () => { const task = await taskManager.createTask( TaskType.REQUIREMENT_ANALYSIS, 'req1', FeishuItemType.REQUIREMENT ); const updatedTask = await taskManager.updateTaskStatus(task.id, TaskStatus.RUNNING); expect(updatedTask.status).toBe(TaskStatus.RUNNING); expect(updatedTask.updatedAt).not.toBe(task.updatedAt); // Verify file was updated const taskFile = path.join(storageDir, `${task.id}.json`); const savedTask = JSON.parse(fs.readFileSync(taskFile, 'utf8')); expect(savedTask).toEqual(updatedTask); }); it('should throw error for non-existent task', async () => { await expect( taskManager.updateTaskStatus('non-existent', TaskStatus.RUNNING) ).rejects.toThrow('Task not found'); }); }); describe('getTask', () => { it('should retrieve existing task', async () => { const task = await taskManager.createTask( TaskType.REQUIREMENT_ANALYSIS, 'req1', FeishuItemType.REQUIREMENT ); const retrievedTask = await taskManager.getTask(task.id); expect(retrievedTask).toEqual(task); }); it('should return null for non-existent task', async () => { const task = await taskManager.getTask('non-existent'); expect(task).toBeNull(); }); }); describe('taskState management', () => { it('should save and load task state', async () => { const task = await taskManager.createTask( TaskType.REQUIREMENT_ANALYSIS, 'req1', FeishuItemType.REQUIREMENT ); const state = { analysisResult: 'Test result', completionStatus: 'pending', }; await taskManager.saveTaskState(task.id, state); const loadedState = await taskManager.loadTaskState(task.id); expect(loadedState).toEqual(state); }); it('should return null for non-existent task state', async () => { const state = await taskManager.loadTaskState('non-existent'); expect(state).toBeNull(); }); }); describe('task filtering', () => { beforeEach(async () => { // Create some test tasks await taskManager.createTask( TaskType.REQUIREMENT_ANALYSIS, 'req1', FeishuItemType.REQUIREMENT ); await taskManager.createTask(TaskType.BUG_ANALYSIS, 'bug1', FeishuItemType.BUG); const task = await taskManager.createTask( TaskType.REQUIREMENT_ANALYSIS, 'req2', FeishuItemType.REQUIREMENT ); await taskManager.updateTaskStatus(task.id, TaskStatus.COMPLETED); }); it('should filter tasks by status', async () => { const createdTasks = await taskManager.getTasksByStatus([TaskStatus.CREATED]); expect(createdTasks).toHaveLength(2); const completedTasks = await taskManager.getTasksByStatus([TaskStatus.COMPLETED]); expect(completedTasks).toHaveLength(1); }); it('should filter tasks by complex criteria', async () => { const tasks = await taskManager.getTasksByFilter({ status: [TaskStatus.CREATED], type: [TaskType.REQUIREMENT_ANALYSIS], itemType: [FeishuItemType.REQUIREMENT], }); expect(tasks).toHaveLength(1); expect(tasks[0].type).toBe(TaskType.REQUIREMENT_ANALYSIS); expect(tasks[0].itemType).toBe(FeishuItemType.REQUIREMENT); }); }); describe('cleanup', () => { it('should cleanup completed tasks', async () => { // Create and complete a task const task = await taskManager.createTask( TaskType.REQUIREMENT_ANALYSIS, 'req1', FeishuItemType.REQUIREMENT ); await taskManager.updateTaskStatus(task.id, TaskStatus.COMPLETED); // Create task state await taskManager.saveTaskState(task.id, { result: 'test' }); // Wait a moment to ensure timestamp difference await new Promise(resolve => setTimeout(resolve, 100)); // Cleanup tasks await taskManager.cleanupCompletedTasks(new Date().toISOString()); // Verify task and state files are removed const taskFile = path.join(storageDir, `${task.id}.json`); const stateFile = path.join(storageDir, `${task.id}.state.json`); expect(fs.existsSync(taskFile)).toBe(false); expect(fs.existsSync(stateFile)).toBe(false); }); }); });

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/im47cn/feishu-project-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server