- Move 12 markdown files from root to docs/ subdirectories - Organize documentation by category: • docs/troubleshooting/ (1 file) - Technical troubleshooting guides • docs/deployment/ (4 files) - Deployment and security documentation • docs/guides/ (3 files) - Feature-specific guides • docs/planning/ (4 files) - Planning and improvement proposals Root directory cleanup: - Reduced from 16 to 4 markdown files in root - Only essential project files remain: • CLAUDE.md (AI instructions) • README.md (Main project readme) • CLEANUP_PLAN.md (Current cleanup plan) • SRC_STRUCTURE_IMPROVEMENTS.md (Structure improvements) This improves: ✅ Documentation discoverability ✅ Logical organization by purpose ✅ Clean root directory ✅ Better maintainability
530 lines
21 KiB
PHP
530 lines
21 KiB
PHP
<?php
|
||
|
||
declare(strict_types=1);
|
||
|
||
/**
|
||
* MCP System Test Suite
|
||
*
|
||
* Comprehensive testing of the MCP framework including:
|
||
* - Cache Manager Performance
|
||
* - Concurrent Execution
|
||
* - Result Optimization
|
||
* - Performance Monitoring
|
||
*/
|
||
|
||
require_once __DIR__ . '/../../vendor/autoload.php';
|
||
|
||
use App\Framework\Mcp\Core\Services\IntelligentMcpCacheManager;
|
||
use App\Framework\Mcp\Core\Services\ResultOptimizer;
|
||
use App\Framework\Mcp\Core\Services\McpPerformanceMonitor;
|
||
use App\Framework\Mcp\Core\Services\ConcurrentExecutionManager;
|
||
use App\Framework\Mcp\Core\ValueObjects\CacheStrategy;
|
||
use App\Framework\Mcp\Core\ValueObjects\OptimizationStrategy;
|
||
use App\Framework\Mcp\Core\ValueObjects\OutputFormat;
|
||
use App\Framework\Mcp\Core\ValueObjects\ExecutionTask;
|
||
use App\Framework\Mcp\Core\ValueObjects\ConcurrencyStrategy;
|
||
use App\Framework\Cache\SmartCache;
|
||
use App\Framework\Async\AsyncService;
|
||
|
||
class McpSystemTester
|
||
{
|
||
private array $testResults = [];
|
||
private float $startTime;
|
||
|
||
public function __construct()
|
||
{
|
||
$this->startTime = microtime(true);
|
||
echo "🧪 MCP System Test Suite Starting...\n\n";
|
||
}
|
||
|
||
public function runAllTests(): void
|
||
{
|
||
echo "📋 Running comprehensive MCP system tests...\n\n";
|
||
|
||
// Test Cache Manager
|
||
$this->testCacheManager();
|
||
|
||
// Test Result Optimizer
|
||
$this->testResultOptimizer();
|
||
|
||
// Test Performance Monitor
|
||
$this->testPerformanceMonitor();
|
||
|
||
// Test Concurrent Execution (simulated)
|
||
$this->testConcurrentExecution();
|
||
|
||
// Performance Integration Test
|
||
$this->testIntegratedPerformance();
|
||
|
||
// Generate final report
|
||
$this->generateReport();
|
||
}
|
||
|
||
private function testCacheManager(): void
|
||
{
|
||
echo "🗄️ Testing Cache Manager...\n";
|
||
|
||
try {
|
||
// Create mock cache for testing
|
||
$cache = new SmartCache();
|
||
$cacheManager = new IntelligentMcpCacheManager($cache);
|
||
|
||
// Test 1: Basic caching functionality
|
||
echo " ⚡ Test 1: Basic Caching\n";
|
||
$testData = ['test' => 'data', 'timestamp' => time()];
|
||
|
||
// Test set/get
|
||
$cacheManager->set('test_tool', 'test_method', [], $testData, CacheStrategy::MEDIUM);
|
||
$retrieved = $cacheManager->get('test_tool', 'test_method', [], CacheStrategy::MEDIUM);
|
||
|
||
$this->assert($retrieved === $testData, "Cache set/get should work");
|
||
echo " ✅ Basic caching works\n";
|
||
|
||
// Test 2: Cache strategy selection
|
||
echo " ⚡ Test 2: Cache Strategy Selection\n";
|
||
$healthStrategy = CacheStrategy::recommendFor('health_checker', 'status');
|
||
$routeStrategy = CacheStrategy::recommendFor('route_analyzer', 'analyze');
|
||
|
||
$this->assert($healthStrategy === CacheStrategy::SHORT, "Health tools should use SHORT strategy");
|
||
$this->assert($routeStrategy === CacheStrategy::LONG, "Route tools should use LONG strategy");
|
||
echo " ✅ Strategy selection works\n";
|
||
|
||
// Test 3: Remember pattern
|
||
echo " ⚡ Test 3: Remember Pattern\n";
|
||
$callCount = 0;
|
||
$result = $cacheManager->remember(
|
||
'test_tool',
|
||
'expensive_operation',
|
||
['param' => 'value'],
|
||
function() use (&$callCount) {
|
||
$callCount++;
|
||
return ['expensive' => 'result', 'call_count' => $callCount];
|
||
},
|
||
CacheStrategy::MEDIUM
|
||
);
|
||
|
||
// Should be called once
|
||
$this->assert($callCount === 1, "Callback should be called once");
|
||
|
||
// Second call should use cache
|
||
$result2 = $cacheManager->remember(
|
||
'test_tool',
|
||
'expensive_operation',
|
||
['param' => 'value'],
|
||
function() use (&$callCount) {
|
||
$callCount++;
|
||
return ['expensive' => 'result', 'call_count' => $callCount];
|
||
},
|
||
CacheStrategy::MEDIUM
|
||
);
|
||
|
||
$this->assert($callCount === 1, "Callback should not be called again (cache hit)");
|
||
$this->assert($result === $result2, "Results should be identical");
|
||
echo " ✅ Remember pattern works\n";
|
||
|
||
// Test 4: Cache metrics
|
||
echo " ⚡ Test 4: Cache Metrics\n";
|
||
$metrics = $cacheManager->getMetrics();
|
||
$this->assert(is_array($metrics->toArray()), "Metrics should be returned");
|
||
echo " ✅ Cache metrics available\n";
|
||
|
||
$this->testResults['cache_manager'] = [
|
||
'status' => 'passed',
|
||
'tests' => 4,
|
||
'details' => 'All cache manager tests passed'
|
||
];
|
||
|
||
} catch (\Throwable $e) {
|
||
$this->testResults['cache_manager'] = [
|
||
'status' => 'failed',
|
||
'error' => $e->getMessage(),
|
||
'trace' => $e->getTraceAsString()
|
||
];
|
||
echo " ❌ Cache Manager test failed: " . $e->getMessage() . "\n";
|
||
}
|
||
|
||
echo "\n";
|
||
}
|
||
|
||
private function testResultOptimizer(): void
|
||
{
|
||
echo "🔧 Testing Result Optimizer...\n";
|
||
|
||
try {
|
||
$optimizer = new ResultOptimizer();
|
||
|
||
// Test 1: Basic optimization
|
||
echo " ⚡ Test 1: Basic Optimization\n";
|
||
$largeData = array_fill(0, 1000, 'test data item');
|
||
$result = $optimizer->optimize($largeData, OutputFormat::JSON, OptimizationStrategy::SIZE);
|
||
|
||
$this->assert($result->compressionRatio < 1.0, "Should achieve compression");
|
||
$this->assert($result->optimizedSize < $result->originalSize, "Optimized size should be smaller");
|
||
echo " ✅ Basic optimization works\n";
|
||
|
||
// Test 2: Strategy comparison
|
||
echo " ⚡ Test 2: Strategy Comparison\n";
|
||
$testData = ['large_array' => array_fill(0, 500, 'data'), 'metadata' => 'test'];
|
||
|
||
$sizeResult = $optimizer->optimize($testData, OutputFormat::JSON, OptimizationStrategy::SIZE);
|
||
$speedResult = $optimizer->optimize($testData, OutputFormat::JSON, OptimizationStrategy::SPEED);
|
||
$noneResult = $optimizer->optimize($testData, OutputFormat::JSON, OptimizationStrategy::NONE);
|
||
|
||
$this->assert($sizeResult->compressionRatio <= $speedResult->compressionRatio, "Size strategy should compress more");
|
||
$this->assert($noneResult->compressionRatio === 1.0, "None strategy should not compress");
|
||
echo " ✅ Strategy comparison works\n";
|
||
|
||
// Test 3: Format-specific optimization
|
||
echo " ⚡ Test 3: Format-Specific Optimization\n";
|
||
$complexData = [
|
||
'nodes' => ['A' => ['connections' => ['B', 'C']], 'B' => ['connections' => ['A']]],
|
||
'metadata' => ['type' => 'graph', 'version' => '1.0']
|
||
];
|
||
|
||
$jsonResult = $optimizer->optimize($complexData, OutputFormat::JSON, OptimizationStrategy::BALANCED);
|
||
$tableResult = $optimizer->optimize($complexData, OutputFormat::TABLE, OptimizationStrategy::BALANCED);
|
||
|
||
$this->assert(is_array($jsonResult->optimizedData), "JSON optimization should return array");
|
||
echo " ✅ Format-specific optimization works\n";
|
||
|
||
// Test 4: Compression and decompression
|
||
echo " ⚡ Test 4: Compression and Decompression\n";
|
||
$originalData = array_fill(0, 100, 'This is a test string that should compress well when repeated many times');
|
||
$compressed = $optimizer->compress($originalData, 6);
|
||
|
||
if (is_array($compressed) && isset($compressed['_compressed'])) {
|
||
$decompressed = $optimizer->decompress($compressed);
|
||
$this->assert($decompressed === $originalData, "Decompression should restore original data");
|
||
echo " ✅ Compression/decompression works\n";
|
||
} else {
|
||
echo " ℹ️ Data too small for compression\n";
|
||
}
|
||
|
||
$this->testResults['result_optimizer'] = [
|
||
'status' => 'passed',
|
||
'tests' => 4,
|
||
'details' => 'All result optimizer tests passed'
|
||
];
|
||
|
||
} catch (\Throwable $e) {
|
||
$this->testResults['result_optimizer'] = [
|
||
'status' => 'failed',
|
||
'error' => $e->getMessage(),
|
||
'trace' => $e->getTraceAsString()
|
||
];
|
||
echo " ❌ Result Optimizer test failed: " . $e->getMessage() . "\n";
|
||
}
|
||
|
||
echo "\n";
|
||
}
|
||
|
||
private function testPerformanceMonitor(): void
|
||
{
|
||
echo "📊 Testing Performance Monitor...\n";
|
||
|
||
try {
|
||
$cache = new SmartCache();
|
||
$monitor = new McpPerformanceMonitor($cache);
|
||
|
||
// Test 1: Basic execution monitoring
|
||
echo " ⚡ Test 1: Basic Execution Monitoring\n";
|
||
$executionId = $monitor->startExecution('test_tool', 'test_method', ['param' => 'value']);
|
||
$this->assert(!empty($executionId), "Execution ID should be generated");
|
||
|
||
// Simulate some work
|
||
usleep(100000); // 0.1 seconds
|
||
$metrics = $monitor->endExecution($executionId, ['result' => 'data']);
|
||
|
||
$this->assert($metrics->success, "Execution should be successful");
|
||
$this->assert($metrics->executionTime > 0.09, "Execution time should be recorded");
|
||
echo " ✅ Basic monitoring works\n";
|
||
|
||
// Test 2: Error handling
|
||
echo " ⚡ Test 2: Error Handling\n";
|
||
$errorId = $monitor->startExecution('test_tool', 'failing_method');
|
||
$errorMetrics = $monitor->endExecution($errorId, null, 'Test error');
|
||
|
||
$this->assert(!$errorMetrics->success, "Failed execution should be marked as unsuccessful");
|
||
$this->assert($errorMetrics->error === 'Test error', "Error message should be preserved");
|
||
echo " ✅ Error handling works\n";
|
||
|
||
// Test 3: Monitor wrapper
|
||
echo " ⚡ Test 3: Monitor Wrapper\n";
|
||
$result = $monitor->monitor(
|
||
'test_tool',
|
||
'wrapper_test',
|
||
function() {
|
||
usleep(50000); // 0.05 seconds
|
||
return ['wrapped' => 'result'];
|
||
},
|
||
['wrapper' => 'param']
|
||
);
|
||
|
||
$this->assert($result === ['wrapped' => 'result'], "Monitor wrapper should return result");
|
||
echo " ✅ Monitor wrapper works\n";
|
||
|
||
// Test 4: Performance thresholds
|
||
echo " ⚡ Test 4: Performance Thresholds\n";
|
||
$threshold = $monitor->getThresholds('test_tool');
|
||
$this->assert($threshold->maxExecutionTime > 0, "Threshold should have execution time limit");
|
||
echo " ✅ Performance thresholds work\n";
|
||
|
||
$this->testResults['performance_monitor'] = [
|
||
'status' => 'passed',
|
||
'tests' => 4,
|
||
'details' => 'All performance monitor tests passed'
|
||
];
|
||
|
||
} catch (\Throwable $e) {
|
||
$this->testResults['performance_monitor'] = [
|
||
'status' => 'failed',
|
||
'error' => $e->getMessage(),
|
||
'trace' => $e->getTraceAsString()
|
||
];
|
||
echo " ❌ Performance Monitor test failed: " . $e->getMessage() . "\n";
|
||
}
|
||
|
||
echo "\n";
|
||
}
|
||
|
||
private function testConcurrentExecution(): void
|
||
{
|
||
echo "🔄 Testing Concurrent Execution (Simulated)...\n";
|
||
|
||
try {
|
||
// Note: This is a simplified test since we can't easily test real concurrency in this context
|
||
|
||
// Test 1: Task creation
|
||
echo " ⚡ Test 1: Task Creation\n";
|
||
$task1 = ExecutionTask::create('test_tool', 'method1', ['param1' => 'value1']);
|
||
$task2 = ExecutionTask::highPriority('test_tool', 'method2', ['param2' => 'value2']);
|
||
$task3 = ExecutionTask::lowResource('test_tool', 'method3', ['param3' => 'value3']);
|
||
|
||
$this->assert(!empty($task1->getId()), "Task should have ID");
|
||
$this->assert($task2->getPriority() > $task1->getPriority(), "High priority task should have higher priority");
|
||
$this->assert($task3->getEstimatedMemory() < $task1->getEstimatedMemory(), "Low resource task should estimate less memory");
|
||
echo " ✅ Task creation works\n";
|
||
|
||
// Test 2: Concurrency strategy
|
||
echo " ⚡ Test 2: Concurrency Strategy\n";
|
||
$conservative = ConcurrencyStrategy::conservative();
|
||
$balanced = ConcurrencyStrategy::balanced();
|
||
$aggressive = ConcurrencyStrategy::aggressive();
|
||
|
||
$this->assert($conservative->getMaxConcurrency() < $balanced->getMaxConcurrency(), "Conservative should have lower concurrency");
|
||
$this->assert($balanced->getMaxConcurrency() < $aggressive->getMaxConcurrency(), "Aggressive should have higher concurrency");
|
||
echo " ✅ Concurrency strategy works\n";
|
||
|
||
// Test 3: Task array conversion
|
||
echo " ⚡ Test 3: Task Serialization\n";
|
||
$taskArray = $task1->toArray();
|
||
$this->assert(isset($taskArray['id']), "Task array should contain ID");
|
||
$this->assert(isset($taskArray['tool_name']), "Task array should contain tool name");
|
||
echo " ✅ Task serialization works\n";
|
||
|
||
$this->testResults['concurrent_execution'] = [
|
||
'status' => 'passed',
|
||
'tests' => 3,
|
||
'details' => 'Concurrent execution structure tests passed (simulated)'
|
||
];
|
||
|
||
} catch (\Throwable $e) {
|
||
$this->testResults['concurrent_execution'] = [
|
||
'status' => 'failed',
|
||
'error' => $e->getMessage(),
|
||
'trace' => $e->getTraceAsString()
|
||
];
|
||
echo " ❌ Concurrent Execution test failed: " . $e->getMessage() . "\n";
|
||
}
|
||
|
||
echo "\n";
|
||
}
|
||
|
||
private function testIntegratedPerformance(): void
|
||
{
|
||
echo "🎯 Testing Integrated Performance...\n";
|
||
|
||
try {
|
||
$cache = new SmartCache();
|
||
$cacheManager = new IntelligentMcpCacheManager($cache);
|
||
$optimizer = new ResultOptimizer();
|
||
$monitor = new McpPerformanceMonitor($cache);
|
||
|
||
// Test 1: Full pipeline performance
|
||
echo " ⚡ Test 1: Full Pipeline Performance\n";
|
||
$startTime = microtime(true);
|
||
|
||
// Simulate complex operation
|
||
$complexData = $this->generateComplexTestData();
|
||
|
||
// Optimize the data
|
||
$optimized = $optimizer->optimize($complexData, OutputFormat::JSON, OptimizationStrategy::BALANCED);
|
||
|
||
// Cache the result
|
||
$cacheManager->set('integration_test', 'complex_operation', ['test' => 'param'], $optimized->optimizedData);
|
||
|
||
// Retrieve from cache
|
||
$cached = $cacheManager->get('integration_test', 'complex_operation', ['test' => 'param']);
|
||
|
||
$endTime = microtime(true);
|
||
$totalTime = $endTime - $startTime;
|
||
|
||
$this->assert($cached !== null, "Should retrieve from cache");
|
||
$this->assert($totalTime < 1.0, "Full pipeline should complete in under 1 second");
|
||
echo " ✅ Full pipeline performance acceptable ({$totalTime}s)\n";
|
||
|
||
// Test 2: Memory efficiency
|
||
echo " ⚡ Test 2: Memory Efficiency\n";
|
||
$memoryBefore = memory_get_usage(true);
|
||
|
||
// Process multiple datasets
|
||
for ($i = 0; $i < 10; $i++) {
|
||
$data = array_fill(0, 100, "test data item {$i}");
|
||
$optimized = $optimizer->optimize($data, OutputFormat::JSON, OptimizationStrategy::SIZE);
|
||
$cacheManager->set('memory_test', "operation_{$i}", [], $optimized->optimizedData);
|
||
}
|
||
|
||
$memoryAfter = memory_get_usage(true);
|
||
$memoryIncrease = $memoryAfter - $memoryBefore;
|
||
|
||
$this->assert($memoryIncrease < 50 * 1024 * 1024, "Memory increase should be under 50MB"); // 50MB
|
||
echo " ✅ Memory efficiency acceptable (" . $this->formatBytes($memoryIncrease) . " increase)\n";
|
||
|
||
// Test 3: Cache hit performance
|
||
echo " ⚡ Test 3: Cache Hit Performance\n";
|
||
$hitTimes = [];
|
||
|
||
// Prime the cache
|
||
$cacheManager->set('performance_test', 'hit_test', [], $complexData);
|
||
|
||
// Measure cache hit times
|
||
for ($i = 0; $i < 5; $i++) {
|
||
$hitStart = microtime(true);
|
||
$result = $cacheManager->get('performance_test', 'hit_test', []);
|
||
$hitEnd = microtime(true);
|
||
$hitTimes[] = $hitEnd - $hitStart;
|
||
}
|
||
|
||
$averageHitTime = array_sum($hitTimes) / count($hitTimes);
|
||
$this->assert($averageHitTime < 0.01, "Cache hits should be under 10ms"); // 10ms
|
||
echo " ✅ Cache hit performance excellent ({$averageHitTime}s average)\n";
|
||
|
||
$this->testResults['integrated_performance'] = [
|
||
'status' => 'passed',
|
||
'tests' => 3,
|
||
'details' => "Full pipeline: {$totalTime}s, Memory: " . $this->formatBytes($memoryIncrease) . ", Cache hits: {$averageHitTime}s"
|
||
];
|
||
|
||
} catch (\Throwable $e) {
|
||
$this->testResults['integrated_performance'] = [
|
||
'status' => 'failed',
|
||
'error' => $e->getMessage(),
|
||
'trace' => $e->getTraceAsString()
|
||
];
|
||
echo " ❌ Integrated Performance test failed: " . $e->getMessage() . "\n";
|
||
}
|
||
|
||
echo "\n";
|
||
}
|
||
|
||
private function generateComplexTestData(): array
|
||
{
|
||
return [
|
||
'routes' => array_fill(0, 50, [
|
||
'path' => '/api/test/' . uniqid(),
|
||
'method' => 'GET',
|
||
'controller' => 'TestController',
|
||
'middleware' => ['auth', 'throttle'],
|
||
'parameters' => ['id' => 'integer']
|
||
]),
|
||
'containers' => array_fill(0, 30, [
|
||
'service' => 'Service' . rand(1, 100),
|
||
'binding' => 'Interface' . rand(1, 50),
|
||
'singleton' => rand(0, 1) === 1,
|
||
'dependencies' => array_fill(0, rand(1, 5), 'Dependency' . rand(1, 20))
|
||
]),
|
||
'metadata' => [
|
||
'generated_at' => time(),
|
||
'version' => '1.0.0',
|
||
'environment' => 'test',
|
||
'memory_usage' => memory_get_usage(true),
|
||
'peak_memory' => memory_get_peak_usage(true)
|
||
]
|
||
];
|
||
}
|
||
|
||
private function generateReport(): void
|
||
{
|
||
$totalTime = microtime(true) - $this->startTime;
|
||
|
||
echo "📊 TEST REPORT\n";
|
||
echo "═══════════════════════════════════════════════════════\n\n";
|
||
|
||
$passed = 0;
|
||
$failed = 0;
|
||
|
||
foreach ($this->testResults as $testName => $result) {
|
||
$status = $result['status'] === 'passed' ? '✅' : '❌';
|
||
$statusText = strtoupper($result['status']);
|
||
|
||
echo "{$status} {$testName}: {$statusText}\n";
|
||
|
||
if ($result['status'] === 'passed') {
|
||
$passed++;
|
||
if (isset($result['tests'])) {
|
||
echo " 📋 {$result['tests']} sub-tests passed\n";
|
||
}
|
||
if (isset($result['details'])) {
|
||
echo " 📝 {$result['details']}\n";
|
||
}
|
||
} else {
|
||
$failed++;
|
||
echo " ❌ Error: {$result['error']}\n";
|
||
}
|
||
echo "\n";
|
||
}
|
||
|
||
echo "═══════════════════════════════════════════════════════\n";
|
||
echo "📈 SUMMARY:\n";
|
||
echo " ✅ Passed: {$passed}\n";
|
||
echo " ❌ Failed: {$failed}\n";
|
||
echo " ⏱️ Total time: " . round($totalTime, 3) . "s\n";
|
||
echo " 🧠 Memory usage: " . $this->formatBytes(memory_get_usage(true)) . "\n";
|
||
echo " 📊 Peak memory: " . $this->formatBytes(memory_get_peak_usage(true)) . "\n";
|
||
|
||
if ($failed === 0) {
|
||
echo "\n🎉 ALL TESTS PASSED! MCP System is working correctly.\n";
|
||
} else {
|
||
echo "\n⚠️ Some tests failed. Please check the errors above.\n";
|
||
}
|
||
|
||
echo "\n";
|
||
}
|
||
|
||
private function assert(bool $condition, string $message): void
|
||
{
|
||
if (!$condition) {
|
||
throw new \AssertionError("Assertion failed: {$message}");
|
||
}
|
||
}
|
||
|
||
private function formatBytes(int $bytes): string
|
||
{
|
||
if ($bytes === 0) return '0 B';
|
||
|
||
$units = ['B', 'KB', 'MB', 'GB'];
|
||
$unitIndex = 0;
|
||
$value = $bytes;
|
||
|
||
while ($value >= 1024 && $unitIndex < count($units) - 1) {
|
||
$value /= 1024;
|
||
$unitIndex++;
|
||
}
|
||
|
||
return round($value, 2) . ' ' . $units[$unitIndex];
|
||
}
|
||
}
|
||
|
||
// Run the tests
|
||
$tester = new McpSystemTester();
|
||
$tester->runAllTests(); |