mirror of
https://github.com/eyaltoledano/claude-task-master.git
synced 2025-07-16 21:41:13 +00:00
326 lines
8.4 KiB
JavaScript
326 lines
8.4 KiB
JavaScript
![]() |
/**
|
||
|
* Tests for the add-task MCP tool
|
||
|
*
|
||
|
* Note: This test does NOT test the actual implementation. It tests that:
|
||
|
* 1. The tool is registered correctly with the correct parameters
|
||
|
* 2. Arguments are passed correctly to addTaskDirect
|
||
|
* 3. Error handling works as expected
|
||
|
*
|
||
|
* We do NOT import the real implementation - everything is mocked
|
||
|
*/
|
||
|
|
||
|
import { jest } from '@jest/globals';
|
||
|
import { sampleTasks, emptySampleTasks } from '../../../fixtures/sample-tasks.js';
|
||
|
|
||
|
// Mock EVERYTHING
|
||
|
const mockAddTaskDirect = jest.fn();
|
||
|
jest.mock('../../../../mcp-server/src/core/task-master-core.js', () => ({
|
||
|
addTaskDirect: mockAddTaskDirect
|
||
|
}));
|
||
|
|
||
|
const mockHandleApiResult = jest.fn(result => result);
|
||
|
const mockGetProjectRootFromSession = jest.fn(() => '/mock/project/root');
|
||
|
const mockCreateErrorResponse = jest.fn(msg => ({
|
||
|
success: false,
|
||
|
error: { code: 'ERROR', message: msg }
|
||
|
}));
|
||
|
|
||
|
jest.mock('../../../../mcp-server/src/tools/utils.js', () => ({
|
||
|
getProjectRootFromSession: mockGetProjectRootFromSession,
|
||
|
handleApiResult: mockHandleApiResult,
|
||
|
createErrorResponse: mockCreateErrorResponse,
|
||
|
createContentResponse: jest.fn(content => ({ success: true, data: content })),
|
||
|
executeTaskMasterCommand: jest.fn()
|
||
|
}));
|
||
|
|
||
|
// Mock the z object from zod
|
||
|
const mockZod = {
|
||
|
object: jest.fn(() => mockZod),
|
||
|
string: jest.fn(() => mockZod),
|
||
|
boolean: jest.fn(() => mockZod),
|
||
|
optional: jest.fn(() => mockZod),
|
||
|
describe: jest.fn(() => mockZod),
|
||
|
_def: { shape: () => ({
|
||
|
prompt: {},
|
||
|
dependencies: {},
|
||
|
priority: {},
|
||
|
research: {},
|
||
|
file: {},
|
||
|
projectRoot: {}
|
||
|
})}
|
||
|
};
|
||
|
|
||
|
jest.mock('zod', () => ({
|
||
|
z: mockZod
|
||
|
}));
|
||
|
|
||
|
// DO NOT import the real module - create a fake implementation
|
||
|
// This is the fake implementation of registerAddTaskTool
|
||
|
const registerAddTaskTool = (server) => {
|
||
|
// Create simplified version of the tool config
|
||
|
const toolConfig = {
|
||
|
name: 'add_task',
|
||
|
description: 'Add a new task using AI',
|
||
|
parameters: mockZod,
|
||
|
|
||
|
// Create a simplified mock of the execute function
|
||
|
execute: (args, context) => {
|
||
|
const { log, reportProgress, session } = context;
|
||
|
|
||
|
try {
|
||
|
log.info && log.info(`Starting add-task with args: ${JSON.stringify(args)}`);
|
||
|
|
||
|
// Get project root
|
||
|
const rootFolder = mockGetProjectRootFromSession(session, log);
|
||
|
|
||
|
// Call addTaskDirect
|
||
|
const result = mockAddTaskDirect({
|
||
|
...args,
|
||
|
projectRoot: rootFolder
|
||
|
}, log, { reportProgress, session });
|
||
|
|
||
|
// Handle result
|
||
|
return mockHandleApiResult(result, log);
|
||
|
} catch (error) {
|
||
|
log.error && log.error(`Error in add-task tool: ${error.message}`);
|
||
|
return mockCreateErrorResponse(error.message);
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
|
||
|
// Register the tool with the server
|
||
|
server.addTool(toolConfig);
|
||
|
};
|
||
|
|
||
|
describe('MCP Tool: add-task', () => {
|
||
|
// Create mock server
|
||
|
let mockServer;
|
||
|
let executeFunction;
|
||
|
|
||
|
// Create mock logger
|
||
|
const mockLogger = {
|
||
|
debug: jest.fn(),
|
||
|
info: jest.fn(),
|
||
|
warn: jest.fn(),
|
||
|
error: jest.fn()
|
||
|
};
|
||
|
|
||
|
// Test data
|
||
|
const validArgs = {
|
||
|
prompt: 'Create a new task',
|
||
|
dependencies: '1,2',
|
||
|
priority: 'high',
|
||
|
research: true
|
||
|
};
|
||
|
|
||
|
// Standard responses
|
||
|
const successResponse = {
|
||
|
success: true,
|
||
|
data: {
|
||
|
taskId: '5',
|
||
|
message: 'Successfully added new task #5'
|
||
|
}
|
||
|
};
|
||
|
|
||
|
const errorResponse = {
|
||
|
success: false,
|
||
|
error: {
|
||
|
code: 'ADD_TASK_ERROR',
|
||
|
message: 'Failed to add task'
|
||
|
}
|
||
|
};
|
||
|
|
||
|
beforeEach(() => {
|
||
|
// Reset all mocks
|
||
|
jest.clearAllMocks();
|
||
|
|
||
|
// Create mock server
|
||
|
mockServer = {
|
||
|
addTool: jest.fn(config => {
|
||
|
executeFunction = config.execute;
|
||
|
})
|
||
|
};
|
||
|
|
||
|
// Setup default successful response
|
||
|
mockAddTaskDirect.mockReturnValue(successResponse);
|
||
|
|
||
|
// Register the tool
|
||
|
registerAddTaskTool(mockServer);
|
||
|
});
|
||
|
|
||
|
test('should register the tool correctly', () => {
|
||
|
// Verify tool was registered
|
||
|
expect(mockServer.addTool).toHaveBeenCalledWith(
|
||
|
expect.objectContaining({
|
||
|
name: 'add_task',
|
||
|
description: 'Add a new task using AI',
|
||
|
parameters: expect.any(Object),
|
||
|
execute: expect.any(Function)
|
||
|
})
|
||
|
);
|
||
|
|
||
|
// Verify the tool config was passed
|
||
|
const toolConfig = mockServer.addTool.mock.calls[0][0];
|
||
|
expect(toolConfig).toHaveProperty('parameters');
|
||
|
expect(toolConfig).toHaveProperty('execute');
|
||
|
});
|
||
|
|
||
|
test('should execute the tool with valid parameters', () => {
|
||
|
// Setup context
|
||
|
const mockContext = {
|
||
|
log: mockLogger,
|
||
|
reportProgress: jest.fn(),
|
||
|
session: { workingDirectory: '/mock/dir' }
|
||
|
};
|
||
|
|
||
|
// Execute the function
|
||
|
executeFunction(validArgs, mockContext);
|
||
|
|
||
|
// Verify getProjectRootFromSession was called
|
||
|
expect(mockGetProjectRootFromSession).toHaveBeenCalledWith(
|
||
|
mockContext.session,
|
||
|
mockLogger
|
||
|
);
|
||
|
|
||
|
// Verify addTaskDirect was called with correct arguments
|
||
|
expect(mockAddTaskDirect).toHaveBeenCalledWith(
|
||
|
expect.objectContaining({
|
||
|
...validArgs,
|
||
|
projectRoot: '/mock/project/root'
|
||
|
}),
|
||
|
mockLogger,
|
||
|
{
|
||
|
reportProgress: mockContext.reportProgress,
|
||
|
session: mockContext.session
|
||
|
}
|
||
|
);
|
||
|
|
||
|
// Verify handleApiResult was called
|
||
|
expect(mockHandleApiResult).toHaveBeenCalledWith(
|
||
|
successResponse,
|
||
|
mockLogger
|
||
|
);
|
||
|
});
|
||
|
|
||
|
test('should handle errors from addTaskDirect', () => {
|
||
|
// Setup error response
|
||
|
mockAddTaskDirect.mockReturnValueOnce(errorResponse);
|
||
|
|
||
|
// Setup context
|
||
|
const mockContext = {
|
||
|
log: mockLogger,
|
||
|
reportProgress: jest.fn(),
|
||
|
session: { workingDirectory: '/mock/dir' }
|
||
|
};
|
||
|
|
||
|
// Execute the function
|
||
|
executeFunction(validArgs, mockContext);
|
||
|
|
||
|
// Verify addTaskDirect was called
|
||
|
expect(mockAddTaskDirect).toHaveBeenCalled();
|
||
|
|
||
|
// Verify handleApiResult was called with error response
|
||
|
expect(mockHandleApiResult).toHaveBeenCalledWith(
|
||
|
errorResponse,
|
||
|
mockLogger
|
||
|
);
|
||
|
});
|
||
|
|
||
|
test('should handle unexpected errors', () => {
|
||
|
// Setup error
|
||
|
const testError = new Error('Unexpected error');
|
||
|
mockAddTaskDirect.mockImplementationOnce(() => {
|
||
|
throw testError;
|
||
|
});
|
||
|
|
||
|
// Setup context
|
||
|
const mockContext = {
|
||
|
log: mockLogger,
|
||
|
reportProgress: jest.fn(),
|
||
|
session: { workingDirectory: '/mock/dir' }
|
||
|
};
|
||
|
|
||
|
// Execute the function
|
||
|
executeFunction(validArgs, mockContext);
|
||
|
|
||
|
// Verify error was logged
|
||
|
expect(mockLogger.error).toHaveBeenCalledWith(
|
||
|
'Error in add-task tool: Unexpected error'
|
||
|
);
|
||
|
|
||
|
// Verify error response was created
|
||
|
expect(mockCreateErrorResponse).toHaveBeenCalledWith('Unexpected error');
|
||
|
});
|
||
|
|
||
|
test('should pass research parameter correctly', () => {
|
||
|
// Setup context
|
||
|
const mockContext = {
|
||
|
log: mockLogger,
|
||
|
reportProgress: jest.fn(),
|
||
|
session: { workingDirectory: '/mock/dir' }
|
||
|
};
|
||
|
|
||
|
// Test with research=true
|
||
|
executeFunction({
|
||
|
...validArgs,
|
||
|
research: true
|
||
|
}, mockContext);
|
||
|
|
||
|
// Verify addTaskDirect was called with research=true
|
||
|
expect(mockAddTaskDirect).toHaveBeenCalledWith(
|
||
|
expect.objectContaining({
|
||
|
research: true
|
||
|
}),
|
||
|
expect.any(Object),
|
||
|
expect.any(Object)
|
||
|
);
|
||
|
|
||
|
// Reset mocks
|
||
|
jest.clearAllMocks();
|
||
|
|
||
|
// Test with research=false
|
||
|
executeFunction({
|
||
|
...validArgs,
|
||
|
research: false
|
||
|
}, mockContext);
|
||
|
|
||
|
// Verify addTaskDirect was called with research=false
|
||
|
expect(mockAddTaskDirect).toHaveBeenCalledWith(
|
||
|
expect.objectContaining({
|
||
|
research: false
|
||
|
}),
|
||
|
expect.any(Object),
|
||
|
expect.any(Object)
|
||
|
);
|
||
|
});
|
||
|
|
||
|
test('should pass priority parameter correctly', () => {
|
||
|
// Setup context
|
||
|
const mockContext = {
|
||
|
log: mockLogger,
|
||
|
reportProgress: jest.fn(),
|
||
|
session: { workingDirectory: '/mock/dir' }
|
||
|
};
|
||
|
|
||
|
// Test different priority values
|
||
|
['high', 'medium', 'low'].forEach(priority => {
|
||
|
// Reset mocks
|
||
|
jest.clearAllMocks();
|
||
|
|
||
|
// Execute with specific priority
|
||
|
executeFunction({
|
||
|
...validArgs,
|
||
|
priority
|
||
|
}, mockContext);
|
||
|
|
||
|
// Verify addTaskDirect was called with correct priority
|
||
|
expect(mockAddTaskDirect).toHaveBeenCalledWith(
|
||
|
expect.objectContaining({
|
||
|
priority
|
||
|
}),
|
||
|
expect.any(Object),
|
||
|
expect.any(Object)
|
||
|
);
|
||
|
});
|
||
|
});
|
||
|
});
|