UNPKG

taiga-mcp-server

Version:

Full-featured MCP server for Taiga project management with advanced sprint and issue tracking. Based on mcpTAIGA by adriapedralbes, enhanced with substantial modifications. Developed collaboratively with Claude Code (AI-assisted development).

238 lines (205 loc) โ€ข 7.76 kB
#!/usr/bin/env node /** * Integration Test Suite * Tests end-to-end workflows with actual Taiga credentials */ import { Client } from '@modelcontextprotocol/sdk/client/index.js'; import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js'; import { fileURLToPath } from 'url'; import { dirname, join } from 'path'; import dotenv from 'dotenv'; // Load environment variables dotenv.config({ path: join(dirname(fileURLToPath(import.meta.url)), '..', '.env') }); class IntegrationTestRunner { constructor() { this.passed = 0; this.failed = 0; this.client = null; this.projectSlug = null; this.createdItems = []; // Track items to cleanup } async setup() { console.log('๐Ÿ”„ Starting Integration Tests with Taiga API\n'); // Check if credentials are available if (!process.env.TAIGA_USERNAME || !process.env.TAIGA_PASSWORD) { console.log('โš ๏ธ TAIGA_USERNAME or TAIGA_PASSWORD not found in .env'); console.log(' Skipping integration tests (this is OK for CI/testing)'); return false; } // Create transport const transport = new StdioClientTransport({ command: 'node', args: [join(dirname(fileURLToPath(import.meta.url)), '..', 'src', 'index.js')], }); // Create client this.client = new Client( { name: 'Taiga MCP Integration Test', version: '1.0.0', }, { capabilities: { resources: {}, tools: {}, }, } ); try { await this.client.connect(transport); console.log('โœ… Connected to Taiga MCP server'); return true; } catch (error) { console.error('โŒ Failed to connect:', error.message); return false; } } async test(name, testFn) { try { process.stdout.write(`๐Ÿงช ${name}... `); await testFn(); console.log('โœ… PASS'); this.passed++; } catch (error) { console.log('โŒ FAIL'); console.log(` Error: ${error.message}`); this.failed++; } } assert(condition, message) { if (!condition) { throw new Error(message || 'Assertion failed'); } } async run() { const connected = await this.setup(); if (!connected) { console.log('๐Ÿ“‹ Integration tests skipped (no credentials)\n'); return; } try { // Test 1: Authentication await this.test('Authenticate with Taiga', async () => { const result = await this.client.callTool({ name: 'authenticate', arguments: { username: process.env.TAIGA_USERNAME, password: process.env.TAIGA_PASSWORD } }); this.assert(result && result.content, 'Should return authentication result'); const content = result.content[0]?.text || ''; this.assert(content.includes('successful') || content.includes('authenticated'), 'Should indicate successful authentication'); }); // Test 2: List actual projects await this.test('List actual projects', async () => { const result = await this.client.callTool({ name: 'listProjects', arguments: {} }); this.assert(result && result.content, 'Should return projects'); const content = result.content[0]?.text || ''; if (content.includes('No projects found')) { console.log('\n โ„น๏ธ No projects found in account'); return; } // Extract first project slug for further tests const slugMatch = content.match(/Slug: ([^\)]+)/); if (slugMatch) { this.projectSlug = slugMatch[1]; console.log(`\n ๐Ÿ“ Using project: ${this.projectSlug}`); } }); // Test 3: Get project details (if we have a project) if (this.projectSlug) { await this.test('Get project details', async () => { const result = await this.client.callTool({ name: 'getProject', arguments: { projectIdentifier: this.projectSlug } }); this.assert(result && result.content, 'Should return project details'); const content = result.content[0]?.text || ''; this.assert(content.includes('Project:') || content.includes('Name:'), 'Should contain project information'); }); // Test 4: List sprints/milestones await this.test('List project sprints', async () => { const result = await this.client.callTool({ name: 'listMilestones', arguments: { projectIdentifier: this.projectSlug } }); this.assert(result && result.content, 'Should return milestones list'); const content = result.content[0]?.text || ''; this.assert( content.includes('Milestones') || content.includes('No milestones') || content.includes('Sprint'), 'Should contain milestone information' ); }); // Test 5: List user stories await this.test('List user stories', async () => { const result = await this.client.callTool({ name: 'listUserStories', arguments: { projectIdentifier: this.projectSlug } }); this.assert(result && result.content, 'Should return user stories'); const content = result.content[0]?.text || ''; this.assert( content.includes('User Stories') || content.includes('No user stories') || content.includes('Story'), 'Should contain user story information' ); }); // Test 6: List issues await this.test('List project issues', async () => { const result = await this.client.callTool({ name: 'listIssues', arguments: { projectIdentifier: this.projectSlug } }); this.assert(result && result.content, 'Should return issues list'); const content = result.content[0]?.text || ''; this.assert( content.includes('Issues') || content.includes('No issues') || content.includes('Issue'), 'Should contain issue information' ); }); } // Test 7: Read projects resource await this.test('Read projects resource', async () => { const result = await this.client.readResource('projects://list'); this.assert(result && result.contents, 'Should return resource contents'); const content = result.contents[0]?.text || ''; this.assert(content.includes('Projects') || content.includes('project'), 'Should contain project information'); }); } catch (error) { console.error('\nโŒ Integration test suite failed:', error); process.exit(1); } // Print results console.log('\n๐Ÿ“Š Integration Test Results:'); console.log(`โœ… Passed: ${this.passed}`); console.log(`โŒ Failed: ${this.failed}`); if (this.passed + this.failed > 0) { console.log(`๐Ÿ“ˆ Success Rate: ${Math.round((this.passed / (this.passed + this.failed)) * 100)}%`); } if (this.failed > 0) { console.log('\nโš ๏ธ Some integration tests failed.'); process.exit(1); } else if (this.passed > 0) { console.log('\n๐ŸŽ‰ All integration tests passed!'); } } } // Run tests if this file is executed directly if (import.meta.url === `file://${process.argv[1]}`) { const runner = new IntegrationTestRunner(); runner.run().catch(error => { console.error('Integration test runner failed:', error); process.exit(1); }); } export default IntegrationTestRunner;