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