Files
michaelschiemer/tests/Unit/Framework/HttpClient/Curl/MultiHandlePoolTest.php
Michael Schiemer fc3d7e6357 feat(Production): Complete production deployment infrastructure
- 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.
2025-10-25 19:18:37 +02:00

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);
});
});