- Add comprehensive health check system with multiple endpoints - Add Prometheus metrics endpoint - Add production logging configurations (5 strategies) - Add complete deployment documentation suite: * QUICKSTART.md - 30-minute deployment guide * DEPLOYMENT_CHECKLIST.md - Printable verification checklist * DEPLOYMENT_WORKFLOW.md - Complete deployment lifecycle * PRODUCTION_DEPLOYMENT.md - Comprehensive technical reference * production-logging.md - Logging configuration guide * ANSIBLE_DEPLOYMENT.md - Infrastructure as Code automation * README.md - Navigation hub * DEPLOYMENT_SUMMARY.md - Executive summary - Add deployment scripts and automation - Add DEPLOYMENT_PLAN.md - Concrete plan for immediate deployment - Update README with production-ready features All production infrastructure is now complete and ready for deployment.
268 lines
8.1 KiB
PHP
268 lines
8.1 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
use App\Framework\HttpClient\Curl\Handle;
|
|
use App\Framework\HttpClient\Curl\HandleOption;
|
|
use App\Framework\HttpClient\Curl\Info;
|
|
use App\Framework\HttpClient\Curl\MultiHandlePool;
|
|
|
|
describe('Curl\MultiHandlePool', function () {
|
|
it('can be initialized with default concurrency', function () {
|
|
$pool = new MultiHandlePool();
|
|
|
|
expect($pool)->toBeInstanceOf(MultiHandlePool::class);
|
|
expect($pool->getMaxConcurrent())->toBe(10);
|
|
expect($pool->queueSize())->toBe(0);
|
|
expect($pool->completedCount())->toBe(0);
|
|
});
|
|
|
|
it('can be initialized with custom concurrency limit', function () {
|
|
$pool = new MultiHandlePool(maxConcurrent: 3);
|
|
|
|
expect($pool->getMaxConcurrent())->toBe(3);
|
|
});
|
|
|
|
it('throws on invalid concurrency limit', function () {
|
|
new MultiHandlePool(maxConcurrent: 0);
|
|
})->throws(\InvalidArgumentException::class, 'Max concurrent must be at least 1');
|
|
|
|
it('can add handles to queue', function () {
|
|
$pool = new MultiHandlePool();
|
|
|
|
$pool->add(new Handle('https://httpbin.org/get'));
|
|
$pool->add(new Handle('https://httpbin.org/uuid'));
|
|
|
|
expect($pool->queueSize())->toBe(2);
|
|
expect($pool->totalCount())->toBe(2);
|
|
});
|
|
|
|
it('supports method chaining', function () {
|
|
$pool = new MultiHandlePool();
|
|
|
|
$result = $pool
|
|
->add(new Handle('https://httpbin.org/get'))
|
|
->add(new Handle('https://httpbin.org/uuid'));
|
|
|
|
expect($result)->toBe($pool);
|
|
expect($pool->queueSize())->toBe(2);
|
|
});
|
|
|
|
it('executes all handles respecting concurrency limit', function () {
|
|
$pool = new MultiHandlePool(maxConcurrent: 2);
|
|
|
|
// Add 5 handles
|
|
for ($i = 0; $i < 5; $i++) {
|
|
$pool->add(new Handle('https://httpbin.org/get'));
|
|
}
|
|
|
|
expect($pool->queueSize())->toBe(5);
|
|
|
|
$completed = $pool->executeAll();
|
|
|
|
expect($completed)->toHaveCount(5);
|
|
expect($pool->completedCount())->toBe(5);
|
|
expect($pool->queueSize())->toBe(0);
|
|
expect($pool->isComplete())->toBeTrue();
|
|
});
|
|
|
|
it('executes requests in batches', function () {
|
|
$pool = new MultiHandlePool(maxConcurrent: 2);
|
|
|
|
$urls = [
|
|
'https://httpbin.org/delay/1',
|
|
'https://httpbin.org/delay/1',
|
|
'https://httpbin.org/delay/1',
|
|
'https://httpbin.org/delay/1',
|
|
];
|
|
|
|
foreach ($urls as $url) {
|
|
$pool->add(new Handle($url));
|
|
}
|
|
|
|
$startTime = microtime(true);
|
|
$completed = $pool->executeAll();
|
|
$duration = microtime(true) - $startTime;
|
|
|
|
expect($completed)->toHaveCount(4);
|
|
|
|
// With concurrency 2, should take ~2 seconds (2 batches of 2)
|
|
// If sequential, would take ~4 seconds
|
|
expect($duration)->toBeLessThan(3.5);
|
|
expect($duration)->toBeGreaterThan(1.5);
|
|
});
|
|
|
|
it('tracks progress during execution', function () {
|
|
$pool = new MultiHandlePool(maxConcurrent: 10);
|
|
|
|
for ($i = 0; $i < 10; $i++) {
|
|
$pool->add(new Handle('https://httpbin.org/get'));
|
|
}
|
|
|
|
expect($pool->getProgress())->toBe(0.0);
|
|
|
|
$pool->executeAll();
|
|
|
|
expect($pool->getProgress())->toBe(100.0);
|
|
expect($pool->isComplete())->toBeTrue();
|
|
});
|
|
|
|
it('can clear all handles', function () {
|
|
$pool = new MultiHandlePool();
|
|
|
|
$pool->add(new Handle('https://httpbin.org/get'));
|
|
$pool->add(new Handle('https://httpbin.org/uuid'));
|
|
|
|
expect($pool->queueSize())->toBe(2);
|
|
|
|
$pool->clear();
|
|
|
|
expect($pool->queueSize())->toBe(0);
|
|
expect($pool->completedCount())->toBe(0);
|
|
expect($pool->totalCount())->toBe(0);
|
|
});
|
|
|
|
it('handles empty pool gracefully', function () {
|
|
$pool = new MultiHandlePool();
|
|
|
|
$completed = $pool->executeAll();
|
|
|
|
expect($completed)->toBeArray();
|
|
expect($completed)->toHaveCount(0);
|
|
expect($pool->isComplete())->toBeTrue();
|
|
});
|
|
|
|
it('can execute single handle', function () {
|
|
$pool = new MultiHandlePool(maxConcurrent: 5);
|
|
|
|
$pool->add(new Handle('https://httpbin.org/get'));
|
|
|
|
$completed = $pool->executeAll();
|
|
|
|
expect($completed)->toHaveCount(1);
|
|
});
|
|
|
|
it('handles many requests efficiently', function () {
|
|
$pool = new MultiHandlePool(maxConcurrent: 10);
|
|
|
|
// Add 30 handles
|
|
for ($i = 0; $i < 30; $i++) {
|
|
$pool->add(new Handle('https://httpbin.org/get'));
|
|
}
|
|
|
|
expect($pool->totalCount())->toBe(30);
|
|
|
|
$startTime = microtime(true);
|
|
$completed = $pool->executeAll();
|
|
$duration = microtime(true) - $startTime;
|
|
|
|
expect($completed)->toHaveCount(30);
|
|
expect($pool->completedCount())->toBe(30);
|
|
|
|
// With concurrency 10, should execute in ~3 batches
|
|
// Much faster than sequential (would be 30+ seconds)
|
|
expect($duration)->toBeLessThan(15.0);
|
|
});
|
|
|
|
it('preserves handle state after execution', function () {
|
|
$pool = new MultiHandlePool();
|
|
|
|
$handle = new Handle('https://httpbin.org/get');
|
|
$handle->setOption(HandleOption::Timeout, 10);
|
|
|
|
$pool->add($handle);
|
|
$pool->executeAll();
|
|
|
|
// Handle should still be accessible and have response info
|
|
expect($handle->getInfo(Info::ResponseCode))->toBe(200);
|
|
expect($handle->getInfo(Info::TotalTime))->toBeGreaterThan(0);
|
|
});
|
|
|
|
it('can reuse pool after execution', function () {
|
|
$pool = new MultiHandlePool(maxConcurrent: 5);
|
|
|
|
// First batch
|
|
$pool->add(new Handle('https://httpbin.org/get'));
|
|
$pool->add(new Handle('https://httpbin.org/uuid'));
|
|
|
|
$firstBatch = $pool->executeAll();
|
|
expect($firstBatch)->toHaveCount(2);
|
|
|
|
// Clear and add new batch
|
|
$pool->clear();
|
|
|
|
$pool->add(new Handle('https://httpbin.org/get'));
|
|
$pool->add(new Handle('https://httpbin.org/uuid'));
|
|
$pool->add(new Handle('https://httpbin.org/get'));
|
|
|
|
$secondBatch = $pool->executeAll();
|
|
expect($secondBatch)->toHaveCount(3);
|
|
});
|
|
|
|
it('respects strict concurrency limit', function () {
|
|
$pool = new MultiHandlePool(maxConcurrent: 1);
|
|
|
|
// Add multiple handles
|
|
for ($i = 0; $i < 3; $i++) {
|
|
$pool->add(new Handle('https://httpbin.org/delay/1'));
|
|
}
|
|
|
|
$startTime = microtime(true);
|
|
$pool->executeAll();
|
|
$duration = microtime(true) - $startTime;
|
|
|
|
// With concurrency 1, should execute sequentially (~3 seconds)
|
|
expect($duration)->toBeGreaterThan(2.5);
|
|
});
|
|
|
|
it('provides accurate count methods', function () {
|
|
$pool = new MultiHandlePool(maxConcurrent: 2);
|
|
|
|
// Add 5 handles
|
|
for ($i = 0; $i < 5; $i++) {
|
|
$pool->add(new Handle('https://httpbin.org/get'));
|
|
}
|
|
|
|
expect($pool->queueSize())->toBe(5);
|
|
expect($pool->activeCount())->toBe(0);
|
|
expect($pool->completedCount())->toBe(0);
|
|
expect($pool->totalCount())->toBe(5);
|
|
|
|
// After execution
|
|
$pool->executeAll();
|
|
|
|
expect($pool->queueSize())->toBe(0);
|
|
expect($pool->activeCount())->toBe(0);
|
|
expect($pool->completedCount())->toBe(5);
|
|
expect($pool->totalCount())->toBe(5);
|
|
});
|
|
});
|
|
|
|
describe('Curl\MultiHandlePool edge cases', function () {
|
|
it('handles mix of fast and slow requests', function () {
|
|
$pool = new MultiHandlePool(maxConcurrent: 3);
|
|
|
|
// Mix of instant and delayed requests
|
|
$pool->add(new Handle('https://httpbin.org/get'));
|
|
$pool->add(new Handle('https://httpbin.org/delay/1'));
|
|
$pool->add(new Handle('https://httpbin.org/get'));
|
|
$pool->add(new Handle('https://httpbin.org/delay/1'));
|
|
|
|
$completed = $pool->executeAll();
|
|
|
|
expect($completed)->toHaveCount(4);
|
|
});
|
|
|
|
it('handles large concurrency limit', function () {
|
|
$pool = new MultiHandlePool(maxConcurrent: 100);
|
|
|
|
for ($i = 0; $i < 20; $i++) {
|
|
$pool->add(new Handle('https://httpbin.org/get'));
|
|
}
|
|
|
|
$completed = $pool->executeAll();
|
|
|
|
expect($completed)->toHaveCount(20);
|
|
});
|
|
});
|