more wip
This commit is contained in:
207
tests/e2e/tests/mcp/get-tasks.test.js
Normal file
207
tests/e2e/tests/mcp/get-tasks.test.js
Normal file
@@ -0,0 +1,207 @@
|
||||
import { describe, it, expect, beforeAll, afterAll } from '@jest/globals';
|
||||
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
||||
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
|
||||
import { dirname, join } from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
import fs from 'fs';
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
const projectRoot = join(__dirname, '../../../..');
|
||||
|
||||
describe('MCP Server - get_tasks tool', () => {
|
||||
let client;
|
||||
let transport;
|
||||
|
||||
beforeAll(async () => {
|
||||
// Create transport by spawning the server
|
||||
transport = new StdioClientTransport({
|
||||
command: 'node',
|
||||
args: ['mcp-server/server.js'],
|
||||
env: process.env,
|
||||
cwd: projectRoot
|
||||
});
|
||||
|
||||
// Create client
|
||||
client = new Client(
|
||||
{
|
||||
name: 'test-client',
|
||||
version: '1.0.0'
|
||||
},
|
||||
{
|
||||
capabilities: {
|
||||
sampling: {}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// Connect to server
|
||||
await client.connect(transport);
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
if (client) {
|
||||
await client.close();
|
||||
}
|
||||
});
|
||||
|
||||
it('should connect to MCP server successfully', async () => {
|
||||
const tools = await client.listTools();
|
||||
expect(tools.tools).toBeDefined();
|
||||
expect(tools.tools.length).toBeGreaterThan(0);
|
||||
|
||||
const toolNames = tools.tools.map((t) => t.name);
|
||||
expect(toolNames).toContain('get_tasks');
|
||||
expect(toolNames).toContain('initialize_project');
|
||||
});
|
||||
|
||||
it('should initialize project successfully', async () => {
|
||||
const result = await client.callTool({
|
||||
name: 'initialize_project',
|
||||
arguments: {
|
||||
projectRoot: projectRoot
|
||||
}
|
||||
});
|
||||
|
||||
expect(result.content).toBeDefined();
|
||||
expect(result.content[0].type).toBe('text');
|
||||
expect(result.content[0].text).toContain(
|
||||
'Project initialized successfully'
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle missing tasks file gracefully', async () => {
|
||||
const result = await client.callTool({
|
||||
name: 'get_tasks',
|
||||
arguments: {
|
||||
projectRoot: projectRoot,
|
||||
file: '.taskmaster/non-existent-tasks.json'
|
||||
}
|
||||
});
|
||||
|
||||
expect(result.isError).toBe(true);
|
||||
expect(result.content[0].text).toContain('Error');
|
||||
});
|
||||
|
||||
it('should get tasks with fixture data', async () => {
|
||||
// Create a temporary tasks file with proper structure
|
||||
const testTasksPath = join(projectRoot, '.taskmaster/test-tasks.json');
|
||||
const testTasks = {
|
||||
tasks: [
|
||||
{
|
||||
id: 'test-001',
|
||||
description: 'Test task 1',
|
||||
status: 'pending',
|
||||
priority: 'high',
|
||||
estimatedMinutes: 30,
|
||||
actualMinutes: 0,
|
||||
dependencies: [],
|
||||
tags: ['test'],
|
||||
subtasks: [
|
||||
{
|
||||
id: 'test-001-1',
|
||||
description: 'Test subtask 1.1',
|
||||
status: 'pending',
|
||||
priority: 'medium',
|
||||
estimatedMinutes: 15,
|
||||
actualMinutes: 0
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'test-002',
|
||||
description: 'Test task 2',
|
||||
status: 'in_progress',
|
||||
priority: 'medium',
|
||||
estimatedMinutes: 60,
|
||||
actualMinutes: 15,
|
||||
dependencies: ['test-001'],
|
||||
tags: ['test', 'demo'],
|
||||
subtasks: []
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
// Write test tasks file
|
||||
fs.writeFileSync(testTasksPath, JSON.stringify(testTasks, null, 2));
|
||||
|
||||
try {
|
||||
const result = await client.callTool({
|
||||
name: 'get_tasks',
|
||||
arguments: {
|
||||
projectRoot: projectRoot,
|
||||
file: '.taskmaster/test-tasks.json',
|
||||
withSubtasks: true
|
||||
}
|
||||
});
|
||||
|
||||
expect(result.isError).toBeFalsy();
|
||||
expect(result.content[0].text).toContain('2 tasks found');
|
||||
expect(result.content[0].text).toContain('Test task 1');
|
||||
expect(result.content[0].text).toContain('Test task 2');
|
||||
expect(result.content[0].text).toContain('Test subtask 1.1');
|
||||
} finally {
|
||||
// Cleanup
|
||||
if (fs.existsSync(testTasksPath)) {
|
||||
fs.unlinkSync(testTasksPath);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it('should filter tasks by status', async () => {
|
||||
// Create a temporary tasks file
|
||||
const testTasksPath = join(
|
||||
projectRoot,
|
||||
'.taskmaster/test-status-tasks.json'
|
||||
);
|
||||
const testTasks = {
|
||||
tasks: [
|
||||
{
|
||||
id: 'status-001',
|
||||
description: 'Pending task',
|
||||
status: 'pending',
|
||||
priority: 'high',
|
||||
estimatedMinutes: 30,
|
||||
actualMinutes: 0,
|
||||
dependencies: [],
|
||||
tags: ['test'],
|
||||
subtasks: []
|
||||
},
|
||||
{
|
||||
id: 'status-002',
|
||||
description: 'Done task',
|
||||
status: 'done',
|
||||
priority: 'medium',
|
||||
estimatedMinutes: 60,
|
||||
actualMinutes: 60,
|
||||
dependencies: [],
|
||||
tags: ['test'],
|
||||
subtasks: []
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
fs.writeFileSync(testTasksPath, JSON.stringify(testTasks, null, 2));
|
||||
|
||||
try {
|
||||
// Test filtering by 'done' status
|
||||
const result = await client.callTool({
|
||||
name: 'get_tasks',
|
||||
arguments: {
|
||||
projectRoot: projectRoot,
|
||||
file: '.taskmaster/test-status-tasks.json',
|
||||
status: 'done'
|
||||
}
|
||||
});
|
||||
|
||||
expect(result.isError).toBeFalsy();
|
||||
expect(result.content[0].text).toContain('1 task found');
|
||||
expect(result.content[0].text).toContain('Done task');
|
||||
expect(result.content[0].text).not.toContain('Pending task');
|
||||
} finally {
|
||||
// Cleanup
|
||||
if (fs.existsSync(testTasksPath)) {
|
||||
fs.unlinkSync(testTasksPath);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
146
tests/e2e/tests/mcp/mcp-jest-test.js
Normal file
146
tests/e2e/tests/mcp/mcp-jest-test.js
Normal file
@@ -0,0 +1,146 @@
|
||||
import { mcpTest } from 'mcp-jest';
|
||||
import { join, dirname } from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
import fs from 'fs';
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
const projectRoot = join(__dirname, '../../../..');
|
||||
|
||||
// Create test tasks file for testing
|
||||
const testTasksPath = join(projectRoot, '.taskmaster/test-mcp-tasks.json');
|
||||
const testTasks = {
|
||||
tasks: [
|
||||
{
|
||||
id: 'mcp-test-001',
|
||||
description: 'MCP Test task 1',
|
||||
status: 'pending',
|
||||
priority: 'high',
|
||||
estimatedMinutes: 30,
|
||||
actualMinutes: 0,
|
||||
dependencies: [],
|
||||
tags: ['test'],
|
||||
subtasks: [
|
||||
{
|
||||
id: 'mcp-test-001-1',
|
||||
description: 'MCP Test subtask 1.1',
|
||||
status: 'pending',
|
||||
priority: 'medium',
|
||||
estimatedMinutes: 15,
|
||||
actualMinutes: 0
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'mcp-test-002',
|
||||
description: 'MCP Test task 2',
|
||||
status: 'done',
|
||||
priority: 'medium',
|
||||
estimatedMinutes: 60,
|
||||
actualMinutes: 60,
|
||||
dependencies: ['mcp-test-001'],
|
||||
tags: ['test', 'demo'],
|
||||
subtasks: []
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
// Setup test data
|
||||
fs.mkdirSync(join(projectRoot, '.taskmaster'), { recursive: true });
|
||||
fs.writeFileSync(testTasksPath, JSON.stringify(testTasks, null, 2));
|
||||
|
||||
// Run MCP Jest tests
|
||||
async function runTests() {
|
||||
try {
|
||||
const results = await mcpTest(
|
||||
{
|
||||
command: 'node',
|
||||
args: [join(projectRoot, 'mcp-server/server.js')],
|
||||
env: process.env
|
||||
},
|
||||
{
|
||||
tools: {
|
||||
initialize_project: {
|
||||
args: { projectRoot: projectRoot },
|
||||
expect: (result) =>
|
||||
result.content[0].text.includes(
|
||||
'Project initialized successfully'
|
||||
)
|
||||
},
|
||||
get_tasks: [
|
||||
{
|
||||
name: 'get all tasks with subtasks',
|
||||
args: {
|
||||
projectRoot: projectRoot,
|
||||
file: '.taskmaster/test-mcp-tasks.json',
|
||||
withSubtasks: true
|
||||
},
|
||||
expect: (result) => {
|
||||
const text = result.content[0].text;
|
||||
return (
|
||||
!result.isError &&
|
||||
text.includes('2 tasks found') &&
|
||||
text.includes('MCP Test task 1') &&
|
||||
text.includes('MCP Test task 2') &&
|
||||
text.includes('MCP Test subtask 1.1')
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'filter by done status',
|
||||
args: {
|
||||
projectRoot: projectRoot,
|
||||
file: '.taskmaster/test-mcp-tasks.json',
|
||||
status: 'done'
|
||||
},
|
||||
expect: (result) => {
|
||||
const text = result.content[0].text;
|
||||
return (
|
||||
!result.isError &&
|
||||
text.includes('1 task found') &&
|
||||
text.includes('MCP Test task 2') &&
|
||||
!text.includes('MCP Test task 1')
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'handle non-existent file',
|
||||
args: {
|
||||
projectRoot: projectRoot,
|
||||
file: '.taskmaster/non-existent.json'
|
||||
},
|
||||
expect: (result) =>
|
||||
result.isError && result.content[0].text.includes('Error')
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
console.log('\nTest Results:');
|
||||
console.log('=============');
|
||||
console.log(`✅ Passed: ${results.passed}/${results.total}`);
|
||||
|
||||
if (results.failed > 0) {
|
||||
console.error(`❌ Failed: ${results.failed}`);
|
||||
console.error('\nDetailed Results:');
|
||||
console.log(JSON.stringify(results, null, 2));
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
if (fs.existsSync(testTasksPath)) {
|
||||
fs.unlinkSync(testTasksPath);
|
||||
}
|
||||
|
||||
// Exit with appropriate code
|
||||
process.exit(results.failed > 0 ? 1 : 0);
|
||||
} catch (error) {
|
||||
console.error('Test execution failed:', error);
|
||||
// Cleanup on error
|
||||
if (fs.existsSync(testTasksPath)) {
|
||||
fs.unlinkSync(testTasksPath);
|
||||
}
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
runTests();
|
||||
254
tests/e2e/tests/mcp/simple-mcp-test.js
Normal file
254
tests/e2e/tests/mcp/simple-mcp-test.js
Normal file
@@ -0,0 +1,254 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
||||
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
|
||||
import { join, dirname } from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
import fs from 'fs';
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
const projectRoot = join(__dirname, '../../../..');
|
||||
|
||||
// Create test tasks file for testing
|
||||
const testTasksPath = join(projectRoot, '.taskmaster/test-tasks.json');
|
||||
const testTasks = {
|
||||
tasks: [
|
||||
{
|
||||
id: 'test-001',
|
||||
description: 'Test task 1',
|
||||
status: 'pending',
|
||||
priority: 'high',
|
||||
estimatedMinutes: 30,
|
||||
actualMinutes: 0,
|
||||
dependencies: [],
|
||||
tags: ['test'],
|
||||
subtasks: [
|
||||
{
|
||||
id: 'test-001-1',
|
||||
description: 'Test subtask 1.1',
|
||||
status: 'pending',
|
||||
priority: 'medium',
|
||||
estimatedMinutes: 15,
|
||||
actualMinutes: 0
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'test-002',
|
||||
description: 'Test task 2',
|
||||
status: 'done',
|
||||
priority: 'medium',
|
||||
estimatedMinutes: 60,
|
||||
actualMinutes: 60,
|
||||
dependencies: ['test-001'],
|
||||
tags: ['test', 'demo'],
|
||||
subtasks: []
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
async function runTests() {
|
||||
console.log('Starting MCP server tests...\n');
|
||||
|
||||
// Setup test data
|
||||
fs.mkdirSync(join(projectRoot, '.taskmaster'), { recursive: true });
|
||||
fs.writeFileSync(testTasksPath, JSON.stringify(testTasks, null, 2));
|
||||
|
||||
// Create transport by spawning the server
|
||||
const transport = new StdioClientTransport({
|
||||
command: 'node',
|
||||
args: ['mcp-server/server.js'],
|
||||
env: process.env,
|
||||
cwd: projectRoot
|
||||
});
|
||||
|
||||
// Create client
|
||||
const client = new Client(
|
||||
{
|
||||
name: 'test-client',
|
||||
version: '1.0.0'
|
||||
},
|
||||
{
|
||||
capabilities: {
|
||||
sampling: {}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
let testResults = {
|
||||
total: 0,
|
||||
passed: 0,
|
||||
failed: 0,
|
||||
tests: []
|
||||
};
|
||||
|
||||
async function runTest(name, testFn) {
|
||||
testResults.total++;
|
||||
try {
|
||||
await testFn();
|
||||
testResults.passed++;
|
||||
testResults.tests.push({ name, status: 'passed' });
|
||||
console.log(`✅ ${name}`);
|
||||
} catch (error) {
|
||||
testResults.failed++;
|
||||
testResults.tests.push({ name, status: 'failed', error: error.message });
|
||||
console.error(`❌ ${name}`);
|
||||
console.error(` Error: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
// Connect to server
|
||||
await client.connect(transport);
|
||||
console.log('Connected to MCP server\n');
|
||||
|
||||
// Test 1: List available tools
|
||||
await runTest('List available tools', async () => {
|
||||
const tools = await client.listTools();
|
||||
if (!tools.tools || tools.tools.length === 0) {
|
||||
throw new Error('No tools found');
|
||||
}
|
||||
const toolNames = tools.tools.map((t) => t.name);
|
||||
if (!toolNames.includes('get_tasks')) {
|
||||
throw new Error('get_tasks tool not found');
|
||||
}
|
||||
console.log(` Found ${tools.tools.length} tools`);
|
||||
});
|
||||
|
||||
// Test 2: Initialize project
|
||||
await runTest('Initialize project', async () => {
|
||||
const result = await client.callTool({
|
||||
name: 'initialize_project',
|
||||
arguments: {
|
||||
projectRoot: projectRoot
|
||||
}
|
||||
});
|
||||
if (
|
||||
!result.content[0].text.includes('Project initialized successfully')
|
||||
) {
|
||||
throw new Error('Project initialization failed');
|
||||
}
|
||||
});
|
||||
|
||||
// Test 3: Get all tasks
|
||||
await runTest('Get all tasks with subtasks', async () => {
|
||||
const result = await client.callTool({
|
||||
name: 'get_tasks',
|
||||
arguments: {
|
||||
projectRoot: projectRoot,
|
||||
file: '.taskmaster/test-tasks.json',
|
||||
withSubtasks: true
|
||||
}
|
||||
});
|
||||
|
||||
if (result.isError) {
|
||||
throw new Error(`Tool returned error: ${result.content[0].text}`);
|
||||
}
|
||||
|
||||
const text = result.content[0].text;
|
||||
const data = JSON.parse(text);
|
||||
|
||||
if (!data.data || !data.data.tasks) {
|
||||
throw new Error('Invalid response format');
|
||||
}
|
||||
|
||||
if (data.data.tasks.length !== 2) {
|
||||
throw new Error(`Expected 2 tasks, got ${data.data.tasks.length}`);
|
||||
}
|
||||
|
||||
const taskDescriptions = data.data.tasks.map((t) => t.description);
|
||||
if (
|
||||
!taskDescriptions.includes('Test task 1') ||
|
||||
!taskDescriptions.includes('Test task 2')
|
||||
) {
|
||||
throw new Error('Expected tasks not found');
|
||||
}
|
||||
|
||||
// Check for subtask
|
||||
const task1 = data.data.tasks.find((t) => t.id === 'test-001');
|
||||
if (!task1.subtasks || task1.subtasks.length === 0) {
|
||||
throw new Error('Subtasks not found');
|
||||
}
|
||||
if (task1.subtasks[0].description !== 'Test subtask 1.1') {
|
||||
throw new Error('Expected subtask not found');
|
||||
}
|
||||
});
|
||||
|
||||
// Test 4: Filter by status
|
||||
await runTest('Filter tasks by done status', async () => {
|
||||
const result = await client.callTool({
|
||||
name: 'get_tasks',
|
||||
arguments: {
|
||||
projectRoot: projectRoot,
|
||||
file: '.taskmaster/test-tasks.json',
|
||||
status: 'done'
|
||||
}
|
||||
});
|
||||
|
||||
if (result.isError) {
|
||||
throw new Error(`Tool returned error: ${result.content[0].text}`);
|
||||
}
|
||||
|
||||
const text = result.content[0].text;
|
||||
const data = JSON.parse(text);
|
||||
|
||||
if (!data.data || !data.data.tasks) {
|
||||
throw new Error('Invalid response format');
|
||||
}
|
||||
|
||||
if (data.data.tasks.length !== 1) {
|
||||
throw new Error(
|
||||
`Expected 1 task with done status, got ${data.data.tasks.length}`
|
||||
);
|
||||
}
|
||||
|
||||
const task = data.data.tasks[0];
|
||||
if (task.description !== 'Test task 2') {
|
||||
throw new Error(`Expected 'Test task 2', got '${task.description}'`);
|
||||
}
|
||||
if (task.status !== 'done') {
|
||||
throw new Error(`Expected status 'done', got '${task.status}'`);
|
||||
}
|
||||
});
|
||||
|
||||
// Test 5: Handle non-existent file
|
||||
await runTest('Handle non-existent file gracefully', async () => {
|
||||
const result = await client.callTool({
|
||||
name: 'get_tasks',
|
||||
arguments: {
|
||||
projectRoot: projectRoot,
|
||||
file: '.taskmaster/non-existent.json'
|
||||
}
|
||||
});
|
||||
|
||||
if (!result.isError) {
|
||||
throw new Error('Expected error for non-existent file');
|
||||
}
|
||||
if (!result.content[0].text.includes('Error')) {
|
||||
throw new Error('Expected error message');
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('\nConnection error:', error.message);
|
||||
testResults.failed = testResults.total;
|
||||
} finally {
|
||||
// Clean up
|
||||
await client.close();
|
||||
if (fs.existsSync(testTasksPath)) {
|
||||
fs.unlinkSync(testTasksPath);
|
||||
}
|
||||
|
||||
// Print summary
|
||||
console.log('\n' + '='.repeat(50));
|
||||
console.log('Test Summary:');
|
||||
console.log(`Total: ${testResults.total}`);
|
||||
console.log(`Passed: ${testResults.passed}`);
|
||||
console.log(`Failed: ${testResults.failed}`);
|
||||
console.log('='.repeat(50));
|
||||
|
||||
// Exit with appropriate code
|
||||
process.exit(testResults.failed > 0 ? 1 : 0);
|
||||
}
|
||||
}
|
||||
|
||||
runTests().catch(console.error);
|
||||
Reference in New Issue
Block a user