UNPKG

ms365-mcp-server

Version:

Microsoft 365 MCP Server for managing Microsoft 365 email through natural language interactions with full OAuth2 authentication support

278 lines (277 loc) โ€ข 13.8 kB
import { performanceMonitor } from './batch-performance-monitor.js'; import { logger } from './api.js'; export class BatchTestRunner { constructor(ms365Ops) { this.ms365Ops = ms365Ops; } /** * Test Scenario 1: Bulk Email Retrieval */ async testBulkEmailRetrieval(messageIds) { logger.log(`๐Ÿงช TEST SCENARIO 1: Bulk Email Retrieval (${messageIds.length} emails)`); // Traditional approach - sequential calls const traditionalStart = Date.now(); const traditionalEmails = []; for (const messageId of messageIds) { try { const email = await this.ms365Ops.getEmail(messageId, true); traditionalEmails.push(email); } catch (error) { logger.log(`โŒ Traditional: Failed to get email ${messageId}: ${error}`); } } const traditionalMetrics = performanceMonitor.endOperation('bulk_email_retrieval', 'bulk_email_retrieval', messageIds.length, 'traditional', traditionalStart, messageIds.length * 2, // email + attachments call per email true); // Wait a moment to avoid rate limiting await new Promise(resolve => setTimeout(resolve, 1000)); // Batched approach - native JSON batching const batchedStart = Date.now(); const batchedEmails = await this.ms365Ops.getEmailsBatch(messageIds, true); const batchedMetrics = performanceMonitor.endOperation('bulk_email_retrieval', 'bulk_email_retrieval', messageIds.length, 'batched', batchedStart, Math.ceil(messageIds.length / 20), // 20 requests per batch true); const comparison = performanceMonitor.comparePerformance('bulk_email_retrieval'); return { traditional: { emails: traditionalEmails, metrics: traditionalMetrics }, batched: { emails: batchedEmails, metrics: batchedMetrics }, comparison }; } /** * Test Scenario 2: Bulk Email Operations (Mark/Move/Delete) */ async testBulkEmailOperations(messageIds) { logger.log(`๐Ÿงช TEST SCENARIO 2: Bulk Email Operations (${messageIds.length} operations)`); // Create operations for testing const operations = messageIds.map((id, index) => ({ id: `op_${index}`, operation: 'mark', messageId: id, params: { isRead: true } })); // Traditional approach - sequential operations const traditionalStart = Date.now(); const traditionalResults = []; for (const op of operations) { try { await this.ms365Ops.markEmail(op.messageId, op.params.isRead); traditionalResults.push({ id: op.id, success: true }); } catch (error) { logger.log(`โŒ Traditional: Failed to mark email ${op.messageId}: ${error}`); traditionalResults.push({ id: op.id, success: false, error: String(error) }); } } const traditionalMetrics = performanceMonitor.endOperation('bulk_email_operations', 'bulk_email_operations', operations.length, 'traditional', traditionalStart, operations.length, // one call per operation true); // Wait a moment to avoid rate limiting await new Promise(resolve => setTimeout(resolve, 1000)); // Batched approach - native JSON batching const batchedStart = Date.now(); const batchedResults = await this.ms365Ops.batchEmailOperations(operations); const batchedMetrics = performanceMonitor.endOperation('bulk_email_operations', 'bulk_email_operations', operations.length, 'batched', batchedStart, Math.ceil(operations.length / 20), // 20 operations per batch true); const comparison = performanceMonitor.comparePerformance('bulk_email_operations'); return { traditional: { results: traditionalResults, metrics: traditionalMetrics }, batched: { results: Array.from(batchedResults.entries()), metrics: batchedMetrics }, comparison }; } /** * Test Scenario 3: Folder + Email Parallel Fetching */ async testFolderWithEmails(folderId, emailCount = 10) { logger.log(`๐Ÿงช TEST SCENARIO 3: Folder + Emails Fetching (${emailCount} emails)`); // Traditional approach - sequential calls const traditionalStart = Date.now(); // Get folder info first - using existing folder methods const folders = await this.ms365Ops.listFolders(); const folderInfo = folders.find(f => f.id === folderId || f.displayName.toLowerCase() === folderId.toLowerCase()); // Then get emails const emailList = await this.ms365Ops.listEmails(folderId, emailCount); const traditionalResult = { folder: folderInfo, emails: emailList.messages }; const traditionalMetrics = performanceMonitor.endOperation('folder_with_emails', 'folder_with_emails', emailCount + 1, // folder + emails 'traditional', traditionalStart, 2, // folder call + emails call true); // Wait a moment to avoid rate limiting await new Promise(resolve => setTimeout(resolve, 1000)); // Batched approach - parallel fetching const batchedStart = Date.now(); const batchedResult = await this.ms365Ops.getFolderWithRecentEmails(folderId, emailCount); const batchedMetrics = performanceMonitor.endOperation('folder_with_emails', 'folder_with_emails', emailCount + 1, 'batched', batchedStart, 1, // single batch call true); const comparison = performanceMonitor.comparePerformance('folder_with_emails'); return { traditional: { result: traditionalResult, metrics: traditionalMetrics }, batched: { result: batchedResult, metrics: batchedMetrics }, comparison }; } /** * Test Scenario 4: Mixed Operations (Real-world workflow) */ async testMixedOperations(messageIds) { logger.log(`๐Ÿงช TEST SCENARIO 4: Mixed Operations Workflow (${messageIds.length} emails)`); // Take first few emails for different operations const emailsToMark = messageIds.slice(0, Math.ceil(messageIds.length / 3)); const emailsToMove = messageIds.slice(Math.ceil(messageIds.length / 3), Math.ceil(messageIds.length * 2 / 3)); const emailsToDelete = messageIds.slice(Math.ceil(messageIds.length * 2 / 3)); // Traditional approach const traditionalStart = Date.now(); const traditionalResults = { marked: 0, moved: 0, deleted: 0, errors: 0 }; // Mark emails as read for (const id of emailsToMark) { try { await this.ms365Ops.markEmail(id, true); traditionalResults.marked++; } catch (error) { traditionalResults.errors++; } } // Move emails to archive (simulated) for (const id of emailsToMove) { try { await this.ms365Ops.moveEmail(id, 'archive'); traditionalResults.moved++; } catch (error) { traditionalResults.errors++; } } // Delete emails (simulated - we won't actually delete for testing) for (const id of emailsToDelete) { try { // Simulate delete operation with mark as read instead await this.ms365Ops.markEmail(id, true); traditionalResults.deleted++; } catch (error) { traditionalResults.errors++; } } const traditionalMetrics = performanceMonitor.endOperation('mixed_operations', 'mixed_operations', messageIds.length, 'traditional', traditionalStart, messageIds.length, // one call per operation true); // Wait a moment to avoid rate limiting await new Promise(resolve => setTimeout(resolve, 1000)); // Batched approach const batchedStart = Date.now(); const mixedOperations = [ ...emailsToMark.map((id, index) => ({ id: `mark_${index}`, operation: 'mark', messageId: id, params: { isRead: true } })), ...emailsToMove.map((id, index) => ({ id: `move_${index}`, operation: 'move', messageId: id, params: { destinationFolderId: 'archive' } })), // Simulate delete with mark for testing ...emailsToDelete.map((id, index) => ({ id: `delete_${index}`, operation: 'mark', messageId: id, params: { isRead: true } })) ]; const batchedResults = await this.ms365Ops.batchEmailOperations(mixedOperations); const batchedMetrics = performanceMonitor.endOperation('mixed_operations', 'mixed_operations', messageIds.length, 'batched', batchedStart, Math.ceil(mixedOperations.length / 20), // 20 operations per batch true); const comparison = performanceMonitor.comparePerformance('mixed_operations'); return { traditional: { results: traditionalResults, metrics: traditionalMetrics }, batched: { results: Array.from(batchedResults.entries()), metrics: batchedMetrics }, comparison }; } /** * Run all test scenarios */ async runAllTests(messageIds) { logger.log(`๐Ÿš€ STARTING COMPREHENSIVE BATCH PERFORMANCE TESTS`); logger.log(`๐Ÿ“ง Using ${messageIds.length} test emails`); performanceMonitor.clear(); const results = { bulkRetrieval: null, bulkOperations: null, folderWithEmails: null, mixedOperations: null }; try { // Test 1: Bulk Email Retrieval if (messageIds.length >= 5) { const testIds = messageIds.slice(0, Math.min(10, messageIds.length)); results.bulkRetrieval = await this.testBulkEmailRetrieval(testIds); } // Test 2: Bulk Email Operations if (messageIds.length >= 5) { const testIds = messageIds.slice(0, Math.min(15, messageIds.length)); results.bulkOperations = await this.testBulkEmailOperations(testIds); } // Test 3: Folder + Emails results.folderWithEmails = await this.testFolderWithEmails('inbox', 10); // Test 4: Mixed Operations if (messageIds.length >= 9) { const testIds = messageIds.slice(0, Math.min(12, messageIds.length)); results.mixedOperations = await this.testMixedOperations(testIds); } } catch (error) { logger.error('Error during batch testing:', error); } // Generate comprehensive report const report = this.generateTestReport(results); logger.log(report); return report; } /** * Generate comprehensive test report */ generateTestReport(results) { let report = `\n๐Ÿ† BATCH PERFORMANCE TEST RESULTS\n`; report += `${'='.repeat(50)}\n\n`; if (results.bulkRetrieval?.comparison) { const comp = results.bulkRetrieval.comparison; report += `๐Ÿ“ง BULK EMAIL RETRIEVAL\n`; report += ` Traditional: ${comp.traditional.duration}ms (${comp.traditional.httpCalls} HTTP calls)\n`; report += ` Batched: ${comp.batched.duration}ms (${comp.batched.httpCalls} HTTP calls)\n`; report += ` ๐Ÿ’ก Improvement: ${comp.improvement.durationReduction.toFixed(1)}% faster, ${comp.improvement.httpCallReduction.toFixed(1)}% fewer calls\n\n`; } if (results.bulkOperations?.comparison) { const comp = results.bulkOperations.comparison; report += `โšก BULK EMAIL OPERATIONS\n`; report += ` Traditional: ${comp.traditional.duration}ms (${comp.traditional.httpCalls} HTTP calls)\n`; report += ` Batched: ${comp.batched.duration}ms (${comp.batched.httpCalls} HTTP calls)\n`; report += ` ๐Ÿ’ก Improvement: ${comp.improvement.durationReduction.toFixed(1)}% faster, ${comp.improvement.httpCallReduction.toFixed(1)}% fewer calls\n\n`; } if (results.folderWithEmails?.comparison) { const comp = results.folderWithEmails.comparison; report += `๐Ÿ“ FOLDER + EMAILS FETCHING\n`; report += ` Traditional: ${comp.traditional.duration}ms (${comp.traditional.httpCalls} HTTP calls)\n`; report += ` Batched: ${comp.batched.duration}ms (${comp.batched.httpCalls} HTTP calls)\n`; report += ` ๐Ÿ’ก Improvement: ${comp.improvement.durationReduction.toFixed(1)}% faster, ${comp.improvement.httpCallReduction.toFixed(1)}% fewer calls\n\n`; } if (results.mixedOperations?.comparison) { const comp = results.mixedOperations.comparison; report += `๐Ÿ”„ MIXED OPERATIONS WORKFLOW\n`; report += ` Traditional: ${comp.traditional.duration}ms (${comp.traditional.httpCalls} HTTP calls)\n`; report += ` Batched: ${comp.batched.duration}ms (${comp.batched.httpCalls} HTTP calls)\n`; report += ` ๐Ÿ’ก Improvement: ${comp.improvement.durationReduction.toFixed(1)}% faster, ${comp.improvement.httpCallReduction.toFixed(1)}% fewer calls\n\n`; } report += `๐Ÿ“Š OVERALL PERFORMANCE INSIGHTS:\n`; report += performanceMonitor.generateReport(); return report; } }